First step in splitting up mon-util.cc: move monsters class methods out into monster.cc
B3OWKMOUWIMPAP3XJONELQEVD4DNJWKCP4AYZLBATG3ZD3RSBWKAC H66666WMIXTHJPRGYYCZ5QDU5PT6BUZ4HBL5RZXYFZNKK7AJMQAAC 5Q36ZEQOSXSEWDWYFPS4XHS4THVGGK3JYW4AI3XAYJIYO23CCEWAC CGYF2YJC22BLBJEOK45UCSMCKRHL2NV7GR4XB2EWMOX4ACXBOW2QC ONHQBZXANTHQXMYSYF37EH36FQ6DULFQGVRWJV2HL62YCKYDWMWQC IS6KAEX3ZWJ5FXBICZ6UDPQHO5GETY6ZSMEMF724HGRWHZ4DLUJAC BIZEI6WKSSRDFGK3DUKYIVWIQPX64ZJIBCGMJJNCPYTZ2ER45VZQC T6XAZ5X4H4564QKNW3ULO755XABBOZ5UVCVQQMFNW6UKTDZM4S5AC OFC3GMUNJAZJ5HYA5IXDBAYWSCFV3JQPENIRJSTOWFCPZKGBIMHAC NIOMOMNSFC3XOVHKDG5QKKG6BKCDGKU2Y5WDHK67M37CH2XYNAWAC AFCCNZXCBAIBV4ULSGN266NNH6IBJT54KNL34P46DFLHVA4E4NVQC 5SLG5AQNHES5TBIY4PYS3C52L5NCUHK44Z5WIN32CXS6KPPSHQRAC IZY4YCDATABY7QMPPJYJNO7IO737FZKYESLMDM4IWEWX2B4QOI6AC OJSHASLCO3MS5WZKQ5CD5CUNP6LDOJ2BM7RILHFJPQYX2GUG24IAC AQAOT4BL3CFACM2QHK4WNWYEOSSS2DIZ6VRAYM46H6TYDX3M7OKQC FUONWEYHKTB3ETOKAG6DZ3KYNYOESYEXPGILZBOU77MLEHHAEIKQC CDLHTFFGQB5C2XVHMC76QBPJDZTUGTQ6MS7HC6FFEUASN7KX2NYQC NIXPEWO5FHL4MO24M4NVSBCBCRUD7QTABGMS3FPVGSWGWTA5XFJAC 7GA2VVQJWMYVRXJK4A4L3QLI54LOVRUDCTAFOR2IJZ63YEJWG3SAC JYKJ6GF5KJM3BU4E3DKPAFNS5XB3HN4W5RURRPMIEVZ56A33OVTAC AQT6I5ZEO6NMWIXZBIGOHWUL6ATQHCWCYWBK6UCA666FNFPKQZVAC FOQ6C6XD4VIKHAJFFCHKHZUGVZXAGVRRY73PAUPNEJV5K7HDBEFQC X2NUZNXD2GDH3WCUMAH36O76A2LXCY5EH5TGNMYH6GFDSECYFN7QC ZUEGFWBXFOP5CA5V4XJ5MK2XNOOVDZGUN52VVWEDFXPO3M7NY5NQC KD3AVD5P4W65HRS7L6L77LVQKLRSHYIF6OZNV2X5WBUDBXB6L2NQC XUQWVPZUX3S7W3XGMTNV3OCFMDMX3RBWTU4P4L4FDRBGU4LQWWEQC FHF7FH3Y2LBOA4DXSZRO6TMU5Y7SVYCL34Q4HIFJWIZLWQJ4OOLAC RFEKOHDIJ2SQRFFIP2BSH6T4NH6LX5ADQ7I2PH6BLJHN647QQALAC MBSV5OC4EJI2ZUJWPLGJHQC7UWV4O5UPXEP4PQ7YPSSDETBA3GSAC UNLRT26CCVGNU6PCQ4PVFWUDKFDDM5GLD6XJEIPALB4AYZT4TJ6AC JWGQZB23VQY3SPZQC4ZK7BGLCWZLRSRZDV4LQKIAOJ2QQSLIJVUAC HJKLGY5GAJHOOOLOUFMVKBXRU2OQ73KNE5EFHPHYIIWBLRFV4FFAC RPIWLMIOQFZW64K4YLXVNMNJYDD2NV54KAZAS76ETENWPVPV3MNAC RWOQMFFOYB3OFPVLZ23ZR2TUS3LAQQ43OOPDICOFWBIQMSJ7PVHAC QSKIUCSJC34ITLX62XJFMY7XRMYV34A55EQLBWTBNCH2C7GTVLIAC 2FH2OM6NKQFEAWUCCMW7VIEAMM2SY73K44DRZFR55LVHKFO6G3QAC NQ67EKSPR7UD7ECHWTR2ETD5QFMSVTPWZP5HR422IL2Q37T6SCPQC PSXUCI5JHGF6TWLY337H4UGUATVDT56IVEJVWZJPZVP2PJVNKNVQC CKW4W7QR3YDMI2MVHVCMEMH32UTZTMP2BMX4R7LGHOHWP4FFAJZAC XA6TJQ2VJAUQ6XV7CP7PYSVT2HPIZMYK6UNCMVJT3TFL3XRJEH5QC JNOLNCW6C47SDZT4VEVL7FA3N36AXNZVK5MLANRUTQR4UMBNDDDQC ZKFFRALOU5LG3WVK6AGYLNDXUWJ3F7URPBSA7US5SLL3EI4WAINAC WGA75GQBRKOP5GGHJG5X3KNVKU7X37ALXKKZVKCT2YTTDB6HDMNAC OA23BLZCR5RGLIEHO3J3QQDH4UP33ZTDECXO3ITMP5TUCELKJT6AC AQI2P3AGZETQQRMRDFN3PNU2JTXZ2HF5OJATYTDQTH2GTXD7LKCQC BN36PI6TPFMNYH2JFMFKA5M6BD44ALRGQAKALX722IWU5EZ5EBSAC FSFMUEP5UMJELUKLFTSPWWRWPNU2JBTH44CPS43OX2QLVP4N3GAQC SELTLQ4D34XCM7XJKSUYJ23LMXLHZAESVZGF3IAR2PIVFQ7WNBAQC TPNG7I4B4NN45RAYTE2SUREIMMDKLOJ57FEPXYUN54BWHJLX63GQC A364QLVFT7SBLQD3EFDZVSGTKRM5Y2K43O5AL2RKLP7WIQKIDRPQC BQ57EIEGCFQKJJYB7QQMW6J2CLMWG6XDDF4WEOCFW65BH3A7N7AQC NCVF5EG2MP256AHA625T7QBR3AYJCDA54Q2AUFMVY4YGST5VODMAC DPOIWNVHK52JHZ2ALGXBPMEMBM4GFZFBOVKRUFHLOW4XQ4AFDARQC DGT6UXMT3G66SGPZF5W27RAPVE5YLSM3OFSNBEKVLD4FVADSYCPQC P7SGMEJBXTRMZ52725CAFE2BY5ZMRAYZUGI2BZNTG6F3UXHS3LRQC LSBOXDI2ZYCV7MW446Q234JPHZW7B6VEUDI3W2E4RJX6MMHD4FHQC JN4O2GY2OOLF2LBQT4GJBY2DCOEO2Y7DYZHBDMWY4OCNG2GQZHUAC QNIVOT7FCTRM5EQ3MYMMTSNO5KR7IF7QVBQZ5F5TSJ4SCLCOVPZQC NPHMGUOC2SFA2UL2BX7WRSSD4XQE5Y7WE56QZGBDZNKA7ABY4HBQC YBNZTFKEZDBA2BWBYEVU62CIVIVTA7LYIRJAOYKGQT7V64DAMZUQC 4VQFVECMLXMCHJR6V3FLI6J67JT7VTKG72YAVANCODQQHZD7FNHQC KHLKT4LQPR5FDZD7UA5P5JX2QFELFFJ7343TPDTYZ4GE57Q4VRTAC UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC LCEYOHJQL5GHX3V2S4QF3UMA2ACIGT2WYXCM6QBEMATCDMGAKHRQC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC KOH7BGYORAFAQ5UL5ACMX4X3JGAQFPNBLMKHAWTVW726S7NVB2KQC T733XSIG74E7A6ELESROLHEVNVTTRPMVAWPAG5NYJ73V63H2GWJAC 75OSTCYAQI6PNHPJIL7DCP45PRMYPK7WADGJWS7ZCH5ZAD6Z4DNQC UA5DAEP7KCACHMACVVL2KGE6XNUCWLOUWT6MS2HOZ4P4LQAAV6OAC QSSUOKAA4I7BVCCEQRSSYXNBABJ3DCKOBXNNCB5MJDXXN2Q2KGGQC IE3INS4WUXZOBVXB5VWRBYPVPXADD2U2W5H3TBTOYNWJ3EGRSGQQC MFP3WAGO2JIOT4QSEYPM4OJV5NVXMUOPL75OIE7YZ5RWOYQPIZ7AC IAAPG4VMQN6O4CZYNR5QA7MXWVII5USVV2HRX2MDAF2GLDGRUZGAC DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC 5XSXMOBGXFLTIQE6WDXWWFVDOTUPZSIQ2FWT3YI5QMVU6D76IUYQC NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC SNH6UWOYVWP4A6B4NC6YZ6KRYPYCGGNYCW5TILTALPH552H2EUFQC DCZMEKDHQWSQCQYQD6ZXB3XOMWLVLPSPVSBVVMPXMSZF7GO3BVCAC QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC U3UHWTM2LW64BR5DEBMMZUIGXGS2H3IYSGTWYI5ZU6LD4WWYKRPQC G7CTMQ3VNTAB73ZI3LNZHKTAJ5LEQEGG772MVFQQ5XXLCMJVORTQC ITQ2EXDFLN3EHCURKYHZYVWFACD4KW7NA6CMHNKGWTDKHCHQAE6AC 7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC 7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC RKFHYYANUZQHOWOE6YHVFFJNCZU7X67FWUQPQFF6KJKMNIKXFWEAC 3GH3RH5QFHWAELRYSRB2BAS5TEN6SYRXLNZHN65HPQCJWILENGYQC SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC ACZYEIX7WMPIIODKCATBCUE626AJ4ZGGBOMVC6BGXM27EQU2RECAC R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC LUSG5HYFRMFNS3RACRCUJNQXJNXPL4SYKHHOIEG5RBQG2DKQXPDAC S7KC3IFKCUNBO3XO4TV3KEQEIDZWSRJSKXY65DPONKMG54VHU3UQC Y2NYY7HWFZ2LQDK3ACSLGS37F2J2IJ5LRGCIMZYXLEOSVPD3A4DAC JDZCDMUCQ7VENYVLB62KQWUIRGABID37OKATOKCBUPCDL7W75NPAC PBRFKFYILME3A2YLLVGRWJ2C7PCE2GONSINX65P4EG22ZGCW6JWAC HQTS3VQ6PNUZKHNVEWMML7JNAAAKL4DSW3KFSRCCSLGYTDHZFCCAC PTJJ6D52LDVJN7WA7NMYV2WHH3WJMWWLRGF3D7ZSBUNLAACFRJHAC KS4WBDOLZ45T5Q742JZQIRN3H7XZTYDPM4TCIAILIRO3OV7ANR2AC 7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC ESK6YA7PCV7HIFB2GVYYVBR3GAWFMPX26O3MAAF5TW4H6WI7CWBQC 3NOOJQ5IEPEVLGBQOP3M6M6EHD6ONWV2XCWDQXLEHXIXIJVNS5XQC UWI3ZNJDRQRL7CXFNFEG46TA6SAK24XUFY5YOKVOTJA3VG3OBNFAC G3CXFOBNYSTKZQPTYUJ5YXJUCSFZKNA4RR4KAT77AH6ATTG4TONQC TJ72NVTRNJKF55RTH5MRLTHHVPOJ2M5OOYEWF4UFLTO2PPPTU6PQC FAVME2A2U4OUKN2BTISAG5PCI5Y4BN6YVFKCSQQHHZLEVVVF4LKAC WVKP2MFDE56ZO3OI7FCEKE5LI3MAUIL6IDTUW6MEBCV5OWF4OKEAC PNFDSBFCGH5IZR7QNF4DQTEV4QXIIO4DWYTSFEOUWLZJVSWNDKAAC HGBHRHXFVTEWYYV2D5HKTKL4Z6OSNR2ZOIHFQX55CVRCXQUQ7MQAC 7JQHIWUPK6H2G2WEDGSRL2ZQY4D3AH2VK7BKTV27DZWZGLFULJCAC ETJXWTOUQEK2BECTSNQOQN2GQO4U5GMSCEIOVNW4KY7J3CLEUWCAC GD5LOAMSEHYWQF263PXVBF4UWTYMYAXLXRLSI26PVMD6IDYUSWGQC 5Y5ZZNLJPTZRBBQAIZE3YTRJ47BZB7SY52GWX3U5ICR5IG2AO2DAC RSIUBEQUGNU4LO6KH4PKVROWQS33DAKSY4XFVGN7T3CEKSXABCSAC 33ZMPQC6OXTESW7SRW765GRNJUEJRSYONRVZVIEUDAUEJ2PPMB4AC 6AOBHPEVZFUDSTZ6FR6PRQ6H2V766R2PJRXS4UMYI6SAELGAV5EAC YWSN3SJEZRDX223KTSDBR7IU5Y7Y7SSKQWTOULWRHQV47UK5M5RQC H6VLPIMXKMZVGZVQFUUSW73LRHC3KTIFE3JNSCZ7PJALXP62RIGAC VBG2GGMVC66LQM4OSI67VKXGAQK4GVOEHX3OL6V3IFOO52MQL72QC CVYS2M2XNT3GLAYJB2FJCYLONILJ35TPZTICJGYTGRBXHDMUXLVQC ADIVVYTV2MJ3XVRYDNBLPTAEACCNF27XZDCRVZFQEHRHPMZGNITQC BYRUSOCOJ4C3WE6JFEMYZ25ZMOXWPNGUMTBLNBWFVYPN2TW2TFPAC PSCYVKJ7DGXAL3V5U4O6AJTRV6Q3N3SHQWAZ73VIPRTE4W64F2XAC E5DMZFW6WCFAKTKKOQPYTQXZ2CGLWMVH64LRXDUI2UIG4VYUHIVQC FLAGBNUNSIQNFDN53CDWABJRTTFWDL4PG34AI474ZKPXDEPYHOAQC AY7Z23L4WPEFEL2HMF3KX3IM6OYMLB2QWY3AIHHQ37CMRVCXRCMAC CIPVRZGLOZHCERK6YPOBV3P2E4IAB4H6D5EHLRQE2O5E4P4VCBUAC YYN2QAKGLLHXV5AITZXFAZZDGLQPDXDEPWTSHFF4ZQU7IO6RZFBQC 5G724WYHCSV474ZLORSLGN35R6OHTTLUPT3XLB4CXA5AXTBS6MUQC TYCM7ZQG5JPPK4A5K6IBVRKJYQS2F2A3SESBC53MUNXHMZCKYF5QC VMMUZXOQDXW5ZHA7623F744LS7U4BVLBKW5TNZMG5L2Z44UZGZRAC 5B36ULBPPMKZJBURDAAVN4VBMKY3PIZIIDY3LAZYSIVTWGW4CRIAC WQLOHSNCA3VOMDJF6IINJYKSYVYZEBPJJWBB33QSNE4RP5HEXPMAC XZTGZ7MOPX7ZOHQ4IQPXOHGUH5WPRNOYAQ6IQOMZDZYNVSAYFTNAC PKXXBHS3LWLPZI2QVRX22MSQ4R2626IXRSNHFFYHXYTLJJQU54LQC 5JS3QSE3EIXSBVI4DATH2EIFD7QN3POAFEUM7MK4NRMPH5JOPAAQC YZL4IWIOMIZ3AF3UQEQUAPLCXT2Y7IBO6HWPDC27RNGAF3OCNWLQC GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC KEANRIMF5CGFVZ2XJYNFPOAKLXOSOJUOVA73IWBWOG576265ERHAC ZNMUUTINYIFCCBFSQDKTTXWXEGAXLDCGPX47NK3Q7NUHRH7S4GXQC LL7ZA3RH2A6WGA7YD7XWIZFYG5QNFNSA3UMSUFUMIHHSJZ3SWR4QC H4WHW2YDD5VBZNBL5UNDCAGIDG6K4IVSMN72PDF3PMUFOGPJBUYAC AR3VHZCH5RGEGEFCV2SDOHTTA4WEE4UT3RC3NFA5QNAMGRZTGUVAC CQKH4FU2BZOMP5VWWOH2CEWDAYOV7FV53VOFQRF7R64DCQMP73LAC N6J4ANT3WFBQYTFDPZFVF2PWS2S7CK4GHHX4UV3JW4VWAKETZR5AC 2LJ3PLU4DNUUCK7SYPTK4G4BTDMJ6UEKOKWML6WOIPJKWLFCD5FQC K2MLPJIAXXZRWEWZCNSGICCBNIU2WAAPT7SPIMOH7FLLTOB4QFRAC RX6575DZOHRUXQUZH34YZGPZJF4STUPLBQDIVTINA2L6LVCKRIGQC CGYTZT5QWIEGYKUOLOK7MFXSLJKLYRZONER5ZCDZO5XYWSLG475QC W4YW6B4WTSMU4E5TXG27A6C2ZH3WASWOKCEMVHP7KEF2CZDLADJAC SLS2XJULEDJ4THKXXVQ2XPTO47773EYMG4FPIRDN63BXY3QLMSLQC FDHD7V6HABWK4GZUYVHXW2GOCWCMDXGYQKSGSDGBV6AKO2RIYLDQC 53BVN4ZHKGUAJE53HM6FC6GKUGXOU5QBUT7CLXV2ONS6S5NPW55AC QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC 3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC VEHHZZFWDG5M4IXTCXXMKH3S2FSXTZ4CD23J6AYVFEPY7PBZ7GWQC SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC 3DO3LBKJRTDOI6MBMVQGH6KESDFKRYMNIGKDTCWCMPACRMWTQYIQC EMOBSWJHHB4V6WVMZL7JCF2V3KZN454Z6NS346OKFPMBNO24EJDQC LH4OYDEWEON5QNUCM74R7MNBJDP7O4XRETY74ZMYSXUXYQV427PAC P5TRGRH7XMQSPCZKM5IEEO34TY6WMLGHHX7BU6Y453JFRXLUR2VQC OONYLF4DAPLIYLBNNRW74IVT5BBTWI4XHQBXSNSPVRX3FTKJBTRAC E335DPK7M5WBYKN5C3KG75KY75CVXLN3XTLK55MADVZMHW74AHGQC RBAGQ2PB7V5YAM5KSHSZR2E3MLKDSRVM5XYGI2TIXP5QMVBOQHDQC 6POB4N3ASH4YL2O5ZWCQ5X2RURGTC2KXX7R7IKCJT6ZV6VKQKJMQC X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC 5UC5TI7B6NWGIGN74QRBZKDBYUMHLM2ZO5ATXEZ6TZOGOLF3JX5QC DAQ2V7UKJZGPMSL63L5CU2BQ77QPTV4AV2EQAEIOBKTM7MROTNVAC F7Q5PX44SLPANIZXCY67TG2W5JTRVJMHGQW54VJLGB4XRH7R6JBQC G7DNYFW745Q567EF3TPR2FCQ4ATPN236ON7X5TLYC7TEPZW3BAFAC JFDTUUUT6AKIGPBKGYFC7HZKGQUZOMG7EUWU4QYKSDVXXPQBBFHAC IBITKTCFECTUF6E6WMQVWXQT5Z52O5B4IZBWOLP5DWXGUHGC62AQC LZA3TOLKZ5GDXITB4YW42PP6GGPKSVH75LO6QWGQT5WBYLFGKNEAC 2UO6ZOW7UCP5XJ2TJ26PJNQABILK2BZ4J3BAKLOKMZPJDQHW24MAC XJZL4V5XSLQHMETU66KRZNZNVWORAYXTQZ6O72ZGY2ZRX4WOQWCQC UQ4P6W76CTKR2MQNH3EWPS4QPDXE3YRLJRR6744ZJJ32XKYPXQQQC GLHWVF2JPHBL7NV3D3CNHPIQSHZQPREVUJT6ZN43OBKBX5HL5NKQC LHYTGOCNDWX3CVD2HSQ6LAYC6NLKKI6ZKKNWZ5IQWP6YP5PQEVWQC 3X2MBNOJZ24SJT2NIBQTLDOXHM3RTYRKZFZZY3MHZQGTLGTRAKUAC WMHFDQKUDCUGM3R245LLVZ5NNEZSCXFDSTNMVS2O5EFUHHO7HU3AC CG4TL4AKSN4J7CMATZFJ5N7PSDCQXLS3QYX7PZ3K67KMMBT675OQC OSRZEPPGBIMSZBWIVBTZTTIMV6TEUGVZRZ5AI2ZJW7CVZZQBUIMQC S5RANRFX6SRLISGDLFVHDWHWXZ4IIEMM23FMY4IHK5PZQBLRTTYAC AVCWYX6CU7PR3H5ZSEV2G3B7BSXPCD4ZYLSO2WPCHRMRPPXUKFAAC EHP6PYCIPYQ3KF4JFGBTZXEUQHN3FVAH4NUWEOWDDNKGPYVOTOJQC 3V52MSSK7QX7FWLLUW63DTWCBAJEK674EFZLKP45FLZ5KZKVARHAC SYOUKIZKYUNJ7YVII2XYYZXYDXEN5I6SB7OIUIPXQMBF2K4HZFSQC 247OW5JFN34ZV3GRVHRE25AXLLISUBPOX5YRGUE64BCM2V7QNEHAC UPA65AL4JXYLIHH4D42IWJHRTOAF2BPOVZOAKOXBLZBYIMDZDFFQC C67GX7W5HBCDPQJRSHWMLM4DUJ3ELFSHH42SVICFJVCJW25T5Z3AC ZJE4B4Q2EEUSOOYIOHQNLYJSAGCZRZJDPEBTZ72NPMFXIJ62366AC HE3D6YNIZOLTMT7TCUPL7GMHNYOUUF6B7357F6K5PKXTVDOEDI6AC 4I6DI5BZ4OTBYZCR2FT226PI3WJFJHL3YXPVELPDDMX4MHL5IUXAC 4HA53ET5DT6L6OXZ2GCP3JQ4WUJSXHRKHFDQ4SKOIVNGPTGIHQUQC BR4UJTJEP76VHWKMNCRIQAIBHH4XWRNTXYQU4POSNLMSND65MMHAC 6TQASFI7B552ZNCBVI7AS3MMMACNTGEJESTB74EFCH2MJGH5LW3AC BD5ZPI4AQYR4SHYGKH4FJ6XNNCGSSEXESF2HMD5WN4GJAJXAMGGAC 5DI5IBEYOMNFQOLSI4ZNFJCGG4CFYYEEMIGZNCA42ESVEIBTJLSAC 77KQMSMPKSU2HXCP5UDQJOM6ETXFCF2IYFM2EU566D5A7XE7AHKQC 3VE5TREDJXE2CEGBWLPWZKSVZVU24LYMGGSOZLGRMYCYSFDCEK2QC AVCMVFA3MKCXHO6H44UK5KJNIHTGQV7UA7GYXM26VI6TXXU5ZN6QC WLVGJN2ZH2PDADVF27PMRBQYFMWAZ7TB37BLUNOTPNYRFQ4Z3HKAC ZQ55TG7UDO6QYKNZGYLETDPGZ7TEWMCCT5S4IU4XMQOLTM6DZYHQC C3TZ3MSKH4LCCIOAI6IZNLHQT46X635ZE2OCNPEAM7VNL7QRRCKAC SQ4IRY3FV7SEPC76KP4E6XLEOJVMZQDPJAQD4ZNI2KL24PIP45VQC L4VKAB4CIIE77UFPXY3SUGDDL3PNKRBTV2OTB7Q5BANSQ52SJV5QC GGAYOBBKHIDQNAYPKTMEIRVMJ77LL7Z6NBPYCBBKJZOVI7RS2XQQC RWF7XNUEOS7QIYRHYSJDPBKVUFBYL42PWYVJZX3AV5SR7EQSNBOAC 3AZMOIK3TRC2S3KDUJHBT5W4DQPO7RU6CQ4MK4H2FUTW63EQ2FVAC NRQCYKOHMOFQDS5C57WRMZNGNDFH62F4TNMLAPVBGRZKP463JYHQC AKA32GXKN53M4MU66OADWNFCQ7CN75LPEM5ZEJ7TQVZ6OK4WISSAC JJULXW764V5C2HJKZNWQAEWB6QM5YZADD7ZCE35LYTBFEM6PMYCAC EU2UUHX7Q6BP4U3YQRXRPIWSRSNTXAWGQG7Q65G6LVQ4X5VFPDJQC QJDO7Q62UNFMTSTYP3BFRTJFOETOGTK3AJVN4YY23TNVX3OUQZ4QC 3XLRECYFMP6F32IMOZ6UHIFP7CL4ILMUURZC6YIGWZX3SGLFVVQAC S2TK5V6DRLNCLZGN4ZXHY6D2HU5TNW5VYUZW5F4QD64N534OAWCAC OZDLDECEY3ZLWC2J3ALQKXE7VXSOBSCSSDJQ7ETMJZG7FN2IBDEQC G2JEGJW7XN55HXKF4FRD6BJGXTGNMZFQHKHESXJOZFQMPW36UQGQC G7SEHMXQP4UDAMACBLBAQTUBB44WDVUCSFWGI37HTZ7UQ2YT7VXQC 2OY3EXIBFR22QCCKPZOA37YUI7CX7BEKRRYSDSBDKQN6VTDBD7LAC YCOIKIHIKDUTYCNQZASNG6FG2VY2YYZHCA435K7XECLXDZDCSXHQC W52V2LIYPIAH7SAOWAM5FZ326CJAS3T44AMMERLQP5ET2NGJOIWAC UDRNXPII6FQPOAR4YMKC4JOIMY3UA6MCTRZXPY645KU4P3M3ABWAC JXO53C3SJ5KPLXVEBFGFWCBI4EFY6TCA6QQVH5NII5NP5BC4EWUAC PP642B7OPNJ4WKF7X3UD6AP6OQYVM2C6NF73US3FC5MQ7JJZ3S7QC HUO3FHULSLK3Q7ASMA6D6IJFGHB5EZLY44OPDUBFPQZFOL6W3GBQC 7EIA4DGVDBJRNHEAN3DK6SNHGALSSEUWSOL57C2VPGN2IZCQ633AC UUY534OLJRLTQDHHEP2UAMDCPEFFL4BW63AWB5WZWCEYSAXPE6RAC ORZ4ZAHPUVIWAI6UODCKDS3TB4U4Y6GL3TYSNL3ALNBERG3K4UHQC G3HJCS277WA2VONPOJW6FAOQO7VHMSMLXZWYIXKFE4N4Y3OV7UZAC S4W5EBZLFX3HA3JNR524LF5RJ7PAPMSMN3FVRZNLQZY4QNEILGLQC IIK2W5J4NQWD3BVXPJDUCTONSF2ENXPSZBWMZCBC25RCTNMKSK6AC BSZCHUEYK4ZSTIFMYKIHBOJGMNYKMILJVHQH2EYFKK3C3OR2ALTAC J7XFP7WPZP43TDW7D6HBUMVTOPYWZTPU7PZU6TYEX32MFV5SVFLAC R3ZUGT5VJ2DG5NFPG4RBWDWTULDE7L4REYSGVPHJPDXFG6OBIXYAC L5W2SXCJEVQOLRIN2IQO3AITQGHAS7PYSL4I7M5BQMG7MYZ2AQQQC L254F6ZIU2HWGLFFGPIORTN4C3TDQ3E5JZ7Z7GQA5AEDIKL6PKDAC PMCHUVWMCDXOWGXB4SWMBXFGHJYZG7KZ34SE33HFUGHPQYP3LYEAC 3RY3A4K3ZCAJ235ID3ZDKFWYSYFD42AZOVAKL6767D5GH2QJTCPQC UASSVST2AY5625MS67C4FMEVQFXEVTG2SOIHW4AVWE22PLP27VUQC AIVXE6QBRVCZAASKQRZO6LBDGTYEYSSD2DZCWRX4VLSKE3GCNIDAC JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC KDRK6FPQK6PNQWCE7CZ2XEGPJL6ZQX4BFNPOIGZEPTI7JV4WG6HQC NDGNTJWFOIU5EYAJU75FRRUICLCVQAJJ65NJ33ZGWDEYZZECPQEAC 47NSOFQMBZCDIBHEAZSENFUGDSQCX3GJHFBUZ65ARDKCYIZ435LAC YX2LDGNQNB6AQRKAVXNYQ473X6EVPQEBT5AJKBIIWFIMS3U2BNQQC XE5FBYASPYLMBNHWKL5MRN7TQGIWR4O6PF72I7WXRTUJ7HS2O2LAC S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC TVC7W7C2XKBQSD2IJFMWFVGXZAOD4EUOW43NAQTOF5KFMAUOJABQC OSGS3PH2L5CBTDVZCZS6OCFQNA4A7RMEXBYJQB7DDZBYYJW7QSSAC ASH5CK6CPBKMLGGIRJ5GKTWMS5W3OBVHTL66RTYZIPFM6KFBYA3QC S7Y7E2KDAFMTLDIXUTR673SYL5N35VXYGLRU67L42WHVYG5SEPBQC HHJZKVBVTR2WTALW2HONZF4XSDTADKTWUJIQAFGDZOJZUGXIXOJQC HT3H2NAJLIUIZS2XKGODGDZPRGPCORKOCWSCTMZBRMIXJP4FEYIQC NRIZKLUO26UHNKB4IERXI6ECMD2IJYZACQNIUU3SH6BPLGHAJYVAC JW5LVDYDDQMYLQLEB4WS7V3CV7FTHW7PSADCNY5JOJW4ZR34X6GQC HQADYMHFLUXCNSDSPPZWGDMWYZPO24277OL4VKENIBC35FYU2M3QC KEWUUIWZCM2VE6WZX4BC4DXGL7LBAWGLUPCPA4KCBCUL5ZQLECBAC ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC UAJN2CFA2QHYDHW2UFAVPPHDQFCD54RKM6V2UC4AMEDJUBBLNWIQC LH2E37LV4XOP5HABYSS4PEJ3DRXMRI2R5SZ7J6P3E576PELY6GMQC 47WMN3S37XT6E2JM5S77CNZ37SD5IZKMSHINGY3CDY4QOOS7CLXQC TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC 4S7GTHOCXO5MLS3DKSZWUYYZFBVNHDG6RFJONUALXVA7CCPFKXKAC CWQGOO5TNRXAXGZ57JAQ7JUPZVV2QUOOYUJAIMMPOVS4REDJB4ZAC ACYR3YL7DTTIJP3N7ITQ5IRAWSRW23VQTGJHBDAATXW6LXMSA4AAC 3P6R443Q7BHDL2CB34CW5XNWWJ5RXQZUAL7RKVZCNRZPZRLTJS6QC BMF6Y2AW2UKQ4J2KKWZOF4MFRMIU4PJG5DT72D4H6ZUV56MXR7SQC 47RJZCYIM3B7IXJT7FFT6NBZZREY6REK5DZWKZ5E7G66BXEXFN6QC TRWPSFTO32PTSWMTZ476HJER6NTJIFRRMDSKHLJ7JLDRIOVQL6BAC VCQYSNAWZZHOZMARWQ4AJBDNFSS7T7CZBQISSPZ2YIIK5PVAWPRQC OXNDTSQQSTHSJZP5P75BXZBGPYGMZC46NHNE42ZULXSIIPAPY3SAC XKDPUWDOAFP4ZVWY6AOYPX3TJLTFBNZWRYSPIMEJHFX577WAJGMAC YPD42V7VOQFGAVLLOSQAQ4SD77L5L3LZHPLFVZIAOHBNDKPO3O4QC 6VXX5ATWAPN5W5NGQK2GHGWNOOAAFTMB2D4JDKPTAYGALS27SBXAC OWERGKLVPNPGIIS23FZ7ZDOBWUIXCKYAFG3URXU75JAUDX3N5ENAC NVK2OISSU4XF2CGSK2BAWZE2OPCLTXYHNSE6UDJ5M54EHY2I7BLAC MDBRMHGYSA46DMKAGSVFGUG6AJGPA54I4OUJP5Z262RVD7SZ7LFQC PS3OYZOHCD464IMPYTISQO2NDD4WLHZHKAT5TBB2VMAU2JBNPYRQC T7CUIVICB74342RA32BR37T36FOX4RBSQIB5PNOHTGTGUYGDKSTQC Q6QWWEURCCP5CRAMRDPFPMM7B6FLLOQZZMI553QKG4KEYVMXARCAC 6MGA75GVQUUYGF64YT4BPPYY2ZE7SKL44H3H66LIFMRT2VAONT7QC LFBNFE3PZBXTR2ROPKYPARUWLJAYWAKGTS7VBWADZWVVSJ5CLX6AC GRSLMD5WMJD2WEKMYORL5K5FBA6QT3QIO4FNQQYD5XFFCD3EOL3QC TTCA2KKE56DAAKEVUGAP7ZRNF5KDLOWRVO37E3D6KDNMLDBVL7MAC GRVGLW4QW6BMLK4E3HY7YMHFV2PWGV7BPVFMOTX5APYKCWNUELOQC HAM54HXIO2245W6REO4RZDY2QMIH476AWWJSMYAMSYYNEBBJSHWAC 3BJ2OOF4F524G6UKVGOZVT6W3FSTSHHTKRJADUBZCHDXZWV3KANQC W424WG7JEQN344GQAXTTPLLNYVQJZQJ5X4MLA4ACEVJYV6ECR45AC GHP3H4FU6XZTSZ7IWNOT5Z34GJL3BFRMARHPXXW5MJUQ4SOZ7ATQC Q7UVH4DI46F2E2KUPOTHZTAJKMMYM2UJ5KRUC5CJDZGR4AZCR52AC 4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC 6FOJLFMHFOIDE6F47OSS3N5PDPVP6MEBOP3JJ3QQ4RRGSDMVAE5AC Z43XGSUHRSSPIV5EUCKKXE3LVCEJMKON7WLJDQO2A2EXCM2T7RLQC GDISJTTXFDQYMOFLUHIDCYWMU7O2SGXN2YQ3VRUIHU5YE3SUVJ7AC P4SJCLGJJZ4HF5WTOKBJRKI2HFJYAGFAV2P3NWRKEYCJDLNRVU5QC 5ZV2CS7GWT4FJWA5WCIZ52T4L5OLNVWMNRCBWV3UCFPNPRK4ZQTAC Z3H73NQZ4GWOCQUAOSBV5L2DCEBGAMA5WDZ3ZO523PQANJZIRYXAC UBJ3K77N3ESM5ZHZAND4FDTONECSSB3NUNL6QQA7ATSESSCW5QUQC PC6K5OQF3BWPMWTIN5JXAKHTZF453JQOELDWRUOSBZXR256FTUYQC BINKDWGFGUPTOA7IE5KK4ZIELGU5WC3X47MYXOWU4X43EGAC5DUAC YNRHNJMZQVKSOW3HDFRSILEYMDN5TPDXFOXI4VXC3P2CSF5AUD2QC 6TEISZD7HYSSL24EOKIBNURU66KGSQX7B7SNAHBP4DQSAOTGH2MQC JGKYRZ34S3I23PMJX6IUBR7EHEFD6I4XXEGXNT7GKT2M2VIRBSMQC EHNSGDT6V6X5V2FLZZFSJWIUM5AAQF5KK3ZTSWO6TTWULPOOI4AAC ZUV76RGZDBOY745YXVMGFUOWEH2B47J74BCT5MDKHIDHC4URGMKAC UUQ5U2OURPGWPHSHKJPLB6L4BWULX7VYCOUV6RNBIGALAZTWFHUQC 2FHNLTLN3DVVD67N4QMAQ2LO7YY4GVZWXJAOIWGCCYYGIX7UHSVQC NI6NL67QHXLGX4BHOKGMYDAGYKEGCZ2ASSQT7CT633SUOHOERMVAC NWUWP5QCMJ7ZZMLCVB74PW7HDEFB6ICEQHNDHCGG7UWDQNNLGVMQC QYQKV4R47PTERXVFQNNWWQVICGSOMBHW6WM5TAZAKLIYOLLPUAJAC 542UIZKI65UDRNEMGFFDBWYD5XC7AYLTZ3JZQRR2GHYJALD3YY6QC IB4IOXRL7AMMYTUE7GKYDYI6FE5ZFDIUG7DXAZIRMNEPA7KVSSWQC W5VEC2PBIM5DMU5233HOWAZUEPTGWJRZZIA3H35YYQQW6BTP6XUAC HM6NOS7BN5665KWIFGBXOHZIDMRSVCESN72GMGI6NVBFZGCF3L6QC HMVUQMYFXCNIXVHZK4HKLE5VDBUNR72GYIYQ352EFES6QB7AL4KAC TV3ZC6WOZKSQQJQN26JIVKCHK6UK7WMDBYZDUYRWEAZ4JB4YVNAAC B3FDHXVBFR7YQF56JLQT2EYWRTWV6Y4N44GI6EWVWUU2KBNMZDCAC ZB6W7774FFHLI3AYC3ZTTTEFHR6MVMMMCX6GJDF27B5H442ZETHQC 7GCM5WFIKX5N2PQ5UCVNMFJEKTZTBCUVZH5RZ7CPDL3Z6GB26KAQC CB7U6IEEN2Z74672EYQQHWTB4GXVNNMAYDRQYAGY6K3QSDYYEHYAC T4FNOPMWYYJHJBTTY33PB43HTJPKEC46L62YERTWIX73HYZSELXQC 774O5HM4CU5QYP4MPKKLHIVZQ5JLLZETYIDZZYBAPOJSO6ILC5RAC KTHZLXQCGPBBO4FIKMXCORB3HTY3F64BA5MIEZE22S4O5IQZRIWQC KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC Z6Q7JVMFQ32SC7FRGOB7CE7JS2HEOPAO3B2VLU3YR3UCUDZFIPFQC VVEULZ7FMS53F6WZUJLNJ23URJMCCWHBUEDVGKI6R72JO2DLL5HQC 5XNQ3SSNBFXFNWA6DPM74W6FH65NX665P3DMH6YCWVFOPZTJSYCQC TMN6MGCYNMQL3GG5P3JKKT4ROF2RB26H4AE6LHNEE76MBRVCDZMQC JI4NDSOXGGZ7QHXXFB3ZTHAKHABXYBZXPDGLUFV5SKYEOL5FT7JQC RI66KX7UKHLUZR4GK6Z7T3CCRVJWXTQOIWMPWQ5TULMYFBCWISRAC JOX6FZZBR6HJHTUMM3XQVYAM47WCACHMD2B5IGD6CEHQKZO4O4TQC OP6CTAKWCAU64JXQ3USQYR5E5IFHQHNCACII5UMVRXUTZXJQOAZAC ZMED4SUEZTGX762DHBTMAZYNJZDV7RFFKMELFSTCM4KZFX553H5AC HDPWEY6JHVKHZMEQVXL4BE2WUOEDACFUKZDWPEH75PAEYOWBNRIAC P57SSZVSEUR22RXYA2S44VWPIYU2RPYT244QBP4QDLHZBW7CMHZAC 3ANUQ333WACKBBC4XHJWADIWDFWWX4RQM2MEFZPJ6GBRWSZ6WMCQC TPJYUAKSEZMCCCJANJ5EQ7F67QVTPFEOWBD7WYK33NLRN657Y5VQC K7J2IGELTIQL5KAMBV62MFVIVMEZEWCOLZ7GHFXHYPANYIF7BRZAC Z37LPWKBVEMAADJPZDI3DIPZTEIE4HI456I5PM2XTAG74X237XFQC 5WKKONCYM73GBJ6GDYK3OLZ2PB7KJHFDYGEMHQQMPQMFJJHTLWCQC 5I3RE6QCG6W3HD54OBDOZ7IDGHKEL7XBIOU5XVWTUDQQTDZV22NQC H55P74Y6NHAPF3VPWXIAP7DODA3DV7NCT3SK6VVLWNNWZ7JIMBTAC YSUUJTK4F4QM353U4D4FEUQ4NYKTZF4I5VWGX7EXBEWBN6IFEKWAC 6GT5JAWOIIL4SQ5MWIID6ZVO3KKQFWDQDZNVFHZ6DNK5QCBXJ4UAC 7G3PQFF6KRL7XLFTHVDNPFNFHNJRACG2SQK32WXK2D22AMUPFP3QC ZJSJGWYEOMUS4VK7VWFVLKW2E42X7KPFLK6HAA5LN3URQWISQULQC OXQNVH5GIMZERIBTC2S4EXHOEE4ZRIYZNY6IIY7HITBNBNI42DKAC 6SSW2WIX3LUPDP2QI6Z75YUE53G5H3CZBAPCQA7OEQEPOCIXT73AC 6LT6USGJOTDMRJGXLAN2NSZXK2GKWEXDKKUV6SVV7ZC6WI6EKMDQC UWKS5RFSHDUBRVQBBH7NLTZSDV63N4RQU6I6HMYN7JGPZOQ5FGXQC PWY4VZVHDLYL7UVNCCOW7BM7LYK2BOGTL23P75HXUJ33MHJPEJPQC 634UBQG7RYANZYTNHYLPHMXXO3FUJYUVHGFSK53TH6572QDVUOBQC IZ2FRVXI5H4TXLFZETEZEDBBC4KKSS7F4W6NWAZ37L7B6HU7HXKAC KDK4YJQS4P7HRB334SZ3EFCW27AEGRPE3PXW6S6TKRIG6HLOKQNAC GR6QXMDUVPNXA2XVDRJUWGVSDI2FXQCKLXAXZUVEI6NYRMK6I5LQC MFOXVZEY4AR3FDNGKAIB7YSXWTE6DOUXPS7UGSJGSBIK7VCRWLKAC 3NWBNYOBOSOGZMKDHFJKLL3ZDUXGXFU7OEOSLL26KPC3S3DRNHUAC ZYT77ACBDJW2MNENQ6ROTW662DBOIOQDAEV2GZBEXKC3OXXBFITAC SCRNLNQV4NWVMD7346D2DD23SWBH2TDI4QVG7WUZ7XRBHVRRQLMAC 6LTQT4CP7LJZLAE7JTPQ4OT4JOG3IFYM7RNXL3FINKBFO6452DTAC RM2JXW3ATVYRYHF3NMG5ALGI64OJ7IP2F3MDUDPUT5TBKSSN4KVQC PM2AFCUMNJD4UBWQ4GQPETHSE4T2C75JB5B4RHN3DBD6KTX3VBLQC 6RJVKSL7RVZIZIZIC4TWIYJQ24RUSB346ODS3NYGDHUIRTC4Z5FQC WXJLQMSTJV5ZVB5DQVEYEJJEG3CXGNPHJZYFBFV36BCFEVWYW3HAC QZVQOERFNTC7N3YQXERX7VXK5SJGTIASJSQEZW3FFZFEJK2PLLUQC ISUJEAPPWKP2UIYPT6BJUUNSVH52NEXWGXNUATL7I3IO7TPO32HAC MIUX76FPUNEC4UX6DRFUTBJ6BRA5HHJ32UTZXVRMQMTVGDT3XHVQC LJL6LLBFCSEIML577ZTICTX2DZIX2LOSDYFA5IOY55RCQNKC5VRAC 6L4EP4ZRWWYLT55PD5KTTJON5J2JB5VV5MWNHF5VPZQZ5BKEYZ4QC LRNA65FNZCSZBRU64O3UBUQ72J6ZL6WCWANADSL5VUAURQXJL6GQC CZWERNJVMJFBGKQHHR6I5U2FCRUJD7H2DG3C43V2CBAW74GYU2HAC YW5UVOSZFDRNHV5BCQ4JGP5CU3WFL4T3W5LRYX2NRVALQURONJ7QC WBNFHKUIC3JAIE5ZN6FZ5WIKBGET4U2JJGUDLDZNYQRLQLUYQYGQC IOYA3OMA3WCNR6AFR4LKE6EGK2TN23YWCRPPRRBKKQ43DS6MKIZQC BLVMXQXJOHLQA3HZ7YJ3DD36L346TZKQHMQDZ2NPEUP4V3U5KO7QC BJ63PXJSMITJY6T2AQGN4S3PA6C6Q5KNZNFP4Z2DKD4EQVD5YEUQC 3B3GZ4E2L4RIPHILKXACK4GF7DCQA5XC4UG75PHQXIVH4UCQWYAAC DXYAQFFVZ5YDUPT2MIKGAMVODSOJ4UJR5WTQKNCFMB5WJ6KH4D3QC 2KKHQWC5ML42HRY3UCOOEU7CUKAZMPDDD35OARA6HVXU4GDJ3P4QC 5FHWTG7M6FW4B3I33YI7QSM3OZIB6ZGC6TI6JISSLY5Y43HI56VAC FTDI4OSR3G5KKGJ5UILSHTHOP4GF4XJ4DHKYPUWFKWAGGXTOCJSQC JLAGD2FAV3CWVO7PLXTHLT7MBI23TUSYLSNHFAWP5IB5UO77VKJAC IPYQUTCWLI46CL62UXHR6PULPO4JPPXPFAJCGZULBQM67SHGPXTAC JM36UF5EFWQLT44YSLFAUM7IGILCC6WZHOBJOEL3HWIXGYLJRRXAC EJZK7RJA2HE4S3SNFM2RDLT2UFVXJ64N5D2X7K2EUPSUS2UITB5AC H6BKDO7FWEURT2BOJWZ55GKJMZ6MFJP5BCDVZVZJUJAUNYDXQOPAC HMC247EGUJ3Q25DQ3VKUCIGLIO4SZORFQQWAPAF6S2WLQY3WU5TQC EGOPBQO4WTEWLIZN2WS55CXPLYEL2IRCFL6B22QB2TRHSPKWRPPQC NWJ5IHZJXYE4I7CKSDNMTLGTIKOWK42LC2UYZL2TLXSZLM67WKRAC QXI6JLO5RGDKUG5H7RBMQTTT3234L2NGHJTMWAF6UJEBJDBDMMNAC UADYVV3UD5ERJTZZJGY4EUQ4NJ2JSBG7YYUJ75ZRBIXRQXQKOJPAC 4OKEEU32EGHIDAJ75GH3USSQHQPZBDZVC7TOP7ALMHZRUFPE5WEQC JQSFAW5HQ5VVHPEP26VJK3A46W2DT5S3WMRCVY3OGOAFXQZBO6WAC 2DWQ5F6EYSBKF7CN3MFNMXQWS6NWHKDJN6RN6AH2FWAJUZGLB6RAC VAOEP3YGJV3LTTLSJ5IMHP7JBAXQFE3D7OTPZ3SORGUXOJXNYHFQC YIRNX4FVAMPDL4AAZAHBT45GV32IWCN7NYUNL2ZN34VSFVPQARAAC 7KHOKTB2DCRQ5UF6A2VKAWQSAH7O4YOFBL3N3LVQRKVHLU6TOBVAC GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC 6GSPAIEMWJXYSCR5EC2WBOGYDEDR6ESIZC6TKN2FVE2CVPSHUHXAC XJRP5MAESWFGYS4CBM7K2ENA3UBPL6N6MDNAHJBH2WRMZ73E5JGQC NW3WF4AHXC2D3ZO3VUH73T3L5SJGVDTUGG4PPCDFZTKJKSTPURXAC JMBL7S3FVFBUQGRYXVHY4LCL3RTWMFFW42EG4FQPH45YTERD35RAC ROODXPAWIP2V2SHJVO4CI567E3CYLBTL6MSYAG3IFEGHIDIKNAAQC Y4WR4EIOFFXHRQI2PJAZWD4MVL6A7MNBHLN4KOV6BIKGGA7FXQTQC THSEYQZ4KVU6WF2AXH6XBPAIX7PQVJ455MNPFEXHXQA4LLCYIPCQC TPQPFPC3AZVMTVG6YS3G7NGSNNBGYY2NYTB36WZJNIGHHMWBABXAC TY33BSNJWG6DYLBARYXL72GPP7MZRA3QTG3A6H2OVUKW5QK7TH3AC QW73U22SVPKFZ367CURWGZCUJZIG73MRKWE2EBRG535N4UUYKMNAC 6F2J5HCYTKKIGZD4O6FETFA4WOUI4A5VHPVD4U37JPLX6SYX6DQQC 6EZYOF7W56XYRLZ7JSIX3DLQS7BOYVQP7TSSCNU7WIJT4FPZIM6QC 77KVM6ATUPTWF7EK66QG4VALXRAHXLW46ECSBS3PVI36QCW6MH3AC KO7TLN7OM7UQF33RRTOFPFTDQLTYCOE4N7KWQUTWRIUSEB6PQ4WQC PFEJ4LMDNEKLMGRCMWQ7EIRVU4JMYGICI4G7X4WVWOROVXQCBZ7QC QEEJFAETO6B2J4IWDIDCJ5UNIFNNHHG22IWF2CUJRTJJBNE47CWQC YH23OR26YDA6C5K74QRWHROMTPGRNZHTHE4HJGPC3JVNAJ62CHGAC GLAIQU2KCJKEY2NWIZKSHBAG5G54SCM5IH3XPFKXZVU67J5DIPLQC GDGTTDTUKCEDGPWRUYKSYQT4PYVSOMETUCSJGEXMIUQKSJACH6CQC PGON7LWTK6XGVLZ5PGL5SEP7WQOECAAKYJGITBDEQHRCYALN3YWAC HYS3HXTXATFPN7GLN3WBGDCP22UT2D3JBVUS7SP5FE7L54TDCJRQC S6ZETU2X3HBTKBDL2KTAMCNPMPGHEZO2JGMILIWETWC7XXRS5RYQC G5G4JTXF6JP3GN7ESZTDLEDNJMTLCCE6BCDBORNAEBOUO5WYHAPQC 3A6E2MQQZDGITBNWJ4G2GUTXADMVOYXL6ZHEFTE675YH2N5GKXNQC GTXKQTORYHZ7XB2VIH6372UM5GMWAN7IVRXWY5FGBCHFGBV6D6NAC 3HGELZU7NELOQ635HZO6IJIYLBSNCJ5VPH46IE22KA3OSLEFK7AQC W6JMQP26KNVIZOJTITBWR447OX63DUKBIQE442Q7QYLUUKJVBELAC ARNDCQD3NXQRD5CZFTBQOMID5RPPRRIK647T36FLGHMUN62MF7WAC JPMNAPRVTMOIM62AD56L6MRBGJGNEF6GNLUXDFVOSZG3KU7BUGEQC CXLVKMD4BRS25Z63CIIFCROMWY4K2U6K4UM7VTE7M2BBWWSQFEYQC BA4ZFX7HMFMZN7ATQ4WXRMWRYWLPPB23ESU7QEXGYHOHOIMPQETAC C5U3HSOOQ7BKXKXIDS7MLVXUKDTHAWJ5NXNX6YDXTM3GWY5UWX4QC IO5CHPT4QBYSFAPSZGGJAYMN33RDAQKGLVW6OKK25HVIEEQEI4IQC LTX72QGIPNUGWQN5ULPOMFCOPZTK7472DQY4AYX5WM3WHSUVXI5QC AHDR6VAKT6NGNERU244ZBTXYK73SWZVF5Q3SH3GPRNHNTJSJY7FAC AV3TMWHWB3XBXQCT34UPMZBSIIKVXIGWQPNEFU4CZSBS3ZOF2CUQC FVEPL7AUMO6QPLVPGQVZTPSI5ITBIW6Q7CYUWQYGVNUHKTK5FQHAC GN3X2AI373HZRX53EQEWSILYEAMKBJBN4NKW5CT7PHYKPEWOPX4AC 3S35ZFTUNMEOQIWJNAJM5JMATQCSRBOOL7NXCHGFZGVXQZOCXZFQC B62LRY6EDGLIYGZCQC6UG76KGGCHQZUP6ZDFBPSWYF352ULQJQOQC CK7CT5TUFUL2AQY7FUHB5JI3FC2KSPWUWHXC6VEUJJ7G4OWUQFTAC 5WP65PFRPN43O6CMEE3AIAWPENG3LTMJJ62FP5MQWS3Q3ZRNDMNQC RCPQNIRPR3NQXEMVLBAQW6657UJFIP43N54UJVEZHJH22OH4UQIQC THECEUKJWIPEPUHPV3A5H73H6O4QZ65IE4XUXB7VPYM5LRMA6X2AC JSDIPKR5WZ37KDVQRRSLS5ADWRBGEZOOJQTGCZUISCTPPWBD6BKQC A2SAU7YL5ZS4TODRUTKLI4W4RZOMZSSLBEEXOKPKQKP5SEWU7QOQC LTRJLR2YO3SYH5ZHXFWIGJOZXHYZC2HBUEXEHREM2NMKIV2Q4DDAC ZUSQN4OYHQPW7YSX4W2LK3YS7TAVRRNAP2SCENZX2CQY2A5U4L6AC SHFSQYLN7WPQC35V3XYYEIZ4CQMREASUNR474CIKFG3C2FYI4SHQC VQD5BW56OTUNPILMCASXZ6YZ3OQGXKQ7CSNDMNZUX72AQZXKOE3QC Z3E3EG25UDOX2TT2OLEGHF76PA3WVNU72I5SL43BXMFYOZ25Y7OQC 3KQZKOH3ILSXKL2KVT3BPHQ7AMR2B7T6GNWYC7T3SG2UQBXQZDCQC 3SMUPAVD4ABSOH4FKUJH3SDGAJPNLNLBET6VZBEEIQCVHYRI7KUAC YWGZPRW53TXVMBWAYZWTMNHOMNNW5HMGFQDSRST2RZBHBRKWT3XQC FAXIQ43BLFCLIL6ZOBTIKHU6RWEXSRQ2TS567UVZM7VV3V3GHELAC 7Y26W23LYFDU2XAMJF2XOIIQA2EEMGHG2YN72QRVN2UCDUVK3GDQC BHSSCPAG6MHLIZY4VDAHHBWURMYMVMF2IIMVEQ2XQ4U2ZEFF3XNAC RGY2525RQH7SSGM6ZVI7CZL4WMNFZK2WRABOSIWRKQYYOU2RWN4QC BRGAZR5AXWC2IALBVXO5SB354IRQEIRHSK55RZPGFI4AGIOD4LUQC 3XL4DKV7PSWAU3ZBJSKZFJ2VUYABC47I7KKWGIRCJYXULQD453MQC NNQPJEMQAJC6V45OOHEX7OA54DE5IEQI73SJTS5O6PPU3KMHF4WQC X4OCLD5YEXCYVQNMOQORLIO72AKUEMT3BT6FB3TW2HARKN5X7MEQC TS7X6ZTTQRB2I53VV4LXZPXZWM3DIKYI3T5PPHZANSEW4Q7QZBOAC 4SWAT5KCKQV527NKELAXFQ5XA4Q5HONQXD4VBXMUZNPVPQKPCPNAC 57JTN5J5UWWO5NP2CSHQ3DSJRA6HEHPDYTRNEZRNG5Y5TPEYARQAC 2VUJ2IMYI7MYLFMSPYAH6IEXIMFG4JUSUYLVCW7SZELHSLPIEL3AC QNIQ2NBBIERVCA2YTD3O3P6QPJ5M6VDVGGX7V2BWXTW5553T4PVAC U2CE3EGQNU6WJMRSUMVRLWPY5Q32VAE2ESTQARJQHJAG4EIHOJCQC 7I3W2GQ2USM3JHD7BG24FSIBZDAWUMI24BSEMDQ6PRR3D76SRDDAC JOBNTX4QILB3BEPWWIQHKWPXXZJWUOOSRBKSYMRDOMNVTZKCSBNAC OY7HY5MTXM2OO43HM2GYUS5U3MBTKDM66IALE5OXAXF2EGYVAEGQC YLZ5G5CR7526RPCDPMBJ6DSADFWVASB4T3DWENT3HUUCD422QITAC 23PFLB2E4QHL5SF3Q2YV5FXRH6MFHENEU2ACVC572ZCYDXCBZVQAC UB67ZWGT7FOPKZ7SO5PAO3V4PUBPWPWXKQIQIUABNB533XP4JUMQC 2G7MZ653N3JUHJ4DA5Q7VRO3S5T27DLPKDCJEKB6DGYSTXULUVWAC BUSA7O6EFBZVAG2RL5T7MD2WTWKOEKKIAAZ2VS4Y3L6ECT4HQR6QC OREY5XZ7FHN4UHDW4E6EQKGZHQUGK26LOGVHLKFN3YJI3B2734BAC KZIBMMGUSWOMS5NQ2KTWGY3SRYUYGZGD2YRZPQ2ISN5Z3JRSMEWQC KZWOHH536QBWAPWROR32EOXEK5LRY6I5VHVL7HHI7GOFKCMQTCSQC EH4FFBOHIUH2QM43B4CHXRETOAKYV53EAQYA3FETDD5VWV2WX6QQC QVVHS5GCDMIMZHADBBTNG2ICTHKALU2MK42PUZWT4H53HIMVSJUQC UIRWVLX5LDNWURTZOG7EFLXE5OOEL4XBPSLSUHUQSKHC4A7WCVLQC GZ4FB5VKL4C22KK3GSKQPLJYMRGLFXUDCOBNEBC2OKK6KVZL3OSQC IXO5VNCDYDYI46Y5QWNJMD7TURKYO526CJA4TIMH5PDDAYHMK5YQC WDEFQ6YABDQIGJXW5KT3OGR3EO6FZHXZELIRVIXQ4XDYTVOV5V6AC 6QOJM3P5IONWA3LWLN7YT7KZKNSFNIGNRLL5ZIKFRQ42BDURNAEAC 2HA26BZZOJOMSIP3NI4YCB7DPWUDR6VTAD2K25EI7FZOQ3KITYCAC X76YXE6RFL7QY5WL6MSSS44WHVA5FQMDQY3XTYP2UL6UZB4E6XMQC PHBACPMH3F34GODHVDKNCMXWU373RJQGVTDLBFCCDLLWDXVYOLTAC 3GSAVTNKEG45AT2U734R5STSRP22WJZL3H6KUFRUWCIA6A4CZE5QC PWH7XDI4TKX47RBFLLWDCQAX3C4W5IDZIQGAGRHNEKOALJN2QCNQC JESCFNNBKC7BW3XYLJLAF6RLMP5VCXIUUXQUGEZBF4FYPNMEGNJAC GE7XFWXEYNZXWNCKWHBHYIBSYQPZMBAE43PN7RW4B7WOCJKM7DZQC 32PXX2XJVV7YSLLYNAVS7RYKYRAOQ565TZMTITSEPSSXOYPB5M2AC YCL3W2PFE6ILTGBFODCSXNPDIA46KVSZP2TI7HDMYAOEJT65RIEAC B62ICMDTN5V7R7RBL4JALFVKEMVOOVLRSJASRNYS6CGFWBEEF5JQC RXDU5LKGCQHDGZUVTNMKTF66O5OC5M3SCYMH7535GM7CTFDJE7FAC PR42BCP5BPRFD2MP5H6CIJP7E57Q6TKL6SOXZWFKMFVR2OZWHT7AC TQXNC2YQVJZXBPSLDEANMM2EDGHTH4DSN74OJIRMUF7D46SESHZAC FZ3VCF24YWNHTLVHBZYWX37X4FI2SNCXGZGTV2UFGFGAWMOKFIUAC OKYT7CZ6LO3MUM4YWQUUKBYD4HGA334ILTEF6VE5TT2L7ZNOOXTQC 5MLWKDBEOOD6YJEIVDU7KR3B45DPGH77OSQZR3UQAJPPTEP2XCRQC YZMBVWADA6MSQQ6MZWT4DGCG3FSJGAHBJ3F6NULFJNFLAXHCGZEAC 5WVUTEZLEZEML54CKPR6GACQBYY3EMVNXMLJOREN6SSEUZGC47AQC AYU5OVG2HZO46KDAPKUWAVHS5HTYFKUWIMIRMTHAXVVFEDJE7YPAC G7EESLQ3T7YRTYLAOPPKXVZ5BNA4TKN4X3VNZEAL3SMGHXDQG6IQC VYDMJSFXD2E2CWOJEFFVLRBLHDFINBSVL2X6YE6456IQIRGVZIYQC 6PAG7GHXHIYXJPPTEK4KZQZT4CL2SJDAGTVIUDB4KK66PVSTWUMAC 3523HSJ5BHDJWA4LG27ZJS3QAECBV3NDHUUPIEEU5FVT33L2TGEAC CDKRLJIGVWQE2PMHCSLJBLYQEK7JYC4LQM7H2X3O6NMJMCCDRVIAC EJYK3CKIEMP4NMZUCMUYLHIBE7A3LPZOU7DUON5V7CPHY7QEKLEAC OVD3PHXMVV2EBMCYBZXNGLGTRTPO4J6ARPW2OTKYYWKQE7ZLWGBQC BA3OER3VNFRQUCAFCOOMGD62TXTQTOTDZ4VYNXZ2WWBYF2VICMRAC VXA2DLATDUMJC7LFEQXEKTKYFOKTFD33GOBHQDRPYFFMZ4CENAWQC KXYNUF4XKYHT5RWWZ43GLHBEOUJXWPQBDG76Y2KOL4E3TZQNNVAQC HH3HFWVXABJ4IRMN22PPJCREMULZSN6DA7VYKOGECGMNUQTZ5QNQC HS4YFXH6CMLDAPF5BMFOTHYY4K3LLW4WIWBDR7IEVJC3WHJYSYFAC ACKNLTFL2RI3PMRWLNRVLRWGQAMLRFKNGNS5LED6NFE5GVGFIHFAC E3OI7XFKECKFLJT3KMTGCUDNXEJGS5I6DDV6X3VJPU2R53YSPLXQC 627CM2ZOKVBMPVPBYGWBWWPT2FBMVRRH2VDGPT6Z5XCVJ5R4YQWQC YKVVFNKT2M5WG2KBURRTOJG23BJVI6WUBP5JOGYPHQBS4RNGFGWQC WUT3GMXCKAOUKQMOODQBZR7SWEVCSMX2UDNRM6DC37MD6VTBPRSQC NTHRCKI7DKIQRCYC6I3M6BWDB5SSGDJ64ZM573XCJM7ER632SBSAC BBOSRSB2AV4IJR3LVJM2XVJ544SRCYTZMEG43TIVJDU6MDTQ5IVAC C4CGYUPRJPDPAF6E65Y3MGAK45PNDPF2CNT276V7P37PPLX3AR5QC 2Z6IWKMEWE4OIFVWFSZBA2SIXMBJNV6H6LNEPOXTJZW433ABU4EQC EQDLV5OMIFO5ZPYNE27VQLLZEIRMSALGNEFWVSCFXJN22A43GCWQC AI4DALP6SCBCSK22RCVR6LWLERKIR7HPK3IGBBKQ426RZNC2V6XAC YGHUDZATZ5XHYWXPZ7ZMT6OLUDASG4MO2RAWPVJ67V6A3AJVSXUAC CEK6M777MI5JVDC3KHE3JD3FETVSJ4VN6DGATBI5P3O6P5XHY4DAC RQ24Y25QDO2GY65XESEVBCP6VXCB7BDTWXO42IPPI6UPRJQ5XYDAC WT66JDIRTLLP37SHTV4GI3V64JFJ4D25LNRLGCHFG6CLEFKJ3QGQC I7IISLBH7RZG7JOV635XM4LQ6TP4LDP532QEMHSTF4AHUPRZHEBAC 6DNNPEMZGBQDMA7YG4LCTQUVZ7LYPC3R4A2XBYT5SDQ65GYOLJVAC AACWTVIT7SKC6RDU2FU72JT3U43IT37C37WZE3AH4V6XVHSJFBVQC POP6UTTHSJGDU5NU7ENQJOZVFUHYM63HJ6HLPXDDLK7XFIP4XEIQC S2LIBA2CLTZ6ZU66AUZ2CCNLCDOBSGWQGTZ6HFAFP2XSWAALGLSQC 475LL4U4ND6PTNV4XKC7WQAOJC7RF2VCCVX3DRILP2PKIBFYWE6QC SCXDRLT2W436HM4WX4UZRC5M3XNSA65V7XR7AGHEOTSKBWEHUREAC EUK6RUR53DH5VHNWUCK6LPU4IKDWPOLA7GFAJ6GABH6WIUT2DRBAC NULVQ2A7ES5N3BBQAH6YKFST6UPVKD4KYYFFHYTUKSYLHXR4OYSAC 3CMMAOYW3XCVFBCT2RT6YH5BLVAWQT62QXXWWCMWIZKDFZBY4M2QC FCZSQBKDNMJZRJS2LWQQWLUFGOXSKXDJZQIHC7L5S7HXCXQPOMMAC SYGI2ZUUWAD7IEJ6D6SSDEK5BD7CV4Y6XS4BP7FZJJNUJIFV5IAQC 5WGGUYUI4QLCKZUO76C7ZFZWEIZVG7J2FMSKZOABXFHGRNLQVX7AC U5E476KITX52LCQEDW7OYSEJJS3B7624LHAUFENVMNZM76OAFJ6QC 22MF6OUN62WDBJR5QFNJTKU7Q5TIQ76XWCEIRBFWAZDMZUSKJGCAC Q6WLX2VTRJ3LGDPWBH5WKQM7CIIJ5H6AEIKMXPF2PJA5JX5ZLUZQC Z2GW2JB2K2MS67IU7O566FX7PSJPZIFN52PWECBBSJMB4LS6HXUQC 2RJUPYKEDZZCW4GDG6WLEDVYF32ADC4W5I42KSKHN3MWEBCWSAHQC GHCA7IFJA6CWJSLAJMQAHD2GSBSNV77SHHRPHAF7ACYFW3YCG6FQC TJKJLYEOG4MESLITZIVFVJ3QXS4SSAXJK5A2FYVYP3YRAVPKBZ4AC P4KV3XKK2NCQAUPCMTHJPSA2FETTBY5WOMCVP4IFMNJZABEQWKWQC IH5TVAZGKN7IQOTSOJ56WG5C3CMTG3AI63XT3XVNHC7DI4N2TSUAC GJ67VJMBBWHFU72XBWTQ4HOEBXQSEDOAYFQCC7BSHAXLJAP6RHDQC AA3I6WU2SPJUPKRIPGXCLU4EZOV4QELVVZ7EIAW3H73GES5DSFXQC DUIBRSMW7SG77MPCMR3HJ5D2VHE5SN2UBERN4DCC4N7UE5VKPVEQC DMG73XDQHY2X2PHKWIY56XKD3O4NPGZKKIO6GX3IV2LLRVXPGKYQC 3CKYPX7QMW4HS3XWRADGEYMZ7VUKFQL2PZP5BAXILQS3SCHJGKEQC 2XSNHF7JXHEKABVMXDAU25QKFHJHH256GR4WLYAHKSPF4XLBJW4QC NRTBKC3AXWOHMSLJ2CTBGG3NQHSY6BLK7AOZ7TJV4FPWQ4PUZ7XAC KQNMFSLV62B4ANDKTUZ7LQH2MD2NDGNCP55MKM5YAATQ4T52H2PQC JZCODQCOUBBBPPR4TLOAPIB4BQLDY3ORTJXTWEQSUJVJ7DMK7FVAC YOH32TMLN6QJG4ZFLYWPJF3YUEGIMZPVPYN57RTB26QBBHMICV3AC 4EQI267TNL6F4OA3IKY5WZSWWZKSOBYFYHA7YEEOMIG6FSXBV7EAC 7BRWL2WWX5MCT5PJLWIEV7TCQW7UP2ROBGYR3HDAKM2SZI36YFIAC SJDOBXECX7F3ZPPVR7FPURE53D47PP2TFIF4SB6XPS456PZV3DQAC VY2RJLJQMUOVIJOYPTA7AEXPURIV3H6VF7O5QQ7LF44ZS36RTFYAC J6APXOT4QOGQFONWB7G546VTVF6QG42HVOROMHF7YBDJPR4K26OAC DKYR3HEORWRCTEUA525ZBPUAWS5B6VFV5CCIGGVDH5NG6YA4D3ZAC MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC /** File: monster.cc* Summary: Monsters class methods* Written by: Linley Henzell*/#include "AppHdr.h"#include "beam.h"#include "cloud.h"#include "delay.h"#include "dgnevent.h"#include "directn.h"#include "fight.h"#include "ghost.h"#include "goditem.h"#include "items.h"#include "kills.h"#include "misc.h"#include "monplace.h"#include "monstuff.h"#include "mstuff2.h"#include "mtransit.h"#include "random.h"#include "religion.h"#include "shopping.h" // for item values#include "spells3.h"#include "state.h"#include "traps.h"#include "tutorial.h"#include "view.h"#include "xom.h"struct mon_spellbook{mon_spellbook_type type;spell_type spells[NUM_MONSTER_SPELL_SLOTS];};static mon_spellbook mspell_list[] = {#include "mon-spll.h"};// Macro that saves some typing, nothing more.#define smc get_monster_data(mc)monsters::monsters(): type(MONS_NO_MONSTER), hit_points(0), max_hit_points(0), hit_dice(0),ac(0), ev(0), speed(0), speed_increment(0),target(), patrol_point(), travel_target(MTRAV_NONE),inv(NON_ITEM), spells(), attitude(ATT_HOSTILE), behaviour(BEH_WANDER),foe(MHITYOU), enchantments(), flags(0L), experience(0), number(0),colour(BLACK), foe_memory(0), shield_blocks(0), god(GOD_NO_GOD), ghost(),seen_context(""){travel_path.clear();}// Empty destructor to keep auto_ptr happy with incomplete ghost_demon type.monsters::~monsters(){}monsters::monsters(const monsters &mon){init_with(mon);}monsters &monsters::operator = (const monsters &mon){if (this != &mon)init_with(mon);return (*this);}void monsters::reset(){mname.clear();enchantments.clear();ench_countdown = 0;inv.init(NON_ITEM);flags = 0;experience = 0L;type = MONS_NO_MONSTER;base_monster = MONS_NO_MONSTER;hit_points = 0;max_hit_points = 0;hit_dice = 0;ac = 0;ev = 0;speed_increment = 0;attitude = ATT_HOSTILE;behaviour = BEH_SLEEP;foe = MHITNOT;number = 0;if (in_bounds(pos()))mgrd(pos()) = NON_MONSTER;position.reset();patrol_point.reset();travel_target = MTRAV_NONE;travel_path.clear();ghost.reset(NULL);seen_context = "";}void monsters::init_with(const monsters &mon){mname = mon.mname;type = mon.type;base_monster = mon.base_monster;hit_points = mon.hit_points;max_hit_points = mon.max_hit_points;hit_dice = mon.hit_dice;ac = mon.ac;ev = mon.ev;speed = mon.speed;speed_increment = mon.speed_increment;position = mon.position;target = mon.target;patrol_point = mon.patrol_point;travel_target = mon.travel_target;travel_path = mon.travel_path;inv = mon.inv;spells = mon.spells;attitude = mon.attitude;behaviour = mon.behaviour;foe = mon.foe;enchantments = mon.enchantments;flags = mon.flags;experience = mon.experience;number = mon.number;colour = mon.colour;foe_memory = mon.foe_memory;god = mon.god;if (mon.ghost.get())ghost.reset(new ghost_demon(*mon.ghost));elseghost.reset(NULL);}mon_attitude_type monsters::temp_attitude() const{if (has_ench(ENCH_CHARM))return ATT_FRIENDLY;else if (has_ench(ENCH_NEUTRAL))return ATT_NEUTRAL;elsereturn attitude;}bool monsters::swimming() const{const dungeon_feature_type grid = grd(pos());return (feat_is_watery(grid) && mons_primary_habitat(this) == HT_WATER);}static bool _player_near_water(){for (adjacent_iterator ai(you.pos()); ai; ++ai)if (feat_is_water(grd(*ai)))return (true);return (false);}bool monsters::wants_submerge() const{// Krakens never retreat when food (the player) is in range.if (type == MONS_KRAKEN)if (_player_near_water())return (false);// If we're in distress, we usually want to submerge.if (env.cgrid(pos()) != EMPTY_CLOUD|| (hit_points < max_hit_points / 2&& random2(max_hit_points + 1) >= hit_points)){return (true);}// Trapdoor spiders only hide themselves under the floor when they// can't see their prey.if (type == MONS_TRAPDOOR_SPIDER){const actor* _foe = get_foe();return (_foe == NULL || !can_see(_foe));}const bool has_ranged_attack = (type == MONS_ELECTRIC_EEL|| type == MONS_LAVA_SNAKE|| mons_genus(type) == MONS_MERMAID&& you.species != SP_MERFOLK);int roll = 8;// Shallow water takes a little more effort to submerge in, so we're// less likely to bother.if (grd(pos()) == DNGN_SHALLOW_WATER)roll = roll * 7 / 5;const actor *tfoe = get_foe();if (tfoe && grid_distance(tfoe->pos(), pos()) > 1 && !has_ranged_attack)roll /= 2;// Don't submerge if we just unsubmerged to shoutreturn (one_chance_in(roll) && seen_context != "bursts forth shouting");}bool monsters::submerged() const{// FIXME, switch to 4.1's MF_SUBMERGED system which is much cleaner.// Can't find any reference to MF_SUBMERGED anywhere. Don't know what// this means. - abrahamwlif (has_ench(ENCH_SUBMERGED))return (true);if (grd(pos()) == DNGN_DEEP_WATER&& !monster_habitable_grid(this, DNGN_DEEP_WATER)){return (true);}return (false);}bool monsters::extra_balanced() const{return (mons_genus(type) == MONS_NAGA);}bool monsters::floundering() const{const dungeon_feature_type grid = grd(pos());return (feat_is_water(grid)&& !cannot_fight()// Can't use monster_habitable_grid() because that'll return// true for non-water monsters in shallow water.&& mons_primary_habitat(this) != HT_WATER&& !mons_amphibious(this)&& !mons_flies(this)&& !extra_balanced());}bool monsters::can_pass_through_feat(dungeon_feature_type grid) const{return mons_can_pass(this, grid);}bool monsters::is_habitable_feat(dungeon_feature_type actual_grid) const{return monster_habitable_grid(this, actual_grid);}bool monsters::can_drown() const{// Presumably a shark in lava or a lavafish in deep water could// drown, but that should never happen, so this simple check should// be enough.switch (mons_primary_habitat(this)){case HT_WATER:case HT_LAVA:return (false);default:break;}// Mummies can fall apart in water or be incinerated in lava.// Ghouls, vampires, and demons can drown in water or lava. Others// just "sink like a rock", to never be seen again.return (!res_asphyx()|| mons_genus(type) == MONS_MUMMY|| mons_genus(type) == MONS_GHOUL|| mons_genus(type) == MONS_VAMPIRE|| holiness() == MH_DEMONIC);}size_type monsters::body_size(size_part_type /* psize */, bool /* base */) const{const monsterentry *e = get_monster_data(type);return (e ? e->size : SIZE_MEDIUM);}int monsters::body_weight() const{int mclass = type;switch (mclass){case MONS_SPECTRAL_THING:case MONS_SPECTRAL_WARRIOR:case MONS_ELECTRIC_GOLEM:case MONS_RAKSHASA_FAKE:return (0);case MONS_ZOMBIE_SMALL:case MONS_ZOMBIE_LARGE:case MONS_SKELETON_SMALL:case MONS_SKELETON_LARGE:case MONS_SIMULACRUM_SMALL:case MONS_SIMULACRUM_LARGE:mclass = number;break;default:break;}int weight = mons_weight(mclass);// weight == 0 in the monster entry indicates "no corpse". Can't// use CE_NOCORPSE, because the corpse-effect field is used for// corpseless monsters to indicate what happens if their blood// is sucked. Grrrr.if (weight == 0 && !mons_is_insubstantial(type)){const monsterentry *entry = get_monster_data(mclass);switch (entry->size){case SIZE_TINY:weight = 150;break;case SIZE_LITTLE:weight = 300;break;case SIZE_SMALL:weight = 425;break;case SIZE_MEDIUM:weight = 550;break;case SIZE_LARGE:weight = 1300;break;case SIZE_BIG:weight = 1500;break;case SIZE_GIANT:weight = 1800;break;case SIZE_HUGE:weight = 2200;break;default:mpr("ERROR: invalid monster body weight");perror("monsters::body_weight(): invalid monster body weight");end(0);}switch (mclass){case MONS_IRON_DEVIL:weight += 550;break;case MONS_STONE_GOLEM:case MONS_EARTH_ELEMENTAL:case MONS_CRYSTAL_GOLEM:weight *= 2;break;case MONS_IRON_DRAGON:case MONS_IRON_GOLEM:weight *= 3;break;case MONS_QUICKSILVER_DRAGON:case MONS_SILVER_STATUE:weight *= 4;break;case MONS_WOOD_GOLEM:weight *= 2;weight /= 3;break;case MONS_FLYING_SKULL:case MONS_CURSE_SKULL:case MONS_SKELETAL_DRAGON:case MONS_SKELETAL_WARRIOR:weight /= 2;break;case MONS_SHADOW_FIEND:case MONS_SHADOW_IMP:case MONS_SHADOW_DEMON:weight /= 3;break;}switch (mons_char(mclass)){case 'L':weight /= 2;break;case 'p':weight = 0;break;}}if (type == MONS_SKELETON_SMALL || type == MONS_SKELETON_LARGE)weight /= 2;return (weight);}int monsters::total_weight() const{int burden = 0;for (int i = 0; i < NUM_MONSTER_SLOTS; i++)if (inv[i] != NON_ITEM)burden += item_mass(mitm[inv[i]]) * mitm[inv[i]].quantity;return (body_weight() + burden);}int monsters::damage_brand(int which_attack){const item_def *mweap = weapon(which_attack);if (!mweap){if (mons_is_ghost_demon(type))return (ghost->brand);return (SPWPN_NORMAL);}return (!is_range_weapon(*mweap) ? get_weapon_brand(*mweap) : SPWPN_NORMAL);}int monsters::damage_type(int which_attack){const item_def *mweap = weapon(which_attack);if (!mweap){const mon_attack_def atk = mons_attack_spec(this, which_attack);return ((atk.type == AT_CLAW) ? DVORP_CLAWING :(atk.type == AT_TENTACLE_SLAP) ? DVORP_TENTACLE: DVORP_CRUSHING);}return (get_vorpal_type(*mweap));}item_def *monsters::missiles(){return (inv[MSLOT_MISSILE] != NON_ITEM ? &mitm[inv[MSLOT_MISSILE]] : NULL);}int monsters::missile_count(){if (const item_def *missile = missiles())return (missile->quantity);return (0);}item_def *monsters::launcher(){item_def *weap = mslot_item(MSLOT_WEAPON);if (weap && is_range_weapon(*weap))return (weap);weap = mslot_item(MSLOT_ALT_WEAPON);return (weap && is_range_weapon(*weap) ? weap : NULL);}// Does not check whether the monster can dual-wield - that is the// caller's responsibility.static int _mons_offhand_weapon_index(const monsters *m){return (m->inv[MSLOT_ALT_WEAPON]);}item_def *monsters::weapon(int which_attack){const mon_attack_def attk = mons_attack_spec(this, which_attack);if (attk.type != AT_HIT)return (NULL);// Even/odd attacks use main/offhand weapon.if (which_attack > 1)which_attack &= 1;// This randomly picks one of the wielded weapons for monsters that can use// two weapons. Not ideal, but better than nothing. fight.cc does it right,// for various values of right.int weap = inv[MSLOT_WEAPON];if (which_attack && mons_wields_two_weapons(this)){const int offhand = _mons_offhand_weapon_index(this);if (offhand != NON_ITEM&& (weap == NON_ITEM || which_attack == 1 || coinflip())){weap = offhand;}}return (weap == NON_ITEM ? NULL : &mitm[weap]);}bool monsters::can_wield(const item_def& item, bool ignore_curse,bool ignore_brand, bool ignore_shield,bool ignore_transform) const{// Monsters can only wield weapons or go unarmed (OBJ_UNASSIGNED// means unarmed).if (item.base_type != OBJ_WEAPONS && item.base_type != OBJ_UNASSIGNED)return (false);// These *are* weapons, so they can't wield another weapon or// unwield themselves.if (type == MONS_DANCING_WEAPON)return (false);// MF_HARD_RESET means that all items the monster is carrying will// disappear when it does, so it can't accept new items or give up// the ones it has.if (flags & MF_HARD_RESET)return (false);// Summoned items can only be held by summoned monsters.if ((item.flags & ISFLAG_SUMMONED) && !is_summoned())return (false);item_def* weap1 = NULL;if (inv[MSLOT_WEAPON] != NON_ITEM)weap1 = &mitm[inv[MSLOT_WEAPON]];int avail_slots = 1;item_def* weap2 = NULL;if (mons_wields_two_weapons(this)){if (!weap1 || hands_reqd(*weap1, body_size()) != HANDS_TWO)avail_slots = 2;const int offhand = _mons_offhand_weapon_index(this);if (offhand != NON_ITEM)weap2 = &mitm[offhand];}// If we're already wielding it, then of course we can wield it.if (&item == weap1 || &item == weap2)return (true);// Barehanded needs two hands.const bool two_handed = item.base_type == OBJ_UNASSIGNED|| hands_reqd(item, body_size()) == HANDS_TWO;item_def* _shield = NULL;if (inv[MSLOT_SHIELD] != NON_ITEM){ASSERT(!(weap1 && weap2));if (two_handed && !ignore_shield)return (false);_shield = &mitm[inv[MSLOT_SHIELD]];}if (!ignore_curse){int num_cursed = 0;if (weap1 && item_cursed(*weap1))num_cursed++;if (weap2 && item_cursed(*weap2))num_cursed++;if (_shield && item_cursed(*_shield))num_cursed++;if (two_handed && num_cursed > 0 || num_cursed >= avail_slots)return (false);}return could_wield(item, ignore_brand, ignore_transform);}bool monsters::could_wield(const item_def &item, bool ignore_brand,bool /* ignore_transform */) const{ASSERT(is_valid_item(item));// These *are* weapons, so they can't wield another weapon.if (type == MONS_DANCING_WEAPON)return (false);// Monsters can't use unrandarts with special effects.if (is_special_unrandom_artefact(item) && !crawl_state.arena)return (false);// Wimpy monsters (e.g. kobold, goblin) can't use halberds, etc.if (!check_weapon_wieldable_size(item, body_size()))return (false);if (!ignore_brand){const int brand = get_weapon_brand(item);// Draconians won't use dragon slaying weapons.if (brand == SPWPN_DRAGON_SLAYING && is_dragonkind(this))return (false);// Orcs won't use orc slaying weapons.if (brand == SPWPN_ORC_SLAYING && is_orckind(this))return (false);// Demonic/undead monsters won't use holy weapons.if (is_unholy() && is_holy_item(item))return (false);// Holy monsters and monsters that are gifts of good gods won't// use evil weapons.if ((mons_is_holy(this) || is_good_god(god))&& is_evil_item(item)){return (false);}// Holy monsters that aren't gifts of chaotic gods and monsters// that are gifts of good gods won't use chaotic weapons.if (((mons_is_holy(this) && !is_chaotic_god(this->god))|| is_good_god(god))&& is_chaotic_item(item)){return (false);}}return (true);}bool monsters::can_throw_large_rocks() const{return (type == MONS_STONE_GIANT|| ::mons_species(this->type) == MONS_CYCLOPS|| ::mons_species(this->type) == MONS_OGRE);}bool monsters::has_spell_of_type(unsigned disciplines) const{for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i){if (spells[i] == SPELL_NO_SPELL)continue;if (spell_typematch(spells[i], disciplines))return (true);}return (false);}static bool _needs_ranged_attack(const monsters *mon){// Prevent monsters that have conjurations from grabbing missiles.if (mon->has_spell_of_type(SPTYP_CONJURATION))return (false);// Same for summonings, but make an exception for friendlies.if (!mons_friendly(mon) && mon->has_spell_of_type(SPTYP_SUMMONING))return (false);// Blademasters don't want to throw stuff.if (mon->type == MONS_DEEP_ELF_BLADEMASTER)return (false);return (true);}bool monsters::can_use_missile(const item_def &item) const{// Don't allow monsters to pick up missiles without the corresponding// launcher. The opposite is okay, and sufficient wandering will// hopefully take the monster to a stack of appropriate missiles.if (!_needs_ranged_attack(this))return (false);if (item.base_type == OBJ_WEAPONS|| item.base_type == OBJ_MISSILES && !has_launcher(item)){return (is_throwable(this, item));}// Darts and stones are allowed even without launcher.if (item.sub_type == MI_DART || item.sub_type == MI_STONE)return (true);item_def *launch;for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){launch = mslot_item(static_cast<mon_inv_type>(i));if (launch && fires_ammo_type(*launch) == item.sub_type)return (true);}// No fitting launcher in inventory.return (false);}void monsters::swap_slots(mon_inv_type a, mon_inv_type b){const int swap = inv[a];inv[a] = inv[b];inv[b] = swap;}void monsters::equip_weapon(item_def &item, int near, bool msg){if (msg && !need_message(near))msg = false;if (msg){snprintf(info, INFO_SIZE, " wields %s.",item.name(DESC_NOCAP_A, false, false, true, false,ISFLAG_CURSED).c_str());msg = simple_monster_message(this, info);}const int brand = get_weapon_brand(item);if (brand == SPWPN_PROTECTION)ac += 5;if (msg){bool message_given = true;switch (brand){case SPWPN_FLAMING:mpr("It bursts into flame!");break;case SPWPN_FREEZING:mpr("It glows with a cold blue light!");break;case SPWPN_HOLY_WRATH:mpr("It softly glows with a divine radiance!");break;case SPWPN_ELECTROCUTION:mpr("You hear the crackle of electricity.", MSGCH_SOUND);break;case SPWPN_VENOM:mpr("It begins to drip with poison!");break;case SPWPN_DRAINING:mpr("You sense an unholy aura.");break;case SPWPN_FLAME:mpr("It bursts into flame!");break;case SPWPN_FROST:mpr("It is covered in frost.");break;case SPWPN_RETURNING:mpr("It wiggles slightly.");break;case SPWPN_DISTORTION:mpr("Its appearance distorts for a moment.");break;case SPWPN_CHAOS:mpr("It is briefly surrounded by a scintillating aura of ""random colours.");break;case SPWPN_PENETRATION:mprf("%s %s briefly pass through it before %s manages to get a ""firm grip on it.",pronoun(PRONOUN_CAP_POSSESSIVE).c_str(),hand_name(true).c_str(),pronoun(PRONOUN_NOCAP).c_str());break;case SPWPN_REAPING:mpr("It is briefly surrounded by shifting shadows.");break;default:// A ranged weapon without special message is known to be unbranded.if (brand != SPWPN_NORMAL || !is_range_weapon(item))message_given = false;}if (message_given){if (is_artefact(item) && !is_special_unrandom_artefact(item))artefact_wpn_learn_prop(item, ARTP_BRAND);elseset_ident_flags(item, ISFLAG_KNOW_TYPE);}}}void monsters::equip_armour(item_def &item, int near){if (need_message(near)){snprintf(info, INFO_SIZE, " wears %s.",item.name(DESC_NOCAP_A).c_str());simple_monster_message(this, info);}const equipment_type eq = get_armour_slot(item);if (eq != EQ_SHIELD){ac += property( item, PARM_AC );const int armour_plus = item.plus;ASSERT(abs(armour_plus) < 20);if (abs(armour_plus) < 20)ac += armour_plus;}// Shields can affect evasion.ev += property( item, PARM_EVASION ) / 2;if (ev < 1)ev = 1; // This *shouldn't* happen.}void monsters::equip(item_def &item, int slot, int near){switch (item.base_type){case OBJ_WEAPONS:{bool give_msg = (slot == MSLOT_WEAPON || mons_wields_two_weapons(this));equip_weapon(item, near, give_msg);break;}case OBJ_ARMOUR:equip_armour(item, near);break;default:break;}}void monsters::unequip_weapon(item_def &item, int near, bool msg){if (msg && !need_message(near))msg = false;if (msg){snprintf(info, INFO_SIZE, " unwields %s.",item.name(DESC_NOCAP_A, false, false, true, false,ISFLAG_CURSED).c_str());msg = simple_monster_message(this, info);}const int brand = get_weapon_brand(item);if (brand == SPWPN_PROTECTION)ac -= 5;if (msg && brand != SPWPN_NORMAL){bool message_given = true;switch (brand){case SPWPN_FLAMING:mpr("It stops flaming.");break;case SPWPN_HOLY_WRATH:mpr("It stops glowing.");break;case SPWPN_ELECTROCUTION:mpr("It stops crackling.");break;case SPWPN_VENOM:mpr("It stops dripping with poison.");break;case SPWPN_DISTORTION:mpr("Its appearance distorts for a moment.");break;default:message_given = false;}if (message_given){if (is_artefact(item) && !is_special_unrandom_artefact(item))artefact_wpn_learn_prop(item, ARTP_BRAND);elseset_ident_flags(item, ISFLAG_KNOW_TYPE);}}}void monsters::unequip_armour(item_def &item, int near){if (need_message(near)){snprintf(info, INFO_SIZE, " takes off %s.",item.name(DESC_NOCAP_A).c_str());simple_monster_message(this, info);}const equipment_type eq = get_armour_slot(item);if (eq != EQ_SHIELD){ac -= property( item, PARM_AC );const int armour_plus = item.plus;ASSERT(abs(armour_plus) < 20);if (abs(armour_plus) < 20)ac -= armour_plus;}ev -= property( item, PARM_EVASION ) / 2;if (ev < 1)ev = 1; // This *shouldn't* happen.}bool monsters::unequip(item_def &item, int slot, int near, bool force){if (!force && item.cursed())return (false);if (!force && you.can_see(this))set_ident_flags(item, ISFLAG_KNOW_CURSE);switch (item.base_type){case OBJ_WEAPONS:{bool give_msg = (slot == MSLOT_WEAPON || mons_wields_two_weapons(this));unequip_weapon(item, near, give_msg);break;}case OBJ_ARMOUR:unequip_armour(item, near);break;default:break;}return (true);}void monsters::lose_pickup_energy(){if (const monsterentry* entry = find_monsterentry()){const int delta = speed * entry->energy_usage.pickup_percent / 100;if (speed_increment > 25 && delta < speed_increment)speed_increment -= delta;}}void monsters::pickup_message(const item_def &item, int near){if (need_message(near)){mprf("%s picks up %s.",name(DESC_CAP_THE).c_str(),item.base_type == OBJ_GOLD ? "some gold": item.name(DESC_NOCAP_A).c_str());}}bool monsters::pickup(item_def &item, int slot, int near, bool force_merge){ASSERT(is_valid_item(item));const monsters *other_mon = item.holding_monster();if (other_mon != NULL){if (other_mon == this){if (inv[slot] == item.index()){mprf(MSGCH_DIAGNOSTICS, "Monster %s already holding item %s.",name(DESC_PLAIN, true).c_str(),item.name(DESC_PLAIN, false, true).c_str());return (false);}else{mprf(MSGCH_DIAGNOSTICS, "Item %s thinks it's already held by ""monster %s.",item.name(DESC_PLAIN, false, true).c_str(),name(DESC_PLAIN, true).c_str());}}else if (other_mon->type == MONS_NO_MONSTER){mprf(MSGCH_DIAGNOSTICS, "Item %s, held by dead monster, being ""picked up by monster %s.",item.name(DESC_PLAIN, false, true).c_str(),name(DESC_PLAIN, true).c_str());}else{mprf(MSGCH_DIAGNOSTICS, "Item %s, held by monster %s, being ""picked up by monster %s.",item.name(DESC_PLAIN, false, true).c_str(),other_mon->name(DESC_PLAIN, true).c_str(),name(DESC_PLAIN, true).c_str());}}// If a monster chooses a two-handed weapon as main weapon, it will// first have to drop any shield it might wear.// (Monsters will always favour damage over protection.)if ((slot == MSLOT_WEAPON || slot == MSLOT_ALT_WEAPON)&& inv[MSLOT_SHIELD] != NON_ITEM&& hands_reqd(item, body_size()) == HANDS_TWO){if (!drop_item(MSLOT_SHIELD, near))return (false);}// Similarly, monsters won't pick up shields if they're// wielding (or alt-wielding) a two-handed weapon.if (slot == MSLOT_SHIELD){const item_def* wpn = mslot_item(MSLOT_WEAPON);const item_def* alt = mslot_item(MSLOT_ALT_WEAPON);if (wpn && hands_reqd(*wpn, body_size()) == HANDS_TWO)return (false);if (alt && hands_reqd(*alt, body_size()) == HANDS_TWO)return (false);}if (inv[slot] != NON_ITEM){item_def &dest(mitm[inv[slot]]);if (items_stack(item, dest, force_merge)){dungeon_events.fire_position_event(dgn_event(DET_ITEM_PICKUP, pos(), 0, item.index(),monster_index(this)),pos());pickup_message(item, near);inc_mitm_item_quantity( inv[slot], item.quantity );merge_item_stacks(item, dest);destroy_item(item.index());equip(item, slot, near);lose_pickup_energy();return (true);}return (false);}dungeon_events.fire_position_event(dgn_event(DET_ITEM_PICKUP, pos(), 0, item.index(),monster_index(this)),pos());const int item_index = item.index();unlink_item(item_index);inv[slot] = item_index;item.set_holding_monster(mindex());pickup_message(item, near);equip(item, slot, near);lose_pickup_energy();return (true);}bool monsters::drop_item(int eslot, int near){if (eslot < 0 || eslot >= NUM_MONSTER_SLOTS)return (false);int item_index = inv[eslot];if (item_index == NON_ITEM)return (true);item_def* pitem = &mitm[item_index];// Unequip equipped items before dropping them; unequip() prevents// cursed items from being removed.bool was_unequipped = false;if (eslot == MSLOT_WEAPON || eslot == MSLOT_ARMOUR|| eslot == MSLOT_ALT_WEAPON && mons_wields_two_weapons(this)){if (!unequip(*pitem, eslot, near))return (false);was_unequipped = true;}bool on_floor = true;if (pitem->flags & ISFLAG_SUMMONED){on_floor = false;if (need_message(near))mprf("%s %s as %s drops %s!",pitem->name(DESC_CAP_THE).c_str(),summoned_poof_msg(this, *pitem).c_str(),name(DESC_NOCAP_THE).c_str(),pitem->quantity > 1 ? "them" : "it");item_was_destroyed(*pitem, mindex());destroy_item(item_index);}else if (!move_item_to_grid(&item_index, pos())){// Re-equip item if we somehow failed to drop it.if (was_unequipped)equip(*pitem, eslot, near);return (false);}// move_item_to_grid could change item_index, so// update pitem.pitem = &mitm[item_index];if (on_floor){if (mons_friendly(this))pitem->flags |= ISFLAG_DROPPED_BY_ALLY;if (need_message(near)){mprf("%s drops %s.", name(DESC_CAP_THE).c_str(),pitem->name(DESC_NOCAP_A).c_str());}dungeon_feature_type feat = grd(pos());if (feat_destroys_items(feat)){if ( player_can_hear(pos()) )mprf(MSGCH_SOUND, feat_item_destruction_message(feat));item_was_destroyed(*pitem, mindex());unlink_item(item_index);}}inv[eslot] = NON_ITEM;return (true);}// We don't want monsters to pick up ammunition that cancels out with// the launcher brand or that is identical to the launcher brand,// the latter in hope of another monster wandering by who may want to// use the ammo in question.static bool _compatible_launcher_ammo_brands(item_def *launcher,const item_def *ammo){// If the monster has no ammo then there's no compatibility problems// to check.if (ammo == NULL)return (true);const int bow_brand = get_weapon_brand(*launcher);const int ammo_brand = get_ammo_brand(*ammo);switch (ammo_brand){case SPMSL_FLAME:case SPMSL_FROST:return (bow_brand != SPWPN_FLAME && bow_brand != SPWPN_FROST);case SPMSL_CHAOS:return (bow_brand != SPWPN_CHAOS);default:return (true);}}bool monsters::pickup_launcher(item_def &launch, int near){// Don't allow monsters to pick up launchers that would also// refuse to pick up the matching ammo.if (!_needs_ranged_attack(this))return (false);// Don't allow monsters to switch to another type of launcher// as that would require them to also drop their ammunition// and then try to find ammunition for their new launcher.// However, they may switch to another launcher if they're// out of ammo. (jpeg)const int mdam_rating = mons_weapon_damage_rating(launch);const missile_type mt = fires_ammo_type(launch);int eslot = -1;for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){if (const item_def *elaunch = mslot_item(static_cast<mon_inv_type>(i))){if (!is_range_weapon(*elaunch))continue;return ((fires_ammo_type(*elaunch) == mt || !missiles())&& (mons_weapon_damage_rating(*elaunch) < mdam_rating|| mons_weapon_damage_rating(*elaunch) == mdam_rating&& get_weapon_brand(*elaunch) == SPWPN_NORMAL&& get_weapon_brand(launch) != SPWPN_NORMAL&& _compatible_launcher_ammo_brands(&launch,missiles()))&& drop_item(i, near) && pickup(launch, i, near));}elseeslot = i;}return (eslot == -1 ? false : pickup(launch, eslot, near));}static bool _is_signature_weapon(monsters *monster, const item_def &weapon){if (weapon.base_type != OBJ_WEAPONS)return (false);if (monster->type == MONS_DAEVA)return (weapon.sub_type == WPN_BLESSED_EUDEMON_BLADE);// We might allow Sigmund to pick up a better scythe if he finds one...if (monster->type == MONS_SIGMUND)return (weapon.sub_type == WPN_SCYTHE);if (is_unrandom_artefact(weapon)){switch (weapon.special){case UNRAND_ASMODEUS:return (monster->type == MONS_ASMODEUS);case UNRAND_DISPATER:return (monster->type == MONS_DISPATER);case UNRAND_CEREBOV:return (monster->type == MONS_CEREBOV);}}return (false);}static int _ego_damage_bonus(item_def &item){switch (get_weapon_brand(item)){case SPWPN_NORMAL: return 0;case SPWPN_PROTECTION: return 1;default: return 2;case SPWPN_VORPAL: return 3;}}static bool _item_race_matches_monster(const item_def &item, monsters *mons){return (get_equip_race(item) == ISFLAG_ELVEN&& mons_genus(mons->type) == MONS_ELF|| get_equip_race(item) == ISFLAG_ORCISH&& mons_genus(mons->type) == MONS_ORC);}bool monsters::pickup_melee_weapon(item_def &item, int near){// Throwable weapons may be picked up as though dual-wielding.const bool dual_wielding = (mons_wields_two_weapons(this)|| is_throwable(this, item));if (dual_wielding){// If we have either weapon slot free, pick up the weapon.if (inv[MSLOT_WEAPON] == NON_ITEM)return pickup(item, MSLOT_WEAPON, near);if (inv[MSLOT_ALT_WEAPON] == NON_ITEM)return pickup(item, MSLOT_ALT_WEAPON, near);}const int new_wpn_dam = mons_weapon_damage_rating(item)+ _ego_damage_bonus(item);int eslot = -1;item_def *weap;// Monsters have two weapon slots, one of which can be a ranged, and// the other a melee weapon. (The exception being dual-wielders who can// wield two melee weapons). The weapon in MSLOT_WEAPON is the one// currently wielded (can be empty).for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){weap = mslot_item(static_cast<mon_inv_type>(i));if (!weap){// If no weapon in this slot, mark this one.if (eslot == -1)eslot = i;}else{if (is_range_weapon(*weap))continue;// Don't drop weapons specific to the monster.if (_is_signature_weapon(this, *weap) && !dual_wielding)return (false);// If we get here, the weapon is a melee weapon.// If the new weapon is better than the current one and not cursed,// replace it. Otherwise, give up.const int old_wpn_dam = mons_weapon_damage_rating(*weap)+ _ego_damage_bonus(*weap);bool new_wpn_better = (new_wpn_dam > old_wpn_dam);if (new_wpn_dam == old_wpn_dam){// Use shopping value as a crude estimate of resistances etc.// XXX: This is not really logical as many properties don't// apply to monsters (e.g. levitation, blink, berserk).// For simplicity, don't apply this check to secondary weapons// for dual wielding monsters.int oldval = item_value(*weap, true);int newval = item_value(item, true);// Vastly prefer matching racial type.if (_item_race_matches_monster(*weap, this))oldval *= 2;if (_item_race_matches_monster(item, this))newval *= 2;if (newval > oldval)new_wpn_better = true;}if (new_wpn_better && !weap->cursed()){if (!dual_wielding|| i == MSLOT_WEAPON|| old_wpn_dam< mons_weapon_damage_rating(*mslot_item(MSLOT_WEAPON))+ _ego_damage_bonus(*mslot_item(MSLOT_WEAPON))){eslot = i;if (!dual_wielding)break;}}else if (!dual_wielding){// We've got a good melee weapon, that's enough.return (false);}}}// No slot found to place this item.if (eslot == -1)return (false);// Current item cannot be dropped.if (inv[eslot] != NON_ITEM && !drop_item(eslot, near))return (false);return (pickup(item, eslot, near));}// Arbitrary damage adjustment for quantity of missiles. So sue me.static int _q_adj_damage(int damage, int qty){return (damage * std::min(qty, 8));}bool monsters::pickup_throwable_weapon(item_def &item, int near){const mon_inv_type slot = item_to_mslot(item);// If it's a melee weapon then pickup_melee_weapon() already rejected// it, even though it can also be thrown.if (slot == MSLOT_WEAPON)return (false);ASSERT(slot == MSLOT_MISSILE);// If occupied, don't pick up a throwable weapons if it would just// stack with an existing one. (Upgrading is possible.)if (mslot_item(slot)&& (mons_is_wandering(this) || mons_friendly(this) && foe == MHITYOU)&& pickup(item, slot, near, true)){return (true);}item_def *launch = NULL;const int exist_missile = mons_pick_best_missile(this, &launch, true);if (exist_missile == NON_ITEM|| (_q_adj_damage(mons_missile_damage(this, launch,&mitm[exist_missile]),mitm[exist_missile].quantity)< _q_adj_damage(mons_thrown_weapon_damage(&item), item.quantity))){if (inv[slot] != NON_ITEM && !drop_item(slot, near))return (false);return pickup(item, slot, near);}return (false);}bool monsters::wants_weapon(const item_def &weap) const{if (!could_wield(weap))return (false);// Blademasters and master archers like their starting weapon and// don't want another, thank you.if (type == MONS_DEEP_ELF_BLADEMASTER|| type == MONS_DEEP_ELF_MASTER_ARCHER){return (false);}// Monsters capable of dual-wielding will always prefer two weapons// to a single two-handed one, however strong.if (mons_wields_two_weapons(this)&& hands_reqd(weap, body_size()) == HANDS_TWO){return (false);}// Nobody picks up giant clubs. Starting equipment is okay, of course.if (weap.sub_type == WPN_GIANT_CLUB|| weap.sub_type == WPN_GIANT_SPIKED_CLUB){return (false);}return (true);}bool monsters::wants_armour(const item_def &item) const{// Monsters that are capable of dual wielding won't pick up shields.// Neither will monsters that are already wielding a two-hander.if (is_shield(item)&& (mons_wields_two_weapons(this)|| mslot_item(MSLOT_WEAPON)&& hands_reqd(*mslot_item(MSLOT_WEAPON), body_size())== HANDS_TWO)){return (false);}// Returns whether this armour is the monster's size.return (check_armour_size(item, body_size()));}bool monsters::pickup_armour(item_def &item, int near, bool force){ASSERT(item.base_type == OBJ_ARMOUR);if (!force && !wants_armour(item))return (false);equipment_type eq = EQ_NONE;// HACK to allow nagas/centaurs to wear bardings. (jpeg)switch (item.sub_type){case ARM_NAGA_BARDING:if (::mons_species(this->type) == MONS_NAGA)eq = EQ_BODY_ARMOUR;break;case ARM_CENTAUR_BARDING:if (::mons_species(this->type) == MONS_CENTAUR|| ::mons_species(this->type) == MONS_YAKTAUR){eq = EQ_BODY_ARMOUR;}break;// And another hack or two...case ARM_WIZARD_HAT:if (this->type == MONS_GASTRONOK)eq = EQ_BODY_ARMOUR;break;case ARM_CLOAK:if (this->type == MONS_MAURICE)eq = EQ_BODY_ARMOUR;break;default:eq = get_armour_slot(item);}// Bardings are only wearable by the appropriate monster.if (eq == EQ_NONE)return (false);// XXX: Monsters can only equip body armour and shields (as of 0.4).if (!force && eq != EQ_BODY_ARMOUR && eq != EQ_SHIELD)return (false);const mon_inv_type mslot = equip_slot_to_mslot(eq);if (mslot == NUM_MONSTER_SLOTS)return (false);int newAC = item.armour_rating();// No armour yet -> get this one.if (!mslot_item(mslot) && newAC > 0)return pickup(item, mslot, near);// Very simplistic armour evaluation (AC comparison).if (const item_def *existing_armour = slot_item(eq)){if (!force){int oldAC = existing_armour->armour_rating();if (oldAC > newAC)return (false);if (oldAC == newAC){// Use shopping value as a crude estimate of resistances etc.// XXX: This is not really logical as many properties don't// apply to monsters (e.g. levitation, blink, berserk).int oldval = item_value(*existing_armour, true);int newval = item_value(item, true);// Vastly prefer matching racial type.if (_item_race_matches_monster(*existing_armour, this))oldval *= 2;if (_item_race_matches_monster(item, this))newval *= 2;if (oldval >= newval)return (false);}}if (!drop_item(mslot, near))return (false);}return pickup(item, mslot, near);}bool monsters::pickup_weapon(item_def &item, int near, bool force){if (!force && !wants_weapon(item))return (false);// Weapon pickup involves:// - If we have no weapons, always pick this up.// - If this is a melee weapon and we already have a melee weapon, pick// it up if it is superior to the one we're carrying (and drop the// one we have).// - If it is a ranged weapon, and we already have a ranged weapon,// pick it up if it is better than the one we have.// - If it is a throwable weapon, and we're carrying no missiles (or our// missiles are the same type), pick it up.if (is_range_weapon(item))return (pickup_launcher(item, near));if (pickup_melee_weapon(item, near))return (true);return (can_use_missile(item) && pickup_throwable_weapon(item, near));}bool monsters::pickup_missile(item_def &item, int near, bool force){const item_def *miss = missiles();if (!force){if (item.sub_type == MI_THROWING_NET){// Monster may not pick up trapping net.if (mons_is_caught(this) && item_is_stationary(item))return (false);}else // None of these exceptions hold for throwing nets.{// Spellcasters should not waste time with ammunition.// Neither summons nor hostile enchantments are counted for// this purpose.if (mons_has_ranged_spell(this, true, false))return (false);// Monsters in a fight will only pick up missiles if doing so// is worthwhile.if (!mons_is_wandering(this)&& (!mons_friendly(this) || foe != MHITYOU)&& (item.quantity < 5 || miss && miss->quantity >= 7)){return (false);}}}if (miss && items_stack(*miss, item))return (pickup(item, MSLOT_MISSILE, near));if (!force && !can_use_missile(item))return (false);if (miss){item_def *launch;for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){launch = mslot_item(static_cast<mon_inv_type>(i));if (launch){const int item_brand = get_ammo_brand(item);// If this ammunition is better, drop the old ones.// Don't upgrade to ammunition whose brand cancels the// launcher brand or doesn't improve it further.if (fires_ammo_type(*launch) == item.sub_type&& (fires_ammo_type(*launch) != miss->sub_type|| item.plus > miss->plus&& get_ammo_brand(*miss) == item_brand|| item.plus >= miss->plus&& get_ammo_brand(*miss) == SPMSL_NORMAL&& item_brand != SPMSL_NORMAL&&_compatible_launcher_ammo_brands(launch, miss))){if (!drop_item(MSLOT_MISSILE, near))return (false);break;}}}// Darts don't absolutely need a launcher - still allow upgrading.if (item.sub_type == miss->sub_type&& item.sub_type == MI_DART&& (item.plus > miss->plus|| item.plus == miss->plus&& get_ammo_brand(*miss) == SPMSL_NORMAL&& get_ammo_brand(item) != SPMSL_NORMAL)){if (!drop_item(MSLOT_MISSILE, near))return (false);}}return pickup(item, MSLOT_MISSILE, near);}bool monsters::pickup_wand(item_def &item, int near){// Don't pick up empty wands.if (item.plus == 0)return (false);// Only low-HD monsters bother with wands.if (hit_dice >= 14)return (false);// Holy monsters and worshippers of good gods won't pick up evil// wands.if ((mons_is_holy(this) || is_good_god(god)) && is_evil_item(item))return (false);// If a monster already has a charged wand, don't bother.// Otherwise, replace with a charged one.if (item_def *wand = mslot_item(MSLOT_WAND)){if (wand->plus > 0)return (false);if (!drop_item(MSLOT_WAND, near))return (false);}return (pickup(item, MSLOT_WAND, near));}bool monsters::pickup_scroll(item_def &item, int near){if (item.sub_type != SCR_TELEPORTATION&& item.sub_type != SCR_BLINKING&& item.sub_type != SCR_SUMMONING){return (false);}// Holy monsters and worshippers of good gods won't pick up evil// scrolls.if ((mons_is_holy(this) || is_good_god(god)) && is_evil_item(item))return (false);return (pickup(item, MSLOT_SCROLL, near));}bool monsters::pickup_potion(item_def &item, int near){// Only allow monsters to pick up potions if they can actually use// them.const potion_type ptype = static_cast<potion_type>(item.sub_type);if (!this->can_drink_potion(ptype))return (false);return (pickup(item, MSLOT_POTION, near));}bool monsters::pickup_gold(item_def &item, int near){return (pickup(item, MSLOT_GOLD, near));}bool monsters::pickup_misc(item_def &item, int near){// Never pick up runes.if (item.sub_type == MISC_RUNE_OF_ZOT)return (false);// Holy monsters and worshippers of good gods won't pick up evil// miscellaneous items.if ((mons_is_holy(this) || is_good_god(god)) && is_evil_item(item))return (false);return (pickup(item, MSLOT_MISCELLANY, near));}// Eaten items are handled elsewhere, in _handle_pickup() in monstuff.cc.bool monsters::pickup_item(item_def &item, int near, bool force){// Equipping stuff can be forced when initially equipping monsters.if (!force){// If a monster isn't otherwise occupied (has a foe, is fleeing, etc.)// it is considered wandering.bool wandering = (mons_is_wandering(this)|| mons_friendly(this) && foe == MHITYOU);const int itype = item.base_type;// Weak(ened) monsters won't stop to pick up things as long as they// feel unsafe.if (!wandering && (hit_points * 10 < max_hit_points || hit_points < 10)&& mon_enemies_around(this)){return (false);}if (mons_friendly(this)){// Never pick up gold or misc. items, it'd only annoy the player.if (itype == OBJ_MISCELLANY || itype == OBJ_GOLD)return (false);// Depending on the friendly pickup toggle, your allies may not// pick up anything, or only stuff dropped by (other) allies.if (you.friendly_pickup == FRIENDLY_PICKUP_NONE|| you.friendly_pickup == FRIENDLY_PICKUP_FRIEND&& !testbits(item.flags, ISFLAG_DROPPED_BY_ALLY)|| you.friendly_pickup == FRIENDLY_PICKUP_PLAYER&& !(item.flags & (ISFLAG_DROPPED | ISFLAG_THROWN| ISFLAG_DROPPED_BY_ALLY))){return (false);}}if (!wandering){// These are not important enough for pickup when// seeking, fleeing etc.if (itype == OBJ_ARMOUR || itype == OBJ_CORPSES|| itype == OBJ_MISCELLANY || itype == OBJ_GOLD){return (false);}if (itype == OBJ_WEAPONS || itype == OBJ_MISSILES){// Fleeing monsters only pick up emergency equipment.if (mons_is_fleeing(this))return (false);// While occupied, hostile monsters won't pick up items// dropped or thrown by you. (You might have done that to// distract them.)if (!mons_friendly(this)&& (testbits(item.flags, ISFLAG_DROPPED)|| testbits(item.flags, ISFLAG_THROWN))){return (false);}}}}switch (item.base_type){// Pickup some stuff only if WANDERING.case OBJ_ARMOUR:return pickup_armour(item, near, force);case OBJ_MISCELLANY:return pickup_misc(item, near);case OBJ_GOLD:return pickup_gold(item, near);// Fleeing monsters won't pick up these.// Hostiles won't pick them up if they were ever dropped/thrown by you.case OBJ_WEAPONS:return pickup_weapon(item, near, force);case OBJ_MISSILES:return pickup_missile(item, near, force);// Other types can always be picked up// (barring other checks depending on subtype, of course).case OBJ_WANDS:return pickup_wand(item, near);case OBJ_SCROLLS:return pickup_scroll(item, near);case OBJ_POTIONS:return pickup_potion(item, near);case OBJ_BOOKS:if (force)return pickup_misc(item, near);// else fall throughdefault:return (false);}}bool monsters::need_message(int &near) const{return (near != -1 ? near: (near = observable()));}void monsters::swap_weapons(int near){item_def *weap = mslot_item(MSLOT_WEAPON);item_def *alt = mslot_item(MSLOT_ALT_WEAPON);if (weap && !unequip(*weap, MSLOT_WEAPON, near)){// Item was cursed.return;}swap_slots(MSLOT_WEAPON, MSLOT_ALT_WEAPON);if (alt)equip(*alt, MSLOT_WEAPON, near);// Monsters can swap weapons really fast. :-)if ((weap || alt) && speed_increment >= 2){if (const monsterentry *entry = find_monsterentry())speed_increment -= div_rand_round(entry->energy_usage.attack, 5);}}void monsters::wield_melee_weapon(int near){const item_def *weap = mslot_item(MSLOT_WEAPON);if (!weap || (!weap->cursed() && is_range_weapon(*weap))){const item_def *alt = mslot_item(MSLOT_ALT_WEAPON);// Switch to the alternate weapon if it's not a ranged weapon, too,// or switch away from our main weapon if it's a ranged weapon.if (alt && !is_range_weapon(*alt) || weap && !alt)swap_weapons(near);}}item_def *monsters::slot_item(equipment_type eq){return (mslot_item(equip_slot_to_mslot(eq)));}item_def *monsters::mslot_item(mon_inv_type mslot) const{const int mi = (mslot == NUM_MONSTER_SLOTS) ? NON_ITEM : inv[mslot];return (mi == NON_ITEM ? NULL : &mitm[mi]);}item_def *monsters::shield(){return (mslot_item(MSLOT_SHIELD));}bool monsters::is_named() const{return (!mname.empty() || mons_is_unique(type));}bool monsters::has_base_name() const{// Any non-ghost, non-Pandemonium demon that has an explicitly set// name has a base name.return (!mname.empty() && !ghost.get());}static const char *ugly_colour_names[] = {"red", "brown", "green", "cyan", "purple", "white"};static std::string _ugly_thing_colour_name(const monsters *mon){const int colour_offset = ugly_thing_colour_offset(mon);if (colour_offset == -1)return ("buggy");return (ugly_colour_names[colour_offset]);}static std::string _str_monam(const monsters& mon, description_level_type desc,bool force_seen){if (mon.type == MONS_NO_MONSTER)return ("DEAD MONSTER");else if (invalid_monster_type(mon.type) && mon.type != MONS_PROGRAM_BUG)return make_stringf("INVALID MONSTER (#%d)", mon.type);const bool arena_submerged = crawl_state.arena && !force_seen&& mon.submerged();// Handle non-visible case first.if (!force_seen && !you.can_see(&mon) && !arena_submerged){switch (desc){case DESC_CAP_THE: case DESC_CAP_A:return ("It");case DESC_NOCAP_THE: case DESC_NOCAP_A: case DESC_PLAIN:return ("it");default:return ("it (buggy)");}}// Assumed visible from now on.// Various special cases:// non-gold mimics, dancing weapons, ghosts, Pan demonsif (mons_is_mimic(mon.type) && mon.type != MONS_GOLD_MIMIC){item_def item;get_mimic_item(&mon, item);return (item.name(desc));}if (mon.type == MONS_DANCING_WEAPON && mon.inv[MSLOT_WEAPON] != NON_ITEM){unsigned long ignore_flags = ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES;bool use_inscrip = true;if (desc == DESC_BASENAME || desc == DESC_QUALNAME|| desc == DESC_DBNAME){use_inscrip = false;}const item_def& item = mitm[mon.inv[MSLOT_WEAPON]];return (item.name(desc, false, false, use_inscrip, false,ignore_flags));}if (desc == DESC_DBNAME)return (get_monster_data(mon.type)->name);if (mon.type == MONS_PLAYER_GHOST)return (apostrophise(mon.mname) + " ghost");// Some monsters might want the name of a different creature.monster_type nametype = mon.type;// Tack on other prefixes.switch (mon.type){case MONS_ZOMBIE_SMALL: case MONS_ZOMBIE_LARGE:case MONS_SKELETON_SMALL: case MONS_SKELETON_LARGE:case MONS_SIMULACRUM_SMALL: case MONS_SIMULACRUM_LARGE:case MONS_SPECTRAL_THING:nametype = mon.base_monster;break;default:break;}// If the monster has an explicit name, return that, handling it like// a unique's name. Special handling for named hydras.if (desc != DESC_BASENAME && !mon.mname.empty()&& mons_genus(nametype) != MONS_HYDRA){return (mon.mname);}std::string result;// Start building the name string.// Start with the prefix.// (Uniques don't get this, because their names are proper nouns.)if (!mons_is_unique(nametype)&& (mon.mname.empty() || mons_genus(nametype) == MONS_HYDRA)){const bool use_your = mons_friendly(&mon);switch (desc){case DESC_CAP_THE:result = (use_your ? "Your " : "The ");break;case DESC_NOCAP_THE:result = (use_your ? "your " : "the ");break;case DESC_CAP_A:if (mon.mname.empty())result = "A ";elseresult = "The ";break;case DESC_NOCAP_A:if (mon.mname.empty())result = "a ";elseresult = "the ";break;case DESC_PLAIN:default:break;}}if (arena_submerged)result += "submerged ";// Tack on other prefixes.switch (mon.type){case MONS_UGLY_THING:case MONS_VERY_UGLY_THING:result += _ugly_thing_colour_name(&mon) + " ";break;case MONS_SPECTRAL_THING:result += "spectral ";break;case MONS_DRACONIAN_CALLER:case MONS_DRACONIAN_MONK:case MONS_DRACONIAN_ZEALOT:case MONS_DRACONIAN_SHIFTER:case MONS_DRACONIAN_ANNIHILATOR:case MONS_DRACONIAN_KNIGHT:case MONS_DRACONIAN_SCORCHER:if (mon.base_monster != MONS_NO_MONSTER) // database searchresult += draconian_colour_name(mon.base_monster) + " ";break;default:break;}if (mon.mons_species() == MONS_SLIME_CREATURE && desc != DESC_DBNAME){ASSERT(mon.number <= 5);const char* cardinals[] = {"", "large ", "very large ","enormous ", "titanic "};result += cardinals[mon.number - 1];}// Done here to cover cases of undead versions of hydras.if (mons_species(nametype) == MONS_HYDRA&& mon.number > 0 && desc != DESC_DBNAME){if (nametype == MONS_LERNAEAN_HYDRA)result += "the ";if (mon.number < 11){const char* cardinals[] = {"one", "two", "three", "four", "five","six", "seven", "eight", "nine", "ten"};result += cardinals[mon.number - 1];}elseresult += make_stringf("%d", mon.number);result += "-headed ";}if (!mon.mname.empty())result += mon.mname;else if (nametype == MONS_LERNAEAN_HYDRA)result += "Lernaean hydra";else{// Add the base name.if (invalid_monster_type(nametype) && nametype != MONS_PROGRAM_BUG)result += make_stringf("INVALID MONSTER (#%d)", nametype);elseresult += get_monster_data(nametype)->name;}// Add suffixes.switch (mon.type){case MONS_ZOMBIE_SMALL:case MONS_ZOMBIE_LARGE:result += " zombie";break;case MONS_SKELETON_SMALL:case MONS_SKELETON_LARGE:result += " skeleton";break;case MONS_SIMULACRUM_SMALL:case MONS_SIMULACRUM_LARGE:result += " simulacrum";break;default:break;}// Vowel fix: Change 'a orc' to 'an orc'.if (result.length() >= 3&& (result[0] == 'a' || result[0] == 'A')&& result[1] == ' '&& is_vowel(result[2])// XXX: Hack&& !starts_with(&result[2], "one-")){result.insert(1, "n");}if (mons_is_unique(mon.type) && starts_with(result, "the ")){switch (desc){case DESC_CAP_THE:case DESC_CAP_A:result = upcase_first(result);break;default:break;}}if ((mon.flags & MF_KNOWN_MIMIC) && mons_is_shapeshifter(&mon)){// If momentarily in original form, don't display "shaped// shifter".if (mons_genus(mon.type) != MONS_SHAPESHIFTER)result += " shaped shifter";}// All done.return (result);}std::string monsters::name(description_level_type desc, bool force_vis) const{if (desc == DESC_NONE)return ("");const bool possessive =(desc == DESC_NOCAP_YOUR || desc == DESC_NOCAP_ITS);if (possessive)desc = DESC_NOCAP_THE;std::string monnam;if ((flags & MF_NAME_MASK) && (force_vis || you.can_see(this))|| crawl_state.arena && mons_class_is_zombified(type)){monnam = full_name(desc);}elsemonnam = _str_monam(*this, desc, force_vis);return (possessive ? apostrophise(monnam) : monnam);}std::string monsters::base_name(description_level_type desc, bool force_vis)const{if (desc == DESC_NONE)return ("");if (ghost.get() || mons_is_unique(type))return (name(desc, force_vis));else{unwind_var<std::string> tmname(const_cast<monsters*>(this)->mname, "");return (name(desc, force_vis));}}std::string monsters::full_name(description_level_type desc,bool use_comma) const{if (desc == DESC_NONE)return ("");std::string title = _str_monam(*this, desc, true);const unsigned long flag = flags & MF_NAME_MASK;const int _type = mons_is_zombified(this) ? base_monster : type;if (mons_genus(_type) == MONS_HYDRA && flag == 0)return (title);if (has_base_name()){if (flag == MF_NAME_SUFFIX){title = base_name(desc, true);title += " ";title += mname;}else if (flag == MF_NAME_NO_THE){title += " ";title += base_name(DESC_PLAIN, true);}else if (flag == MF_NAME_REPLACE);else{if (use_comma)title += ",";title += " ";title += base_name(DESC_NOCAP_THE, true);}}return (title);}std::string monsters::pronoun(pronoun_type pro, bool force_visible) const{return (mons_pronoun(static_cast<monster_type>(type), pro,force_visible || you.can_see(this)));}std::string monsters::conj_verb(const std::string &verb) const{if (!verb.empty() && verb[0] == '!')return (verb.substr(1));if (verb == "are")return ("is");if (ends_with(verb, "f") || ends_with(verb, "fe")|| ends_with(verb, "y")){return (verb + "s");}return (pluralise(verb));}std::string monsters::hand_name(bool plural, bool *can_plural) const{bool _can_plural;if (can_plural == NULL)can_plural = &_can_plural;*can_plural = true;std::string str;char ch = mons_char(type);const bool rand = (type == MONS_CHAOS_SPAWN);switch (get_mon_shape(this)){case MON_SHAPE_CENTAUR:case MON_SHAPE_NAGA:// Defaults to "hand"break;case MON_SHAPE_HUMANOID:case MON_SHAPE_HUMANOID_WINGED:case MON_SHAPE_HUMANOID_TAILED:case MON_SHAPE_HUMANOID_WINGED_TAILED:if (ch == 'T' || ch == 'd' || ch == 'n' || mons_is_demon(type))str = "claw";break;case MON_SHAPE_QUADRUPED:case MON_SHAPE_QUADRUPED_TAILLESS:case MON_SHAPE_QUADRUPED_WINGED:case MON_SHAPE_ARACHNID:if (type == MONS_SCORPION || rand && one_chance_in(4))str = "pincer";else{str = "front ";return (str + foot_name(plural, can_plural));}break;case MON_SHAPE_BLOB:case MON_SHAPE_SNAKE:case MON_SHAPE_FISH:return foot_name(plural, can_plural);case MON_SHAPE_BAT:str = "wing";break;case MON_SHAPE_INSECT:case MON_SHAPE_INSECT_WINGED:case MON_SHAPE_CENTIPEDE:str = "antenna";break;case MON_SHAPE_SNAIL:str = "eye-stalk";break;case MON_SHAPE_PLANT:str = "leaf";break;case MON_SHAPE_MISC:if (ch == 'x' || ch == 'X' || rand){str = "tentacle";break;}// Deliberate fallthrough.case MON_SHAPE_FUNGUS:str = "body";*can_plural = false;break;case MON_SHAPE_ORB:switch (type){case MONS_GIANT_SPORE:str = "rhizome";break;case MONS_GIANT_EYEBALL:case MONS_EYE_OF_DRAINING:case MONS_SHINING_EYE:case MONS_EYE_OF_DEVASTATION:*can_plural = false;// Deliberate fallthrough.case MONS_GREAT_ORB_OF_EYES:str = "pupil";break;case MONS_GIANT_ORANGE_BRAIN:default:if (rand)str = "rhizome";else{str = "body";*can_plural = false;}break;}}if (str.empty()){// Reduce the chance of a random-shaped monster having hands.if (rand && coinflip())return (hand_name(plural, can_plural));str = "hand";}if (plural && *can_plural)str = pluralise(str);return (str);}std::string monsters::foot_name(bool plural, bool *can_plural) const{bool _can_plural;if (can_plural == NULL)can_plural = &_can_plural;*can_plural = true;std::string str;char ch = mons_char(type);const bool rand = (type == MONS_CHAOS_SPAWN);switch (get_mon_shape(this)){case MON_SHAPE_INSECT:case MON_SHAPE_INSECT_WINGED:case MON_SHAPE_ARACHNID:case MON_SHAPE_CENTIPEDE:str = "leg";break;case MON_SHAPE_HUMANOID:case MON_SHAPE_HUMANOID_WINGED:case MON_SHAPE_HUMANOID_TAILED:case MON_SHAPE_HUMANOID_WINGED_TAILED:if (type == MONS_MINOTAUR)str = "hoof";else if (swimming()&& (type == MONS_MERFOLK || mons_genus(type) == MONS_MERMAID)){str = "tail";*can_plural = false;}break;case MON_SHAPE_CENTAUR:str = "hoof";break;case MON_SHAPE_QUADRUPED:case MON_SHAPE_QUADRUPED_TAILLESS:case MON_SHAPE_QUADRUPED_WINGED:if (rand){const char* feet[] = {"paw", "talon", "hoof"};str = RANDOM_ELEMENT(feet);}else if (ch == 'h')str = "paw";else if (ch == 'l' || ch == 'D')str = "talon";else if (type == MONS_YAK || type == MONS_DEATH_YAK)str = "hoof";else if (ch == 'H'){if (type == MONS_MANTICORE || type == MONS_SPHINX)str = "paw";elsestr = "talon";}break;case MON_SHAPE_BAT:str = "claw";break;case MON_SHAPE_SNAKE:case MON_SHAPE_FISH:str = "tail";*can_plural = false;break;case MON_SHAPE_PLANT:str = "root";break;case MON_SHAPE_FUNGUS:str = "stem";*can_plural = false;break;case MON_SHAPE_BLOB:str = "pseudopod";break;case MON_SHAPE_MISC:if (ch == 'x' || ch == 'X' || rand){str = "tentacle";break;}// Deliberate fallthrough.case MON_SHAPE_SNAIL:case MON_SHAPE_NAGA:case MON_SHAPE_ORB:str = "underside";*can_plural = false;break;}if (str.empty()){// Reduce the chance of a random-shaped monster having feet.if (rand && coinflip())return (foot_name(plural, can_plural));return (plural ? "feet" : "foot");}if (plural && *can_plural)str = pluralise(str);return (str);}std::string monsters::arm_name(bool plural, bool *can_plural) const{mon_body_shape shape = get_mon_shape(this);if (shape > MON_SHAPE_NAGA)return hand_name(plural, can_plural);if (can_plural != NULL)*can_plural = true;std::string str;switch (mons_genus(type)){case MONS_NAGA:case MONS_DRACONIAN: str = "scaled arm"; break;case MONS_MUMMY: str = "bandaged wrapped arm"; break;case MONS_SKELETAL_WARRIOR:case MONS_LICH: str = "bony arm"; break;default: str = "arm"; break;}if (plural)str = pluralise(str);return (str);}monster_type monsters::id() const{return (type);}int monsters::mindex() const{return (monster_index(this));}int monsters::get_experience_level() const{return (hit_dice);}void monsters::moveto(const coord_def& c){if (c != pos() && in_bounds(pos()))mons_clear_trapping_net(this);position = c;}bool monsters::fumbles_attack(bool verbose){if (floundering() && one_chance_in(4)){if (verbose){if (you.can_see(this)){mprf("%s splashes around in the water.",this->name(DESC_CAP_THE).c_str());}else if (player_can_hear(pos()))mpr("You hear a splashing noise.", MSGCH_SOUND);}return (true);}if (submerged())return (true);return (false);}bool monsters::cannot_fight() const{return (mons_class_flag(type, M_NO_EXP_GAIN)|| mons_is_statue(type));}void monsters::attacking(actor * /* other */){}void monsters::go_berserk(bool /* intentional */){if (!this->can_go_berserk())return;if (has_ench(ENCH_SLOW)){del_ench(ENCH_SLOW, true); // Give no additional message.simple_monster_message(this,make_stringf(" shakes off %s lethargy.",pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str()).c_str());}del_ench(ENCH_HASTE, true);del_ench(ENCH_FATIGUE, true); // Give no additional message.const int duration = 16 + random2avg(13, 2);add_ench(mon_enchant(ENCH_BERSERK, 0, KC_OTHER, duration * 10));add_ench(mon_enchant(ENCH_HASTE, 0, KC_OTHER, duration * 10));add_ench(mon_enchant(ENCH_MIGHT, 0, KC_OTHER, duration * 10));if (simple_monster_message( this, " goes berserk!" ))// Xom likes monsters going berserk.xom_is_stimulated(mons_friendly(this) ? 32 : 128);}void monsters::expose_to_element(beam_type flavour, int strength){switch (flavour){case BEAM_COLD:if (mons_class_flag(this->type, M_COLD_BLOOD) && this->res_cold() <= 0 && coinflip())slow_down(this, strength);break;default:break;}}void monsters::banish(const std::string &){monster_die(this, KILL_RESET, NON_MONSTER);}bool monsters::has_spell(spell_type spell) const{for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)if (spells[i] == spell)return (true);return (false);}bool monsters::has_attack_flavour(int flavour) const{for (int i = 0; i < 4; ++i){const int attk_flavour = mons_attack_spec(this, i).flavour;if (attk_flavour == flavour)return (true);}return (false);}bool monsters::has_damage_type(int dam_type){for (int i = 0; i < 4; ++i){const int dmg_type = damage_type(i);if (dmg_type == dam_type)return (true);}return (false);}bool monsters::confused() const{return (mons_is_confused(this));}bool monsters::confused_by_you() const{if (mons_class_flag(type, M_CONFUSED))return false;const mon_enchant me = get_ench(ENCH_CONFUSION);return (me.ench == ENCH_CONFUSION && me.who == KC_YOU);}bool monsters::paralysed() const{return (mons_is_paralysed(this));}bool monsters::cannot_act() const{return (mons_cannot_act(this));}bool monsters::cannot_move() const{return (mons_cannot_move(this));}bool monsters::asleep() const{return (behaviour == BEH_SLEEP);}bool monsters::backlit(bool check_haloed) const{return (has_ench(ENCH_BACKLIGHT)|| ((check_haloed) ? haloed() : false));}bool monsters::haloed() const{return (inside_halo(pos()));}bool monsters::caught() const{return (mons_is_caught(this));}int monsters::shield_bonus() const{const item_def *shld = const_cast<monsters*>(this)->shield();if (shld){// Note that 0 is not quite no-blocking.if (incapacitated())return (0);int shld_c = property(*shld, PARM_AC) + shld->plus;return (random2avg(shld_c + hit_dice * 2 / 3, 2));}return (-100);}int monsters::shield_block_penalty() const{return (4 * shield_blocks * shield_blocks);}void monsters::shield_block_succeeded(){++shield_blocks;}int monsters::shield_bypass_ability(int) const{return (15 + hit_dice * 2 / 3);}int monsters::armour_class() const{return (ac);}int monsters::melee_evasion(const actor *act) const{int evasion = ev;if (paralysed() || asleep())evasion = 0;else if (caught())evasion /= (body_size(PSIZE_BODY) + 2);else if (confused())evasion /= 2;return (evasion);}void monsters::heal(int amount, bool max_too){hit_points += amount;if (max_too)max_hit_points += amount;if (hit_points > max_hit_points)hit_points = max_hit_points;}mon_holy_type monsters::holiness() const{if (testbits(flags, MF_HONORARY_UNDEAD))return (MH_UNDEAD);return (mons_class_holiness(type));}bool monsters::is_unholy() const{const mon_holy_type holi = holiness();return (holi == MH_UNDEAD || holi == MH_DEMONIC);}int monsters::res_fire() const{const mon_resist_def res = get_mons_resists(this);int u = std::min(res.fire + res.hellfire * 3, 3);if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += scan_mon_inv_randarts(this, ARTP_FIRE);const int armour = inv[MSLOT_ARMOUR];const int shld = inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// intrinsic armour abilitiesswitch (mitm[armour].sub_type){case ARM_DRAGON_ARMOUR: u += 2; break;case ARM_GOLD_DRAGON_ARMOUR: u += 1; break;case ARM_ICE_DRAGON_ARMOUR: u -= 1; break;default: break;}// check ego resistanceconst int ego = get_armour_ego_type(mitm[armour]);if (ego == SPARM_FIRE_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// check ego resistanceconst int ego = get_armour_ego_type(mitm[shld]);if (ego == SPARM_FIRE_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}}if (u < -3)u = -3;else if (u > 3)u = 3;return (u);}int monsters::res_steam() const{int res = get_mons_resists(this).steam;if (has_equipped(EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR))res += 3;return (res + res_fire() / 2);}int monsters::res_cold() const{int u = get_mons_resists(this).cold;if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += scan_mon_inv_randarts(this, ARTP_COLD);const int armour = inv[MSLOT_ARMOUR];const int shld = inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// intrinsic armour abilitiesswitch (mitm[armour].sub_type){case ARM_ICE_DRAGON_ARMOUR: u += 2; break;case ARM_GOLD_DRAGON_ARMOUR: u += 1; break;case ARM_DRAGON_ARMOUR: u -= 1; break;default: break;}// check ego resistanceconst int ego = get_armour_ego_type(mitm[armour]);if (ego == SPARM_COLD_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// check ego resistanceconst int ego = get_armour_ego_type(mitm[shld]);if (ego == SPARM_COLD_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}}if (u < -3)u = -3;else if (u > 3)u = 3;return (u);}int monsters::res_elec() const{// This is a variable, not a player_xx() function, so can be above 1.int u = 0;u += get_mons_resists(this).elec;// Don't bother checking equipment if the monster can't use it.if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += scan_mon_inv_randarts(this, ARTP_ELECTRICITY);// No ego armour, but storm dragon.const int armour = inv[MSLOT_ARMOUR];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR&& mitm[armour].sub_type == ARM_STORM_DRAGON_ARMOUR){u += 1;}}// Monsters can legitimately get multiple levels of electricity resistance.return (u);}int monsters::res_asphyx() const{int res = get_mons_resists(this).asphyx;const mon_holy_type holi = holiness();if (is_unholy()|| holi == MH_NONLIVING|| holi == MH_PLANT){res += 1;}return (res);}int monsters::res_poison() const{int u = get_mons_resists(this).poison;if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += scan_mon_inv_randarts( this, ARTP_POISON );const int armour = this->inv[MSLOT_ARMOUR];const int shld = this->inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// intrinsic armour abilitiesswitch (mitm[armour].sub_type){case ARM_SWAMP_DRAGON_ARMOUR: u += 1; break;case ARM_GOLD_DRAGON_ARMOUR: u += 1; break;default: break;}// ego armour resistanceif (get_armour_ego_type(mitm[armour]) == SPARM_POISON_RESISTANCE)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// ego armour resistanceif (get_armour_ego_type(mitm[shld]) == SPARM_POISON_RESISTANCE)u += 1;}}// Monsters can legitimately get multiple levels of poison resistance.return (u);}int monsters::res_sticky_flame() const{int res = get_mons_resists(this).sticky_flame;if (has_equipped(EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR))res += 1;return (res);}int monsters::res_rotting() const{int res = get_mons_resists(this).rotting;if (holiness() != MH_NATURAL)res += 1;return (res);}int monsters::res_holy_energy(const actor *attacker) const{if (mons_is_evil(this))return (-1);if (is_unholy())return (-2);if (is_good_god(god)|| mons_is_holy(this)|| mons_neutral(this)|| is_unchivalric_attack(attacker, this)|| is_good_god(you.religion) && is_follower(this)){return (1);}return (0);}int monsters::res_negative_energy() const{if (holiness() != MH_NATURAL|| type == MONS_SHADOW_DRAGON){return (3);}int u = 0;if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += scan_mon_inv_randarts(this, ARTP_NEGATIVE_ENERGY);const int armour = this->inv[MSLOT_ARMOUR];const int shld = this->inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// check for ego resistanceif (get_armour_ego_type(mitm[armour]) == SPARM_POSITIVE_ENERGY)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// check for ego resistanceif (get_armour_ego_type(mitm[shld]) == SPARM_POSITIVE_ENERGY)u += 1;}}if (u > 3)u = 3;return (u);}int monsters::res_torment() const{const mon_holy_type holy = holiness();if (holy == MH_UNDEAD|| holy == MH_DEMONIC|| holy == MH_NONLIVING){return (1);}return (0);}int monsters::res_acid() const{return (get_mons_resists(this).acid);}flight_type monsters::flight_mode() const{return (mons_flies(this));}bool monsters::is_levitating() const{// Checking class flags is not enough - see mons_flies.return (flight_mode() == FL_LEVITATE);}int monsters::mons_species() const{return ::mons_species(type);}void monsters::poison(actor *agent, int amount){if (amount <= 0)return;// Scale poison down for monsters.if (!(amount /= 2))amount = 1;poison_monster(this, agent ? agent->kill_alignment() : KC_OTHER, amount);}int monsters::skill(skill_type sk, bool) const{switch (sk){case SK_NECROMANCY:return (holiness() == MH_UNDEAD ? hit_dice / 2 : hit_dice / 3);default:return (0);}}void monsters::blink(bool){monster_blink(this);}void monsters::teleport(bool now, bool){monster_teleport(this, now, false);}bool monsters::alive() const{return (hit_points > 0 && type != MONS_NO_MONSTER);}god_type monsters::deity() const{return (god);}bool monsters::drain_exp(actor *agent, bool quiet, int pow){if (x_chance_in_y(res_negative_energy(), 3))return (false);if (!quiet && you.can_see(this))mprf("%s is drained!", name(DESC_CAP_THE).c_str());// If quiet, don't clean up the monster in order to credit properly.hurt(agent, 2 + random2(pow), BEAM_NEG, !quiet);if (alive()){if (x_chance_in_y(pow, 15)){hit_dice--;experience = 0;}max_hit_points -= 2 + random2(pow);hit_points = std::min(max_hit_points, hit_points);}return (true);}bool monsters::rot(actor *agent, int amount, int immediate, bool quiet){if (res_rotting() > 0 || amount <= 0)return (false);if (!quiet && you.can_see(this)){mprf("%s %s!", name(DESC_CAP_THE).c_str(),amount > 0 ? "rots" : "looks less resilient");}// Apply immediate damage because we can't handle rotting for// monsters yet.if (immediate > 0){// If quiet, don't clean up the monster in order to credit// properly.hurt(agent, immediate, BEAM_MISSILE, !quiet);if (alive()){max_hit_points -= immediate * 2;hit_points = std::min(max_hit_points, hit_points);}}add_ench(mon_enchant(ENCH_ROT, std::min(amount, 4),agent->kill_alignment()));return (true);}int monsters::hurt(const actor *agent, int amount, beam_type flavour,bool cleanup_dead){if (hit_points > 0 && type != -1){if (amount == INSTANT_DEATH)amount = hit_points;else if (hit_dice <= 0)amount = hit_points;else if (amount <= 0 && hit_points <= max_hit_points)return (0);amount = std::min(amount, hit_points);hit_points -= amount;if (hit_points > max_hit_points){amount += hit_points - max_hit_points;hit_points = max_hit_points;}// Allow the victim to exhibit passive damage behaviour (royal// jelly).kill_category whose = (agent == NULL) ? KC_OTHER :(agent->atype() == ACT_PLAYER) ? KC_YOU :mons_friendly_real((monsters*)agent) ? KC_FRIENDLY :KC_OTHER;react_to_damage(amount, flavour, whose);}if (cleanup_dead && (hit_points <= 0 || hit_dice <= 0) && type != -1){if (agent == NULL)monster_die(this, KILL_MISC, NON_MONSTER);else if (agent->atype() == ACT_PLAYER)monster_die(this, KILL_YOU, NON_MONSTER);elsemonster_die(this, KILL_MON, agent->mindex());}return (amount);}void monsters::confuse(actor *atk, int strength){enchant_monster_with_flavour(this, atk, BEAM_CONFUSION, strength);}void monsters::paralyse(actor *atk, int strength){enchant_monster_with_flavour(this, atk, BEAM_PARALYSIS, strength);}void monsters::petrify(actor *atk, int strength){if (mons_is_insubstantial(type))return;enchant_monster_with_flavour(this, atk, BEAM_PETRIFY, strength);}void monsters::slow_down(actor *atk, int strength){enchant_monster_with_flavour(this, atk, BEAM_SLOW, strength);}void monsters::set_ghost(const ghost_demon &g, bool has_name){ghost.reset(new ghost_demon(g));if (has_name)mname = ghost->name;}void monsters::pandemon_init(){hit_dice = ghost->xl;max_hit_points = ghost->max_hp;hit_points = max_hit_points;ac = ghost->ac;ev = ghost->ev;flags = MF_INTERESTING;// Don't make greased-lightning Pandemonium demons in the dungeon// max speed = 17). Demons in Pandemonium can be up to speed 24.if (you.level_type == LEVEL_DUNGEON)speed = (one_chance_in(3) ? 10 : 7 + roll_dice(2, 5));elsespeed = (one_chance_in(3) ? 10 : 10 + roll_dice(2, 7));speed_increment = 70;if (you.char_direction == GDT_ASCENDING && you.level_type == LEVEL_DUNGEON)colour = LIGHTRED;elsecolour = ghost->colour;load_spells(MST_GHOST);}void monsters::ghost_init(){type = MONS_PLAYER_GHOST;god = ghost->religion;hit_dice = ghost->xl;max_hit_points = ghost->max_hp;hit_points = max_hit_points;ac = ghost->ac;ev = ghost->ev;speed = ghost->speed;speed_increment = 70;attitude = ATT_HOSTILE;behaviour = BEH_WANDER;flags = MF_INTERESTING;foe = MHITNOT;foe_memory = 0;colour = ghost->colour;number = MONS_NO_MONSTER;load_spells(MST_GHOST);inv.init(NON_ITEM);enchantments.clear();ench_countdown = 0;find_place_to_live();}void monsters::uglything_init(bool only_mutate){// If we're mutating an ugly thing, leave its experience level, hit// dice and maximum hit points as they are.if (!only_mutate){hit_dice = ghost->xl;max_hit_points = ghost->max_hp;}hit_points = max_hit_points;ac = ghost->ac;ev = ghost->ev;speed = ghost->speed;speed_increment = 70;colour = ghost->colour;}void monsters::uglything_mutate(unsigned char force_colour){ghost->init_ugly_thing(type == MONS_VERY_UGLY_THING, true, force_colour);uglything_init(true);}void monsters::uglything_upgrade(){ghost->ugly_thing_to_very_ugly_thing();uglything_init();}bool monsters::check_set_valid_home(const coord_def &place,coord_def &chosen,int &nvalid) const{if (!in_bounds(place))return (false);if (actor_at(place))return (false);if (!monster_habitable_grid(this, grd(place)))return (false);if (one_chance_in(++nvalid))chosen = place;return (true);}bool monsters::find_home_around(const coord_def &c, int radius){coord_def place(-1, -1);int nvalid = 0;for (int yi = -radius; yi <= radius; ++yi){const coord_def c1(c.x - radius, c.y + yi);const coord_def c2(c.x + radius, c.y + yi);check_set_valid_home(c1, place, nvalid);check_set_valid_home(c2, place, nvalid);}for (int xi = -radius + 1; xi < radius; ++xi){const coord_def c1(c.x + xi, c.y - radius);const coord_def c2(c.x + xi, c.y + radius);check_set_valid_home(c1, place, nvalid);check_set_valid_home(c2, place, nvalid);}if (nvalid){moveto(place);return (true);}return (false);}bool monsters::find_home_near_place(const coord_def &c){for (int radius = 1; radius < 7; ++radius)if (find_home_around(c, radius))return (true);return (false);}bool monsters::find_home_near_player(){return (find_home_near_place(you.pos()));}bool monsters::find_home_anywhere(){coord_def place(-1, -1);int nvalid = 0;for (int tries = 0; tries < 600; ++tries){if (check_set_valid_home(random_in_bounds(), place, nvalid)){moveto(place);return (true);}}return (false);}bool monsters::find_place_to_live(bool near_player){if (near_player && find_home_near_player()|| find_home_anywhere()){mgrd(pos()) = mindex();return (true);}return (false);}void monsters::destroy_inventory(){for (int j = 0; j < NUM_MONSTER_SLOTS; j++){if (inv[j] != NON_ITEM){destroy_item( inv[j] );inv[j] = NON_ITEM;}}}bool monsters::is_travelling() const{return (!travel_path.empty());}bool monsters::is_patrolling() const{return (!patrol_point.origin());}bool monsters::needs_transit() const{return ((mons_is_unique(type)|| (flags & MF_BANISHED)|| you.level_type == LEVEL_DUNGEON&& hit_dice > 8 + random2(25)&& mons_can_use_stairs(this))&& !mons_is_summoned(this));}void monsters::set_transit(const level_id &dest){add_monster_to_transit(dest, *this);}void monsters::load_spells(mon_spellbook_type book){spells.init(SPELL_NO_SPELL);if (book == MST_NO_SPELLS || book == MST_GHOST && !ghost.get())return;#if DEBUG_DIAGNOSTICSmprf( MSGCH_DIAGNOSTICS, "%s: loading spellbook #%d",name(DESC_PLAIN).c_str(), static_cast<int>(book) );#endifif (book == MST_GHOST)spells = ghost->spells;else{for (unsigned int i = 0; i < ARRAYSZ(mspell_list); ++i){if (mspell_list[i].type == book){for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)spells[j] = mspell_list[i].spells[j];break;}}}#if DEBUG_DIAGNOSTICS// Only for ghosts, too spammy to use for all monsters.if (book == MST_GHOST){for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++){mprf( MSGCH_DIAGNOSTICS, "Spell #%d: %d (%s)",i, spells[i], spell_title(spells[i]) );}}#endif}bool monsters::has_hydra_multi_attack() const{return (mons_species() == MONS_HYDRA|| mons_is_zombified(this) && base_monster == MONS_HYDRA);}bool monsters::has_multitargeting() const{if (mons_class_wields_two_weapons(type))return (true);// Hacky little list for now. evkreturn (type == MONS_HYDRA|| type == MONS_TENTACLED_MONSTROSITY|| type == MONS_ELECTRIC_GOLEM);}bool monsters::has_ench(enchant_type ench) const{return (enchantments.find(ench) != enchantments.end());}bool monsters::has_ench(enchant_type ench, enchant_type ench2) const{if (ench2 == ENCH_NONE)ench2 = ench;for (int i = ench; i <= ench2; ++i)if (has_ench(static_cast<enchant_type>(i)))return (true);return (false);}mon_enchant monsters::get_ench(enchant_type ench1,enchant_type ench2) const{if (ench2 == ENCH_NONE)ench2 = ench1;for (int e = ench1; e <= ench2; ++e){mon_enchant_list::const_iterator i =enchantments.find(static_cast<enchant_type>(e));if (i != enchantments.end())return (i->second);}return mon_enchant();}void monsters::update_ench(const mon_enchant &ench){if (ench.ench != ENCH_NONE){mon_enchant_list::iterator i = enchantments.find(ench.ench);if (i != enchantments.end())i->second = ench;}}bool monsters::add_ench(const mon_enchant &ench){// sillinessif (ench.ench == ENCH_NONE)return (false);if (ench.ench == ENCH_FEAR&& (holiness() == MH_NONLIVING || has_ench(ENCH_BERSERK))){return (false);}mon_enchant_list::iterator i = enchantments.find(ench.ench);bool new_enchantment = false;mon_enchant *added = NULL;if (i == enchantments.end()){new_enchantment = true;added = &(enchantments[ench.ench] = ench);}else{i->second += ench;added = &i->second;}// If the duration is not set, we must calculate it (depending on the// enchantment).if (!ench.duration)added->set_duration(this, new_enchantment ? NULL : &ench);if (new_enchantment)add_enchantment_effect(ench);return (true);}void monsters::forget_random_spell(){int which_spell = -1;int count = 0;for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)if (spells[i] != SPELL_NO_SPELL && one_chance_in(++count))which_spell = i;if (which_spell != -1)spells[which_spell] = SPELL_NO_SPELL;}void monsters::add_enchantment_effect(const mon_enchant &ench, bool quiet){// Check for slow/haste.switch (ench.ench){case ENCH_BERSERK:// Inflate hp.scale_hp(3, 2);if (has_ench(ENCH_SUBMERGED))del_ench(ENCH_SUBMERGED);if (mons_is_lurking(this)){behaviour = BEH_WANDER;behaviour_event(this, ME_EVAL);}break;case ENCH_HASTE:if (speed >= 100)speed = 100 + ((speed - 100) * 2);elsespeed *= 2;break;case ENCH_SLOW:if (speed >= 100)speed = 100 + ((speed - 100) / 2);elsespeed /= 2;break;case ENCH_SUBMERGED:mons_clear_trapping_net(this);// Don't worry about invisibility. You should be able to see if// something has submerged.if (!quiet && mons_near(this)){if (type == MONS_AIR_ELEMENTAL){mprf("%s merges itself into the air.",name(DESC_CAP_A, true).c_str());}else if (type == MONS_TRAPDOOR_SPIDER){mprf("%s hides itself under the floor.",name(DESC_CAP_A, true).c_str());}else if (seen_context == "surfaces"|| seen_context == "bursts forth"|| seen_context == "emerges"){// The monster surfaced and submerged in the same turn// without doing anything else.interrupt_activity(AI_SEE_MONSTER,activity_interrupt_data(this,"surfaced"));}else if (crawl_state.arena)mprf("%s submerges.", name(DESC_CAP_A, true).c_str());}// Pacified monsters leave the level when they submerge.if (mons_is_pacified(this))make_mons_leave_level(this);break;case ENCH_CONFUSION:if (type == MONS_TRAPDOOR_SPIDER && has_ench(ENCH_SUBMERGED))del_ench(ENCH_SUBMERGED);if (mons_is_lurking(this)){behaviour = BEH_WANDER;behaviour_event(this, ME_EVAL);}break;case ENCH_CHARM:behaviour = BEH_SEEK;target = you.pos();foe = MHITYOU;if (is_patrolling()){// Enslaved monsters stop patrolling and forget their patrol// point; they're supposed to follow you now.patrol_point.reset();}if (you.can_see(this))learned_something_new(TUT_MONSTER_FRIENDLY, pos());break;default:break;}}static bool _prepare_del_ench(monsters* mon, const mon_enchant &me){if (me.ench != ENCH_SUBMERGED)return (true);// Lurking monsters only unsubmerge when their foe is in sight if the foe// is right next to them.if (mons_is_lurking(mon)){const actor* foe = mon->get_foe();if (foe != NULL && mon->can_see(foe)&& !adjacent(mon->pos(), foe->pos())){return (false);}}int midx = mon->mindex();if (mgrd(mon->pos()) == NON_MONSTER)mgrd(mon->pos()) = midx;if (mon->pos() != you.pos() && midx == mgrd(mon->pos()))return (true);if (midx != mgrd(mon->pos())){monsters* other_mon = &menv[mgrd(mon->pos())];if (other_mon->type == MONS_NO_MONSTER|| other_mon->type == MONS_PROGRAM_BUG){mgrd(mon->pos()) = midx;mprf(MSGCH_ERROR, "mgrd(%d,%d) points to %s monster, even ""though it contains submerged monster %s (see bug 2293518)",mon->pos().x, mon->pos().y,other_mon->type == MONS_NO_MONSTER ? "dead" : "buggy",mon->name(DESC_PLAIN, true).c_str());if (mon->pos() != you.pos())return (true);}elsemprf(MSGCH_ERROR, "%s tried to unsubmerge while on same square as ""%s (see bug 2293518)", mon->name(DESC_CAP_THE, true).c_str(),mon->name(DESC_NOCAP_A, true).c_str());}// Monster un-submerging while under player or another monster. Try to// move to an adjacent square in which the monster could have been// submerged and have it unsubmerge from there.coord_def target_square;int okay_squares = 0;for (adjacent_iterator ai; ai; ++ai)if (mgrd(*ai) == NON_MONSTER && *ai != you.pos()&& monster_can_submerge(mon, grd(*ai))&& one_chance_in(++okay_squares)){target_square = *ai;}if (okay_squares > 0){mon->move_to_pos(target_square);return (true);}// No available adjacent squares from which the monster could also// have unsubmerged. Can it just stay submerged where it is?if (monster_can_submerge(mon, grd(mon->pos())))return (false);// The terrain changed and the monster can't remain submerged.// Try to move to an adjacent square where it would be happy.for (adjacent_iterator ai; ai; ++ai){if (mgrd(*ai) == NON_MONSTER&& monster_habitable_grid(mon, grd(*ai))&& !find_trap(*ai)){if (one_chance_in(++okay_squares))target_square = *ai;}}if (okay_squares > 0)mon->move_to_pos(target_square);return (true);}bool monsters::del_ench(enchant_type ench, bool quiet, bool effect){mon_enchant_list::iterator i = enchantments.find(ench);if (i == enchantments.end())return (false);const mon_enchant me = i->second;const enchant_type et = i->first;if (!_prepare_del_ench(this, me))return (false);enchantments.erase(et);if (effect)remove_enchantment_effect(me, quiet);return (true);}void monsters::remove_enchantment_effect(const mon_enchant &me, bool quiet){switch (me.ench){case ENCH_BERSERK:scale_hp(2, 3);break;case ENCH_HASTE:if (speed >= 100)speed = 100 + ((speed - 100) / 2);elsespeed /= 2;if (!quiet)simple_monster_message(this, " is no longer moving quickly.");break;case ENCH_MIGHT:if (!quiet)simple_monster_message(this, " no longer looks unusually strong.");break;case ENCH_SLOW:if (!quiet)simple_monster_message(this, " is no longer moving slowly.");if (speed >= 100)speed = 100 + ((speed - 100) * 2);elsespeed *= 2;break;case ENCH_PARALYSIS:if (!quiet)simple_monster_message(this, " is no longer paralysed.");behaviour_event(this, ME_EVAL);break;case ENCH_NEUTRAL:if (!quiet)simple_monster_message(this, " is no longer neutral.");behaviour_event(this, ME_EVAL);break;case ENCH_PETRIFIED:if (!quiet)simple_monster_message(this, " is no longer petrified.");del_ench(ENCH_PETRIFYING);behaviour_event(this, ME_EVAL);break;case ENCH_PETRIFYING:if (!has_ench(ENCH_PETRIFIED))break;if (!quiet)simple_monster_message(this, " stops moving altogether!");behaviour_event(this, ME_EVAL);break;case ENCH_FEAR:if (holiness() == MH_NONLIVING || has_ench(ENCH_BERSERK)){// This should only happen because of fleeing sanctuarysnprintf(info, INFO_SIZE, " stops retreating.");}else{snprintf(info, INFO_SIZE, " seems to regain %s courage.",this->pronoun(PRONOUN_NOCAP_POSSESSIVE, true).c_str());}if (!quiet)simple_monster_message(this, info);// Reevaluate behaviour.behaviour_event(this, ME_EVAL);break;case ENCH_CONFUSION:if (!quiet)simple_monster_message(this, " seems less confused.");// Reevaluate behaviour.behaviour_event(this, ME_EVAL);break;case ENCH_INVIS:// Invisible monsters stay invisible.if (mons_class_flag(type, M_INVIS))add_ench(mon_enchant(ENCH_INVIS));else if (mons_near(this) && !you.can_see_invisible()&& !has_ench(ENCH_SUBMERGED)){if (!quiet){mprf("%s appears from thin air!",name(DESC_CAP_A, true).c_str());autotoggle_autopickup(false);}handle_seen_interrupt(this);}break;case ENCH_CHARM:if (!quiet)simple_monster_message(this, " is no longer charmed.");if (you.can_see(this)){// and fire activity interruptsinterrupt_activity(AI_SEE_MONSTER,activity_interrupt_data(this, "uncharm"));}if (is_patrolling()){// Enslaved monsters stop patrolling and forget their patrol point,// in case they were on order to wait.patrol_point.reset();}// Reevaluate behaviour.behaviour_event(this, ME_EVAL);break;case ENCH_BACKLIGHT:if (!quiet){if (visible_to(&you))simple_monster_message(this, " stops glowing.");else if (has_ench(ENCH_INVIS) && mons_near(this)){mprf("%s stops glowing and disappears.",name(DESC_CAP_THE, true).c_str());}}break;case ENCH_STICKY_FLAME:if (!quiet)simple_monster_message(this, " stops burning.");break;case ENCH_POISON:if (!quiet)simple_monster_message(this, " looks more healthy.");break;case ENCH_ROT:if (!quiet)simple_monster_message(this, " is no longer rotting.");break;case ENCH_HELD:{int net = get_trapping_net(this->pos());if (net != NON_ITEM)remove_item_stationary(mitm[net]);if (!quiet)simple_monster_message(this, " breaks free.");break;}case ENCH_ABJ:case ENCH_SHORT_LIVED:// Set duration to -1 so that monster_die() and any of its// callees can tell that the monster ran out of time or was// abjured.add_ench(mon_enchant(ENCH_ABJ, 0, KC_OTHER, -1));if (this->has_ench(ENCH_BERSERK))simple_monster_message(this, " is no longer berserk.");monster_die(this, quiet ? KILL_DISMISSED : KILL_RESET, NON_MONSTER);break;case ENCH_SUBMERGED:if (mons_is_wandering(this)){behaviour = BEH_SEEK;behaviour_event(this, ME_EVAL);}if (you.pos() == this->pos()){mprf(MSGCH_ERROR, "%s is on the same square as you!",name(DESC_CAP_A).c_str());}if (you.can_see(this)){if (!mons_is_safe(this) && is_run_delay(current_delay_action())){// Already set somewhere else.if (!seen_context.empty())return;if (type == MONS_AIR_ELEMENTAL)seen_context = "thin air";else if (type == MONS_TRAPDOOR_SPIDER)seen_context = "leaps out";else if (!monster_habitable_grid(this, DNGN_FLOOR))seen_context = "bursts forth";elseseen_context = "surfaces";}else if (!quiet){if (type == MONS_AIR_ELEMENTAL){mprf("%s forms itself from the air!",name(DESC_CAP_A, true).c_str() );}else if (type == MONS_TRAPDOOR_SPIDER){mprf("%s leaps out from its hiding place under the floor!",name(DESC_CAP_A, true).c_str() );}else if (crawl_state.arena)mprf("%s surfaces.", name(DESC_CAP_A, true).c_str() );}}else if (mons_near(this)&& feat_compatible(grd(pos()), DNGN_DEEP_WATER)){mpr("Something invisible bursts forth from the water.");interrupt_activity(AI_FORCE_INTERRUPT);}break;case ENCH_SOUL_RIPE:if (!quiet){simple_monster_message(this,"'s soul is no longer ripe for the taking.");}break;default:break;}}bool monsters::lose_ench_levels(const mon_enchant &e, int lev){if (!lev)return (false);if (e.degree <= lev){del_ench(e.ench);return (true);}else{mon_enchant newe(e);newe.degree -= lev;update_ench(newe);return (false);}}bool monsters::lose_ench_duration(const mon_enchant &e, int dur){if (!dur)return (false);if (e.duration <= dur){del_ench(e.ench);return (true);}else{mon_enchant newe(e);newe.duration -= dur;update_ench(newe);return (false);}}//---------------------------------------------------------------//// timeout_enchantments//// Update a monster's enchantments when the player returns// to the level.//// Management for enchantments... problems with this are the oddities// (monster dying from poison several thousands of turns later), and// game balance.//// Consider: Poison/Sticky Flame a monster at range and leave, monster// dies but can't leave level to get to player (implied game balance of// the delayed damage is that the monster could be a danger before// it dies). This could be fixed by keeping some monsters active// off level and allowing them to take stairs (a very serious change).//// Compare this to the current abuse where the player gets// effectively extended duration of these effects (although only// the actual effects only occur on level, the player can leave// and heal up without having the effect disappear).//// This is a simple compromise between the two... the enchantments// go away, but the effects don't happen off level. -- bwr////---------------------------------------------------------------void monsters::timeout_enchantments(int levels){if (enchantments.empty())return;const mon_enchant_list ec = enchantments;for (mon_enchant_list::const_iterator i = ec.begin();i != ec.end(); ++i){switch (i->first){case ENCH_POISON: case ENCH_ROT: case ENCH_BACKLIGHT:case ENCH_STICKY_FLAME: case ENCH_ABJ: case ENCH_SHORT_LIVED:case ENCH_SLOW: case ENCH_HASTE: case ENCH_MIGHT: case ENCH_FEAR:case ENCH_INVIS: case ENCH_CHARM: case ENCH_SLEEP_WARY:case ENCH_SICK: case ENCH_SLEEPY: case ENCH_PARALYSIS:case ENCH_PETRIFYING: case ENCH_PETRIFIED:case ENCH_BATTLE_FRENZY: case ENCH_NEUTRAL:case ENCH_LOWERED_MR: case ENCH_SOUL_RIPE:lose_ench_levels(i->second, levels);break;case ENCH_BERSERK:del_ench(i->first);del_ench(ENCH_HASTE, true);del_ench(ENCH_MIGHT, true);break;case ENCH_FATIGUE:del_ench(i->first);del_ench(ENCH_SLOW);break;case ENCH_TP:del_ench(i->first);teleport(true);break;case ENCH_CONFUSION:if (!mons_class_flag(type, M_CONFUSED))del_ench(i->first);blink();break;case ENCH_HELD:del_ench(i->first);break;case ENCH_SLOWLY_DYING:{const int actdur = speed_to_duration(speed) * levels;if (lose_ench_duration(i->first, actdur))monster_die(this, KILL_MISC, NON_MONSTER, true);break;}default:break;}if (!alive())break;}}std::string monsters::describe_enchantments() const{std::ostringstream oss;for (mon_enchant_list::const_iterator i = enchantments.begin();i != enchantments.end(); ++i){if (i != enchantments.begin())oss << ", ";oss << std::string(i->second);}return (oss.str());}// Used to adjust time durations in calc_duration() for monster speed.static inline int _mod_speed( int val, int speed ){if (!speed)speed = 10;const int modded = (speed ? (val * 10) / speed : val);return (modded? modded : 1);}bool monsters::decay_enchantment(const mon_enchant &me, bool decay_degree){// Faster monsters can wiggle out of the net more quickly.const int spd = (me.ench == ENCH_HELD) ? speed :10;const int actdur = speed_to_duration(spd);if (lose_ench_duration(me, actdur))return (true);if (!decay_degree)return (false);// Decay degree so that higher degrees decay faster than lower// degrees, and a degree of 1 does not decay (it expires when the// duration runs out).const int level = me.degree;if (level <= 1)return (false);const int decay_factor = level * (level + 1) / 2;if (me.duration < me.maxduration * (decay_factor - 1) / decay_factor){mon_enchant newme = me;--newme.degree;newme.maxduration = newme.duration;if (newme.degree <= 0){del_ench(me.ench);return (true);}elseupdate_ench(newme);}return (false);}void monsters::apply_enchantment(const mon_enchant &me){const int spd = 10;switch (me.ench){case ENCH_BERSERK:if (decay_enchantment(me)){simple_monster_message(this, " is no longer berserk.");del_ench(ENCH_HASTE, true);del_ench(ENCH_MIGHT, true);const int duration = random_range(70, 130);add_ench(mon_enchant(ENCH_FATIGUE, 0, KC_OTHER, duration));add_ench(mon_enchant(ENCH_SLOW, 0, KC_OTHER, duration));}break;case ENCH_FATIGUE:if (decay_enchantment(me)){simple_monster_message(this, " looks more energetic.");del_ench(ENCH_SLOW, true);}break;case ENCH_SLOW:case ENCH_HASTE:case ENCH_MIGHT:case ENCH_FEAR:case ENCH_PARALYSIS:case ENCH_NEUTRAL:case ENCH_PETRIFYING:case ENCH_PETRIFIED:case ENCH_SICK:case ENCH_BACKLIGHT:case ENCH_ABJ:case ENCH_CHARM:case ENCH_SLEEP_WARY:case ENCH_LOWERED_MR:case ENCH_SOUL_RIPE:decay_enchantment(me);break;case ENCH_BATTLE_FRENZY:decay_enchantment(me, false);break;case ENCH_AQUATIC_LAND:// Aquatic monsters lose hit points every turn they spend on dry land.ASSERT(mons_habitat(this) == HT_WATER&& !feat_is_watery( grd(pos()) ));// Zombies don't take damage from flopping about on land.if (mons_is_zombified(this))break;// We don't have a reasonable agent to give.// Don't clean up the monster in order to credit properly.hurt(NULL, 1 + random2(5), BEAM_NONE, false);// Credit the kill.if (hit_points < 1){monster_die(this, me.killer(), me.kill_agent());break;}break;case ENCH_HELD:{if (mons_is_stationary(this) || mons_cannot_act(this)|| asleep()){break;}int net = get_trapping_net(this->pos(), true);if (net == NON_ITEM) // Really shouldn't happen!{del_ench(ENCH_HELD);break;}// Handled in handle_pickup().if (mons_eats_items(this))break;// The enchantment doubles as the durability of a net// the more corroded it gets, the more easily it will break.const int hold = mitm[net].plus; // This will usually be negative.const int mon_size = body_size(PSIZE_BODY);// Smaller monsters can escape more quickly.if (mon_size < random2(SIZE_BIG) // BIG = 5&& !has_ench(ENCH_BERSERK) && type != MONS_DANCING_WEAPON){if (mons_near(this) && !visible_to(&you))mpr("Something wriggles in the net.");elsesimple_monster_message(this, " struggles to escape the net.");// Confused monsters have trouble finding the exit.if (has_ench(ENCH_CONFUSION) && !one_chance_in(5))break;decay_enchantment(me, 2*(NUM_SIZE_LEVELS - mon_size) - hold);// Frayed nets are easier to escape.if (mon_size <= -(hold-1)/2)decay_enchantment(me, (NUM_SIZE_LEVELS - mon_size));}else // Large (and above) monsters always thrash the net and destroy it{ // e.g. ogre, large zombie (large); centaur, naga, hydra (big).if (mons_near(this) && !visible_to(&you))mpr("Something wriggles in the net.");elsesimple_monster_message(this, " struggles against the net.");// Confused monsters more likely to struggle without result.if (has_ench(ENCH_CONFUSION) && one_chance_in(3))break;// Nets get destroyed more quickly for larger monsters// and if already strongly frayed.int damage = 0;// tiny: 1/6, little: 2/5, small: 3/4, medium and above: alwaysif (x_chance_in_y(mon_size + 1, SIZE_GIANT - mon_size))damage++;// Handled specially to make up for its small size.if (type == MONS_DANCING_WEAPON){damage += one_chance_in(3);if (can_cut_meat(mitm[inv[MSLOT_WEAPON]]))damage++;}// Extra damage for large (50%) and big (always).if (mon_size == SIZE_BIG || mon_size == SIZE_LARGE && coinflip())damage++;// overall damage per struggle:// tiny -> 1/6// little -> 2/5// small -> 3/4// medium -> 1// large -> 1,5// big -> 2// extra damage if already damagedif (random2(body_size(PSIZE_BODY) - hold + 1) >= 4)damage++;// Berserking doubles damage dealt.if (has_ench(ENCH_BERSERK))damage *= 2;// Faster monsters can damage the net more often per// time period.if (speed != 0)damage = div_rand_round(damage * speed, spd);mitm[net].plus -= damage;if (mitm[net].plus < -7){if (mons_near(this)){if (visible_to(&you)){mprf("The net rips apart, and %s comes free!",name(DESC_NOCAP_THE).c_str());}else{mpr("All of a sudden the net rips apart!");}}destroy_item(net);del_ench(ENCH_HELD, true);}}break;}case ENCH_CONFUSION:if (!mons_class_flag(type, M_CONFUSED))decay_enchantment(me);break;case ENCH_INVIS:if (!mons_class_flag(type, M_INVIS))decay_enchantment(me);break;case ENCH_SUBMERGED:{// Not even air elementals unsubmerge into clouds.if (env.cgrid(pos()) != EMPTY_CLOUD)break;// Air elementals are a special case, as their submerging in air// isn't up to choice. - bwrif (type == MONS_AIR_ELEMENTAL){heal_monster( this, 1, one_chance_in(5) );if (one_chance_in(5))del_ench(ENCH_SUBMERGED);break;}// Now we handle the others:const dungeon_feature_type grid = grd(pos());// Badly injured monsters prefer to stay submerged...// electric eels and lava snakes have ranged attacks// and are more likely to surface. -- bwrif (!monster_can_submerge(this, grid))del_ench(ENCH_SUBMERGED); // forced to surfaceelse if (hit_points <= max_hit_points / 2)break;else if (type == MONS_TRAPDOOR_SPIDER){// This should probably never happen.if (!mons_is_lurking(this))del_ench(ENCH_SUBMERGED);break;}else if (((type == MONS_ELECTRIC_EEL || type == MONS_LAVA_SNAKE || type == MONS_KRAKEN)&& (one_chance_in(50) || (mons_near(this)&& hit_points == max_hit_points&& !one_chance_in(10))))|| one_chance_in(200)|| (mons_near(this)&& hit_points == max_hit_points&& !one_chance_in(5))){del_ench(ENCH_SUBMERGED);}break;}case ENCH_POISON:{const int poisonval = me.degree;int dam = (poisonval >= 4) ? 1 : 0;if (coinflip())dam += roll_dice(1, poisonval + 1);if (res_poison() < 0)dam += roll_dice(2, poisonval) - 1;if (dam > 0){// We don't have a reasonable agent to give.// Don't clean up the monster in order to credit properly.hurt(NULL, dam, BEAM_POISON, false);#if DEBUG_DIAGNOSTICS// For debugging, we don't have this silent.simple_monster_message( this, " takes poison damage.",MSGCH_DIAGNOSTICS );mprf(MSGCH_DIAGNOSTICS, "poison damage: %d", dam );#endif// Credit the kill.if (hit_points < 1){monster_die(this, me.killer(), me.kill_agent());break;}}decay_enchantment(me, true);break;}case ENCH_ROT:{if (hit_points > 1 && one_chance_in(3)){hurt(NULL, 1); // nonlethal so we don't care about agentif (hit_points < max_hit_points && coinflip())--max_hit_points;}decay_enchantment(me, true);break;}// Assumption: monsters::res_fire has already been checked.case ENCH_STICKY_FLAME:{if (feat_is_watery(grd(pos()))){if (mons_near(this) && visible_to(&you))mprf("The flames covering %s go out.",this->name(DESC_NOCAP_THE, false).c_str());del_ench(ENCH_STICKY_FLAME);break;}int dam = resist_adjust_damage(this, BEAM_FIRE, res_fire(),roll_dice(2, 4) - 1);if (dam > 0){simple_monster_message(this, " burns!");// We don't have a reasonable agent to give.// Don't clean up the monster in order to credit properly.hurt(NULL, dam, BEAM_NAPALM, false);#if DEBUG_DIAGNOSTICSmprf( MSGCH_DIAGNOSTICS, "sticky flame damage: %d", dam );#endif// Credit the kill.if (hit_points < 1){monster_die(this, me.killer(), me.kill_agent());break;}}decay_enchantment(me, true);break;}case ENCH_SHORT_LIVED:// This should only be used for ball lightning -- bwrif (decay_enchantment(me))hit_points = -1;break;case ENCH_SLOWLY_DYING:// If you are no longer dying, you must be dead.if (decay_enchantment(me)){if (::see_cell(this->position)){mprf("A nearby %s withers and dies.",this->name(DESC_PLAIN, false).c_str());}monster_die(this, KILL_MISC, NON_MONSTER, true);}break;case ENCH_SPORE_PRODUCTION:// Very low chance of actually making a spore on each turn.if(one_chance_in(5000)){int idx[] = {0, 1, 2, 3, 4, 5, 6, 7};std::random_shuffle(idx, idx + 8);for (unsigned i = 0; i < 8; ++i){coord_def adjacent = this->pos() + Compass[idx[i]];if (mons_class_can_pass(MONS_GIANT_SPORE, env.grid(adjacent))&& !actor_at(adjacent)){beh_type created_behavior = BEH_HOSTILE;if (this->attitude == ATT_FRIENDLY)created_behavior = BEH_FRIENDLY;int rc = create_monster(mgen_data(MONS_GIANT_SPORE,created_behavior,0,0,adjacent,MHITNOT,MG_FORCE_PLACE));if (rc != -1){env.mons[rc].behaviour = BEH_WANDER;if (::see_cell(adjacent) && ::see_cell(pos()))mpr("A nearby fungus spawns a giant spore.");}break;}}}break;case ENCH_GLOWING_SHAPESHIFTER: // This ench never runs out!// Number of actions is fine for shapeshifters. Don't change// shape while taking the stairs because monster_polymorph() has// an assert about it. -caoif (!(this->flags & MF_TAKING_STAIRS) && !asleep()&& (type == MONS_GLOWING_SHAPESHIFTER|| one_chance_in(4))){monster_polymorph(this, RANDOM_MONSTER);}break;case ENCH_SHAPESHIFTER: // This ench never runs out!if (!(this->flags & MF_TAKING_STAIRS) && !asleep()&& (type == MONS_SHAPESHIFTER|| x_chance_in_y(1000 / (15 * hit_dice / 5), 1000))){monster_polymorph(this, RANDOM_MONSTER);}break;case ENCH_TP:if (decay_enchantment(me, true))monster_teleport(this, true);break;case ENCH_SLEEPY:del_ench(ENCH_SLEEPY);break;case ENCH_EAT_ITEMS:break;default:break;}}void monsters::mark_summoned(int longevity, bool mark_items, int summon_type){add_ench( mon_enchant(ENCH_ABJ, longevity) );if (summon_type != 0)add_ench( mon_enchant(ENCH_SUMMON, summon_type, KC_OTHER, INT_MAX) );if (mark_items){for (int i = 0; i < NUM_MONSTER_SLOTS; ++i){const int item = inv[i];if (item != NON_ITEM)mitm[item].flags |= ISFLAG_SUMMONED;}}}bool monsters::is_summoned(int* duration, int* summon_type) const{return mons_is_summoned(this, duration, summon_type);}void monsters::apply_enchantments(){if (enchantments.empty())return;// The ordering in enchant_type makes sure that "super-enchantments"// like berserk time out before their parts.const mon_enchant_list ec = enchantments;for (mon_enchant_list::const_iterator i = ec.begin(); i != ec.end(); ++i){apply_enchantment(i->second);if (!alive())break;}}void monsters::scale_hp(int num, int den){hit_points = hit_points * num / den;max_hit_points = max_hit_points * num / den;if (hit_points < 1)hit_points = 1;if (max_hit_points < 1)max_hit_points = 1;if (hit_points > max_hit_points)hit_points = max_hit_points;}kill_category monsters::kill_alignment() const{return (mons_friendly_real(this) ? KC_FRIENDLY : KC_OTHER);}bool monsters::sicken(int amount){if (res_rotting() || (amount /= 2) < 1)return (false);if (!has_ench(ENCH_SICK) && you.can_see(this)){// Yes, could be confused with poisoning.mprf("%s looks sick.", name(DESC_CAP_THE).c_str());}add_ench(mon_enchant(ENCH_SICK, 0, KC_OTHER, amount * 10));return (true);}// Recalculate movement speed.void monsters::fix_speed(){speed = mons_real_base_speed(type);if (has_ench(ENCH_HASTE))speed *= 2;else if (has_ench(ENCH_SLOW))speed /= 2;}// Check speed and speed_increment sanity.void monsters::check_speed(){// FIXME: If speed is borked, recalculate. Need to figure out how// speed is getting borked.if (speed < 0 || speed > 130){#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS,"Bad speed: %s, spd: %d, spi: %d, hd: %d, ench: %s",name(DESC_PLAIN).c_str(),speed, speed_increment, hit_dice,describe_enchantments().c_str());#endiffix_speed();#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS, "Fixed speed for %s to %d",name(DESC_PLAIN).c_str(), speed);#endif}if (speed_increment < 0)speed_increment = 0;if (speed_increment > 200){#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS,"Clamping speed increment on %s: %d",name(DESC_PLAIN).c_str(), speed_increment);#endifspeed_increment = 140;}}actor *monsters::get_foe() const{if (foe == MHITNOT)return (NULL);else if (foe == MHITYOU)return (mons_friendly(this) ? NULL : &you);// Must be a monster!monsters *my_foe = &menv[foe];return (my_foe->alive()? my_foe : NULL);}int monsters::foe_distance() const{const actor *afoe = get_foe();return (afoe ? pos().distance_from(afoe->pos()): INFINITE_DISTANCE);}bool monsters::can_go_berserk() const{if (holiness() != MH_NATURAL || type == MONS_KRAKEN_TENTACLE)return (false);if (has_ench(ENCH_FATIGUE) || has_ench(ENCH_BERSERK))return (false);// If we have no melee attack, going berserk is pointless.const mon_attack_def attk = mons_attack_spec(this, 0);if (attk.type == AT_NONE || attk.damage == 0)return (false);return (true);}bool monsters::needs_berserk(bool check_spells) const{if (!can_go_berserk())return (false);if (has_ench(ENCH_HASTE) || has_ench(ENCH_TP))return (false);if (foe_distance() > 3)return (false);if (check_spells){for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i){const int spell = spells[i];if (spell != SPELL_NO_SPELL && spell != SPELL_BERSERKER_RAGE)return (false);}}return (true);}bool monsters::can_see_invisible() const{if (mons_is_ghost_demon(this->type))return (this->ghost->see_invis);else if (mons_class_flag(this->type, M_SEE_INVIS))return (true);else if (scan_mon_inv_randarts(this, ARTP_EYESIGHT) > 0)return (true);return (false);}bool monsters::invisible() const{return (has_ench(ENCH_INVIS) && !backlit());}bool monsters::visible_to(const actor *looker) const{bool vis = !invisible() || looker->can_see_invisible();return (vis && (this == looker || !has_ench(ENCH_SUBMERGED)));}bool monsters::mon_see_cell(const coord_def& p, bool reach) const{if (distance(pos(), p) > LOS_RADIUS * LOS_RADIUS + 1)return (false);dungeon_feature_type max_disallowed = DNGN_MAXOPAQUE;if (reach)max_disallowed = DNGN_MAX_NONREACH;// XXX: Ignoring clouds for now.return (!num_feats_between(pos(), p, DNGN_UNSEEN, max_disallowed,true, true));}bool monsters::see_cell(const coord_def &c) const{// XXX: using env.show since that's been filled anywa.if (c == you.pos())return (you.see_cell(pos()));// TODO: Proper monster LOS.return (mon_see_cell(c));}bool monsters::can_see(const actor *targ) const{return (targ->visible_to(this) && see_cell(targ->pos()));}bool monsters::near_foe() const{const actor *afoe = get_foe();return (afoe && see_cell(afoe->pos()));}bool monsters::can_mutate() const{return (holiness() == MH_NATURAL || holiness() == MH_PLANT);}bool monsters::can_safely_mutate() const{return (can_mutate());}bool monsters::can_bleed() const{return (mons_has_blood(type));}bool monsters::mutate(){if (!can_mutate())return (false);// Polymorphing a (very) ugly thing will mutate it into a different// (very) ugly thing.if (type == MONS_UGLY_THING || type == MONS_VERY_UGLY_THING){ugly_thing_mutate(this);return (true);}// Polymorphing a shapeshifter will make it revert to its original// form.if (this->has_ench(ENCH_GLOWING_SHAPESHIFTER))return (monster_polymorph(this, MONS_GLOWING_SHAPESHIFTER));if (this->has_ench(ENCH_SHAPESHIFTER))return (monster_polymorph(this, MONS_SHAPESHIFTER));return (monster_polymorph(this, RANDOM_MONSTER));}bool monsters::is_icy() const{return (mons_is_icy(type));}static bool _mons_is_fiery(int mc){return (mc == MONS_FIRE_VORTEX|| mc == MONS_FIRE_ELEMENTAL|| mc == MONS_FLAMING_CORPSE|| mc == MONS_EFREET|| mc == MONS_AZRAEL|| mc == MONS_LAVA_WORM|| mc == MONS_LAVA_FISH|| mc == MONS_LAVA_SNAKE|| mc == MONS_SALAMANDER|| mc == MONS_MOLTEN_GARGOYLE|| mc == MONS_ORB_OF_FIRE);}bool monsters::is_fiery() const{return (_mons_is_fiery(type));}bool monsters::has_action_energy() const{return (speed_increment >= 80);}void monsters::check_redraw(const coord_def &old) const{const bool see_new = ::see_cell(pos());const bool see_old = ::see_cell(old);if ((see_new || see_old) && !view_update()){if (see_new)view_update_at(pos());if (see_old)view_update_at(old);update_screen();}}void monsters::apply_location_effects(const coord_def &oldpos){if (oldpos != pos())dungeon_events.fire_position_event(DET_MONSTER_MOVED, pos());if (alive() && has_ench(ENCH_AQUATIC_LAND)){if (!feat_is_watery( grd(pos()) ))simple_monster_message(this, " flops around on dry land!");else if (!feat_is_watery( grd(oldpos) )){simple_monster_message(this, " dives back into the water!");del_ench(ENCH_AQUATIC_LAND);}}// Monsters stepping on traps:trap_def* ptrap = find_trap(pos());if (ptrap)ptrap->trigger(*this);if (alive())mons_check_pool(this, pos());if (alive() && has_ench(ENCH_SUBMERGED)&& (!monster_can_submerge(this, grd(pos()))|| type == MONS_TRAPDOOR_SPIDER)){del_ench(ENCH_SUBMERGED);if (type == MONS_TRAPDOOR_SPIDER)behaviour_event(this, ME_EVAL);}unsigned long &prop = env.map(pos()).property;if (prop & FPROP_BLOODY){monster_type genus = mons_genus(type);if (genus == MONS_JELLY || genus == MONS_GIANT_SLUG){prop &= ~FPROP_BLOODY;if (see_cell(pos()) && !visible_to(&you)){std::string desc =feature_description(pos(), false, DESC_NOCAP_THE, false);mprf("The bloodstain on %s disappears!", desc.c_str());}}}}bool monsters::move_to_pos(const coord_def &newpos){if (actor_at(newpos))return (false);// Clear old cell pointer.mgrd(pos()) = NON_MONSTER;// Set monster x,y to new value.moveto(newpos);// Set new monster grid pointer to this monster.mgrd(newpos) = mindex();return (true);}// Returns true if the trap should be revealed to the player.bool monsters::do_shaft(){if (!is_valid_shaft_level())return (false);// Handle instances of do_shaft() being invoked magically when// the monster isn't standing over a shaft.if (get_trap_type(this->pos()) != TRAP_SHAFT){switch (grd(pos())){case DNGN_FLOOR:case DNGN_OPEN_DOOR:case DNGN_TRAP_MECHANICAL:case DNGN_TRAP_MAGICAL:case DNGN_TRAP_NATURAL:case DNGN_UNDISCOVERED_TRAP:case DNGN_ENTER_SHOP:break;default:return (false);}if (airborne() || total_weight() == 0){if (mons_near(this)){if (visible_to(&you)){mprf("A shaft briefly opens up underneath %s!",name(DESC_NOCAP_THE).c_str());}elsempr("A shaft briefly opens up in the floor!");}handle_items_on_shaft(this->pos(), false);return (false);}}level_id lev = shaft_dest();if (lev == level_id::current())return (false);// If a pacified monster is leaving the level via a shaft trap, and// has reached its goal, handle it here.if (!mons_is_pacified(this))set_transit(lev);const bool reveal =simple_monster_message(this, " falls through a shaft!");handle_items_on_shaft(this->pos(), false);// Monster is no longer on this level.destroy_inventory();monster_cleanup(this);return (reveal);}void monsters::put_to_sleep(int){if (has_ench(ENCH_BERSERK))return;behaviour = BEH_SLEEP;add_ench(ENCH_SLEEPY);add_ench(ENCH_SLEEP_WARY);}void monsters::check_awaken(int){// XXX}const monsterentry *monsters::find_monsterentry() const{return (type == MONS_NO_MONSTER || type == MONS_PROGRAM_BUG) ? NULL: get_monster_data(type);}int monsters::action_energy(energy_use_type et) const{if (const monsterentry *me = find_monsterentry()){const mon_energy_usage &mu = me->energy_usage;switch (et){case EUT_MOVE: return mu.move;case EUT_SWIM:// [ds] Amphibious monsters get a significant speed boost// when swimming, as discussed with dpeg. We do not// distinguish between amphibians that favour land// (HT_AMPHIBIOUS_LAND, such as hydras) and those that// favour water (HT_AMPHIBIOUS_WATER, such as merfolk), but// that's something we can think about.if (mons_amphibious(this))return div_rand_round(mu.swim * 7, 10);elsereturn mu.swim;case EUT_MISSILE: return mu.missile;case EUT_ITEM: return mu.item;case EUT_SPECIAL: return mu.special;case EUT_SPELL: return mu.spell;case EUT_ATTACK: return mu.attack;case EUT_PICKUP: return mu.pickup_percent;}}return 10;}void monsters::lose_energy(energy_use_type et, int div, int mult){int energy_loss = div_round_up(mult * action_energy(et), div);if (has_ench(ENCH_PETRIFYING)){energy_loss *= 3;energy_loss /= 2;}speed_increment -= energy_loss;}bool monsters::can_drink_potion(potion_type ptype) const{if (mons_class_is_stationary(this->type))return (false);if (mons_itemuse(this) < MONUSE_STARTING_EQUIPMENT)return (false);// These monsters cannot drink.if (mons_is_skeletal(type) || mons_is_insubstantial(type)|| mons_species() == MONS_LICH || mons_genus(type) == MONS_MUMMY){return (false);}switch (ptype){case POT_HEALING:case POT_HEAL_WOUNDS:return (holiness() != MH_NONLIVING&& holiness() != MH_PLANT);case POT_BLOOD:case POT_BLOOD_COAGULATED:return (mons_species() == MONS_VAMPIRE);case POT_SPEED:case POT_MIGHT:case POT_BERSERK_RAGE:case POT_INVISIBILITY:// If there are any item using monsters that are permanently// invisible, this might have to be restricted.return (true);default:break;}return (false);}bool monsters::should_drink_potion(potion_type ptype) const{switch (ptype){case POT_HEALING:return (hit_points <= max_hit_points / 2)|| has_ench(ENCH_POISON)|| has_ench(ENCH_SICK)|| has_ench(ENCH_CONFUSION)|| has_ench(ENCH_ROT);case POT_HEAL_WOUNDS:return (hit_points <= max_hit_points / 2);case POT_BLOOD:case POT_BLOOD_COAGULATED:return (hit_points <= max_hit_points / 2);case POT_SPEED:return (!has_ench(ENCH_HASTE));case POT_MIGHT:return (!has_ench(ENCH_MIGHT) && foe_distance() <= 2);case POT_BERSERK_RAGE:// this implies !has_ench(ENCH_BERSERK_RAGE)return (!has_ench(ENCH_MIGHT) && !has_ench(ENCH_HASTE)&& needs_berserk());case POT_INVISIBILITY:// We're being nice: friendlies won't go invisible if the player// won't be able to see them.return (!has_ench(ENCH_INVIS)&& (you.can_see_invisible(false) || !mons_friendly(this)));default:break;}return (false);}// Return the ID status gained.item_type_id_state_type monsters::drink_potion_effect(potion_type ptype){simple_monster_message(this, " drinks a potion.");item_type_id_state_type ident = ID_MON_TRIED_TYPE;switch (ptype){case POT_HEALING:{heal(5 + random2(7));simple_monster_message(this, " is healed!");const enchant_type cured_enchants[] = {ENCH_POISON, ENCH_SICK, ENCH_CONFUSION, ENCH_ROT};// We can differentiate healing and heal wounds (and blood,// for vampires) by seeing if any status ailments are cured.for (unsigned int i = 0; i < ARRAYSZ(cured_enchants); ++i)if (del_ench(cured_enchants[i]))ident = ID_KNOWN_TYPE;}break;case POT_HEAL_WOUNDS:heal(10 + random2avg(28, 3));simple_monster_message(this, " is healed!");break;case POT_BLOOD:case POT_BLOOD_COAGULATED:if (mons_species() == MONS_VAMPIRE){heal(10 + random2avg(28, 3));simple_monster_message(this, " is healed!");}break;case POT_SPEED:if (enchant_monster_with_flavour(this, this, BEAM_HASTE))ident = ID_KNOWN_TYPE;break;case POT_INVISIBILITY:if (enchant_monster_with_flavour(this, this, BEAM_INVISIBILITY))ident = ID_KNOWN_TYPE;break;case POT_MIGHT:if (enchant_monster_with_flavour(this, this, BEAM_MIGHT))ident = ID_KNOWN_TYPE;break;case POT_BERSERK_RAGE:if (enchant_monster_with_flavour(this, this, BEAM_BERSERK))ident = ID_KNOWN_TYPE;break;default:break;}return (ident);}void monsters::react_to_damage(int damage, beam_type flavour, kill_category whose){if (!alive())return;// The royal jelly objects to taking damage and will SULK. :-)if (type == MONS_ROYAL_JELLY && flavour != BEAM_TORMENT_DAMAGE&& damage > 8 && x_chance_in_y(damage, 50)){mon_acting mact(this);const int tospawn =1 + random2avg(1 + std::min((damage - 8) / 8, 5), 2);#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS, "Trying to spawn %d jellies.", tospawn);#endifconst beh_type beha = SAME_ATTITUDE(this);int spawned = 0;for (int i = 0; i < tospawn; ++i){const monster_type jelly = royal_jelly_ejectable_monster();coord_def jpos = find_newmons_square_contiguous(jelly, pos());if (!in_bounds(jpos))continue;const int nmons = mons_place(mgen_data(jelly, beha, 0, 0,jpos, foe, 0, god));if (nmons != -1 && nmons != NON_MONSTER){// Don't allow milking the royal jelly.menv[nmons].flags |= MF_CREATED_FRIENDLY;spawned++;}}const bool needs_message = spawned && mons_near(this)&& visible_to(&you);if (needs_message){const std::string monnam = name(DESC_CAP_THE);mprf("%s shudders%s.", monnam.c_str(),spawned >= 5 ? " alarmingly" :spawned >= 3 ? " violently" :spawned > 1 ? " vigorously" : "");if (spawned == 1)mprf("%s spits out another jelly.", monnam.c_str());else{mprf("%s spits out %s more jellies.",monnam.c_str(),number_in_words(spawned).c_str());}}}else if (type == MONS_KRAKEN_TENTACLE && flavour != BEAM_TORMENT_DAMAGE){if (!invalid_monster_index(number)&& menv[number].type == MONS_KRAKEN){menv[number].hurt(&you, damage, flavour);}}else if (type == MONS_BUSH && flavour == BEAM_FIRE&& damage>8 && x_chance_in_y(damage, 20)){place_cloud(CLOUD_FIRE, pos(), 20+random2(15), whose, 5);}}/////////////////////////////////////////////////////////////////////////// mon_enchantstatic const char *enchant_names[] ={"none", "slow", "haste", "might", "fear", "conf", "inv", "pois", "bers","rot", "summon", "abj", "backlit", "charm", "fire","gloshifter", "shifter", "tp", "wary", "submerged","short-lived", "paralysis", "sick", "sleep", "fatigue", "held","blood-lust", "neutral", "petrifying", "petrified", "magic-vulnerable","soul-ripe", "decay", "hungry", "flopping", "spore-producing","downtrodden", "bug"};static const char *_mons_enchantment_name(enchant_type ench){COMPILE_CHECK(ARRAYSZ(enchant_names) == NUM_ENCHANTMENTS+1, c1);if (ench > NUM_ENCHANTMENTS)ench = NUM_ENCHANTMENTS;return (enchant_names[ench]);}mon_enchant::mon_enchant(enchant_type e, int deg, kill_category whose,int dur): ench(e), degree(deg), duration(dur), maxduration(0), who(whose){}mon_enchant::operator std::string () const{return make_stringf("%s (%d:%d%s)",_mons_enchantment_name(ench),degree,duration,kill_category_desc(who));}const char *mon_enchant::kill_category_desc(kill_category k) const{return (k == KC_YOU? " you" :k == KC_FRIENDLY? " pet" : "");}void mon_enchant::merge_killer(kill_category k){who = who < k? who : k;}void mon_enchant::cap_degree(){// Sickness is not capped.if (ench == ENCH_SICK)return;// Hard cap to simulate old enum behaviour, we should really throw this// out entirely.const int max = ench == ENCH_ABJ? 6 : 4;if (degree > max)degree = max;}mon_enchant &mon_enchant::operator += (const mon_enchant &other){if (ench == other.ench){degree += other.degree;cap_degree();duration += other.duration;merge_killer(other.who);}return (*this);}mon_enchant mon_enchant::operator + (const mon_enchant &other) const{mon_enchant tmp(*this);tmp += other;return (tmp);}killer_type mon_enchant::killer() const{return (who == KC_YOU ? KILL_YOU :who == KC_FRIENDLY ? KILL_MON: KILL_MISC);}int mon_enchant::kill_agent() const{return (who == KC_FRIENDLY? ANON_FRIENDLY_MONSTER : 0);}int mon_enchant::modded_speed(const monsters *mons, int hdplus) const{return (_mod_speed(mons->hit_dice + hdplus, mons->speed));}int mon_enchant::calc_duration(const monsters *mons,const mon_enchant *added) const{int cturn = 0;const int newdegree = added ? added->degree : degree;const int deg = newdegree ? newdegree : 1;// Beneficial enchantments (like Haste) should not be throttled by// monster HD via modded_speed(). Use mod_speed instead!switch (ench){case ENCH_HASTE:case ENCH_MIGHT:case ENCH_INVIS:cturn = 1000 / _mod_speed(25, mons->speed);break;case ENCH_SLOW:cturn = 250 / (1 + modded_speed(mons, 10));break;case ENCH_FEAR:cturn = 150 / (1 + modded_speed(mons, 5));break;case ENCH_PARALYSIS:cturn = std::max(90 / modded_speed(mons, 5), 3);break;case ENCH_PETRIFIED:cturn = std::max(8, 150 / (1 + modded_speed(mons, 5)));break;case ENCH_PETRIFYING:cturn = 50 / _mod_speed(10, mons->speed);break;case ENCH_CONFUSION:cturn = std::max(100 / modded_speed(mons, 5), 3);break;case ENCH_HELD:cturn = 120 / _mod_speed(25, mons->speed);break;case ENCH_POISON:cturn = 1000 * deg / _mod_speed(125, mons->speed);break;case ENCH_STICKY_FLAME:cturn = 1000 * deg / _mod_speed(200, mons->speed);break;case ENCH_ROT:if (deg > 1)cturn = 1000 * (deg - 1) / _mod_speed(333, mons->speed);cturn += 1000 / _mod_speed(250, mons->speed);break;case ENCH_BACKLIGHT:if (deg > 1)cturn = 1000 * (deg - 1) / _mod_speed(200, mons->speed);cturn += 1000 / _mod_speed(100, mons->speed);break;case ENCH_SHORT_LIVED:cturn = 1000 / _mod_speed(200, mons->speed);break;case ENCH_SLOWLY_DYING:// This may be a little too direct but the randomization at the end// of this function is excessive for toadstools. -caoreturn (2 * FRESHEST_CORPSE + random2(10))* speed_to_duration(mons->speed) * mons->speed / 10;case ENCH_ABJ:if (deg >= 6)cturn = 1000 / _mod_speed(10, mons->speed);if (deg >= 5)cturn += 1000 / _mod_speed(20, mons->speed);cturn += 1000 * std::min(4, deg) / _mod_speed(100, mons->speed);break;case ENCH_CHARM:cturn = 500 / modded_speed(mons, 10);break;case ENCH_TP:cturn = 1000 * deg / _mod_speed(1000, mons->speed);break;case ENCH_SLEEP_WARY:cturn = 1000 / _mod_speed(50, mons->speed);break;default:break;}cturn = std::max(2, cturn);int raw_duration = (cturn * speed_to_duration(mons->speed));raw_duration = std::max(15, fuzz_value(raw_duration, 60, 40));return (raw_duration);}// Calculate the effective duration (in terms of normal player time - 10// duration units being one normal player action) of this enchantment.void mon_enchant::set_duration(const monsters *mons, const mon_enchant *added){if (duration && !added)return;if (added && added->duration)duration += added->duration;elseduration += calc_duration(mons, added);if (duration > maxduration)maxduration = duration;}
static mon_spellbook mspell_list[] = {#include "mon-spll.h"};
}static std::string _str_monam(const monsters& mon, description_level_type desc,bool force_seen){if (mon.type == MONS_NO_MONSTER)return ("DEAD MONSTER");else if (invalid_monster_type(mon.type) && mon.type != MONS_PROGRAM_BUG)return make_stringf("INVALID MONSTER (#%d)", mon.type);const bool arena_submerged = crawl_state.arena && !force_seen&& mon.submerged();// Handle non-visible case first.if (!force_seen && !you.can_see(&mon) && !arena_submerged){switch (desc){case DESC_CAP_THE: case DESC_CAP_A:return ("It");case DESC_NOCAP_THE: case DESC_NOCAP_A: case DESC_PLAIN:return ("it");default:return ("it (buggy)");}}// Assumed visible from now on.// Various special cases:// non-gold mimics, dancing weapons, ghosts, Pan demonsif (mons_is_mimic(mon.type) && mon.type != MONS_GOLD_MIMIC){item_def item;get_mimic_item(&mon, item);return (item.name(desc));}if (mon.type == MONS_DANCING_WEAPON && mon.inv[MSLOT_WEAPON] != NON_ITEM){unsigned long ignore_flags = ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES;bool use_inscrip = true;if (desc == DESC_BASENAME || desc == DESC_QUALNAME|| desc == DESC_DBNAME){use_inscrip = false;}const item_def& item = mitm[mon.inv[MSLOT_WEAPON]];return (item.name(desc, false, false, use_inscrip, false,ignore_flags));}if (desc == DESC_DBNAME)return (get_monster_data(mon.type)->name);if (mon.type == MONS_PLAYER_GHOST)return (apostrophise(mon.mname) + " ghost");// Some monsters might want the name of a different creature.monster_type nametype = mon.type;// Tack on other prefixes.switch (mon.type){case MONS_ZOMBIE_SMALL: case MONS_ZOMBIE_LARGE:case MONS_SKELETON_SMALL: case MONS_SKELETON_LARGE:case MONS_SIMULACRUM_SMALL: case MONS_SIMULACRUM_LARGE:case MONS_SPECTRAL_THING:nametype = mon.base_monster;break;default:break;}// If the monster has an explicit name, return that, handling it like// a unique's name. Special handling for named hydras.if (desc != DESC_BASENAME && !mon.mname.empty()&& mons_genus(nametype) != MONS_HYDRA){return (mon.mname);}std::string result;// Start building the name string.// Start with the prefix.// (Uniques don't get this, because their names are proper nouns.)if (!mons_is_unique(nametype)&& (mon.mname.empty() || mons_genus(nametype) == MONS_HYDRA)){const bool use_your = mons_friendly(&mon);switch (desc){case DESC_CAP_THE:result = (use_your ? "Your " : "The ");break;case DESC_NOCAP_THE:result = (use_your ? "your " : "the ");break;case DESC_CAP_A:if (mon.mname.empty())result = "A ";elseresult = "The ";break;case DESC_NOCAP_A:if (mon.mname.empty())result = "a ";elseresult = "the ";break;case DESC_PLAIN:default:break;}}if (arena_submerged)result += "submerged ";// Tack on other prefixes.switch (mon.type){case MONS_UGLY_THING:case MONS_VERY_UGLY_THING:result += _ugly_thing_colour_name(&mon) + " ";break;case MONS_SPECTRAL_THING:result += "spectral ";break;case MONS_DRACONIAN_CALLER:case MONS_DRACONIAN_MONK:case MONS_DRACONIAN_ZEALOT:case MONS_DRACONIAN_SHIFTER:case MONS_DRACONIAN_ANNIHILATOR:case MONS_DRACONIAN_KNIGHT:case MONS_DRACONIAN_SCORCHER:if (mon.base_monster != MONS_NO_MONSTER) // database searchresult += _draconian_colour_name(mon.base_monster) + " ";break;default:break;}if (mon.mons_species() == MONS_SLIME_CREATURE && desc != DESC_DBNAME){ASSERT(mon.number <= 5);const char* cardinals[] = {"", "large ", "very large ","enormous ", "titanic "};result += cardinals[mon.number - 1];}// Done here to cover cases of undead versions of hydras.if (mons_species(nametype) == MONS_HYDRA&& mon.number > 0 && desc != DESC_DBNAME){if (nametype == MONS_LERNAEAN_HYDRA)result += "the ";if (mon.number < 11){const char* cardinals[] = {"one", "two", "three", "four", "five","six", "seven", "eight", "nine", "ten"};result += cardinals[mon.number - 1];}elseresult += make_stringf("%d", mon.number);result += "-headed ";}if (!mon.mname.empty())result += mon.mname;else if (nametype == MONS_LERNAEAN_HYDRA)result += "Lernaean hydra";else{// Add the base name.if (invalid_monster_type(nametype) && nametype != MONS_PROGRAM_BUG)result += make_stringf("INVALID MONSTER (#%d)", nametype);elseresult += get_monster_data(nametype)->name;}// Add suffixes.switch (mon.type){case MONS_ZOMBIE_SMALL:case MONS_ZOMBIE_LARGE:result += " zombie";break;case MONS_SKELETON_SMALL:case MONS_SKELETON_LARGE:result += " skeleton";break;case MONS_SIMULACRUM_SMALL:case MONS_SIMULACRUM_LARGE:result += " simulacrum";break;default:break;}// Vowel fix: Change 'a orc' to 'an orc'.if (result.length() >= 3&& (result[0] == 'a' || result[0] == 'A')&& result[1] == ' '&& is_vowel(result[2])// XXX: Hack&& !starts_with(&result[2], "one-")){result.insert(1, "n");}if (mons_is_unique(mon.type) && starts_with(result, "the ")){switch (desc){case DESC_CAP_THE:case DESC_CAP_A:result = upcase_first(result);break;default:break;}}if ((mon.flags & MF_KNOWN_MIMIC) && mons_is_shapeshifter(&mon)){// If momentarily in original form, don't display "shaped// shifter".if (mons_genus(mon.type) != MONS_SHAPESHIFTER)result += " shaped shifter";}// All done.return (result);
}int mons_real_base_speed(int mc){ASSERT(smc);int speed = smc->speed;switch (mc){case MONS_ABOMINATION_SMALL:speed = 7 + random2avg(9, 2);break;case MONS_ABOMINATION_LARGE:speed = 6 + random2avg(7, 2);break;case MONS_BEAST:speed = 10 + random2(8);break;}return (speed);
// Does not check whether the monster can dual-wield - that is the// caller's responsibility.int mons_offhand_weapon_index(const monsters *m){return (m->inv[MSLOT_ALT_WEAPON]);}
}///////////////////////////////////////////////////////////////////////////////// monsters methodsmonsters::monsters(): type(MONS_NO_MONSTER), hit_points(0), max_hit_points(0), hit_dice(0),ac(0), ev(0), speed(0), speed_increment(0),target(), patrol_point(), travel_target(MTRAV_NONE),inv(NON_ITEM), spells(), attitude(ATT_HOSTILE), behaviour(BEH_WANDER),foe(MHITYOU), enchantments(), flags(0L), experience(0), number(0),colour(BLACK), foe_memory(0), shield_blocks(0), god(GOD_NO_GOD), ghost(),seen_context(""){travel_path.clear();}// Empty destructor to keep auto_ptr happy with incomplete ghost_demon type.monsters::~monsters(){}monsters::monsters(const monsters &mon){init_with(mon);}monsters &monsters::operator = (const monsters &mon){if (this != &mon)init_with(mon);return (*this);}void monsters::reset(){mname.clear();enchantments.clear();ench_countdown = 0;inv.init(NON_ITEM);flags = 0;experience = 0L;type = MONS_NO_MONSTER;base_monster = MONS_NO_MONSTER;hit_points = 0;max_hit_points = 0;hit_dice = 0;ac = 0;ev = 0;speed_increment = 0;attitude = ATT_HOSTILE;behaviour = BEH_SLEEP;foe = MHITNOT;number = 0;if (in_bounds(pos()))mgrd(pos()) = NON_MONSTER;position.reset();patrol_point.reset();travel_target = MTRAV_NONE;travel_path.clear();ghost.reset(NULL);seen_context = "";}void monsters::init_with(const monsters &mon){mname = mon.mname;type = mon.type;base_monster = mon.base_monster;hit_points = mon.hit_points;max_hit_points = mon.max_hit_points;hit_dice = mon.hit_dice;ac = mon.ac;ev = mon.ev;speed = mon.speed;speed_increment = mon.speed_increment;position = mon.position;target = mon.target;patrol_point = mon.patrol_point;travel_target = mon.travel_target;travel_path = mon.travel_path;inv = mon.inv;spells = mon.spells;attitude = mon.attitude;behaviour = mon.behaviour;foe = mon.foe;enchantments = mon.enchantments;flags = mon.flags;experience = mon.experience;number = mon.number;colour = mon.colour;foe_memory = mon.foe_memory;god = mon.god;if (mon.ghost.get())ghost.reset(new ghost_demon(*mon.ghost));elseghost.reset(NULL);}bool monsters::swimming() const{const dungeon_feature_type grid = grd(pos());return (feat_is_watery(grid) && mons_primary_habitat(this) == HT_WATER);}static bool _player_near_water(){for (adjacent_iterator ai(you.pos()); ai; ++ai)if (feat_is_water(grd(*ai)))return (true);return (false);}bool monsters::wants_submerge() const{// Krakens never retreat when food (the player) is in range.if (type == MONS_KRAKEN)if (_player_near_water())return (false);// If we're in distress, we usually want to submerge.if (env.cgrid(pos()) != EMPTY_CLOUD|| (hit_points < max_hit_points / 2&& random2(max_hit_points + 1) >= hit_points)){return (true);}// Trapdoor spiders only hide themselves under the floor when they// can't see their prey.if (type == MONS_TRAPDOOR_SPIDER){const actor* _foe = get_foe();return (_foe == NULL || !can_see(_foe));}const bool has_ranged_attack = (type == MONS_ELECTRIC_EEL|| type == MONS_LAVA_SNAKE|| mons_genus(type) == MONS_MERMAID&& you.species != SP_MERFOLK);int roll = 8;// Shallow water takes a little more effort to submerge in, so we're// less likely to bother.if (grd(pos()) == DNGN_SHALLOW_WATER)roll = roll * 7 / 5;const actor *tfoe = get_foe();if (tfoe && grid_distance(tfoe->pos(), pos()) > 1 && !has_ranged_attack)roll /= 2;// Don't submerge if we just unsubmerged to shoutreturn (one_chance_in(roll) && seen_context != "bursts forth shouting");}bool monsters::submerged() const{// FIXME, switch to 4.1's MF_SUBMERGED system which is much cleaner.// Can't find any reference to MF_SUBMERGED anywhere. Don't know what// this means. - abrahamwlif (has_ench(ENCH_SUBMERGED))return (true);if (grd(pos()) == DNGN_DEEP_WATER&& !monster_habitable_grid(this, DNGN_DEEP_WATER)){return (true);}return (false);}bool monsters::extra_balanced() const{return (mons_genus(type) == MONS_NAGA);
bool monsters::floundering() const{const dungeon_feature_type grid = grd(pos());return (feat_is_water(grid)&& !cannot_fight()// Can't use monster_habitable_grid() because that'll return// true for non-water monsters in shallow water.&& mons_primary_habitat(this) != HT_WATER&& !mons_amphibious(this)&& !mons_flies(this)&& !extra_balanced());}
}bool monsters::can_pass_through_feat(dungeon_feature_type grid) const{return mons_can_pass(this, grid);}bool monsters::is_habitable_feat(dungeon_feature_type actual_grid) const{return monster_habitable_grid(this, actual_grid);}bool monsters::can_drown() const{// Presumably a shark in lava or a lavafish in deep water could// drown, but that should never happen, so this simple check should// be enough.switch (mons_primary_habitat(this)){case HT_WATER:case HT_LAVA:return (false);default:break;}// Mummies can fall apart in water or be incinerated in lava.// Ghouls, vampires, and demons can drown in water or lava. Others// just "sink like a rock", to never be seen again.return (!res_asphyx()|| mons_genus(type) == MONS_MUMMY|| mons_genus(type) == MONS_GHOUL|| mons_genus(type) == MONS_VAMPIRE|| holiness() == MH_DEMONIC);}size_type monsters::body_size(size_part_type /* psize */, bool /* base */) const{const monsterentry *e = get_monster_data(type);return (e ? e->size : SIZE_MEDIUM);}int monsters::body_weight() const{int mclass = type;switch (mclass){case MONS_SPECTRAL_THING:case MONS_SPECTRAL_WARRIOR:case MONS_ELECTRIC_GOLEM:case MONS_RAKSHASA_FAKE:return (0);case MONS_ZOMBIE_SMALL:case MONS_ZOMBIE_LARGE:case MONS_SKELETON_SMALL:case MONS_SKELETON_LARGE:case MONS_SIMULACRUM_SMALL:case MONS_SIMULACRUM_LARGE:mclass = number;break;default:break;}int weight = mons_weight(mclass);// weight == 0 in the monster entry indicates "no corpse". Can't// use CE_NOCORPSE, because the corpse-effect field is used for// corpseless monsters to indicate what happens if their blood// is sucked. Grrrr.if (weight == 0 && !mons_is_insubstantial(type)){const monsterentry *entry = get_monster_data(mclass);switch (entry->size){case SIZE_TINY:weight = 150;break;case SIZE_LITTLE:weight = 300;break;case SIZE_SMALL:weight = 425;break;case SIZE_MEDIUM:weight = 550;break;case SIZE_LARGE:weight = 1300;break;case SIZE_BIG:weight = 1500;break;case SIZE_GIANT:weight = 1800;break;case SIZE_HUGE:weight = 2200;break;default:mpr("ERROR: invalid monster body weight");perror("monsters::body_weight(): invalid monster body weight");end(0);}switch (mclass){case MONS_IRON_DEVIL:weight += 550;break;case MONS_STONE_GOLEM:case MONS_EARTH_ELEMENTAL:case MONS_CRYSTAL_GOLEM:weight *= 2;break;case MONS_IRON_DRAGON:case MONS_IRON_GOLEM:weight *= 3;break;case MONS_QUICKSILVER_DRAGON:case MONS_SILVER_STATUE:weight *= 4;break;case MONS_WOOD_GOLEM:weight *= 2;weight /= 3;break;case MONS_FLYING_SKULL:case MONS_CURSE_SKULL:case MONS_SKELETAL_DRAGON:case MONS_SKELETAL_WARRIOR:weight /= 2;break;case MONS_SHADOW_FIEND:case MONS_SHADOW_IMP:case MONS_SHADOW_DEMON:weight /= 3;break;}switch (monster_symbols[mclass].glyph){case 'L':weight /= 2;break;case 'p':weight = 0;break;}}if (type == MONS_SKELETON_SMALL || type == MONS_SKELETON_LARGE)weight /= 2;return (weight);}int monsters::total_weight() const{int burden = 0;for (int i = 0; i < NUM_MONSTER_SLOTS; i++)if (inv[i] != NON_ITEM)burden += item_mass(mitm[inv[i]]) * mitm[inv[i]].quantity;return (body_weight() + burden);}int monsters::damage_brand(int which_attack){const item_def *mweap = weapon(which_attack);if (!mweap){if (mons_is_ghost_demon(type))return (ghost->brand);return (SPWPN_NORMAL);}return (!is_range_weapon(*mweap) ? get_weapon_brand(*mweap) : SPWPN_NORMAL);}int monsters::damage_type(int which_attack){const item_def *mweap = weapon(which_attack);if (!mweap){const mon_attack_def atk = mons_attack_spec(this, which_attack);return ((atk.type == AT_CLAW) ? DVORP_CLAWING :(atk.type == AT_TENTACLE_SLAP) ? DVORP_TENTACLE: DVORP_CRUSHING);}return (get_vorpal_type(*mweap));}item_def *monsters::missiles(){return (inv[MSLOT_MISSILE] != NON_ITEM ? &mitm[inv[MSLOT_MISSILE]] : NULL);}int monsters::missile_count(){if (const item_def *missile = missiles())return (missile->quantity);return (0);}item_def *monsters::launcher(){item_def *weap = mslot_item(MSLOT_WEAPON);if (weap && is_range_weapon(*weap))return (weap);weap = mslot_item(MSLOT_ALT_WEAPON);return (weap && is_range_weapon(*weap) ? weap : NULL);}item_def *monsters::weapon(int which_attack){const mon_attack_def attk = mons_attack_spec(this, which_attack);if (attk.type != AT_HIT)return (NULL);// Even/odd attacks use main/offhand weapon.if (which_attack > 1)which_attack &= 1;// This randomly picks one of the wielded weapons for monsters that can use// two weapons. Not ideal, but better than nothing. fight.cc does it right,// for various values of right.int weap = inv[MSLOT_WEAPON];if (which_attack && mons_wields_two_weapons(this)){const int offhand = mons_offhand_weapon_index(this);if (offhand != NON_ITEM&& (weap == NON_ITEM || which_attack == 1 || coinflip())){weap = offhand;}}return (weap == NON_ITEM ? NULL : &mitm[weap]);}bool monsters::can_wield(const item_def& item, bool ignore_curse,bool ignore_brand, bool ignore_shield,bool ignore_transform) const{// Monsters can only wield weapons or go unarmed (OBJ_UNASSIGNED// means unarmed).if (item.base_type != OBJ_WEAPONS && item.base_type != OBJ_UNASSIGNED)return (false);// These *are* weapons, so they can't wield another weapon or// unwield themselves.if (type == MONS_DANCING_WEAPON)return (false);// MF_HARD_RESET means that all items the monster is carrying will// disappear when it does, so it can't accept new items or give up// the ones it has.if (flags & MF_HARD_RESET)return (false);// Summoned items can only be held by summoned monsters.if ((item.flags & ISFLAG_SUMMONED) && !is_summoned())return (false);item_def* weap1 = NULL;if (inv[MSLOT_WEAPON] != NON_ITEM)weap1 = &mitm[inv[MSLOT_WEAPON]];int avail_slots = 1;item_def* weap2 = NULL;if (mons_wields_two_weapons(this)){if (!weap1 || hands_reqd(*weap1, body_size()) != HANDS_TWO)avail_slots = 2;const int offhand = mons_offhand_weapon_index(this);if (offhand != NON_ITEM)weap2 = &mitm[offhand];}// If we're already wielding it, then of course we can wield it.if (&item == weap1 || &item == weap2)return (true);// Barehanded needs two hands.const bool two_handed = item.base_type == OBJ_UNASSIGNED|| hands_reqd(item, body_size()) == HANDS_TWO;item_def* _shield = NULL;if (inv[MSLOT_SHIELD] != NON_ITEM){ASSERT(!(weap1 && weap2));if (two_handed && !ignore_shield)return (false);_shield = &mitm[inv[MSLOT_SHIELD]];}if (!ignore_curse){int num_cursed = 0;if (weap1 && item_cursed(*weap1))num_cursed++;if (weap2 && item_cursed(*weap2))num_cursed++;if (_shield && item_cursed(*_shield))num_cursed++;if (two_handed && num_cursed > 0 || num_cursed >= avail_slots)return (false);}return could_wield(item, ignore_brand, ignore_transform);}bool monsters::could_wield(const item_def &item, bool ignore_brand,bool /* ignore_transform */) const{ASSERT(is_valid_item(item));// These *are* weapons, so they can't wield another weapon.if (type == MONS_DANCING_WEAPON)return (false);// Monsters can't use unrandarts with special effects.if (is_special_unrandom_artefact(item) && !crawl_state.arena)return (false);// Wimpy monsters (e.g. kobold, goblin) can't use halberds, etc.if (!check_weapon_wieldable_size(item, body_size()))return (false);if (!ignore_brand){const int brand = get_weapon_brand(item);// Draconians won't use dragon slaying weapons.if (brand == SPWPN_DRAGON_SLAYING && is_dragonkind(this))return (false);// Orcs won't use orc slaying weapons.if (brand == SPWPN_ORC_SLAYING && is_orckind(this))return (false);// Demonic/undead monsters won't use holy weapons.if (is_unholy() && is_holy_item(item))return (false);// Holy monsters and monsters that are gifts of good gods won't// use evil weapons.if ((mons_is_holy(this) || is_good_god(god))&& is_evil_item(item)){return (false);}// Holy monsters that aren't gifts of chaotic gods and monsters// that are gifts of good gods won't use chaotic weapons.if (((mons_is_holy(this) && !is_chaotic_god(this->god))|| is_good_god(god))&& is_chaotic_item(item)){return (false);}}return (true);}bool monsters::can_throw_large_rocks() const{return (type == MONS_STONE_GIANT|| ::mons_species(this->type) == MONS_CYCLOPS|| ::mons_species(this->type) == MONS_OGRE);}bool monsters::has_spell_of_type(unsigned disciplines) const{for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i){if (spells[i] == SPELL_NO_SPELL)continue;if (spell_typematch(spells[i], disciplines))return (true);}return (false);}static bool _needs_ranged_attack(const monsters *mon){// Prevent monsters that have conjurations from grabbing missiles.if (mon->has_spell_of_type(SPTYP_CONJURATION))return (false);// Same for summonings, but make an exception for friendlies.if (!mons_friendly(mon) && mon->has_spell_of_type(SPTYP_SUMMONING))return (false);// Blademasters don't want to throw stuff.if (mon->type == MONS_DEEP_ELF_BLADEMASTER)return (false);return (true);}bool monsters::can_use_missile(const item_def &item) const{// Don't allow monsters to pick up missiles without the corresponding// launcher. The opposite is okay, and sufficient wandering will// hopefully take the monster to a stack of appropriate missiles.if (!_needs_ranged_attack(this))return (false);if (item.base_type == OBJ_WEAPONS|| item.base_type == OBJ_MISSILES && !has_launcher(item)){return (is_throwable(this, item));}// Darts and stones are allowed even without launcher.if (item.sub_type == MI_DART || item.sub_type == MI_STONE)return (true);item_def *launch;for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){launch = mslot_item(static_cast<mon_inv_type>(i));if (launch && fires_ammo_type(*launch) == item.sub_type)return (true);}// No fitting launcher in inventory.return (false);}void monsters::swap_slots(mon_inv_type a, mon_inv_type b){const int swap = inv[a];inv[a] = inv[b];inv[b] = swap;}void monsters::equip_weapon(item_def &item, int near, bool msg){if (msg && !need_message(near))msg = false;if (msg){snprintf(info, INFO_SIZE, " wields %s.",item.name(DESC_NOCAP_A, false, false, true, false,ISFLAG_CURSED).c_str());msg = simple_monster_message(this, info);}const int brand = get_weapon_brand(item);if (brand == SPWPN_PROTECTION)ac += 5;if (msg){bool message_given = true;switch (brand){case SPWPN_FLAMING:mpr("It bursts into flame!");break;case SPWPN_FREEZING:mpr("It glows with a cold blue light!");break;case SPWPN_HOLY_WRATH:mpr("It softly glows with a divine radiance!");break;case SPWPN_ELECTROCUTION:mpr("You hear the crackle of electricity.", MSGCH_SOUND);break;case SPWPN_VENOM:mpr("It begins to drip with poison!");break;case SPWPN_DRAINING:mpr("You sense an unholy aura.");break;case SPWPN_FLAME:mpr("It bursts into flame!");break;case SPWPN_FROST:mpr("It is covered in frost.");break;case SPWPN_RETURNING:mpr("It wiggles slightly.");break;case SPWPN_DISTORTION:mpr("Its appearance distorts for a moment.");break;case SPWPN_CHAOS:mpr("It is briefly surrounded by a scintillating aura of ""random colours.");break;case SPWPN_PENETRATION:mprf("%s %s briefly pass through it before %s manages to get a ""firm grip on it.",pronoun(PRONOUN_CAP_POSSESSIVE).c_str(),hand_name(true).c_str(),pronoun(PRONOUN_NOCAP).c_str());break;case SPWPN_REAPING:mpr("It is briefly surrounded by shifting shadows.");break;default:// A ranged weapon without special message is known to be unbranded.if (brand != SPWPN_NORMAL || !is_range_weapon(item))message_given = false;}if (message_given){if (is_artefact(item) && !is_special_unrandom_artefact(item))artefact_wpn_learn_prop(item, ARTP_BRAND);elseset_ident_flags(item, ISFLAG_KNOW_TYPE);}}}void monsters::equip_armour(item_def &item, int near){if (need_message(near)){snprintf(info, INFO_SIZE, " wears %s.",item.name(DESC_NOCAP_A).c_str());simple_monster_message(this, info);}const equipment_type eq = get_armour_slot(item);if (eq != EQ_SHIELD){ac += property( item, PARM_AC );const int armour_plus = item.plus;ASSERT(abs(armour_plus) < 20);if (abs(armour_plus) < 20)ac += armour_plus;}// Shields can affect evasion.ev += property( item, PARM_EVASION ) / 2;if (ev < 1)ev = 1; // This *shouldn't* happen.}void monsters::equip(item_def &item, int slot, int near){switch (item.base_type){case OBJ_WEAPONS:{bool give_msg = (slot == MSLOT_WEAPON || mons_wields_two_weapons(this));equip_weapon(item, near, give_msg);break;}case OBJ_ARMOUR:equip_armour(item, near);break;default:break;}}void monsters::unequip_weapon(item_def &item, int near, bool msg){if (msg && !need_message(near))msg = false;if (msg){snprintf(info, INFO_SIZE, " unwields %s.",item.name(DESC_NOCAP_A, false, false, true, false,ISFLAG_CURSED).c_str());msg = simple_monster_message(this, info);}const int brand = get_weapon_brand(item);if (brand == SPWPN_PROTECTION)ac -= 5;if (msg && brand != SPWPN_NORMAL){bool message_given = true;switch (brand){case SPWPN_FLAMING:mpr("It stops flaming.");break;case SPWPN_HOLY_WRATH:mpr("It stops glowing.");break;case SPWPN_ELECTROCUTION:mpr("It stops crackling.");break;case SPWPN_VENOM:mpr("It stops dripping with poison.");break;case SPWPN_DISTORTION:mpr("Its appearance distorts for a moment.");break;default:message_given = false;}if (message_given){if (is_artefact(item) && !is_special_unrandom_artefact(item))artefact_wpn_learn_prop(item, ARTP_BRAND);elseset_ident_flags(item, ISFLAG_KNOW_TYPE);}}}void monsters::unequip_armour(item_def &item, int near){if (need_message(near)){snprintf(info, INFO_SIZE, " takes off %s.",item.name(DESC_NOCAP_A).c_str());simple_monster_message(this, info);}const equipment_type eq = get_armour_slot(item);if (eq != EQ_SHIELD){ac -= property( item, PARM_AC );const int armour_plus = item.plus;ASSERT(abs(armour_plus) < 20);if (abs(armour_plus) < 20)ac -= armour_plus;}ev -= property( item, PARM_EVASION ) / 2;if (ev < 1)ev = 1; // This *shouldn't* happen.}bool monsters::unequip(item_def &item, int slot, int near, bool force){if (!force && item.cursed())return (false);if (!force && you.can_see(this))set_ident_flags(item, ISFLAG_KNOW_CURSE);switch (item.base_type){case OBJ_WEAPONS:{bool give_msg = (slot == MSLOT_WEAPON || mons_wields_two_weapons(this));unequip_weapon(item, near, give_msg);break;}case OBJ_ARMOUR:unequip_armour(item, near);break;default:break;}return (true);}void monsters::lose_pickup_energy(){if (const monsterentry* entry = find_monsterentry()){const int delta = speed * entry->energy_usage.pickup_percent / 100;if (speed_increment > 25 && delta < speed_increment)speed_increment -= delta;}}void monsters::pickup_message(const item_def &item, int near){if (need_message(near)){mprf("%s picks up %s.",name(DESC_CAP_THE).c_str(),item.base_type == OBJ_GOLD ? "some gold": item.name(DESC_NOCAP_A).c_str());}}bool monsters::pickup(item_def &item, int slot, int near, bool force_merge){ASSERT(is_valid_item(item));const monsters *other_mon = item.holding_monster();if (other_mon != NULL){if (other_mon == this){if (inv[slot] == item.index()){mprf(MSGCH_DIAGNOSTICS, "Monster %s already holding item %s.",name(DESC_PLAIN, true).c_str(),item.name(DESC_PLAIN, false, true).c_str());return (false);}else{mprf(MSGCH_DIAGNOSTICS, "Item %s thinks it's already held by ""monster %s.",item.name(DESC_PLAIN, false, true).c_str(),name(DESC_PLAIN, true).c_str());}}else if (other_mon->type == MONS_NO_MONSTER){mprf(MSGCH_DIAGNOSTICS, "Item %s, held by dead monster, being ""picked up by monster %s.",item.name(DESC_PLAIN, false, true).c_str(),name(DESC_PLAIN, true).c_str());}else{mprf(MSGCH_DIAGNOSTICS, "Item %s, held by monster %s, being ""picked up by monster %s.",item.name(DESC_PLAIN, false, true).c_str(),other_mon->name(DESC_PLAIN, true).c_str(),name(DESC_PLAIN, true).c_str());}}// If a monster chooses a two-handed weapon as main weapon, it will// first have to drop any shield it might wear.// (Monsters will always favour damage over protection.)if ((slot == MSLOT_WEAPON || slot == MSLOT_ALT_WEAPON)&& inv[MSLOT_SHIELD] != NON_ITEM&& hands_reqd(item, body_size()) == HANDS_TWO){if (!drop_item(MSLOT_SHIELD, near))return (false);}// Similarly, monsters won't pick up shields if they're// wielding (or alt-wielding) a two-handed weapon.if (slot == MSLOT_SHIELD){const item_def* wpn = mslot_item(MSLOT_WEAPON);const item_def* alt = mslot_item(MSLOT_ALT_WEAPON);if (wpn && hands_reqd(*wpn, body_size()) == HANDS_TWO)return (false);if (alt && hands_reqd(*alt, body_size()) == HANDS_TWO)return (false);}if (inv[slot] != NON_ITEM){item_def &dest(mitm[inv[slot]]);if (items_stack(item, dest, force_merge)){dungeon_events.fire_position_event(dgn_event(DET_ITEM_PICKUP, pos(), 0, item.index(),monster_index(this)),pos());pickup_message(item, near);inc_mitm_item_quantity( inv[slot], item.quantity );merge_item_stacks(item, dest);destroy_item(item.index());equip(item, slot, near);lose_pickup_energy();return (true);}return (false);}dungeon_events.fire_position_event(dgn_event(DET_ITEM_PICKUP, pos(), 0, item.index(),monster_index(this)),pos());const int item_index = item.index();unlink_item(item_index);inv[slot] = item_index;item.set_holding_monster(mindex());pickup_message(item, near);equip(item, slot, near);lose_pickup_energy();return (true);}bool monsters::drop_item(int eslot, int near){if (eslot < 0 || eslot >= NUM_MONSTER_SLOTS)return (false);int item_index = inv[eslot];if (item_index == NON_ITEM)return (true);item_def* pitem = &mitm[item_index];// Unequip equipped items before dropping them; unequip() prevents// cursed items from being removed.bool was_unequipped = false;if (eslot == MSLOT_WEAPON || eslot == MSLOT_ARMOUR|| eslot == MSLOT_ALT_WEAPON && mons_wields_two_weapons(this)){if (!unequip(*pitem, eslot, near))return (false);was_unequipped = true;}bool on_floor = true;if (pitem->flags & ISFLAG_SUMMONED){on_floor = false;if (need_message(near))mprf("%s %s as %s drops %s!",pitem->name(DESC_CAP_THE).c_str(),summoned_poof_msg(this, *pitem).c_str(),name(DESC_NOCAP_THE).c_str(),pitem->quantity > 1 ? "them" : "it");item_was_destroyed(*pitem, mindex());destroy_item(item_index);}else if (!move_item_to_grid(&item_index, pos())){// Re-equip item if we somehow failed to drop it.if (was_unequipped)equip(*pitem, eslot, near);return (false);}// move_item_to_grid could change item_index, so// update pitem.pitem = &mitm[item_index];if (on_floor){if (mons_friendly(this))pitem->flags |= ISFLAG_DROPPED_BY_ALLY;if (need_message(near)){mprf("%s drops %s.", name(DESC_CAP_THE).c_str(),pitem->name(DESC_NOCAP_A).c_str());}dungeon_feature_type feat = grd(pos());if (feat_destroys_items(feat)){if ( player_can_hear(pos()) )mprf(MSGCH_SOUND, feat_item_destruction_message(feat));item_was_destroyed(*pitem, mindex());unlink_item(item_index);}}inv[eslot] = NON_ITEM;return (true);}// We don't want monsters to pick up ammunition that cancels out with// the launcher brand or that is identical to the launcher brand,// the latter in hope of another monster wandering by who may want to// use the ammo in question.static bool _compatible_launcher_ammo_brands(item_def *launcher,const item_def *ammo){// If the monster has no ammo then there's no compatibility problems// to check.if (ammo == NULL)return (true);const int bow_brand = get_weapon_brand(*launcher);const int ammo_brand = get_ammo_brand(*ammo);switch (ammo_brand){case SPMSL_FLAME:case SPMSL_FROST:return (bow_brand != SPWPN_FLAME && bow_brand != SPWPN_FROST);case SPMSL_CHAOS:return (bow_brand != SPWPN_CHAOS);default:return (true);}}bool monsters::pickup_launcher(item_def &launch, int near){// Don't allow monsters to pick up launchers that would also// refuse to pick up the matching ammo.if (!_needs_ranged_attack(this))return (false);// Don't allow monsters to switch to another type of launcher// as that would require them to also drop their ammunition// and then try to find ammunition for their new launcher.// However, they may switch to another launcher if they're// out of ammo. (jpeg)const int mdam_rating = mons_weapon_damage_rating(launch);const missile_type mt = fires_ammo_type(launch);int eslot = -1;for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){if (const item_def *elaunch = mslot_item(static_cast<mon_inv_type>(i))){if (!is_range_weapon(*elaunch))continue;return ((fires_ammo_type(*elaunch) == mt || !missiles())&& (mons_weapon_damage_rating(*elaunch) < mdam_rating|| mons_weapon_damage_rating(*elaunch) == mdam_rating&& get_weapon_brand(*elaunch) == SPWPN_NORMAL&& get_weapon_brand(launch) != SPWPN_NORMAL&& _compatible_launcher_ammo_brands(&launch,missiles()))&& drop_item(i, near) && pickup(launch, i, near));}elseeslot = i;}return (eslot == -1 ? false : pickup(launch, eslot, near));}static bool _is_signature_weapon(monsters *monster, const item_def &weapon){if (weapon.base_type != OBJ_WEAPONS)return (false);if (monster->type == MONS_DAEVA)return (weapon.sub_type == WPN_BLESSED_EUDEMON_BLADE);// We might allow Sigmund to pick up a better scythe if he finds one...if (monster->type == MONS_SIGMUND)return (weapon.sub_type == WPN_SCYTHE);if (is_unrandom_artefact(weapon)){switch (weapon.special){case UNRAND_ASMODEUS:return (monster->type == MONS_ASMODEUS);case UNRAND_DISPATER:return (monster->type == MONS_DISPATER);case UNRAND_CEREBOV:return (monster->type == MONS_CEREBOV);}}return (false);}static int _ego_damage_bonus(item_def &item){switch (get_weapon_brand(item)){case SPWPN_NORMAL: return 0;case SPWPN_PROTECTION: return 1;default: return 2;case SPWPN_VORPAL: return 3;}}static bool _item_race_matches_monster(const item_def &item, monsters *mons){return (get_equip_race(item) == ISFLAG_ELVEN&& mons_genus(mons->type) == MONS_ELF|| get_equip_race(item) == ISFLAG_ORCISH&& mons_genus(mons->type) == MONS_ORC);}bool monsters::pickup_melee_weapon(item_def &item, int near){// Throwable weapons may be picked up as though dual-wielding.const bool dual_wielding = (mons_wields_two_weapons(this)|| is_throwable(this, item));if (dual_wielding){// If we have either weapon slot free, pick up the weapon.if (inv[MSLOT_WEAPON] == NON_ITEM)return pickup(item, MSLOT_WEAPON, near);if (inv[MSLOT_ALT_WEAPON] == NON_ITEM)return pickup(item, MSLOT_ALT_WEAPON, near);}const int new_wpn_dam = mons_weapon_damage_rating(item)+ _ego_damage_bonus(item);int eslot = -1;item_def *weap;// Monsters have two weapon slots, one of which can be a ranged, and// the other a melee weapon. (The exception being dual-wielders who can// wield two melee weapons). The weapon in MSLOT_WEAPON is the one// currently wielded (can be empty).for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){weap = mslot_item(static_cast<mon_inv_type>(i));if (!weap){// If no weapon in this slot, mark this one.if (eslot == -1)eslot = i;}else{if (is_range_weapon(*weap))continue;// Don't drop weapons specific to the monster.if (_is_signature_weapon(this, *weap) && !dual_wielding)return (false);// If we get here, the weapon is a melee weapon.// If the new weapon is better than the current one and not cursed,// replace it. Otherwise, give up.const int old_wpn_dam = mons_weapon_damage_rating(*weap)+ _ego_damage_bonus(*weap);bool new_wpn_better = (new_wpn_dam > old_wpn_dam);if (new_wpn_dam == old_wpn_dam){// Use shopping value as a crude estimate of resistances etc.// XXX: This is not really logical as many properties don't// apply to monsters (e.g. levitation, blink, berserk).// For simplicity, don't apply this check to secondary weapons// for dual wielding monsters.int oldval = item_value(*weap, true);int newval = item_value(item, true);// Vastly prefer matching racial type.if (_item_race_matches_monster(*weap, this))oldval *= 2;if (_item_race_matches_monster(item, this))newval *= 2;if (newval > oldval)new_wpn_better = true;}if (new_wpn_better && !weap->cursed()){if (!dual_wielding|| i == MSLOT_WEAPON|| old_wpn_dam< mons_weapon_damage_rating(*mslot_item(MSLOT_WEAPON))+ _ego_damage_bonus(*mslot_item(MSLOT_WEAPON))){eslot = i;if (!dual_wielding)break;}}else if (!dual_wielding){// We've got a good melee weapon, that's enough.return (false);}}}// No slot found to place this item.if (eslot == -1)return (false);// Current item cannot be dropped.if (inv[eslot] != NON_ITEM && !drop_item(eslot, near))return (false);return (pickup(item, eslot, near));}// Arbitrary damage adjustment for quantity of missiles. So sue me.static int _q_adj_damage(int damage, int qty){return (damage * std::min(qty, 8));
bool monsters::pickup_throwable_weapon(item_def &item, int near){const mon_inv_type slot = item_to_mslot(item);// If it's a melee weapon then pickup_melee_weapon() already rejected// it, even though it can also be thrown.if (slot == MSLOT_WEAPON)return (false);ASSERT(slot == MSLOT_MISSILE);
// If occupied, don't pick up a throwable weapons if it would just// stack with an existing one. (Upgrading is possible.)if (mslot_item(slot)&& (mons_is_wandering(this) || mons_friendly(this) && foe == MHITYOU)&& pickup(item, slot, near, true)){return (true);}item_def *launch = NULL;const int exist_missile = mons_pick_best_missile(this, &launch, true);if (exist_missile == NON_ITEM|| (_q_adj_damage(mons_missile_damage(this, launch,&mitm[exist_missile]),mitm[exist_missile].quantity)< _q_adj_damage(mons_thrown_weapon_damage(&item), item.quantity))){if (inv[slot] != NON_ITEM && !drop_item(slot, near))return (false);return pickup(item, slot, near);}return (false);}bool monsters::wants_weapon(const item_def &weap) const{if (!could_wield(weap))return (false);// Blademasters and master archers like their starting weapon and// don't want another, thank you.if (type == MONS_DEEP_ELF_BLADEMASTER|| type == MONS_DEEP_ELF_MASTER_ARCHER){return (false);}// Monsters capable of dual-wielding will always prefer two weapons// to a single two-handed one, however strong.if (mons_wields_two_weapons(this)&& hands_reqd(weap, body_size()) == HANDS_TWO){return (false);}// Nobody picks up giant clubs. Starting equipment is okay, of course.if (weap.sub_type == WPN_GIANT_CLUB|| weap.sub_type == WPN_GIANT_SPIKED_CLUB){return (false);}return (true);}bool monsters::wants_armour(const item_def &item) const{// Monsters that are capable of dual wielding won't pick up shields.// Neither will monsters that are already wielding a two-hander.if (is_shield(item)&& (mons_wields_two_weapons(this)|| mslot_item(MSLOT_WEAPON)&& hands_reqd(*mslot_item(MSLOT_WEAPON), body_size())== HANDS_TWO)){return (false);}// Returns whether this armour is the monster's size.return (check_armour_size(item, body_size()));}
}}bool monsters::pickup_armour(item_def &item, int near, bool force){ASSERT(item.base_type == OBJ_ARMOUR);if (!force && !wants_armour(item))return (false);equipment_type eq = EQ_NONE;// HACK to allow nagas/centaurs to wear bardings. (jpeg)switch (item.sub_type){case ARM_NAGA_BARDING:if (::mons_species(this->type) == MONS_NAGA)eq = EQ_BODY_ARMOUR;break;case ARM_CENTAUR_BARDING:if (::mons_species(this->type) == MONS_CENTAUR|| ::mons_species(this->type) == MONS_YAKTAUR){eq = EQ_BODY_ARMOUR;}break;// And another hack or two...case ARM_WIZARD_HAT:if (this->type == MONS_GASTRONOK)eq = EQ_BODY_ARMOUR;break;case ARM_CLOAK:if (this->type == MONS_MAURICE)eq = EQ_BODY_ARMOUR;break;default:eq = get_armour_slot(item);}// Bardings are only wearable by the appropriate monster.if (eq == EQ_NONE)return (false);// XXX: Monsters can only equip body armour and shields (as of 0.4).if (!force && eq != EQ_BODY_ARMOUR && eq != EQ_SHIELD)return (false);const mon_inv_type mslot = equip_slot_to_mslot(eq);if (mslot == NUM_MONSTER_SLOTS)return (false);int newAC = item.armour_rating();// No armour yet -> get this one.if (!mslot_item(mslot) && newAC > 0)return pickup(item, mslot, near);// Very simplistic armour evaluation (AC comparison).if (const item_def *existing_armour = slot_item(eq)){if (!force){int oldAC = existing_armour->armour_rating();if (oldAC > newAC)return (false);if (oldAC == newAC){// Use shopping value as a crude estimate of resistances etc.// XXX: This is not really logical as many properties don't// apply to monsters (e.g. levitation, blink, berserk).int oldval = item_value(*existing_armour, true);int newval = item_value(item, true);// Vastly prefer matching racial type.if (_item_race_matches_monster(*existing_armour, this))oldval *= 2;if (_item_race_matches_monster(item, this))newval *= 2;if (oldval >= newval)return (false);}}if (!drop_item(mslot, near))return (false);}return pickup(item, mslot, near);}bool monsters::pickup_weapon(item_def &item, int near, bool force){if (!force && !wants_weapon(item))return (false);// Weapon pickup involves:// - If we have no weapons, always pick this up.// - If this is a melee weapon and we already have a melee weapon, pick// it up if it is superior to the one we're carrying (and drop the// one we have).// - If it is a ranged weapon, and we already have a ranged weapon,// pick it up if it is better than the one we have.// - If it is a throwable weapon, and we're carrying no missiles (or our// missiles are the same type), pick it up.if (is_range_weapon(item))return (pickup_launcher(item, near));if (pickup_melee_weapon(item, near))return (true);return (can_use_missile(item) && pickup_throwable_weapon(item, near));}bool monsters::pickup_missile(item_def &item, int near, bool force){const item_def *miss = missiles();if (!force){if (item.sub_type == MI_THROWING_NET){// Monster may not pick up trapping net.if (mons_is_caught(this) && item_is_stationary(item))return (false);}else // None of these exceptions hold for throwing nets.{// Spellcasters should not waste time with ammunition.// Neither summons nor hostile enchantments are counted for// this purpose.if (mons_has_ranged_spell(this, true, false))return (false);// Monsters in a fight will only pick up missiles if doing so// is worthwhile.if (!mons_is_wandering(this)&& (!mons_friendly(this) || foe != MHITYOU)&& (item.quantity < 5 || miss && miss->quantity >= 7)){return (false);}}}if (miss && items_stack(*miss, item))return (pickup(item, MSLOT_MISSILE, near));if (!force && !can_use_missile(item))return (false);if (miss){item_def *launch;for (int i = MSLOT_WEAPON; i <= MSLOT_ALT_WEAPON; ++i){launch = mslot_item(static_cast<mon_inv_type>(i));if (launch){const int item_brand = get_ammo_brand(item);// If this ammunition is better, drop the old ones.// Don't upgrade to ammunition whose brand cancels the// launcher brand or doesn't improve it further.if (fires_ammo_type(*launch) == item.sub_type&& (fires_ammo_type(*launch) != miss->sub_type|| item.plus > miss->plus&& get_ammo_brand(*miss) == item_brand|| item.plus >= miss->plus&& get_ammo_brand(*miss) == SPMSL_NORMAL&& item_brand != SPMSL_NORMAL&&_compatible_launcher_ammo_brands(launch, miss))){if (!drop_item(MSLOT_MISSILE, near))return (false);break;}}}// Darts don't absolutely need a launcher - still allow upgrading.if (item.sub_type == miss->sub_type&& item.sub_type == MI_DART&& (item.plus > miss->plus|| item.plus == miss->plus&& get_ammo_brand(*miss) == SPMSL_NORMAL&& get_ammo_brand(item) != SPMSL_NORMAL)){if (!drop_item(MSLOT_MISSILE, near))return (false);}}return pickup(item, MSLOT_MISSILE, near);}bool monsters::pickup_wand(item_def &item, int near){// Don't pick up empty wands.if (item.plus == 0)return (false);// Only low-HD monsters bother with wands.if (hit_dice >= 14)return (false);// Holy monsters and worshippers of good gods won't pick up evil// wands.if ((mons_is_holy(this) || is_good_god(god)) && is_evil_item(item))return (false);// If a monster already has a charged wand, don't bother.// Otherwise, replace with a charged one.if (item_def *wand = mslot_item(MSLOT_WAND)){if (wand->plus > 0)return (false);if (!drop_item(MSLOT_WAND, near))return (false);}return (pickup(item, MSLOT_WAND, near));}bool monsters::pickup_scroll(item_def &item, int near){if (item.sub_type != SCR_TELEPORTATION&& item.sub_type != SCR_BLINKING&& item.sub_type != SCR_SUMMONING){return (false);}// Holy monsters and worshippers of good gods won't pick up evil// scrolls.if ((mons_is_holy(this) || is_good_god(god)) && is_evil_item(item))return (false);return (pickup(item, MSLOT_SCROLL, near));}bool monsters::pickup_potion(item_def &item, int near){// Only allow monsters to pick up potions if they can actually use// them.const potion_type ptype = static_cast<potion_type>(item.sub_type);if (!this->can_drink_potion(ptype))return (false);return (pickup(item, MSLOT_POTION, near));}bool monsters::pickup_gold(item_def &item, int near){return (pickup(item, MSLOT_GOLD, near));}bool monsters::pickup_misc(item_def &item, int near){// Never pick up runes.if (item.sub_type == MISC_RUNE_OF_ZOT)return (false);// Holy monsters and worshippers of good gods won't pick up evil// miscellaneous items.if ((mons_is_holy(this) || is_good_god(god)) && is_evil_item(item))return (false);return (pickup(item, MSLOT_MISCELLANY, near));}// Eaten items are handled elsewhere, in _handle_pickup() in monstuff.cc.bool monsters::pickup_item(item_def &item, int near, bool force){// Equipping stuff can be forced when initially equipping monsters.if (!force){// If a monster isn't otherwise occupied (has a foe, is fleeing, etc.)// it is considered wandering.bool wandering = (mons_is_wandering(this)|| mons_friendly(this) && foe == MHITYOU);const int itype = item.base_type;// Weak(ened) monsters won't stop to pick up things as long as they// feel unsafe.if (!wandering && (hit_points * 10 < max_hit_points || hit_points < 10)&& mon_enemies_around(this)){return (false);}if (mons_friendly(this)){// Never pick up gold or misc. items, it'd only annoy the player.if (itype == OBJ_MISCELLANY || itype == OBJ_GOLD)return (false);// Depending on the friendly pickup toggle, your allies may not// pick up anything, or only stuff dropped by (other) allies.if (you.friendly_pickup == FRIENDLY_PICKUP_NONE|| you.friendly_pickup == FRIENDLY_PICKUP_FRIEND&& !testbits(item.flags, ISFLAG_DROPPED_BY_ALLY)|| you.friendly_pickup == FRIENDLY_PICKUP_PLAYER&& !(item.flags & (ISFLAG_DROPPED | ISFLAG_THROWN| ISFLAG_DROPPED_BY_ALLY))){return (false);}}if (!wandering){// These are not important enough for pickup when// seeking, fleeing etc.if (itype == OBJ_ARMOUR || itype == OBJ_CORPSES|| itype == OBJ_MISCELLANY || itype == OBJ_GOLD){return (false);}if (itype == OBJ_WEAPONS || itype == OBJ_MISSILES){// Fleeing monsters only pick up emergency equipment.if (mons_is_fleeing(this))return (false);// While occupied, hostile monsters won't pick up items// dropped or thrown by you. (You might have done that to// distract them.)if (!mons_friendly(this)&& (testbits(item.flags, ISFLAG_DROPPED)|| testbits(item.flags, ISFLAG_THROWN))){return (false);}}}}switch (item.base_type){// Pickup some stuff only if WANDERING.case OBJ_ARMOUR:return pickup_armour(item, near, force);case OBJ_MISCELLANY:return pickup_misc(item, near);case OBJ_GOLD:return pickup_gold(item, near);// Fleeing monsters won't pick up these.// Hostiles won't pick them up if they were ever dropped/thrown by you.case OBJ_WEAPONS:return pickup_weapon(item, near, force);case OBJ_MISSILES:return pickup_missile(item, near, force);// Other types can always be picked up// (barring other checks depending on subtype, of course).case OBJ_WANDS:return pickup_wand(item, near);case OBJ_SCROLLS:return pickup_scroll(item, near);case OBJ_POTIONS:return pickup_potion(item, near);case OBJ_BOOKS:if (force)return pickup_misc(item, near);// else fall throughdefault:return (false);}}bool monsters::need_message(int &near) const{return (near != -1 ? near: (near = observable()));}void monsters::swap_weapons(int near){item_def *weap = mslot_item(MSLOT_WEAPON);item_def *alt = mslot_item(MSLOT_ALT_WEAPON);if (weap && !unequip(*weap, MSLOT_WEAPON, near)){// Item was cursed.return;}swap_slots(MSLOT_WEAPON, MSLOT_ALT_WEAPON);if (alt)equip(*alt, MSLOT_WEAPON, near);// Monsters can swap weapons really fast. :-)if ((weap || alt) && speed_increment >= 2){if (const monsterentry *entry = find_monsterentry())speed_increment -= div_rand_round(entry->energy_usage.attack, 5);}}void monsters::wield_melee_weapon(int near){const item_def *weap = mslot_item(MSLOT_WEAPON);if (!weap || (!weap->cursed() && is_range_weapon(*weap))){const item_def *alt = mslot_item(MSLOT_ALT_WEAPON);// Switch to the alternate weapon if it's not a ranged weapon, too,// or switch away from our main weapon if it's a ranged weapon.if (alt && !is_range_weapon(*alt) || weap && !alt)swap_weapons(near);}}item_def *monsters::slot_item(equipment_type eq){return (mslot_item(equip_slot_to_mslot(eq)));}item_def *monsters::mslot_item(mon_inv_type mslot) const{const int mi = (mslot == NUM_MONSTER_SLOTS) ? NON_ITEM : inv[mslot];return (mi == NON_ITEM ? NULL : &mitm[mi]);}item_def *monsters::shield(){return (mslot_item(MSLOT_SHIELD));}bool monsters::is_named() const{return (!mname.empty() || mons_is_unique(type));}bool monsters::has_base_name() const{// Any non-ghost, non-Pandemonium demon that has an explicitly set// name has a base name.return (!mname.empty() && !ghost.get());}std::string monsters::name(description_level_type desc, bool force_vis) const{if (desc == DESC_NONE)return ("");const bool possessive =(desc == DESC_NOCAP_YOUR || desc == DESC_NOCAP_ITS);if (possessive)desc = DESC_NOCAP_THE;std::string monnam;if ((flags & MF_NAME_MASK) && (force_vis || you.can_see(this))|| crawl_state.arena && mons_class_is_zombified(type)){monnam = full_name(desc);}elsemonnam = _str_monam(*this, desc, force_vis);return (possessive ? apostrophise(monnam) : monnam);}std::string monsters::base_name(description_level_type desc, bool force_vis)const{if (desc == DESC_NONE)return ("");if (ghost.get() || mons_is_unique(type))return (name(desc, force_vis));else{unwind_var<std::string> tmname(const_cast<monsters*>(this)->mname, "");return (name(desc, force_vis));}}std::string monsters::full_name(description_level_type desc,bool use_comma) const{if (desc == DESC_NONE)return ("");std::string title = _str_monam(*this, desc, true);const unsigned long flag = flags & MF_NAME_MASK;const int _type = mons_is_zombified(this) ? base_monster : type;if (mons_genus(_type) == MONS_HYDRA && flag == 0)return (title);if (has_base_name()){if (flag == MF_NAME_SUFFIX){title = base_name(desc, true);title += " ";title += mname;}else if (flag == MF_NAME_NO_THE){title += " ";title += base_name(DESC_PLAIN, true);}else if (flag == MF_NAME_REPLACE);else{if (use_comma)title += ",";title += " ";title += base_name(DESC_NOCAP_THE, true);}}return (title);}std::string monsters::pronoun(pronoun_type pro, bool force_visible) const{return (mons_pronoun(static_cast<monster_type>(type), pro,force_visible || you.can_see(this)));}std::string monsters::conj_verb(const std::string &verb) const{if (!verb.empty() && verb[0] == '!')return (verb.substr(1));if (verb == "are")return ("is");if (ends_with(verb, "f") || ends_with(verb, "fe")|| ends_with(verb, "y")){return (verb + "s");}return (pluralise(verb));}std::string monsters::hand_name(bool plural, bool *can_plural) const{bool _can_plural;if (can_plural == NULL)can_plural = &_can_plural;*can_plural = true;std::string str;char ch = mons_char(type);const bool rand = (type == MONS_CHAOS_SPAWN);switch (get_mon_shape(this)){case MON_SHAPE_CENTAUR:case MON_SHAPE_NAGA:// Defaults to "hand"break;case MON_SHAPE_HUMANOID:case MON_SHAPE_HUMANOID_WINGED:case MON_SHAPE_HUMANOID_TAILED:case MON_SHAPE_HUMANOID_WINGED_TAILED:if (ch == 'T' || ch == 'd' || ch == 'n' || mons_is_demon(type))str = "claw";break;case MON_SHAPE_QUADRUPED:case MON_SHAPE_QUADRUPED_TAILLESS:case MON_SHAPE_QUADRUPED_WINGED:case MON_SHAPE_ARACHNID:if (type == MONS_SCORPION || rand && one_chance_in(4))str = "pincer";else{str = "front ";return (str + foot_name(plural, can_plural));}break;case MON_SHAPE_BLOB:case MON_SHAPE_SNAKE:case MON_SHAPE_FISH:return foot_name(plural, can_plural);case MON_SHAPE_BAT:str = "wing";break;case MON_SHAPE_INSECT:case MON_SHAPE_INSECT_WINGED:case MON_SHAPE_CENTIPEDE:str = "antenna";break;case MON_SHAPE_SNAIL:str = "eye-stalk";break;case MON_SHAPE_PLANT:str = "leaf";break;case MON_SHAPE_MISC:if (ch == 'x' || ch == 'X' || rand){str = "tentacle";break;}// Deliberate fallthrough.case MON_SHAPE_FUNGUS:str = "body";*can_plural = false;break;case MON_SHAPE_ORB:switch (type){case MONS_GIANT_SPORE:str = "rhizome";break;case MONS_GIANT_EYEBALL:case MONS_EYE_OF_DRAINING:case MONS_SHINING_EYE:case MONS_EYE_OF_DEVASTATION:*can_plural = false;// Deliberate fallthrough.case MONS_GREAT_ORB_OF_EYES:str = "pupil";break;case MONS_GIANT_ORANGE_BRAIN:default:if (rand)str = "rhizome";else{str = "body";*can_plural = false;}break;}}if (str.empty()){// Reduce the chance of a random-shaped monster having hands.if (rand && coinflip())return (hand_name(plural, can_plural));str = "hand";}if (plural && *can_plural)str = pluralise(str);return (str);}std::string monsters::foot_name(bool plural, bool *can_plural) const{bool _can_plural;if (can_plural == NULL)can_plural = &_can_plural;*can_plural = true;std::string str;char ch = mons_char(type);const bool rand = (type == MONS_CHAOS_SPAWN);switch (get_mon_shape(this)){case MON_SHAPE_INSECT:case MON_SHAPE_INSECT_WINGED:case MON_SHAPE_ARACHNID:case MON_SHAPE_CENTIPEDE:str = "leg";break;case MON_SHAPE_HUMANOID:case MON_SHAPE_HUMANOID_WINGED:case MON_SHAPE_HUMANOID_TAILED:case MON_SHAPE_HUMANOID_WINGED_TAILED:if (type == MONS_MINOTAUR)str = "hoof";else if (swimming()&& (type == MONS_MERFOLK || mons_genus(type) == MONS_MERMAID)){str = "tail";*can_plural = false;}break;case MON_SHAPE_CENTAUR:str = "hoof";break;case MON_SHAPE_QUADRUPED:case MON_SHAPE_QUADRUPED_TAILLESS:case MON_SHAPE_QUADRUPED_WINGED:if (rand){const char* feet[] = {"paw", "talon", "hoof"};str = RANDOM_ELEMENT(feet);}else if (ch == 'h')str = "paw";else if (ch == 'l' || ch == 'D')str = "talon";else if (type == MONS_YAK || type == MONS_DEATH_YAK)str = "hoof";else if (ch == 'H'){if (type == MONS_MANTICORE || type == MONS_SPHINX)str = "paw";elsestr = "talon";}break;case MON_SHAPE_BAT:str = "claw";break;case MON_SHAPE_SNAKE:case MON_SHAPE_FISH:str = "tail";*can_plural = false;break;case MON_SHAPE_PLANT:str = "root";break;case MON_SHAPE_FUNGUS:str = "stem";*can_plural = false;break;case MON_SHAPE_BLOB:str = "pseudopod";break;case MON_SHAPE_MISC:if (ch == 'x' || ch == 'X' || rand){str = "tentacle";break;}// Deliberate fallthrough.case MON_SHAPE_SNAIL:case MON_SHAPE_NAGA:case MON_SHAPE_ORB:str = "underside";*can_plural = false;break;}if (str.empty()){// Reduce the chance of a random-shaped monster having feet.if (rand && coinflip())return (foot_name(plural, can_plural));return (plural ? "feet" : "foot");}if (plural && *can_plural)str = pluralise(str);return (str);}std::string monsters::arm_name(bool plural, bool *can_plural) const{mon_body_shape shape = get_mon_shape(this);if (shape > MON_SHAPE_NAGA)return hand_name(plural, can_plural);if (can_plural != NULL)*can_plural = true;std::string str;switch (mons_genus(type)){case MONS_NAGA:case MONS_DRACONIAN: str = "scaled arm"; break;case MONS_MUMMY: str = "bandaged wrapped arm"; break;case MONS_SKELETAL_WARRIOR:case MONS_LICH: str = "bony arm"; break;default: str = "arm"; break;}if (plural)str = pluralise(str);return (str);}monster_type monsters::id() const{return (type);}int monsters::mindex() const{return (monster_index(this));}int monsters::get_experience_level() const{return (hit_dice);}void monsters::moveto(const coord_def& c){if (c != pos() && in_bounds(pos()))mons_clear_trapping_net(this);position = c;}bool monsters::fumbles_attack(bool verbose){if (floundering() && one_chance_in(4)){if (verbose){if (you.can_see(this)){mprf("%s splashes around in the water.",this->name(DESC_CAP_THE).c_str());}else if (player_can_hear(pos()))mpr("You hear a splashing noise.", MSGCH_SOUND);}return (true);}if (submerged())return (true);return (false);}bool monsters::cannot_fight() const{return (mons_class_flag(type, M_NO_EXP_GAIN)|| mons_is_statue(type));}void monsters::attacking(actor * /* other */){}void monsters::go_berserk(bool /* intentional */){if (!this->can_go_berserk())return;if (has_ench(ENCH_SLOW)){del_ench(ENCH_SLOW, true); // Give no additional message.simple_monster_message(this,make_stringf(" shakes off %s lethargy.",pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str()).c_str());}del_ench(ENCH_HASTE, true);del_ench(ENCH_FATIGUE, true); // Give no additional message.const int duration = 16 + random2avg(13, 2);add_ench(mon_enchant(ENCH_BERSERK, 0, KC_OTHER, duration * 10));add_ench(mon_enchant(ENCH_HASTE, 0, KC_OTHER, duration * 10));add_ench(mon_enchant(ENCH_MIGHT, 0, KC_OTHER, duration * 10));if (simple_monster_message( this, " goes berserk!" ))// Xom likes monsters going berserk.xom_is_stimulated(mons_friendly(this) ? 32 : 128);}void monsters::expose_to_element(beam_type flavour, int strength){switch (flavour){case BEAM_COLD:if (mons_class_flag(this->type, M_COLD_BLOOD) && this->res_cold() <= 0 && coinflip())slow_down(this, strength);break;default:break;}}void monsters::banish(const std::string &){monster_die(this, KILL_RESET, NON_MONSTER);}bool monsters::has_spell(spell_type spell) const{for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)if (spells[i] == spell)return (true);return (false);}bool monsters::has_attack_flavour(int flavour) const{for (int i = 0; i < 4; ++i){const int attk_flavour = mons_attack_spec(this, i).flavour;if (attk_flavour == flavour)return (true);
bool monsters::has_damage_type(int dam_type){for (int i = 0; i < 4; ++i){const int dmg_type = damage_type(i);if (dmg_type == dam_type)return (true);}return (false);}bool monsters::confused() const{return (mons_is_confused(this));}bool monsters::confused_by_you() const{if (mons_class_flag(type, M_CONFUSED))return false;const mon_enchant me = get_ench(ENCH_CONFUSION);return (me.ench == ENCH_CONFUSION && me.who == KC_YOU);}bool monsters::paralysed() const{return (mons_is_paralysed(this));}bool monsters::cannot_act() const{return (mons_cannot_act(this));}bool monsters::cannot_move() const{return (mons_cannot_move(this));}bool monsters::asleep() const{return (behaviour == BEH_SLEEP);}bool monsters::backlit(bool check_haloed) const{return (has_ench(ENCH_BACKLIGHT)|| ((check_haloed) ? haloed() : false));}bool monsters::haloed() const{return (inside_halo(pos()));}bool monsters::caught() const{return (mons_is_caught(this));}int monsters::shield_bonus() const{const item_def *shld = const_cast<monsters*>(this)->shield();if (shld){// Note that 0 is not quite no-blocking.if (incapacitated())return (0);int shld_c = property(*shld, PARM_AC) + shld->plus;return (random2avg(shld_c + hit_dice * 2 / 3, 2));}return (-100);}int monsters::shield_block_penalty() const{return (4 * shield_blocks * shield_blocks);}void monsters::shield_block_succeeded(){++shield_blocks;}int monsters::shield_bypass_ability(int) const{return (15 + hit_dice * 2 / 3);}int monsters::armour_class() const{return (ac);}int monsters::melee_evasion(const actor *act) const{int evasion = ev;if (paralysed() || asleep())evasion = 0;else if (caught())evasion /= (body_size(PSIZE_BODY) + 2);else if (confused())evasion /= 2;return (evasion);}void monsters::heal(int amount, bool max_too){hit_points += amount;if (max_too)max_hit_points += amount;if (hit_points > max_hit_points)hit_points = max_hit_points;}mon_holy_type monsters::holiness() const{if (testbits(flags, MF_HONORARY_UNDEAD))return (MH_UNDEAD);return (mons_class_holiness(type));}bool monsters::is_unholy() const{const mon_holy_type holi = holiness();return (holi == MH_UNDEAD || holi == MH_DEMONIC);}int monsters::res_fire() const{const mon_resist_def res = get_mons_resists(this);int u = std::min(res.fire + res.hellfire * 3, 3);if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += _scan_mon_inv_randarts(this, ARTP_FIRE);const int armour = inv[MSLOT_ARMOUR];const int shld = inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// intrinsic armour abilitiesswitch (mitm[armour].sub_type){case ARM_DRAGON_ARMOUR: u += 2; break;case ARM_GOLD_DRAGON_ARMOUR: u += 1; break;case ARM_ICE_DRAGON_ARMOUR: u -= 1; break;default: break;}// check ego resistanceconst int ego = get_armour_ego_type(mitm[armour]);if (ego == SPARM_FIRE_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// check ego resistanceconst int ego = get_armour_ego_type(mitm[shld]);if (ego == SPARM_FIRE_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}}if (u < -3)u = -3;else if (u > 3)u = 3;return (u);}int monsters::res_steam() const{int res = get_mons_resists(this).steam;if (has_equipped(EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR))res += 3;return (res + res_fire() / 2);}int monsters::res_cold() const{int u = get_mons_resists(this).cold;if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += _scan_mon_inv_randarts(this, ARTP_COLD);const int armour = inv[MSLOT_ARMOUR];const int shld = inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// intrinsic armour abilitiesswitch (mitm[armour].sub_type){case ARM_ICE_DRAGON_ARMOUR: u += 2; break;case ARM_GOLD_DRAGON_ARMOUR: u += 1; break;case ARM_DRAGON_ARMOUR: u -= 1; break;default: break;}// check ego resistanceconst int ego = get_armour_ego_type(mitm[armour]);if (ego == SPARM_COLD_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// check ego resistanceconst int ego = get_armour_ego_type(mitm[shld]);if (ego == SPARM_COLD_RESISTANCE || ego == SPARM_RESISTANCE)u += 1;}}if (u < -3)u = -3;else if (u > 3)u = 3;return (u);}int monsters::res_elec() const{// This is a variable, not a player_xx() function, so can be above 1.int u = 0;u += get_mons_resists(this).elec;// Don't bother checking equipment if the monster can't use it.if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += _scan_mon_inv_randarts(this, ARTP_ELECTRICITY);// No ego armour, but storm dragon.const int armour = inv[MSLOT_ARMOUR];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR&& mitm[armour].sub_type == ARM_STORM_DRAGON_ARMOUR){u += 1;}}// Monsters can legitimately get multiple levels of electricity resistance.return (u);}int monsters::res_asphyx() const{int res = get_mons_resists(this).asphyx;const mon_holy_type holi = holiness();if (is_unholy()|| holi == MH_NONLIVING|| holi == MH_PLANT){res += 1;}return (res);}int monsters::res_poison() const{int u = get_mons_resists(this).poison;if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += _scan_mon_inv_randarts( this, ARTP_POISON );const int armour = this->inv[MSLOT_ARMOUR];const int shld = this->inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// intrinsic armour abilitiesswitch (mitm[armour].sub_type){case ARM_SWAMP_DRAGON_ARMOUR: u += 1; break;case ARM_GOLD_DRAGON_ARMOUR: u += 1; break;default: break;}// ego armour resistanceif (get_armour_ego_type(mitm[armour]) == SPARM_POISON_RESISTANCE)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// ego armour resistanceif (get_armour_ego_type(mitm[shld]) == SPARM_POISON_RESISTANCE)u += 1;}}// Monsters can legitimately get multiple levels of poison resistance.return (u);}int monsters::res_sticky_flame() const{int res = get_mons_resists(this).sticky_flame;if (has_equipped(EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR))res += 1;return (res);}int monsters::res_rotting() const{int res = get_mons_resists(this).rotting;if (holiness() != MH_NATURAL)res += 1;return (res);}int monsters::res_holy_energy(const actor *attacker) const{if (mons_is_evil(this))return (-1);if (is_unholy())return (-2);if (is_good_god(god)|| mons_is_holy(this)|| mons_neutral(this)|| is_unchivalric_attack(attacker, this)|| is_good_god(you.religion) && is_follower(this)){return (1);}return (0);}int monsters::res_negative_energy() const{if (holiness() != MH_NATURAL|| type == MONS_SHADOW_DRAGON){return (3);}int u = 0;if (mons_itemuse(this) >= MONUSE_STARTING_EQUIPMENT){u += _scan_mon_inv_randarts(this, ARTP_NEGATIVE_ENERGY);const int armour = this->inv[MSLOT_ARMOUR];const int shld = this->inv[MSLOT_SHIELD];if (armour != NON_ITEM && mitm[armour].base_type == OBJ_ARMOUR){// check for ego resistanceif (get_armour_ego_type(mitm[armour]) == SPARM_POSITIVE_ENERGY)u += 1;}if (shld != NON_ITEM && mitm[shld].base_type == OBJ_ARMOUR){// check for ego resistanceif (get_armour_ego_type(mitm[shld]) == SPARM_POSITIVE_ENERGY)u += 1;}}if (u > 3)u = 3;return (u);}int monsters::res_torment() const{const mon_holy_type holy = holiness();if (holy == MH_UNDEAD|| holy == MH_DEMONIC|| holy == MH_NONLIVING){return (1);}return (0);}int monsters::res_acid() const{return (get_mons_resists(this).acid);}flight_type monsters::flight_mode() const{return (mons_flies(this));}bool monsters::is_levitating() const{// Checking class flags is not enough - see mons_flies.return (flight_mode() == FL_LEVITATE);}int monsters::mons_species() const{return ::mons_species(type);}void monsters::poison(actor *agent, int amount){if (amount <= 0)return;// Scale poison down for monsters.if (!(amount /= 2))amount = 1;poison_monster(this, agent ? agent->kill_alignment() : KC_OTHER, amount);}int monsters::skill(skill_type sk, bool) const{switch (sk){case SK_NECROMANCY:return (holiness() == MH_UNDEAD ? hit_dice / 2 : hit_dice / 3);default:return (0);}}void monsters::blink(bool){monster_blink(this);}void monsters::teleport(bool now, bool){monster_teleport(this, now, false);}bool monsters::alive() const{return (hit_points > 0 && type != MONS_NO_MONSTER);}god_type monsters::deity() const{return (god);}bool monsters::drain_exp(actor *agent, bool quiet, int pow){if (x_chance_in_y(res_negative_energy(), 3))return (false);if (!quiet && you.can_see(this))mprf("%s is drained!", name(DESC_CAP_THE).c_str());// If quiet, don't clean up the monster in order to credit properly.hurt(agent, 2 + random2(pow), BEAM_NEG, !quiet);if (alive()){if (x_chance_in_y(pow, 15)){hit_dice--;experience = 0;}max_hit_points -= 2 + random2(pow);hit_points = std::min(max_hit_points, hit_points);}return (true);}bool monsters::rot(actor *agent, int amount, int immediate, bool quiet){if (res_rotting() > 0 || amount <= 0)return (false);if (!quiet && you.can_see(this)){mprf("%s %s!", name(DESC_CAP_THE).c_str(),amount > 0 ? "rots" : "looks less resilient");}// Apply immediate damage because we can't handle rotting for// monsters yet.if (immediate > 0){// If quiet, don't clean up the monster in order to credit// properly.hurt(agent, immediate, BEAM_MISSILE, !quiet);if (alive()){max_hit_points -= immediate * 2;hit_points = std::min(max_hit_points, hit_points);}}add_ench(mon_enchant(ENCH_ROT, std::min(amount, 4),agent->kill_alignment()));return (true);}int monsters::hurt(const actor *agent, int amount, beam_type flavour,bool cleanup_dead){if (hit_points > 0 && type != -1){if (amount == INSTANT_DEATH)amount = hit_points;else if (hit_dice <= 0)amount = hit_points;else if (amount <= 0 && hit_points <= max_hit_points)return (0);amount = std::min(amount, hit_points);hit_points -= amount;if (hit_points > max_hit_points){amount += hit_points - max_hit_points;hit_points = max_hit_points;}// Allow the victim to exhibit passive damage behaviour (royal// jelly).kill_category whose = (agent == NULL) ? KC_OTHER :(agent->atype() == ACT_PLAYER) ? KC_YOU :mons_friendly_real((monsters*)agent) ? KC_FRIENDLY :KC_OTHER;react_to_damage(amount, flavour, whose);}if (cleanup_dead && (hit_points <= 0 || hit_dice <= 0) && type != -1){if (agent == NULL)monster_die(this, KILL_MISC, NON_MONSTER);else if (agent->atype() == ACT_PLAYER)monster_die(this, KILL_YOU, NON_MONSTER);elsemonster_die(this, KILL_MON, agent->mindex());}return (amount);}void monsters::confuse(actor *atk, int strength){enchant_monster_with_flavour(this, atk, BEAM_CONFUSION, strength);}void monsters::paralyse(actor *atk, int strength){enchant_monster_with_flavour(this, atk, BEAM_PARALYSIS, strength);}void monsters::petrify(actor *atk, int strength){if (mons_is_insubstantial(type))return;enchant_monster_with_flavour(this, atk, BEAM_PETRIFY, strength);}void monsters::slow_down(actor *atk, int strength){enchant_monster_with_flavour(this, atk, BEAM_SLOW, strength);}void monsters::set_ghost(const ghost_demon &g, bool has_name){ghost.reset(new ghost_demon(g));if (has_name)mname = ghost->name;}void monsters::pandemon_init(){hit_dice = ghost->xl;max_hit_points = ghost->max_hp;hit_points = max_hit_points;ac = ghost->ac;ev = ghost->ev;flags = MF_INTERESTING;// Don't make greased-lightning Pandemonium demons in the dungeon// max speed = 17). Demons in Pandemonium can be up to speed 24.if (you.level_type == LEVEL_DUNGEON)speed = (one_chance_in(3) ? 10 : 7 + roll_dice(2, 5));elsespeed = (one_chance_in(3) ? 10 : 10 + roll_dice(2, 7));speed_increment = 70;if (you.char_direction == GDT_ASCENDING && you.level_type == LEVEL_DUNGEON)colour = LIGHTRED;elsecolour = ghost->colour;load_spells(MST_GHOST);}void monsters::ghost_init(){type = MONS_PLAYER_GHOST;god = ghost->religion;hit_dice = ghost->xl;max_hit_points = ghost->max_hp;hit_points = max_hit_points;ac = ghost->ac;ev = ghost->ev;speed = ghost->speed;speed_increment = 70;attitude = ATT_HOSTILE;behaviour = BEH_WANDER;flags = MF_INTERESTING;foe = MHITNOT;foe_memory = 0;colour = ghost->colour;number = MONS_NO_MONSTER;load_spells(MST_GHOST);inv.init(NON_ITEM);enchantments.clear();ench_countdown = 0;find_place_to_live();}void monsters::uglything_init(bool only_mutate){// If we're mutating an ugly thing, leave its experience level, hit// dice and maximum hit points as they are.if (!only_mutate){hit_dice = ghost->xl;max_hit_points = ghost->max_hp;}hit_points = max_hit_points;ac = ghost->ac;ev = ghost->ev;speed = ghost->speed;speed_increment = 70;colour = ghost->colour;}void monsters::uglything_mutate(unsigned char force_colour){ghost->init_ugly_thing(type == MONS_VERY_UGLY_THING, true, force_colour);uglything_init(true);}void monsters::uglything_upgrade(){ghost->ugly_thing_to_very_ugly_thing();uglything_init();}bool monsters::check_set_valid_home(const coord_def &place,coord_def &chosen,int &nvalid) const{if (!in_bounds(place))return (false);if (actor_at(place))return (false);if (!monster_habitable_grid(this, grd(place)))return (false);if (one_chance_in(++nvalid))chosen = place;return (true);}bool monsters::find_home_around(const coord_def &c, int radius){coord_def place(-1, -1);int nvalid = 0;for (int yi = -radius; yi <= radius; ++yi){const coord_def c1(c.x - radius, c.y + yi);const coord_def c2(c.x + radius, c.y + yi);check_set_valid_home(c1, place, nvalid);check_set_valid_home(c2, place, nvalid);}for (int xi = -radius + 1; xi < radius; ++xi){const coord_def c1(c.x + xi, c.y - radius);const coord_def c2(c.x + xi, c.y + radius);check_set_valid_home(c1, place, nvalid);check_set_valid_home(c2, place, nvalid);}if (nvalid){moveto(place);return (true);}return (false);}bool monsters::find_home_near_place(const coord_def &c){for (int radius = 1; radius < 7; ++radius)if (find_home_around(c, radius))return (true);return (false);}bool monsters::find_home_near_player(){return (find_home_near_place(you.pos()));}bool monsters::find_home_anywhere(){coord_def place(-1, -1);int nvalid = 0;for (int tries = 0; tries < 600; ++tries){if (check_set_valid_home(random_in_bounds(), place, nvalid)){moveto(place);return (true);}}return (false);}bool monsters::find_place_to_live(bool near_player){if (near_player && find_home_near_player()|| find_home_anywhere()){mgrd(pos()) = mindex();return (true);}return (false);}void monsters::destroy_inventory(){for (int j = 0; j < NUM_MONSTER_SLOTS; j++){if (inv[j] != NON_ITEM){destroy_item( inv[j] );inv[j] = NON_ITEM;}}}bool monsters::is_travelling() const{return (!travel_path.empty());}bool monsters::is_patrolling() const{return (!patrol_point.origin());}bool monsters::needs_transit() const{return ((mons_is_unique(type)|| (flags & MF_BANISHED)|| you.level_type == LEVEL_DUNGEON&& hit_dice > 8 + random2(25)&& mons_can_use_stairs(this))&& !mons_is_summoned(this));}void monsters::set_transit(const level_id &dest){add_monster_to_transit(dest, *this);}void monsters::load_spells(mon_spellbook_type book){spells.init(SPELL_NO_SPELL);if (book == MST_NO_SPELLS || book == MST_GHOST && !ghost.get())return;#if DEBUG_DIAGNOSTICSmprf( MSGCH_DIAGNOSTICS, "%s: loading spellbook #%d",name(DESC_PLAIN).c_str(), static_cast<int>(book) );#endifif (book == MST_GHOST)spells = ghost->spells;else{for (unsigned int i = 0; i < ARRAYSZ(mspell_list); ++i){if (mspell_list[i].type == book){for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j)spells[j] = mspell_list[i].spells[j];break;}}}#if DEBUG_DIAGNOSTICS// Only for ghosts, too spammy to use for all monsters.if (book == MST_GHOST){for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++){mprf( MSGCH_DIAGNOSTICS, "Spell #%d: %d (%s)",i, spells[i], spell_title(spells[i]) );}}#endif}bool monsters::has_hydra_multi_attack() const{return (mons_species() == MONS_HYDRA|| mons_is_zombified(this) && base_monster == MONS_HYDRA);}bool monsters::has_multitargeting() const{if (mons_class_wields_two_weapons(type))return (true);// Hacky little list for now. evkreturn (type == MONS_HYDRA|| type == MONS_TENTACLED_MONSTROSITY|| type == MONS_ELECTRIC_GOLEM);}bool monsters::has_ench(enchant_type ench) const{return (enchantments.find(ench) != enchantments.end());}bool monsters::has_ench(enchant_type ench, enchant_type ench2) const{if (ench2 == ENCH_NONE)ench2 = ench;for (int i = ench; i <= ench2; ++i)if (has_ench(static_cast<enchant_type>(i)))return (true);return (false);}mon_enchant monsters::get_ench(enchant_type ench1,enchant_type ench2) const{if (ench2 == ENCH_NONE)ench2 = ench1;for (int e = ench1; e <= ench2; ++e){mon_enchant_list::const_iterator i =enchantments.find(static_cast<enchant_type>(e));if (i != enchantments.end())return (i->second);}return mon_enchant();}void monsters::update_ench(const mon_enchant &ench){if (ench.ench != ENCH_NONE){mon_enchant_list::iterator i = enchantments.find(ench.ench);if (i != enchantments.end())i->second = ench;}}bool monsters::add_ench(const mon_enchant &ench){// sillinessif (ench.ench == ENCH_NONE)return (false);if (ench.ench == ENCH_FEAR&& (holiness() == MH_NONLIVING || has_ench(ENCH_BERSERK))){return (false);}mon_enchant_list::iterator i = enchantments.find(ench.ench);bool new_enchantment = false;mon_enchant *added = NULL;if (i == enchantments.end()){new_enchantment = true;added = &(enchantments[ench.ench] = ench);}else{i->second += ench;added = &i->second;}// If the duration is not set, we must calculate it (depending on the// enchantment).if (!ench.duration)added->set_duration(this, new_enchantment ? NULL : &ench);if (new_enchantment)add_enchantment_effect(ench);return (true);}void monsters::forget_random_spell(){int which_spell = -1;int count = 0;for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)if (spells[i] != SPELL_NO_SPELL && one_chance_in(++count))which_spell = i;if (which_spell != -1)spells[which_spell] = SPELL_NO_SPELL;}void monsters::add_enchantment_effect(const mon_enchant &ench, bool quiet){// Check for slow/haste.switch (ench.ench){case ENCH_BERSERK:// Inflate hp.scale_hp(3, 2);if (has_ench(ENCH_SUBMERGED))del_ench(ENCH_SUBMERGED);if (mons_is_lurking(this)){behaviour = BEH_WANDER;behaviour_event(this, ME_EVAL);}break;case ENCH_HASTE:if (speed >= 100)speed = 100 + ((speed - 100) * 2);elsespeed *= 2;break;case ENCH_SLOW:if (speed >= 100)speed = 100 + ((speed - 100) / 2);elsespeed /= 2;break;case ENCH_SUBMERGED:mons_clear_trapping_net(this);// Don't worry about invisibility. You should be able to see if// something has submerged.if (!quiet && mons_near(this)){if (type == MONS_AIR_ELEMENTAL){mprf("%s merges itself into the air.",name(DESC_CAP_A, true).c_str());}else if (type == MONS_TRAPDOOR_SPIDER){mprf("%s hides itself under the floor.",name(DESC_CAP_A, true).c_str());}else if (seen_context == "surfaces"|| seen_context == "bursts forth"|| seen_context == "emerges"){// The monster surfaced and submerged in the same turn// without doing anything else.interrupt_activity(AI_SEE_MONSTER,activity_interrupt_data(this,"surfaced"));}else if (crawl_state.arena)mprf("%s submerges.", name(DESC_CAP_A, true).c_str());}// Pacified monsters leave the level when they submerge.if (mons_is_pacified(this))make_mons_leave_level(this);break;case ENCH_CONFUSION:if (type == MONS_TRAPDOOR_SPIDER && has_ench(ENCH_SUBMERGED))del_ench(ENCH_SUBMERGED);if (mons_is_lurking(this)){behaviour = BEH_WANDER;behaviour_event(this, ME_EVAL);}break;case ENCH_CHARM:behaviour = BEH_SEEK;target = you.pos();foe = MHITYOU;if (is_patrolling()){// Enslaved monsters stop patrolling and forget their patrol// point; they're supposed to follow you now.patrol_point.reset();}if (you.can_see(this))learned_something_new(TUT_MONSTER_FRIENDLY, pos());break;default:break;}}static bool _prepare_del_ench(monsters* mon, const mon_enchant &me){if (me.ench != ENCH_SUBMERGED)return (true);// Lurking monsters only unsubmerge when their foe is in sight if the foe// is right next to them.if (mons_is_lurking(mon)){const actor* foe = mon->get_foe();if (foe != NULL && mon->can_see(foe)&& !adjacent(mon->pos(), foe->pos())){return (false);}}int midx = mon->mindex();if (mgrd(mon->pos()) == NON_MONSTER)mgrd(mon->pos()) = midx;if (mon->pos() != you.pos() && midx == mgrd(mon->pos()))return (true);if (midx != mgrd(mon->pos())){monsters* other_mon = &menv[mgrd(mon->pos())];if (other_mon->type == MONS_NO_MONSTER|| other_mon->type == MONS_PROGRAM_BUG){mgrd(mon->pos()) = midx;mprf(MSGCH_ERROR, "mgrd(%d,%d) points to %s monster, even ""though it contains submerged monster %s (see bug 2293518)",mon->pos().x, mon->pos().y,other_mon->type == MONS_NO_MONSTER ? "dead" : "buggy",mon->name(DESC_PLAIN, true).c_str());if (mon->pos() != you.pos())return (true);}elsemprf(MSGCH_ERROR, "%s tried to unsubmerge while on same square as ""%s (see bug 2293518)", mon->name(DESC_CAP_THE, true).c_str(),mon->name(DESC_NOCAP_A, true).c_str());}// Monster un-submerging while under player or another monster. Try to// move to an adjacent square in which the monster could have been// submerged and have it unsubmerge from there.coord_def target_square;int okay_squares = 0;for (adjacent_iterator ai; ai; ++ai)if (mgrd(*ai) == NON_MONSTER && *ai != you.pos()&& monster_can_submerge(mon, grd(*ai))&& one_chance_in(++okay_squares)){target_square = *ai;}if (okay_squares > 0){mon->move_to_pos(target_square);return (true);}// No available adjacent squares from which the monster could also// have unsubmerged. Can it just stay submerged where it is?if (monster_can_submerge(mon, grd(mon->pos())))return (false);// The terrain changed and the monster can't remain submerged.// Try to move to an adjacent square where it would be happy.for (adjacent_iterator ai; ai; ++ai){if (mgrd(*ai) == NON_MONSTER&& monster_habitable_grid(mon, grd(*ai))&& !find_trap(*ai)){if (one_chance_in(++okay_squares))target_square = *ai;}}if (okay_squares > 0)mon->move_to_pos(target_square);return (true);}bool monsters::del_ench(enchant_type ench, bool quiet, bool effect){mon_enchant_list::iterator i = enchantments.find(ench);if (i == enchantments.end())return (false);const mon_enchant me = i->second;const enchant_type et = i->first;if (!_prepare_del_ench(this, me))return (false);enchantments.erase(et);if (effect)remove_enchantment_effect(me, quiet);return (true);}void monsters::remove_enchantment_effect(const mon_enchant &me, bool quiet){switch (me.ench){case ENCH_BERSERK:scale_hp(2, 3);break;case ENCH_HASTE:if (speed >= 100)speed = 100 + ((speed - 100) / 2);elsespeed /= 2;if (!quiet)simple_monster_message(this, " is no longer moving quickly.");break;case ENCH_MIGHT:if (!quiet)simple_monster_message(this, " no longer looks unusually strong.");break;case ENCH_SLOW:if (!quiet)simple_monster_message(this, " is no longer moving slowly.");if (speed >= 100)speed = 100 + ((speed - 100) * 2);elsespeed *= 2;break;case ENCH_PARALYSIS:if (!quiet)simple_monster_message(this, " is no longer paralysed.");behaviour_event(this, ME_EVAL);break;case ENCH_NEUTRAL:if (!quiet)simple_monster_message(this, " is no longer neutral.");behaviour_event(this, ME_EVAL);break;case ENCH_PETRIFIED:if (!quiet)simple_monster_message(this, " is no longer petrified.");del_ench(ENCH_PETRIFYING);behaviour_event(this, ME_EVAL);break;case ENCH_PETRIFYING:if (!has_ench(ENCH_PETRIFIED))break;if (!quiet)simple_monster_message(this, " stops moving altogether!");behaviour_event(this, ME_EVAL);break;case ENCH_FEAR:if (holiness() == MH_NONLIVING || has_ench(ENCH_BERSERK)){// This should only happen because of fleeing sanctuarysnprintf(info, INFO_SIZE, " stops retreating.");}else{snprintf(info, INFO_SIZE, " seems to regain %s courage.",this->pronoun(PRONOUN_NOCAP_POSSESSIVE, true).c_str());}if (!quiet)simple_monster_message(this, info);// Reevaluate behaviour.behaviour_event(this, ME_EVAL);break;case ENCH_CONFUSION:if (!quiet)simple_monster_message(this, " seems less confused.");// Reevaluate behaviour.behaviour_event(this, ME_EVAL);break;case ENCH_INVIS:// Invisible monsters stay invisible.if (mons_class_flag(type, M_INVIS))add_ench(mon_enchant(ENCH_INVIS));else if (mons_near(this) && !you.can_see_invisible()&& !has_ench(ENCH_SUBMERGED)){if (!quiet){mprf("%s appears from thin air!",name(DESC_CAP_A, true).c_str());autotoggle_autopickup(false);}handle_seen_interrupt(this);}break;case ENCH_CHARM:if (!quiet)simple_monster_message(this, " is no longer charmed.");if (you.can_see(this)){// and fire activity interruptsinterrupt_activity(AI_SEE_MONSTER,activity_interrupt_data(this, "uncharm"));}if (is_patrolling()){// Enslaved monsters stop patrolling and forget their patrol point,// in case they were on order to wait.patrol_point.reset();}// Reevaluate behaviour.behaviour_event(this, ME_EVAL);break;case ENCH_BACKLIGHT:if (!quiet){if (visible_to(&you))simple_monster_message(this, " stops glowing.");else if (has_ench(ENCH_INVIS) && mons_near(this)){mprf("%s stops glowing and disappears.",name(DESC_CAP_THE, true).c_str());}}break;case ENCH_STICKY_FLAME:if (!quiet)simple_monster_message(this, " stops burning.");break;case ENCH_POISON:if (!quiet)simple_monster_message(this, " looks more healthy.");break;case ENCH_ROT:if (!quiet)simple_monster_message(this, " is no longer rotting.");break;case ENCH_HELD:{int net = get_trapping_net(this->pos());if (net != NON_ITEM)remove_item_stationary(mitm[net]);if (!quiet)simple_monster_message(this, " breaks free.");break;}case ENCH_ABJ:case ENCH_SHORT_LIVED:// Set duration to -1 so that monster_die() and any of its// callees can tell that the monster ran out of time or was// abjured.add_ench(mon_enchant(ENCH_ABJ, 0, KC_OTHER, -1));if (this->has_ench(ENCH_BERSERK))simple_monster_message(this, " is no longer berserk.");monster_die(this, quiet ? KILL_DISMISSED : KILL_RESET, NON_MONSTER);break;case ENCH_SUBMERGED:if (mons_is_wandering(this)){behaviour = BEH_SEEK;behaviour_event(this, ME_EVAL);}if (you.pos() == this->pos()){mprf(MSGCH_ERROR, "%s is on the same square as you!",name(DESC_CAP_A).c_str());}if (you.can_see(this)){if (!mons_is_safe(this) && is_run_delay(current_delay_action())){// Already set somewhere else.if (!seen_context.empty())return;if (type == MONS_AIR_ELEMENTAL)seen_context = "thin air";else if (type == MONS_TRAPDOOR_SPIDER)seen_context = "leaps out";else if (!monster_habitable_grid(this, DNGN_FLOOR))seen_context = "bursts forth";elseseen_context = "surfaces";}else if (!quiet){if (type == MONS_AIR_ELEMENTAL){mprf("%s forms itself from the air!",name(DESC_CAP_A, true).c_str() );}else if (type == MONS_TRAPDOOR_SPIDER){mprf("%s leaps out from its hiding place under the floor!",name(DESC_CAP_A, true).c_str() );}else if (crawl_state.arena)mprf("%s surfaces.", name(DESC_CAP_A, true).c_str() );}}else if (mons_near(this)&& feat_compatible(grd(pos()), DNGN_DEEP_WATER)){mpr("Something invisible bursts forth from the water.");interrupt_activity(AI_FORCE_INTERRUPT);}break;case ENCH_SOUL_RIPE:if (!quiet){simple_monster_message(this,"'s soul is no longer ripe for the taking.");}break;default:break;}}bool monsters::lose_ench_levels(const mon_enchant &e, int lev){if (!lev)return (false);if (e.degree <= lev){del_ench(e.ench);return (true);}else{mon_enchant newe(e);newe.degree -= lev;update_ench(newe);return (false);}}bool monsters::lose_ench_duration(const mon_enchant &e, int dur){if (!dur)return (false);if (e.duration <= dur){del_ench(e.ench);return (true);}else{mon_enchant newe(e);newe.duration -= dur;update_ench(newe);return (false);}}//---------------------------------------------------------------//// timeout_enchantments//// Update a monster's enchantments when the player returns// to the level.//// Management for enchantments... problems with this are the oddities// (monster dying from poison several thousands of turns later), and// game balance.//// Consider: Poison/Sticky Flame a monster at range and leave, monster// dies but can't leave level to get to player (implied game balance of// the delayed damage is that the monster could be a danger before// it dies). This could be fixed by keeping some monsters active// off level and allowing them to take stairs (a very serious change).//// Compare this to the current abuse where the player gets// effectively extended duration of these effects (although only// the actual effects only occur on level, the player can leave// and heal up without having the effect disappear).//// This is a simple compromise between the two... the enchantments// go away, but the effects don't happen off level. -- bwr////---------------------------------------------------------------void monsters::timeout_enchantments(int levels){if (enchantments.empty())return;const mon_enchant_list ec = enchantments;for (mon_enchant_list::const_iterator i = ec.begin();i != ec.end(); ++i){switch (i->first){case ENCH_POISON: case ENCH_ROT: case ENCH_BACKLIGHT:case ENCH_STICKY_FLAME: case ENCH_ABJ: case ENCH_SHORT_LIVED:case ENCH_SLOW: case ENCH_HASTE: case ENCH_MIGHT: case ENCH_FEAR:case ENCH_INVIS: case ENCH_CHARM: case ENCH_SLEEP_WARY:case ENCH_SICK: case ENCH_SLEEPY: case ENCH_PARALYSIS:case ENCH_PETRIFYING: case ENCH_PETRIFIED:case ENCH_BATTLE_FRENZY: case ENCH_NEUTRAL:case ENCH_LOWERED_MR: case ENCH_SOUL_RIPE:lose_ench_levels(i->second, levels);break;case ENCH_BERSERK:del_ench(i->first);del_ench(ENCH_HASTE, true);del_ench(ENCH_MIGHT, true);break;case ENCH_FATIGUE:del_ench(i->first);del_ench(ENCH_SLOW);break;case ENCH_TP:del_ench(i->first);teleport(true);break;case ENCH_CONFUSION:if (!mons_class_flag(type, M_CONFUSED))del_ench(i->first);blink();break;case ENCH_HELD:del_ench(i->first);break;case ENCH_SLOWLY_DYING:{const int actdur = speed_to_duration(speed) * levels;if (lose_ench_duration(i->first, actdur))monster_die(this, KILL_MISC, NON_MONSTER, true);break;}default:break;}if (!alive())break;}}std::string monsters::describe_enchantments() const{std::ostringstream oss;for (mon_enchant_list::const_iterator i = enchantments.begin();i != enchantments.end(); ++i){if (i != enchantments.begin())oss << ", ";oss << std::string(i->second);}return (oss.str());}// Used to adjust time durations in calc_duration() for monster speed.static inline int _mod_speed( int val, int speed ){if (!speed)speed = 10;const int modded = (speed ? (val * 10) / speed : val);return (modded? modded : 1);}bool monsters::decay_enchantment(const mon_enchant &me, bool decay_degree){// Faster monsters can wiggle out of the net more quickly.const int spd = (me.ench == ENCH_HELD) ? speed :10;const int actdur = speed_to_duration(spd);if (lose_ench_duration(me, actdur))return (true);if (!decay_degree)return (false);// Decay degree so that higher degrees decay faster than lower// degrees, and a degree of 1 does not decay (it expires when the// duration runs out).const int level = me.degree;if (level <= 1)return (false);const int decay_factor = level * (level + 1) / 2;if (me.duration < me.maxduration * (decay_factor - 1) / decay_factor){mon_enchant newme = me;--newme.degree;newme.maxduration = newme.duration;if (newme.degree <= 0){del_ench(me.ench);return (true);}elseupdate_ench(newme);}return (false);}void monsters::apply_enchantment(const mon_enchant &me){const int spd = 10;switch (me.ench){case ENCH_BERSERK:if (decay_enchantment(me)){simple_monster_message(this, " is no longer berserk.");del_ench(ENCH_HASTE, true);del_ench(ENCH_MIGHT, true);const int duration = random_range(70, 130);add_ench(mon_enchant(ENCH_FATIGUE, 0, KC_OTHER, duration));add_ench(mon_enchant(ENCH_SLOW, 0, KC_OTHER, duration));}break;case ENCH_FATIGUE:if (decay_enchantment(me)){simple_monster_message(this, " looks more energetic.");del_ench(ENCH_SLOW, true);}break;case ENCH_SLOW:case ENCH_HASTE:case ENCH_MIGHT:case ENCH_FEAR:case ENCH_PARALYSIS:case ENCH_NEUTRAL:case ENCH_PETRIFYING:case ENCH_PETRIFIED:case ENCH_SICK:case ENCH_BACKLIGHT:case ENCH_ABJ:case ENCH_CHARM:case ENCH_SLEEP_WARY:case ENCH_LOWERED_MR:case ENCH_SOUL_RIPE:decay_enchantment(me);break;case ENCH_BATTLE_FRENZY:decay_enchantment(me, false);break;case ENCH_AQUATIC_LAND:// Aquatic monsters lose hit points every turn they spend on dry land.ASSERT(mons_habitat(this) == HT_WATER&& !feat_is_watery( grd(pos()) ));// Zombies don't take damage from flopping about on land.if (mons_is_zombified(this))break;// We don't have a reasonable agent to give.// Don't clean up the monster in order to credit properly.hurt(NULL, 1 + random2(5), BEAM_NONE, false);// Credit the kill.if (hit_points < 1){monster_die(this, me.killer(), me.kill_agent());break;}break;case ENCH_HELD:{if (mons_is_stationary(this) || mons_cannot_act(this)|| asleep()){break;}int net = get_trapping_net(this->pos(), true);if (net == NON_ITEM) // Really shouldn't happen!{del_ench(ENCH_HELD);break;}// Handled in handle_pickup().if (mons_eats_items(this))break;// The enchantment doubles as the durability of a net// the more corroded it gets, the more easily it will break.const int hold = mitm[net].plus; // This will usually be negative.const int mon_size = body_size(PSIZE_BODY);// Smaller monsters can escape more quickly.if (mon_size < random2(SIZE_BIG) // BIG = 5&& !has_ench(ENCH_BERSERK) && type != MONS_DANCING_WEAPON){if (mons_near(this) && !visible_to(&you))mpr("Something wriggles in the net.");elsesimple_monster_message(this, " struggles to escape the net.");// Confused monsters have trouble finding the exit.if (has_ench(ENCH_CONFUSION) && !one_chance_in(5))break;decay_enchantment(me, 2*(NUM_SIZE_LEVELS - mon_size) - hold);// Frayed nets are easier to escape.if (mon_size <= -(hold-1)/2)decay_enchantment(me, (NUM_SIZE_LEVELS - mon_size));}else // Large (and above) monsters always thrash the net and destroy it{ // e.g. ogre, large zombie (large); centaur, naga, hydra (big).if (mons_near(this) && !visible_to(&you))mpr("Something wriggles in the net.");elsesimple_monster_message(this, " struggles against the net.");// Confused monsters more likely to struggle without result.if (has_ench(ENCH_CONFUSION) && one_chance_in(3))break;// Nets get destroyed more quickly for larger monsters// and if already strongly frayed.int damage = 0;// tiny: 1/6, little: 2/5, small: 3/4, medium and above: alwaysif (x_chance_in_y(mon_size + 1, SIZE_GIANT - mon_size))damage++;// Handled specially to make up for its small size.if (type == MONS_DANCING_WEAPON){damage += one_chance_in(3);if (can_cut_meat(mitm[inv[MSLOT_WEAPON]]))damage++;}// Extra damage for large (50%) and big (always).if (mon_size == SIZE_BIG || mon_size == SIZE_LARGE && coinflip())damage++;// overall damage per struggle:// tiny -> 1/6// little -> 2/5// small -> 3/4// medium -> 1// large -> 1,5// big -> 2// extra damage if already damagedif (random2(body_size(PSIZE_BODY) - hold + 1) >= 4)damage++;// Berserking doubles damage dealt.if (has_ench(ENCH_BERSERK))damage *= 2;// Faster monsters can damage the net more often per// time period.if (speed != 0)damage = div_rand_round(damage * speed, spd);mitm[net].plus -= damage;if (mitm[net].plus < -7){if (mons_near(this)){if (visible_to(&you)){mprf("The net rips apart, and %s comes free!",name(DESC_NOCAP_THE).c_str());}else{mpr("All of a sudden the net rips apart!");}}destroy_item(net);del_ench(ENCH_HELD, true);}}break;}case ENCH_CONFUSION:if (!mons_class_flag(type, M_CONFUSED))decay_enchantment(me);break;case ENCH_INVIS:if (!mons_class_flag(type, M_INVIS))decay_enchantment(me);break;case ENCH_SUBMERGED:{// Not even air elementals unsubmerge into clouds.if (env.cgrid(pos()) != EMPTY_CLOUD)break;// Air elementals are a special case, as their submerging in air// isn't up to choice. - bwrif (type == MONS_AIR_ELEMENTAL){heal_monster( this, 1, one_chance_in(5) );if (one_chance_in(5))del_ench(ENCH_SUBMERGED);break;}// Now we handle the others:const dungeon_feature_type grid = grd(pos());// Badly injured monsters prefer to stay submerged...// electric eels and lava snakes have ranged attacks// and are more likely to surface. -- bwrif (!monster_can_submerge(this, grid))del_ench(ENCH_SUBMERGED); // forced to surfaceelse if (hit_points <= max_hit_points / 2)break;else if (type == MONS_TRAPDOOR_SPIDER){// This should probably never happen.if (!mons_is_lurking(this))del_ench(ENCH_SUBMERGED);break;}else if (((type == MONS_ELECTRIC_EEL || type == MONS_LAVA_SNAKE || type == MONS_KRAKEN)&& (one_chance_in(50) || (mons_near(this)&& hit_points == max_hit_points&& !one_chance_in(10))))|| one_chance_in(200)|| (mons_near(this)&& hit_points == max_hit_points&& !one_chance_in(5))){del_ench(ENCH_SUBMERGED);}break;}case ENCH_POISON:{const int poisonval = me.degree;int dam = (poisonval >= 4) ? 1 : 0;if (coinflip())dam += roll_dice(1, poisonval + 1);if (res_poison() < 0)dam += roll_dice(2, poisonval) - 1;if (dam > 0){// We don't have a reasonable agent to give.// Don't clean up the monster in order to credit properly.hurt(NULL, dam, BEAM_POISON, false);#if DEBUG_DIAGNOSTICS// For debugging, we don't have this silent.simple_monster_message( this, " takes poison damage.",MSGCH_DIAGNOSTICS );mprf(MSGCH_DIAGNOSTICS, "poison damage: %d", dam );#endif// Credit the kill.if (hit_points < 1){monster_die(this, me.killer(), me.kill_agent());break;}}decay_enchantment(me, true);break;}case ENCH_ROT:{if (hit_points > 1 && one_chance_in(3)){hurt(NULL, 1); // nonlethal so we don't care about agentif (hit_points < max_hit_points && coinflip())--max_hit_points;}decay_enchantment(me, true);break;}// Assumption: monsters::res_fire has already been checked.case ENCH_STICKY_FLAME:{if (feat_is_watery(grd(pos()))){if (mons_near(this) && visible_to(&you))mprf("The flames covering %s go out.",this->name(DESC_NOCAP_THE, false).c_str());del_ench(ENCH_STICKY_FLAME);break;}int dam = resist_adjust_damage(this, BEAM_FIRE, res_fire(),roll_dice(2, 4) - 1);if (dam > 0){simple_monster_message(this, " burns!");// We don't have a reasonable agent to give.// Don't clean up the monster in order to credit properly.hurt(NULL, dam, BEAM_NAPALM, false);#if DEBUG_DIAGNOSTICSmprf( MSGCH_DIAGNOSTICS, "sticky flame damage: %d", dam );#endif// Credit the kill.if (hit_points < 1){monster_die(this, me.killer(), me.kill_agent());break;}}decay_enchantment(me, true);break;}case ENCH_SHORT_LIVED:// This should only be used for ball lightning -- bwrif (decay_enchantment(me))hit_points = -1;break;case ENCH_SLOWLY_DYING:// If you are no longer dying, you must be dead.if (decay_enchantment(me)){if (::see_cell(this->position)){mprf("A nearby %s withers and dies.",this->name(DESC_PLAIN, false).c_str());}monster_die(this, KILL_MISC, NON_MONSTER, true);}break;case ENCH_SPORE_PRODUCTION:// Very low chance of actually making a spore on each turn.if(one_chance_in(5000)){int idx[] = {0, 1, 2, 3, 4, 5, 6, 7};std::random_shuffle(idx, idx + 8);for (unsigned i = 0; i < 8; ++i){coord_def adjacent = this->pos() + Compass[idx[i]];if (mons_class_can_pass(MONS_GIANT_SPORE, env.grid(adjacent))&& !actor_at(adjacent)){beh_type created_behavior = BEH_HOSTILE;if (this->attitude == ATT_FRIENDLY)created_behavior = BEH_FRIENDLY;int rc = create_monster(mgen_data(MONS_GIANT_SPORE,created_behavior,0,0,adjacent,MHITNOT,MG_FORCE_PLACE));if (rc != -1){env.mons[rc].behaviour = BEH_WANDER;if (::see_cell(adjacent) && ::see_cell(pos()))mpr("A nearby fungus spawns a giant spore.");}break;}}}break;case ENCH_GLOWING_SHAPESHIFTER: // This ench never runs out!// Number of actions is fine for shapeshifters. Don't change// shape while taking the stairs because monster_polymorph() has// an assert about it. -caoif (!(this->flags & MF_TAKING_STAIRS) && !asleep()&& (type == MONS_GLOWING_SHAPESHIFTER|| one_chance_in(4))){monster_polymorph(this, RANDOM_MONSTER);}break;case ENCH_SHAPESHIFTER: // This ench never runs out!if (!(this->flags & MF_TAKING_STAIRS) && !asleep()&& (type == MONS_SHAPESHIFTER|| x_chance_in_y(1000 / (15 * hit_dice / 5), 1000))){monster_polymorph(this, RANDOM_MONSTER);}break;case ENCH_TP:if (decay_enchantment(me, true))monster_teleport(this, true);break;case ENCH_SLEEPY:del_ench(ENCH_SLEEPY);break;case ENCH_EAT_ITEMS:break;default:break;}}void monsters::mark_summoned(int longevity, bool mark_items, int summon_type){add_ench( mon_enchant(ENCH_ABJ, longevity) );if (summon_type != 0)add_ench( mon_enchant(ENCH_SUMMON, summon_type, KC_OTHER, INT_MAX) );if (mark_items){for (int i = 0; i < NUM_MONSTER_SLOTS; ++i){const int item = inv[i];if (item != NON_ITEM)mitm[item].flags |= ISFLAG_SUMMONED;}}}bool monsters::is_summoned(int* duration, int* summon_type) const{return mons_is_summoned(this, duration, summon_type);}void monsters::apply_enchantments(){if (enchantments.empty())return;// The ordering in enchant_type makes sure that "super-enchantments"// like berserk time out before their parts.const mon_enchant_list ec = enchantments;for (mon_enchant_list::const_iterator i = ec.begin(); i != ec.end(); ++i){apply_enchantment(i->second);if (!alive())break;}}void monsters::scale_hp(int num, int den){hit_points = hit_points * num / den;max_hit_points = max_hit_points * num / den;if (hit_points < 1)hit_points = 1;if (max_hit_points < 1)max_hit_points = 1;if (hit_points > max_hit_points)hit_points = max_hit_points;}kill_category monsters::kill_alignment() const{return (mons_friendly_real(this) ? KC_FRIENDLY : KC_OTHER);}bool monsters::sicken(int amount){if (res_rotting() || (amount /= 2) < 1)return (false);if (!has_ench(ENCH_SICK) && you.can_see(this)){// Yes, could be confused with poisoning.mprf("%s looks sick.", name(DESC_CAP_THE).c_str());}add_ench(mon_enchant(ENCH_SICK, 0, KC_OTHER, amount * 10));return (true);}static int _mons_real_base_speed(int mc){ASSERT(smc);int speed = smc->speed;switch (mc){case MONS_ABOMINATION_SMALL:speed = 7 + random2avg(9, 2);break;case MONS_ABOMINATION_LARGE:speed = 6 + random2avg(7, 2);break;case MONS_BEAST:speed = 10 + random2(8);break;}return (speed);}// Recalculate movement speed.void monsters::fix_speed(){speed = _mons_real_base_speed(type);if (has_ench(ENCH_HASTE))speed *= 2;else if (has_ench(ENCH_SLOW))speed /= 2;}// Check speed and speed_increment sanity.void monsters::check_speed(){// FIXME: If speed is borked, recalculate. Need to figure out how// speed is getting borked.if (speed < 0 || speed > 130){#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS,"Bad speed: %s, spd: %d, spi: %d, hd: %d, ench: %s",name(DESC_PLAIN).c_str(),speed, speed_increment, hit_dice,describe_enchantments().c_str());#endiffix_speed();#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS, "Fixed speed for %s to %d",name(DESC_PLAIN).c_str(), speed);#endif}if (speed_increment < 0)speed_increment = 0;if (speed_increment > 200){#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS,"Clamping speed increment on %s: %d",name(DESC_PLAIN).c_str(), speed_increment);#endifspeed_increment = 140;}}actor *monsters::get_foe() const{if (foe == MHITNOT)return (NULL);else if (foe == MHITYOU)return (mons_friendly(this) ? NULL : &you);// Must be a monster!monsters *my_foe = &menv[foe];return (my_foe->alive()? my_foe : NULL);}int monsters::foe_distance() const{const actor *afoe = get_foe();return (afoe ? pos().distance_from(afoe->pos()): INFINITE_DISTANCE);}bool monsters::can_go_berserk() const{if (holiness() != MH_NATURAL || type == MONS_KRAKEN_TENTACLE)return (false);if (has_ench(ENCH_FATIGUE) || has_ench(ENCH_BERSERK))return (false);// If we have no melee attack, going berserk is pointless.const mon_attack_def attk = mons_attack_spec(this, 0);if (attk.type == AT_NONE || attk.damage == 0)return (false);return (true);}bool monsters::needs_berserk(bool check_spells) const{if (!can_go_berserk())return (false);if (has_ench(ENCH_HASTE) || has_ench(ENCH_TP))return (false);if (foe_distance() > 3)return (false);if (check_spells){for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i){const int spell = spells[i];if (spell != SPELL_NO_SPELL && spell != SPELL_BERSERKER_RAGE)return (false);}}return (true);}bool monsters::can_see_invisible() const{if (mons_is_ghost_demon(this->type))return (this->ghost->see_invis);else if (mons_class_flag(this->type, M_SEE_INVIS))return (true);else if (_scan_mon_inv_randarts(this, ARTP_EYESIGHT) > 0)return (true);return (false);}bool monsters::invisible() const{return (has_ench(ENCH_INVIS) && !backlit());}bool monsters::visible_to(const actor *looker) const{bool vis = !invisible() || looker->can_see_invisible();return (vis && (this == looker || !has_ench(ENCH_SUBMERGED)));}bool monsters::mon_see_cell(const coord_def& p, bool reach) const{if (distance(pos(), p) > LOS_RADIUS * LOS_RADIUS + 1)return (false);dungeon_feature_type max_disallowed = DNGN_MAXOPAQUE;if (reach)max_disallowed = DNGN_MAX_NONREACH;// XXX: Ignoring clouds for now.return (!num_feats_between(pos(), p, DNGN_UNSEEN, max_disallowed,true, true));}bool monsters::see_cell(const coord_def &c) const{// XXX: using env.show since that's been filled anywa.if (c == you.pos())return (you.see_cell(pos()));// TODO: Proper monster LOS.return (mon_see_cell(c));}bool monsters::can_see(const actor *targ) const{return (targ->visible_to(this) && see_cell(targ->pos()));}bool monsters::near_foe() const{const actor *afoe = get_foe();return (afoe && see_cell(afoe->pos()));}bool monsters::can_mutate() const{return (holiness() == MH_NATURAL || holiness() == MH_PLANT);}bool monsters::can_safely_mutate() const{return (can_mutate());}bool monsters::can_bleed() const{return (mons_has_blood(type));}bool monsters::mutate(){if (!can_mutate())return (false);// Polymorphing a (very) ugly thing will mutate it into a different// (very) ugly thing.if (type == MONS_UGLY_THING || type == MONS_VERY_UGLY_THING){ugly_thing_mutate(this);return (true);}// Polymorphing a shapeshifter will make it revert to its original// form.if (this->has_ench(ENCH_GLOWING_SHAPESHIFTER))return (monster_polymorph(this, MONS_GLOWING_SHAPESHIFTER));if (this->has_ench(ENCH_SHAPESHIFTER))return (monster_polymorph(this, MONS_SHAPESHIFTER));return (monster_polymorph(this, RANDOM_MONSTER));}bool monsters::is_icy() const{return (mons_is_icy(type));}static bool _mons_is_fiery(int mc){return (mc == MONS_FIRE_VORTEX|| mc == MONS_FIRE_ELEMENTAL|| mc == MONS_FLAMING_CORPSE|| mc == MONS_EFREET|| mc == MONS_AZRAEL|| mc == MONS_LAVA_WORM|| mc == MONS_LAVA_FISH|| mc == MONS_LAVA_SNAKE|| mc == MONS_SALAMANDER|| mc == MONS_MOLTEN_GARGOYLE|| mc == MONS_ORB_OF_FIRE);}bool monsters::is_fiery() const{return (_mons_is_fiery(type));}bool monsters::has_action_energy() const{return (speed_increment >= 80);}void monsters::check_redraw(const coord_def &old) const{const bool see_new = ::see_cell(pos());const bool see_old = ::see_cell(old);if ((see_new || see_old) && !view_update()){if (see_new)view_update_at(pos());if (see_old)view_update_at(old);update_screen();}}void monsters::apply_location_effects(const coord_def &oldpos){if (oldpos != pos())dungeon_events.fire_position_event(DET_MONSTER_MOVED, pos());if (alive() && has_ench(ENCH_AQUATIC_LAND)){if (!feat_is_watery( grd(pos()) ))simple_monster_message(this, " flops around on dry land!");else if (!feat_is_watery( grd(oldpos) )){simple_monster_message(this, " dives back into the water!");del_ench(ENCH_AQUATIC_LAND);}}// Monsters stepping on traps:trap_def* ptrap = find_trap(pos());if (ptrap)ptrap->trigger(*this);if (alive())mons_check_pool(this, pos());if (alive() && has_ench(ENCH_SUBMERGED)&& (!monster_can_submerge(this, grd(pos()))|| type == MONS_TRAPDOOR_SPIDER)){del_ench(ENCH_SUBMERGED);if (type == MONS_TRAPDOOR_SPIDER)behaviour_event(this, ME_EVAL);}unsigned long &prop = env.map(pos()).property;if (prop & FPROP_BLOODY){monster_type genus = mons_genus(type);if (genus == MONS_JELLY || genus == MONS_GIANT_SLUG){prop &= ~FPROP_BLOODY;if (see_cell(pos()) && !visible_to(&you)){std::string desc =feature_description(pos(), false, DESC_NOCAP_THE, false);mprf("The bloodstain on %s disappears!", desc.c_str());}}}}bool monsters::move_to_pos(const coord_def &newpos){if (actor_at(newpos))return (false);// Clear old cell pointer.mgrd(pos()) = NON_MONSTER;// Set monster x,y to new value.moveto(newpos);// Set new monster grid pointer to this monster.mgrd(newpos) = mindex();return (true);}// Returns true if the trap should be revealed to the player.bool monsters::do_shaft(){if (!is_valid_shaft_level())return (false);// Handle instances of do_shaft() being invoked magically when// the monster isn't standing over a shaft.if (get_trap_type(this->pos()) != TRAP_SHAFT){switch (grd(pos())){case DNGN_FLOOR:case DNGN_OPEN_DOOR:case DNGN_TRAP_MECHANICAL:case DNGN_TRAP_MAGICAL:case DNGN_TRAP_NATURAL:case DNGN_UNDISCOVERED_TRAP:case DNGN_ENTER_SHOP:break;default:return (false);}if (airborne() || total_weight() == 0){if (mons_near(this)){if (visible_to(&you)){mprf("A shaft briefly opens up underneath %s!",name(DESC_NOCAP_THE).c_str());}elsempr("A shaft briefly opens up in the floor!");}handle_items_on_shaft(this->pos(), false);return (false);}}level_id lev = shaft_dest();if (lev == level_id::current())return (false);// If a pacified monster is leaving the level via a shaft trap, and// has reached its goal, handle it here.if (!mons_is_pacified(this))set_transit(lev);const bool reveal =simple_monster_message(this, " falls through a shaft!");handle_items_on_shaft(this->pos(), false);// Monster is no longer on this level.destroy_inventory();monster_cleanup(this);return (reveal);}void monsters::put_to_sleep(int){if (has_ench(ENCH_BERSERK))return;behaviour = BEH_SLEEP;add_ench(ENCH_SLEEPY);add_ench(ENCH_SLEEP_WARY);}void monsters::check_awaken(int){// XXX}const monsterentry *monsters::find_monsterentry() const{return (type == MONS_NO_MONSTER || type == MONS_PROGRAM_BUG) ? NULL: get_monster_data(type);}int monsters::action_energy(energy_use_type et) const{if (const monsterentry *me = find_monsterentry()){const mon_energy_usage &mu = me->energy_usage;switch (et){case EUT_MOVE: return mu.move;case EUT_SWIM:// [ds] Amphibious monsters get a significant speed boost// when swimming, as discussed with dpeg. We do not// distinguish between amphibians that favour land// (HT_AMPHIBIOUS_LAND, such as hydras) and those that// favour water (HT_AMPHIBIOUS_WATER, such as merfolk), but// that's something we can think about.if (mons_amphibious(this))return div_rand_round(mu.swim * 7, 10);elsereturn mu.swim;case EUT_MISSILE: return mu.missile;case EUT_ITEM: return mu.item;case EUT_SPECIAL: return mu.special;case EUT_SPELL: return mu.spell;case EUT_ATTACK: return mu.attack;case EUT_PICKUP: return mu.pickup_percent;}}return 10;}void monsters::lose_energy(energy_use_type et, int div, int mult){int energy_loss = div_round_up(mult * action_energy(et), div);if (has_ench(ENCH_PETRIFYING)){energy_loss *= 3;energy_loss /= 2;}speed_increment -= energy_loss;}
}bool monsters::can_drink_potion(potion_type ptype) const{if (mons_class_is_stationary(this->type))return (false);if (mons_itemuse(this) < MONUSE_STARTING_EQUIPMENT)return (false);// These monsters cannot drink.if (mons_is_skeletal(type) || mons_is_insubstantial(type)|| mons_species() == MONS_LICH || mons_genus(type) == MONS_MUMMY){return (false);}switch (ptype){case POT_HEALING:case POT_HEAL_WOUNDS:return (holiness() != MH_NONLIVING&& holiness() != MH_PLANT);case POT_BLOOD:case POT_BLOOD_COAGULATED:return (mons_species() == MONS_VAMPIRE);case POT_SPEED:case POT_MIGHT:case POT_BERSERK_RAGE:case POT_INVISIBILITY:// If there are any item using monsters that are permanently// invisible, this might have to be restricted.return (true);default:break;}return (false);
bool monsters::should_drink_potion(potion_type ptype) const{switch (ptype){case POT_HEALING:return (hit_points <= max_hit_points / 2)|| has_ench(ENCH_POISON)|| has_ench(ENCH_SICK)|| has_ench(ENCH_CONFUSION)|| has_ench(ENCH_ROT);case POT_HEAL_WOUNDS:return (hit_points <= max_hit_points / 2);case POT_BLOOD:case POT_BLOOD_COAGULATED:return (hit_points <= max_hit_points / 2);case POT_SPEED:return (!has_ench(ENCH_HASTE));case POT_MIGHT:return (!has_ench(ENCH_MIGHT) && foe_distance() <= 2);case POT_BERSERK_RAGE:// this implies !has_ench(ENCH_BERSERK_RAGE)return (!has_ench(ENCH_MIGHT) && !has_ench(ENCH_HASTE)&& needs_berserk());case POT_INVISIBILITY:// We're being nice: friendlies won't go invisible if the player// won't be able to see them.return (!has_ench(ENCH_INVIS)&& (you.can_see_invisible(false) || !mons_friendly(this)));default:break;}return (false);}// Return the ID status gained.item_type_id_state_type monsters::drink_potion_effect(potion_type ptype){simple_monster_message(this, " drinks a potion.");item_type_id_state_type ident = ID_MON_TRIED_TYPE;switch (ptype){case POT_HEALING:{heal(5 + random2(7));simple_monster_message(this, " is healed!");const enchant_type cured_enchants[] = {ENCH_POISON, ENCH_SICK, ENCH_CONFUSION, ENCH_ROT};// We can differentiate healing and heal wounds (and blood,// for vampires) by seeing if any status ailments are cured.for (unsigned int i = 0; i < ARRAYSZ(cured_enchants); ++i)if (del_ench(cured_enchants[i]))ident = ID_KNOWN_TYPE;}break;case POT_HEAL_WOUNDS:heal(10 + random2avg(28, 3));simple_monster_message(this, " is healed!");break;case POT_BLOOD:case POT_BLOOD_COAGULATED:if (mons_species() == MONS_VAMPIRE){heal(10 + random2avg(28, 3));simple_monster_message(this, " is healed!");}break;case POT_SPEED:if (enchant_monster_with_flavour(this, this, BEAM_HASTE))ident = ID_KNOWN_TYPE;break;case POT_INVISIBILITY:if (enchant_monster_with_flavour(this, this, BEAM_INVISIBILITY))ident = ID_KNOWN_TYPE;break;case POT_MIGHT:if (enchant_monster_with_flavour(this, this, BEAM_MIGHT))ident = ID_KNOWN_TYPE;break;case POT_BERSERK_RAGE:if (enchant_monster_with_flavour(this, this, BEAM_BERSERK))ident = ID_KNOWN_TYPE;break;default:break;}return (ident);}void monsters::react_to_damage(int damage, beam_type flavour, kill_category whose){if (!alive())return;// The royal jelly objects to taking damage and will SULK. :-)if (type == MONS_ROYAL_JELLY && flavour != BEAM_TORMENT_DAMAGE&& damage > 8 && x_chance_in_y(damage, 50)){mon_acting mact(this);const int tospawn =1 + random2avg(1 + std::min((damage - 8) / 8, 5), 2);#ifdef DEBUG_DIAGNOSTICSmprf(MSGCH_DIAGNOSTICS, "Trying to spawn %d jellies.", tospawn);#endifconst beh_type beha = SAME_ATTITUDE(this);int spawned = 0;for (int i = 0; i < tospawn; ++i){const monster_type jelly = royal_jelly_ejectable_monster();coord_def jpos = find_newmons_square_contiguous(jelly, pos());if (!in_bounds(jpos))continue;const int nmons = mons_place(mgen_data(jelly, beha, 0, 0,jpos, foe, 0, god));if (nmons != -1 && nmons != NON_MONSTER){// Don't allow milking the royal jelly.menv[nmons].flags |= MF_CREATED_FRIENDLY;spawned++;}}const bool needs_message = spawned && mons_near(this)&& visible_to(&you);if (needs_message){const std::string monnam = name(DESC_CAP_THE);mprf("%s shudders%s.", monnam.c_str(),spawned >= 5 ? " alarmingly" :spawned >= 3 ? " violently" :spawned > 1 ? " vigorously" : "");if (spawned == 1)mprf("%s spits out another jelly.", monnam.c_str());else{mprf("%s spits out %s more jellies.",monnam.c_str(),number_in_words(spawned).c_str());}}}else if (type == MONS_KRAKEN_TENTACLE && flavour != BEAM_TORMENT_DAMAGE){if (!invalid_monster_index(number)&& menv[number].type == MONS_KRAKEN){menv[number].hurt(&you, damage, flavour);}}else if (type == MONS_BUSH && flavour == BEAM_FIRE&& damage>8 && x_chance_in_y(damage, 20)){place_cloud(CLOUD_FIRE, pos(), 20+random2(15), whose, 5);}}/////////////////////////////////////////////////////////////////////////// mon_enchantstatic const char *enchant_names[] ={"none", "slow", "haste", "might", "fear", "conf", "inv", "pois", "bers","rot", "summon", "abj", "backlit", "charm", "fire","gloshifter", "shifter", "tp", "wary", "submerged","short-lived", "paralysis", "sick", "sleep", "fatigue", "held","blood-lust", "neutral", "petrifying", "petrified", "magic-vulnerable","soul-ripe", "decay", "hungry", "flopping", "spore-producing","downtrodden", "bug"};static const char *_mons_enchantment_name(enchant_type ench){COMPILE_CHECK(ARRAYSZ(enchant_names) == NUM_ENCHANTMENTS+1, c1);if (ench > NUM_ENCHANTMENTS)ench = NUM_ENCHANTMENTS;return (enchant_names[ench]);}mon_enchant::mon_enchant(enchant_type e, int deg, kill_category whose,int dur): ench(e), degree(deg), duration(dur), maxduration(0), who(whose){}mon_enchant::operator std::string () const{return make_stringf("%s (%d:%d%s)",_mons_enchantment_name(ench),degree,duration,kill_category_desc(who));}const char *mon_enchant::kill_category_desc(kill_category k) const{return (k == KC_YOU? " you" :k == KC_FRIENDLY? " pet" : "");}void mon_enchant::merge_killer(kill_category k){who = who < k? who : k;}void mon_enchant::cap_degree(){// Sickness is not capped.if (ench == ENCH_SICK)return;// Hard cap to simulate old enum behaviour, we should really throw this// out entirely.const int max = ench == ENCH_ABJ? 6 : 4;if (degree > max)degree = max;}mon_enchant &mon_enchant::operator += (const mon_enchant &other){if (ench == other.ench){degree += other.degree;cap_degree();duration += other.duration;merge_killer(other.who);}return (*this);}mon_enchant mon_enchant::operator + (const mon_enchant &other) const{mon_enchant tmp(*this);tmp += other;return (tmp);}killer_type mon_enchant::killer() const{return (who == KC_YOU ? KILL_YOU :who == KC_FRIENDLY ? KILL_MON: KILL_MISC);}int mon_enchant::kill_agent() const{return (who == KC_FRIENDLY? ANON_FRIENDLY_MONSTER : 0);}int mon_enchant::modded_speed(const monsters *mons, int hdplus) const{return (_mod_speed(mons->hit_dice + hdplus, mons->speed));}int mon_enchant::calc_duration(const monsters *mons,const mon_enchant *added) const{int cturn = 0;const int newdegree = added ? added->degree : degree;const int deg = newdegree ? newdegree : 1;// Beneficial enchantments (like Haste) should not be throttled by// monster HD via modded_speed(). Use mod_speed instead!switch (ench){case ENCH_HASTE:case ENCH_MIGHT:case ENCH_INVIS:cturn = 1000 / _mod_speed(25, mons->speed);break;case ENCH_SLOW:cturn = 250 / (1 + modded_speed(mons, 10));break;case ENCH_FEAR:cturn = 150 / (1 + modded_speed(mons, 5));break;case ENCH_PARALYSIS:cturn = std::max(90 / modded_speed(mons, 5), 3);break;case ENCH_PETRIFIED:cturn = std::max(8, 150 / (1 + modded_speed(mons, 5)));break;case ENCH_PETRIFYING:cturn = 50 / _mod_speed(10, mons->speed);break;case ENCH_CONFUSION:cturn = std::max(100 / modded_speed(mons, 5), 3);break;case ENCH_HELD:cturn = 120 / _mod_speed(25, mons->speed);break;case ENCH_POISON:cturn = 1000 * deg / _mod_speed(125, mons->speed);break;case ENCH_STICKY_FLAME:cturn = 1000 * deg / _mod_speed(200, mons->speed);break;case ENCH_ROT:if (deg > 1)cturn = 1000 * (deg - 1) / _mod_speed(333, mons->speed);cturn += 1000 / _mod_speed(250, mons->speed);break;case ENCH_BACKLIGHT:if (deg > 1)cturn = 1000 * (deg - 1) / _mod_speed(200, mons->speed);cturn += 1000 / _mod_speed(100, mons->speed);break;case ENCH_SHORT_LIVED:cturn = 1000 / _mod_speed(200, mons->speed);break;case ENCH_SLOWLY_DYING:// This may be a little too direct but the randomization at the end// of this function is excessive for toadstools. -caoreturn (2 * FRESHEST_CORPSE + random2(10))* speed_to_duration(mons->speed) * mons->speed / 10;case ENCH_ABJ:if (deg >= 6)cturn = 1000 / _mod_speed(10, mons->speed);if (deg >= 5)cturn += 1000 / _mod_speed(20, mons->speed);cturn += 1000 * std::min(4, deg) / _mod_speed(100, mons->speed);break;case ENCH_CHARM:cturn = 500 / modded_speed(mons, 10);break;case ENCH_TP:cturn = 1000 * deg / _mod_speed(1000, mons->speed);break;case ENCH_SLEEP_WARY:cturn = 1000 / _mod_speed(50, mons->speed);break;default:break;}cturn = std::max(2, cturn);int raw_duration = (cturn * speed_to_duration(mons->speed));raw_duration = std::max(15, fuzz_value(raw_duration, 60, 40));return (raw_duration);}// Calculate the effective duration (in terms of normal player time - 10// duration units being one normal player action) of this enchantment.void mon_enchant::set_duration(const monsters *mons, const mon_enchant *added){if (duration && !added)return;if (added && added->duration)duration += added->duration;elseduration += calc_duration(mons, added);if (duration > maxduration)maxduration = duration;}