Devilution
Diablo devolved - magic behind the 1996 computer game
monster.cpp
Go to the documentation of this file.
1 
6 #include "all.h"
7 #include "../3rdParty/Storm/Source/storm.h"
8 
10 
13 
14 // BUGFIX: replace monstkills[MAXMONSTERS] with monstkills[NUM_MTYPES].
25 
26 const char plr2monst[9] = { 0, 5, 3, 7, 1, 4, 6, 0, 2 };
28 
29 /* data */
30 
31 int MWVel[24][3] = {
32  { 256, 512, 1024 },
33  { 128, 256, 512 },
34  { 85, 170, 341 },
35  { 64, 128, 256 },
36  { 51, 102, 204 },
37  { 42, 85, 170 },
38  { 36, 73, 146 },
39  { 32, 64, 128 },
40  { 28, 56, 113 },
41  { 26, 51, 102 },
42  { 23, 46, 93 },
43  { 21, 42, 85 },
44  { 19, 39, 78 },
45  { 18, 36, 73 },
46  { 17, 34, 68 },
47  { 16, 32, 64 },
48  { 15, 30, 60 },
49  { 14, 28, 57 },
50  { 13, 26, 54 },
51  { 12, 25, 51 },
52  { 12, 24, 48 },
53  { 11, 23, 46 },
54  { 11, 22, 44 },
55  { 10, 21, 42 }
56 };
57 char animletter[7] = "nwahds";
58 int left[8] = { 7, 0, 1, 2, 3, 4, 5, 6 };
59 int right[8] = { 1, 2, 3, 4, 5, 6, 7, 0 };
60 int opposite[8] = { 4, 5, 6, 7, 0, 1, 2, 3 };
61 int offset_x[8] = { 1, 0, -1, -1, -1, 0, 1, 1 };
62 int offset_y[8] = { 1, 1, 1, 0, -1, -1, -1, 0 };
63 
65 int rnd5[4] = { 5, 10, 15, 20 };
66 int rnd10[4] = { 10, 15, 20, 30 };
67 int rnd20[4] = { 20, 30, 40, 50 };
68 int rnd60[4] = { 60, 70, 80, 90 };
69 
70 void (*AiProc[])(int i) = {
71  &MAI_Zombie,
72  &MAI_Fat,
73  &MAI_SkelSd,
74  &MAI_SkelBow,
75  &MAI_Scav,
76  &MAI_Rhino,
77  &MAI_GoatMc,
78  &MAI_GoatBow,
79  &MAI_Fallen,
80  &MAI_Magma,
81  &MAI_SkelKing,
82  &MAI_Bat,
83  &MAI_Garg,
84  &MAI_Cleaver,
85  &MAI_Succ,
86  &MAI_Sneak,
87  &MAI_Storm,
88  &MAI_Fireman,
89  &MAI_Garbud,
90  &MAI_Acid,
91  &MAI_AcidUniq,
92  &MAI_Golum,
93  &MAI_Zhar,
94  &MAI_SnotSpil,
95  &MAI_Snake,
97  &MAI_Mega,
98  &MAI_Diablo,
99  &MAI_Lazurus,
100  &MAI_Lazhelp,
101  &MAI_Lachdanan,
102  &MAI_Warlord,
103 };
104 
105 void InitMonsterTRN(int monst, BOOL special)
106 {
107  BYTE *f;
108  int i, n, j;
109 
110  f = Monsters[monst].trans_file;
111  for (i = 0; i < 256; i++) {
112  if (*f == 255) {
113  *f = 0;
114  }
115  f++;
116  }
117 
118  n = special ? 6 : 5;
119  for (i = 0; i < n; i++) {
120  if (i != 1 || Monsters[monst].mtype < MT_COUNSLR || Monsters[monst].mtype > MT_ADVOCATE) {
121  for (j = 0; j < 8; j++) {
123  Monsters[monst].Anims[i].Data[j],
124  Monsters[monst].trans_file,
125  Monsters[monst].Anims[i].Frames);
126  }
127  }
128  }
129 }
130 
132 {
133  int i;
134 
135  nummtypes = 0;
136  monstimgtot = 0;
137  MissileFileFlag = 0;
138 
139  for (i = 0; i < MAX_LVLMTYPES; i++) {
140  Monsters[i].mPlaceFlags = 0;
141  }
142 
143  ClrAllMonsters();
144  nummonsters = 0;
146 
147  for (i = 0; i < MAXMONSTERS; i++) {
148  monstactive[i] = i;
149  }
150 
151  uniquetrans = 0;
152 }
153 
154 int AddMonsterType(int type, int placeflag)
155 {
156  BOOL done = FALSE;
157  int i;
158 
159  for (i = 0; i < nummtypes && !done; i++) {
160  done = Monsters[i].mtype == type;
161  }
162 
163  i--;
164 
165  if (!done) {
166  i = nummtypes;
167  nummtypes++;
168  Monsters[i].mtype = type;
169  monstimgtot += monsterdata[type].mImage;
170  InitMonsterGFX(i);
171  InitMonsterSND(i);
172  }
173 
174  Monsters[i].mPlaceFlags |= placeflag;
175  return i;
176 }
177 
179 {
180  int i;
181 
182  // this array is merged with skeltypes down below.
183  int typelist[MAXMONSTERS];
184  int skeltypes[NUM_MTYPES];
185 
186  int minl; // min level
187  int maxl; // max level
188  char mamask;
189  const int numskeltypes = 19;
190 
191  int nt; // number of types
192 
193 #ifdef SPAWN
194  mamask = 1; // monster availability mask
195 #else
196  mamask = 3; // monster availability mask
197 #endif
198 
200  if (currlevel == 16) {
204  return;
205  }
206 
207  if (!setlevel) {
208  if (QuestStatus(Q_BUTCHER))
210  if (QuestStatus(Q_GARBUD))
212  if (QuestStatus(Q_ZHAR))
213  AddMonsterType(UniqMonst[UMT_ZHAR].mtype, 4);
214  if (QuestStatus(Q_LTBANNER))
216  if (QuestStatus(Q_VEIL))
218  if (QuestStatus(Q_WARLORD))
220 
221  if (gbMaxPlayers != 1 && currlevel == quests[Q_SKELKING]._qlevel) {
222 
224 
225  nt = 0;
226  for (i = MT_WSKELAX; i <= MT_WSKELAX + numskeltypes; i++) {
227  if (IsSkel(i)) {
228  minl = 15 * monsterdata[i].mMinDLvl / 30 + 1;
229  maxl = 15 * monsterdata[i].mMaxDLvl / 30 + 1;
230 
231  if (currlevel >= minl && currlevel <= maxl) {
232  if (MonstAvailTbl[i] & mamask) {
233  skeltypes[nt++] = i;
234  }
235  }
236  }
237  }
238  AddMonsterType(skeltypes[random_(88, nt)], 1);
239  }
240 
241  nt = 0;
242  for (i = 0; i < NUM_MTYPES; i++) {
243  minl = 15 * monsterdata[i].mMinDLvl / 30 + 1;
244  maxl = 15 * monsterdata[i].mMaxDLvl / 30 + 1;
245 
246  if (currlevel >= minl && currlevel <= maxl) {
247  if (MonstAvailTbl[i] & mamask) {
248  typelist[nt++] = i;
249  }
250  }
251  }
252 
253  if (monstdebug) {
254  for (i = 0; i < debugmonsttypes; i++)
256  } else {
257 
258  while (nt > 0 && nummtypes < MAX_LVLMTYPES && monstimgtot < 4000) {
259  for (i = 0; i < nt;) {
260  if (monsterdata[typelist[i]].mImage > 4000 - monstimgtot) {
261  typelist[i] = typelist[--nt];
262  continue;
263  }
264 
265  i++;
266  }
267 
268  if (nt != 0) {
269  i = random_(88, nt);
270  AddMonsterType(typelist[i], 1);
271  typelist[i] = typelist[--nt];
272  }
273  }
274  }
275 
276  } else {
277  if (setlvlnum == SL_SKELKING) {
279  }
280  }
281 }
282 
283 void InitMonsterGFX(int monst)
284 {
285  int mtype, anim, i;
286  char strBuff[256];
287  BYTE *celBuf;
288 
289  mtype = Monsters[monst].mtype;
290 
291  for (anim = 0; anim < 6; anim++) {
292  if ((animletter[anim] != 's' || monsterdata[mtype].has_special) && monsterdata[mtype].Frames[anim] > 0) {
293  sprintf(strBuff, monsterdata[mtype].GraphicType, animletter[anim]);
294 
295  celBuf = LoadFileInMem(strBuff, NULL);
296  Monsters[monst].Anims[anim].CMem = celBuf;
297 
298  if (Monsters[monst].mtype != MT_GOLEM || (animletter[anim] != 's' && animletter[anim] != 'd')) {
299 
300  for (i = 0; i < 8; i++) {
301  Monsters[monst].Anims[anim].Data[i] = CelGetFrameStart(celBuf, i);
302  }
303  } else {
304  for (i = 0; i < 8; i++) {
305  Monsters[monst].Anims[anim].Data[i] = celBuf;
306  }
307  }
308  }
309 
310  // TODO: either the AnimStruct members have wrong naming or the MonsterData ones it seems
311  Monsters[monst].Anims[anim].Frames = monsterdata[mtype].Frames[anim];
312  Monsters[monst].Anims[anim].Rate = monsterdata[mtype].Rate[anim];
313  }
314 
315  Monsters[monst].width = monsterdata[mtype].width;
316  Monsters[monst].width2 = (monsterdata[mtype].width - 64) >> 1;
317  Monsters[monst].mMinHP = monsterdata[mtype].mMinHP;
318  Monsters[monst].mMaxHP = monsterdata[mtype].mMaxHP;
320  Monsters[monst].mAFNum = monsterdata[mtype].mAFNum;
321  Monsters[monst].MData = &monsterdata[mtype];
322 
323  if (monsterdata[mtype].has_trans) {
324  Monsters[monst].trans_file = LoadFileInMem(monsterdata[mtype].TransFile, NULL);
325  InitMonsterTRN(monst, monsterdata[mtype].has_special);
326  MemFreeDbg(Monsters[monst].trans_file);
327  }
328 
329  if (mtype >= MT_NMAGMA && mtype <= MT_WMAGMA && !(MissileFileFlag & 1)) {
330  MissileFileFlag |= 1;
332  }
333  if (mtype >= MT_STORM && mtype <= MT_MAEL && !(MissileFileFlag & 2)) {
334  MissileFileFlag |= 2;
336  }
337  if (mtype == MT_SUCCUBUS && !(MissileFileFlag & 4)) {
338  MissileFileFlag |= 4;
341  }
342  if (mtype == MT_SNOWWICH && !(MissileFileFlag & 0x20)) {
343  MissileFileFlag |= 0x20;
346  }
347  if (mtype == MT_HLSPWN && !(MissileFileFlag & 0x40)) {
348  MissileFileFlag |= 0x40;
351  }
352  if (mtype == MT_SOLBRNR && !(MissileFileFlag & 0x80)) {
353  MissileFileFlag |= 0x80;
356  }
357  if (mtype >= MT_INCIN && mtype <= MT_HELLBURN && !(MissileFileFlag & 8)) {
358  MissileFileFlag |= 8;
360  }
361  if (mtype >= MT_NACID && mtype <= MT_XACID && !(MissileFileFlag & 0x10)) {
362  MissileFileFlag |= 0x10;
366  }
367  if (mtype == MT_DIABLO) {
369  }
370 }
371 
372 void ClearMVars(int i)
373 {
374  monster[i]._mVar1 = 0;
375  monster[i]._mVar2 = 0;
376  monster[i]._mVar3 = 0;
377  monster[i]._mVar4 = 0;
378  monster[i]._mVar5 = 0;
379  monster[i]._mVar6 = 0;
380  monster[i]._mVar7 = 0;
381  monster[i]._mVar8 = 0;
382 }
383 
384 void InitMonster(int i, int rd, int mtype, int x, int y)
385 {
386  CMonster *monst = &Monsters[mtype];
387 
388  monster[i]._mdir = rd;
389  monster[i]._mx = x;
390  monster[i]._my = y;
391  monster[i]._mfutx = x;
392  monster[i]._mfuty = y;
393  monster[i]._moldx = x;
394  monster[i]._moldy = y;
395  monster[i]._mMTidx = mtype;
396  monster[i]._mmode = MM_STAND;
397  monster[i].mName = monst->MData->mName;
398  monster[i].MType = monst;
399  monster[i].MData = monst->MData;
400  monster[i]._mAnimData = monst->Anims[MA_STAND].Data[rd];
401  monster[i]._mAnimDelay = monst->Anims[MA_STAND].Rate;
402  monster[i]._mAnimCnt = random_(88, monster[i]._mAnimDelay - 1);
403  monster[i]._mAnimLen = monst->Anims[MA_STAND].Frames;
404  monster[i]._mAnimFrame = random_(88, monster[i]._mAnimLen - 1) + 1;
405 
406  if (monst->mtype == MT_DIABLO) {
407  monster[i]._mmaxhp = (random_(88, 1) + 1666) << 6;
408  } else {
409  monster[i]._mmaxhp = (monst->mMinHP + random_(88, monst->mMaxHP - monst->mMinHP + 1)) << 6;
410  }
411 
412  if (gbMaxPlayers == 1) {
413  monster[i]._mmaxhp >>= 1;
414  if (monster[i]._mmaxhp < 64) {
415  monster[i]._mmaxhp = 64;
416  }
417  }
418 
420  monster[i]._mAi = monst->MData->mAi;
421  monster[i]._mint = monst->MData->mInt;
423  monster[i]._mgoalvar1 = 0;
424  monster[i]._mgoalvar2 = 0;
425  monster[i]._mgoalvar3 = 0;
426  monster[i].field_18 = 0;
427  monster[i]._pathcount = 0;
428  monster[i]._mDelFlag = FALSE;
429  monster[i]._uniqtype = 0;
430  monster[i]._msquelch = 0;
432  monster[i]._mAISeed = GetRndSeed();
433  monster[i].mWhoHit = 0;
434  monster[i].mLevel = monst->MData->mLevel;
435  monster[i].mExp = monst->MData->mExp;
436  monster[i].mHit = monst->MData->mHit;
437  monster[i].mMinDamage = monst->MData->mMinDamage;
438  monster[i].mMaxDamage = monst->MData->mMaxDamage;
439  monster[i].mHit2 = monst->MData->mHit2;
440  monster[i].mMinDamage2 = monst->MData->mMinDamage2;
441  monster[i].mMaxDamage2 = monst->MData->mMaxDamage2;
442  monster[i].mArmorClass = monst->MData->mArmorClass;
443  monster[i].mMagicRes = monst->MData->mMagicRes;
444  monster[i].leader = 0;
445  monster[i].leaderflag = 0;
446  monster[i]._mFlags = monst->MData->mFlags;
447  monster[i].mtalkmsg = 0;
448 
449  if (monster[i]._mAi == AI_GARG) {
450  monster[i]._mAnimData = monst->Anims[MA_SPECIAL].Data[rd];
451  monster[i]._mAnimFrame = 1;
454  }
455 
456  if (gnDifficulty == DIFF_NIGHTMARE) {
457  monster[i]._mmaxhp = 3 * monster[i]._mmaxhp + 64;
459  monster[i].mLevel += 15;
460  monster[i].mExp = 2 * (monster[i].mExp + 1000);
461  monster[i].mHit += 85;
462  monster[i].mMinDamage = 2 * (monster[i].mMinDamage + 2);
463  monster[i].mMaxDamage = 2 * (monster[i].mMaxDamage + 2);
464  monster[i].mHit2 += 85;
465  monster[i].mMinDamage2 = 2 * (monster[i].mMinDamage2 + 2);
466  monster[i].mMaxDamage2 = 2 * (monster[i].mMaxDamage2 + 2);
467  monster[i].mArmorClass += 50;
468  }
469 
470  if (gnDifficulty == DIFF_HELL) {
471  monster[i]._mmaxhp = 4 * monster[i]._mmaxhp + 192;
473  monster[i].mLevel += 30;
474  monster[i].mExp = 4 * (monster[i].mExp + 1000);
475  monster[i].mHit += 120;
476  monster[i].mMinDamage = 4 * monster[i].mMinDamage + 6;
477  monster[i].mMaxDamage = 4 * monster[i].mMaxDamage + 6;
478  monster[i].mHit2 += 120;
479  monster[i].mMinDamage2 = 4 * monster[i].mMinDamage2 + 6;
480  monster[i].mMaxDamage2 = 4 * monster[i].mMaxDamage2 + 6;
481  monster[i].mArmorClass += 80;
482  monster[i].mMagicRes = monst->MData->mMagicRes2;
483  }
484 }
485 
487 {
488  int i;
489  MonsterStruct *Monst;
490 
491  for (i = 0; i < MAXMONSTERS; i++) {
492  Monst = &monster[i];
493  ClearMVars(i);
494  Monst->mName = "Invalid Monster";
495  Monst->_mgoal = 0;
496  Monst->_mmode = MM_STAND;
497  Monst->_mVar1 = 0;
498  Monst->_mVar2 = 0;
499  Monst->_mx = 0;
500  Monst->_my = 0;
501  Monst->_mfutx = 0;
502  Monst->_mfuty = 0;
503  Monst->_moldx = 0;
504  Monst->_moldy = 0;
505  Monst->_mdir = random_(89, 8);
506  Monst->_mxvel = 0;
507  Monst->_myvel = 0;
508  Monst->_mAnimData = NULL;
509  Monst->_mAnimDelay = 0;
510  Monst->_mAnimCnt = 0;
511  Monst->_mAnimLen = 0;
512  Monst->_mAnimFrame = 0;
513  Monst->_mFlags = 0;
514  Monst->_mDelFlag = FALSE;
515  Monst->_menemy = random_(89, gbActivePlayers);
516  Monst->_menemyx = plr[Monst->_menemy]._px;
517  Monst->_menemyy = plr[Monst->_menemy]._py;
518  }
519 }
520 
521 BOOL MonstPlace(int xp, int yp)
522 {
523  char f;
524 
525  if (xp < 0 || xp >= MAXDUNX
526  || yp < 0 || yp >= MAXDUNY
527  || dMonster[xp][yp]
528  || dPlayer[xp][yp]) {
529  return FALSE;
530  }
531 
532  f = dFlags[xp][yp];
533 
534  if (f & BFLAG_VISIBLE) {
535  return FALSE;
536  }
537 
538  if (f & BFLAG_POPULATED) {
539  return FALSE;
540  }
541 
542  return !SolidLoc(xp, yp);
543 }
544 
545 void PlaceMonster(int i, int mtype, int x, int y)
546 {
547  int rd;
548 
549  dMonster[x][y] = i + 1;
550 
551  rd = random_(90, 8);
552  InitMonster(i, rd, mtype, x, y);
553 }
554 
555 #ifndef SPAWN
556 void PlaceUniqueMonst(int uniqindex, int miniontype, int unpackfilesize)
557 {
558  int xp, yp, x, y, i;
559  int uniqtype;
560  int count2;
561  char filestr[64];
562  BOOL zharflag, done;
563  UniqMonstStruct *Uniq;
564  MonsterStruct *Monst;
565  int count;
566 
567  Monst = monster + nummonsters;
568  count = 0;
569  Uniq = UniqMonst + uniqindex;
570 
571  if ((uniquetrans + 19) << 8 >= LIGHTSIZE) {
572  return;
573  }
574 
575  for (uniqtype = 0; uniqtype < nummtypes; uniqtype++) {
576  if (Monsters[uniqtype].mtype == UniqMonst[uniqindex].mtype) {
577  break;
578  }
579  }
580 
581  while (1) {
582  xp = random_(91, 80) + 16;
583  yp = random_(91, 80) + 16;
584  count2 = 0;
585  for (x = xp - 3; x < xp + 3; x++) {
586  for (y = yp - 3; y < yp + 3; y++) {
587  if (y >= 0 && y < MAXDUNY && x >= 0 && x < MAXDUNX && MonstPlace(x, y)) {
588  count2++;
589  }
590  }
591  }
592 
593  if (count2 < 9) {
594  count++;
595  if (count < 1000) {
596  continue;
597  }
598  }
599 
600  if (MonstPlace(xp, yp)) {
601  break;
602  }
603  }
604 
605  if (uniqindex == UMT_SNOTSPIL) {
606  xp = 2 * setpc_x + 24;
607  yp = 2 * setpc_y + 28;
608  }
609  if (uniqindex == UMT_WARLORD) {
610  xp = 2 * setpc_x + 22;
611  yp = 2 * setpc_y + 23;
612  }
613  if (uniqindex == UMT_ZHAR) {
614  zharflag = TRUE;
615  for (i = 0; i < themeCount; i++) {
616  if (i == zharlib && zharflag == TRUE) {
617  zharflag = FALSE;
618  xp = 2 * themeLoc[i].x + 20;
619  yp = 2 * themeLoc[i].y + 20;
620  }
621  }
622  }
623  if (gbMaxPlayers == 1) {
624  if (uniqindex == UMT_LAZURUS) {
625  xp = 32;
626  yp = 46;
627  }
628  if (uniqindex == UMT_RED_VEX) {
629  xp = 40;
630  yp = 45;
631  }
632  if (uniqindex == UMT_BLACKJADE) {
633  xp = 38;
634  yp = 49;
635  }
636  if (uniqindex == UMT_SKELKING) {
637  xp = 35;
638  yp = 47;
639  }
640  } else {
641  if (uniqindex == UMT_LAZURUS) {
642  xp = 2 * setpc_x + 19;
643  yp = 2 * setpc_y + 22;
644  }
645  if (uniqindex == UMT_RED_VEX) {
646  xp = 2 * setpc_x + 21;
647  yp = 2 * setpc_y + 19;
648  }
649  if (uniqindex == UMT_BLACKJADE) {
650  xp = 2 * setpc_x + 21;
651  yp = 2 * setpc_y + 25;
652  }
653  }
654  if (uniqindex == UMT_BUTCHER) {
655  done = FALSE;
656  for (yp = 0; yp < MAXDUNY && !done; yp++) {
657  for (xp = 0; xp < MAXDUNX && !done; xp++) {
658  done = dPiece[xp][yp] == 367;
659  }
660  }
661  }
662 
663  PlaceMonster(nummonsters, uniqtype, xp, yp);
664  Monst->_uniqtype = uniqindex + 1;
665 
666  if (Uniq->mlevel) {
667  Monst->mLevel = 2 * Uniq->mlevel;
668  } else {
669  Monst->mLevel += 5;
670  }
671 
672  Monst->mExp *= 2;
673  Monst->mName = Uniq->mName;
674  Monst->_mmaxhp = Uniq->mmaxhp << 6;
675 
676  if (gbMaxPlayers == 1) {
677  Monst->_mmaxhp = Monst->_mmaxhp >> 1;
678  if (Monst->_mmaxhp < 64) {
679  Monst->_mmaxhp = 64;
680  }
681  }
682 
683  Monst->_mhitpoints = Monst->_mmaxhp;
684  Monst->_mAi = Uniq->mAi;
685  Monst->_mint = Uniq->mint;
686  Monst->mMinDamage = Uniq->mMinDamage;
687  Monst->mMaxDamage = Uniq->mMaxDamage;
688  Monst->mMinDamage2 = Uniq->mMinDamage;
689  Monst->mMaxDamage2 = Uniq->mMaxDamage;
690  Monst->mMagicRes = Uniq->mMagicRes;
691  Monst->mtalkmsg = Uniq->mtalkmsg;
692  Monst->mlid = AddLight(Monst->_mx, Monst->_my, 3);
693 
694  if (gbMaxPlayers == 1) {
695  if (Monst->mtalkmsg) {
696  Monst->_mgoal = MGOAL_INQUIRING;
697  }
698  } else {
699  if (Monst->_mAi == AI_LAZHELP) {
700  Monst->mtalkmsg = 0;
701  }
702 
703  if (Monst->_mAi != AI_LAZURUS || quests[Q_BETRAYER]._qvar1 <= 3) {
704  if (Monst->mtalkmsg) {
705  Monst->_mgoal = MGOAL_INQUIRING;
706  }
707  } else {
708  Monst->_mgoal = MGOAL_NORMAL;
709  }
710  }
711 
712  if (gnDifficulty == DIFF_NIGHTMARE) {
713  Monst->mLevel += 15;
714  Monst->_mmaxhp = 3 * Monst->_mmaxhp + 64;
715  Monst->_mhitpoints = Monst->_mmaxhp;
716  Monst->mExp = 2 * (Monst->mExp + 1000);
717  Monst->mMinDamage = 2 * (Monst->mMinDamage + 2);
718  Monst->mMaxDamage = 2 * (Monst->mMaxDamage + 2);
719  Monst->mMinDamage2 = 2 * (Monst->mMinDamage2 + 2);
720  Monst->mMaxDamage2 = 2 * (Monst->mMaxDamage2 + 2);
721  }
722 
723  if (gnDifficulty == DIFF_HELL) {
724  Monst->mLevel += 30;
725  Monst->_mmaxhp = 4 * Monst->_mmaxhp + 192;
726  Monst->_mhitpoints = Monst->_mmaxhp;
727  Monst->mExp = 4 * (Monst->mExp + 1000);
728  Monst->mMinDamage = 4 * Monst->mMinDamage + 6;
729  Monst->mMaxDamage = 4 * Monst->mMaxDamage + 6;
730  Monst->mMinDamage2 = 4 * Monst->mMinDamage2 + 6;
731  Monst->mMaxDamage2 = 4 * Monst->mMaxDamage2 + 6;
732  }
733 
734  sprintf(filestr, "Monsters\\Monsters\\%s.TRN", Uniq->mTrnName);
735  LoadFileWithMem(filestr, &pLightTbl[256 * (uniquetrans + 19)]);
736 
737  Monst->_uniqtrans = uniquetrans++;
738 
739  if (Uniq->mUnqAttr & 4) {
740  Monst->mHit = Uniq->mUnqVar1;
741  Monst->mHit2 = Uniq->mUnqVar1;
742  }
743  if (Uniq->mUnqAttr & 8) {
744  Monst->mArmorClass = Uniq->mUnqVar1;
745  }
746 
747  nummonsters++;
748 
749  if (Uniq->mUnqAttr & 1) {
750  PlaceGroup(miniontype, unpackfilesize, Uniq->mUnqAttr, nummonsters - 1);
751  }
752 
753  if (Monst->_mAi != AI_GARG) {
754  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[Monst->_mdir];
755  Monst->_mAnimFrame = random_(88, Monst->_mAnimLen - 1) + 1;
756  Monst->_mFlags &= ~MFLAG_ALLOW_SPECIAL;
757  Monst->_mmode = MM_STAND;
758  }
759 }
760 
762 {
763  int skeltype;
764  BYTE *setp;
765 
766  if (!setlevel) {
767  if (QuestStatus(Q_BUTCHER)) {
769  }
770 
771  if (currlevel == quests[Q_SKELKING]._qlevel && gbMaxPlayers != 1) {
772  skeltype = 0;
773 
774  for (skeltype = 0; skeltype < nummtypes; skeltype++) {
775  if (IsSkel(Monsters[skeltype].mtype)) {
776  break;
777  }
778  }
779 
780  PlaceUniqueMonst(UMT_SKELKING, skeltype, 30);
781  }
782 
783  if (QuestStatus(Q_LTBANNER)) {
784  setp = LoadFileInMem("Levels\\L1Data\\Banner1.DUN", NULL);
785  SetMapMonsters(setp, 2 * setpc_x, 2 * setpc_y);
786  mem_free_dbg(setp);
787  }
788  if (QuestStatus(Q_BLOOD)) {
789  setp = LoadFileInMem("Levels\\L2Data\\Blood2.DUN", NULL);
790  SetMapMonsters(setp, 2 * setpc_x, 2 * setpc_y);
791  mem_free_dbg(setp);
792  }
793  if (QuestStatus(Q_BLIND)) {
794  setp = LoadFileInMem("Levels\\L2Data\\Blind2.DUN", NULL);
795  SetMapMonsters(setp, 2 * setpc_x, 2 * setpc_y);
796  mem_free_dbg(setp);
797  }
798  if (QuestStatus(Q_ANVIL)) {
799  setp = LoadFileInMem("Levels\\L3Data\\Anvil.DUN", NULL);
800  SetMapMonsters(setp, 2 * setpc_x + 2, 2 * setpc_y + 2);
801  mem_free_dbg(setp);
802  }
803  if (QuestStatus(Q_WARLORD)) {
804  setp = LoadFileInMem("Levels\\L4Data\\Warlord.DUN", NULL);
805  SetMapMonsters(setp, 2 * setpc_x, 2 * setpc_y);
806  mem_free_dbg(setp);
808  }
809  if (QuestStatus(Q_VEIL)) {
811  }
812  if (QuestStatus(Q_ZHAR) && zharlib == -1) {
814  }
815 
816  if (currlevel == quests[Q_BETRAYER]._qlevel && gbMaxPlayers != 1) {
822  setp = LoadFileInMem("Levels\\L4Data\\Vile1.DUN", NULL);
823  SetMapMonsters(setp, 2 * setpc_x, 2 * setpc_y);
824  mem_free_dbg(setp);
825  }
826  } else if (setlvlnum == SL_SKELKING) {
828  }
829 }
830 #endif
831 
832 void PlaceGroup(int mtype, int num, int leaderf, int leader)
833 {
834  int placed, try1, try2, j;
835  int xp, yp, x1, y1;
836 
837  placed = 0;
838 
839  for (try1 = 0; try1 < 10; try1++) {
840  while (placed) {
841  nummonsters--;
842  placed--;
844  }
845 
846  if (leaderf & 1) {
847  int offset = random_(92, 8);
848  x1 = xp = monster[leader]._mx + offset_x[offset];
849  y1 = yp = monster[leader]._my + offset_y[offset];
850  } else {
851  do {
852  x1 = xp = random_(93, 80) + 16;
853  y1 = yp = random_(93, 80) + 16;
854  } while (!MonstPlace(xp, yp));
855  }
856 
857  if (num + nummonsters > totalmonsters) {
858  num = totalmonsters - nummonsters;
859  }
860 
861  j = 0;
862  for (try2 = 0; j < num && try2 < 100; xp += offset_x[random_(94, 8)], yp += offset_x[random_(94, 8)]) {
863  if (!MonstPlace(xp, yp)
864  || (dTransVal[xp][yp] != dTransVal[x1][y1])
865  || (leaderf & 2) && ((abs(xp - x1) >= 4) || (abs(yp - y1) >= 4))) {
866  try2++;
867  continue;
868  }
869 
870  PlaceMonster(nummonsters, mtype, xp, yp);
871  if (leaderf & 1) {
875 
876  if (leaderf & 2) {
877  monster[nummonsters].leader = leader;
879  monster[nummonsters]._mAi = monster[leader]._mAi;
880  }
881 
882  if (monster[nummonsters]._mAi != AI_GARG) {
884  monster[nummonsters]._mAnimFrame = random_(88, monster[nummonsters]._mAnimLen - 1) + 1;
887  }
888  }
889  nummonsters++;
890  placed++;
891  j++;
892  }
893 
894  if (placed >= num) {
895  break;
896  }
897  }
898 
899  if (leaderf & 2) {
900  monster[leader].packsize = placed;
901  }
902 }
903 
904 #ifndef SPAWN
906 {
907  BYTE *lpSetPiece;
908 
909  lpSetPiece = LoadFileInMem("Levels\\L4Data\\diab1.DUN", NULL);
910  SetMapMonsters(lpSetPiece, 2 * diabquad1x, 2 * diabquad1y);
911  mem_free_dbg(lpSetPiece);
912  lpSetPiece = LoadFileInMem("Levels\\L4Data\\diab2a.DUN", NULL);
913  SetMapMonsters(lpSetPiece, 2 * diabquad2x, 2 * diabquad2y);
914  mem_free_dbg(lpSetPiece);
915  lpSetPiece = LoadFileInMem("Levels\\L4Data\\diab3a.DUN", NULL);
916  SetMapMonsters(lpSetPiece, 2 * diabquad3x, 2 * diabquad3y);
917  mem_free_dbg(lpSetPiece);
918  lpSetPiece = LoadFileInMem("Levels\\L4Data\\diab4a.DUN", NULL);
919  SetMapMonsters(lpSetPiece, 2 * diabquad4x, 2 * diabquad4y);
920  mem_free_dbg(lpSetPiece);
921 }
922 #endif
923 
925 {
926  int na, nt;
927  int i, s, t;
928  int numplacemonsters;
929  int mtype;
930  int numscattypes;
931  int scattertypes[NUM_MTYPES];
932 
933  numscattypes = 0;
934  if (gbMaxPlayers != 1)
936  if (!setlevel) {
937  AddMonster(1, 0, 0, 0, FALSE);
938  AddMonster(1, 0, 0, 0, FALSE);
939  AddMonster(1, 0, 0, 0, FALSE);
940  AddMonster(1, 0, 0, 0, FALSE);
941  }
942 #ifndef SPAWN
943  if (!setlevel && currlevel == 16)
944  LoadDiabMonsts();
945 #endif
946  nt = numtrigs;
947  if (currlevel == 15)
948  nt = 1;
949  for (i = 0; i < nt; i++) {
950  for (s = -2; s < 2; s++) {
951  for (t = -2; t < 2; t++)
952  DoVision(s + trigs[i]._tx, t + trigs[i]._ty, 15, FALSE, FALSE);
953  }
954  }
955 #ifndef SPAWN
957 #endif
958  if (!setlevel) {
959 #ifndef SPAWN
960  PlaceUniques();
961 #endif
962  na = 0;
963  for (s = 16; s < 96; s++)
964  for (t = 16; t < 96; t++)
965  if (!SolidLoc(s, t))
966  na++;
967  numplacemonsters = na / 30;
968  if (gbMaxPlayers != 1)
969  numplacemonsters += numplacemonsters >> 1;
970  if (nummonsters + numplacemonsters > 190)
971  numplacemonsters = 190 - nummonsters;
972  totalmonsters = nummonsters + numplacemonsters;
973  for (i = 0; i < nummtypes; i++) {
974  if (Monsters[i].mPlaceFlags & 1) {
975  scattertypes[numscattypes] = i;
976  numscattypes++;
977  }
978  }
979  while (nummonsters < totalmonsters) {
980  mtype = scattertypes[random_(95, numscattypes)];
981  if (currlevel == 1 || random_(95, 2) == 0)
982  na = 1;
983  else if (currlevel == 2)
984  na = random_(95, 2) + 2;
985  else
986  na = random_(95, 3) + 3;
987  PlaceGroup(mtype, na, 0, 0);
988  }
989  }
990  for (i = 0; i < nt; i++) {
991  for (s = -2; s < 2; s++) {
992  for (t = -2; t < 2; t++)
993  DoUnVision(s + trigs[i]._tx, t + trigs[i]._ty, 15);
994  }
995  }
996 }
997 
998 #ifndef SPAWN
1000 {
1001  int u, mt;
1002  BOOL done;
1003 
1004  for (u = 0; UniqMonst[u].mtype != -1; u++) {
1005  if (UniqMonst[u].mlevel != currlevel)
1006  continue;
1007  done = FALSE;
1008  for (mt = 0; mt < nummtypes; mt++) {
1009  if (done)
1010  break;
1011  done = (Monsters[mt].mtype == UniqMonst[u].mtype);
1012  }
1013  mt--;
1014  if (u == UMT_GARBUD && quests[Q_GARBUD]._qactive == QUEST_NOTAVAIL)
1015  done = FALSE;
1016  if (u == UMT_ZHAR && quests[Q_ZHAR]._qactive == QUEST_NOTAVAIL)
1017  done = FALSE;
1018  if (u == UMT_SNOTSPIL && quests[Q_LTBANNER]._qactive == QUEST_NOTAVAIL)
1019  done = FALSE;
1020  if (u == UMT_LACHDAN && quests[Q_VEIL]._qactive == QUEST_NOTAVAIL)
1021  done = FALSE;
1022  if (u == UMT_WARLORD && quests[Q_WARLORD]._qactive == QUEST_NOTAVAIL)
1023  done = FALSE;
1024  if (done)
1025  PlaceUniqueMonst(u, mt, 8);
1026  }
1027 }
1028 
1029 void SetMapMonsters(BYTE *pMap, int startx, int starty)
1030 {
1031  WORD rw, rh;
1032  WORD *lm;
1033  int i, j;
1034  int mtype;
1035 
1037  AddMonster(1, 0, 0, 0, FALSE);
1038  AddMonster(1, 0, 0, 0, FALSE);
1039  AddMonster(1, 0, 0, 0, FALSE);
1040  AddMonster(1, 0, 0, 0, FALSE);
1041  if (setlevel && setlvlnum == SL_VILEBETRAYER) {
1048  }
1049  lm = (WORD *)pMap;
1050  rw = SDL_SwapLE16(*lm);
1051  lm++;
1052  rh = SDL_SwapLE16(*lm);
1053  lm += (rw * rh + 1);
1054  rw = rw << 1;
1055  rh = rh << 1;
1056  lm += rw * rh;
1057 
1058  for (j = 0; j < rh; j++) {
1059  for (i = 0; i < rw; i++) {
1060  if (*lm) {
1061  mtype = AddMonsterType(MonstConvTbl[SDL_SwapLE16(*lm) - 1], 2);
1062  PlaceMonster(nummonsters++, mtype, i + startx + 16, j + starty + 16);
1063  }
1064  lm++;
1065  }
1066  }
1067 }
1068 #endif
1069 
1070 void DeleteMonster(int i)
1071 {
1072  int temp;
1073 
1074  nummonsters--;
1075  temp = monstactive[nummonsters];
1077  monstactive[i] = temp;
1078 }
1079 
1080 int AddMonster(int x, int y, int dir, int mtype, BOOL InMap)
1081 {
1082  if (nummonsters < MAXMONSTERS) {
1083  int i = monstactive[nummonsters++];
1084  if (InMap)
1085  dMonster[x][y] = i + 1;
1086  InitMonster(i, dir, mtype, x, y);
1087  return i;
1088  }
1089 
1090  return -1;
1091 }
1092 
1093 void NewMonsterAnim(int i, AnimStruct *anim, int md)
1094 {
1095  MonsterStruct *Monst = monster + i;
1096  Monst->_mAnimData = anim->Data[md];
1097  Monst->_mAnimLen = anim->Frames;
1098  Monst->_mAnimCnt = 0;
1099  Monst->_mAnimFrame = 1;
1100  Monst->_mAnimDelay = anim->Rate;
1102  Monst->_mdir = md;
1103 }
1104 
1105 BOOL M_Ranged(int i)
1106 {
1107  char ai = monster[i]._mAi;
1108  return ai == AI_SKELBOW || ai == AI_GOATBOW || ai == AI_SUCC || ai == AI_LAZHELP;
1109 }
1110 
1111 BOOL M_Talker(int i)
1112 {
1113  char ai = monster[i]._mAi;
1114  return ai == AI_LAZURUS
1115  || ai == AI_WARLORD
1116  || ai == AI_GARBUD
1117  || ai == AI_ZHAR
1118  || ai == AI_SNOTSPIL
1119  || ai == AI_LACHDAN
1120  || ai == AI_LAZHELP;
1121 }
1122 
1123 void M_Enemy(int i)
1124 {
1125  int j;
1126  int mi, pnum;
1127  int dist, best_dist;
1128  int _menemy;
1129  BOOL sameroom, bestsameroom;
1130  MonsterStruct *Monst;
1131  BYTE enemyx, enemyy;
1132 
1133  _menemy = -1;
1134  best_dist = -1;
1135  bestsameroom = 0;
1136  Monst = monster + i;
1137  if (!(Monst->_mFlags & MFLAG_GOLEM)) {
1138  for (pnum = 0; pnum < MAX_PLRS; pnum++) {
1139  if (!plr[pnum].plractive || currlevel != plr[pnum].plrlevel || plr[pnum]._pLvlChanging || (plr[pnum]._pHitPoints == 0 && gbMaxPlayers != 1))
1140  continue;
1141  if (dTransVal[Monst->_mx][Monst->_my] == dTransVal[plr[pnum].WorldX][plr[pnum].WorldY])
1142  sameroom = TRUE;
1143  else
1144  sameroom = FALSE;
1145  if (abs(Monst->_mx - plr[pnum].WorldX) > abs(Monst->_my - plr[pnum].WorldY))
1146  dist = Monst->_mx - plr[pnum].WorldX;
1147  else
1148  dist = Monst->_my - plr[pnum].WorldY;
1149  dist = abs(dist);
1150  if ((sameroom && !bestsameroom)
1151  || ((sameroom || !bestsameroom) && dist < best_dist)
1152  || (_menemy == -1)) {
1153  Monst->_mFlags &= ~MFLAG_TARGETS_MONSTER;
1154  _menemy = pnum;
1155  enemyx = plr[pnum]._px;
1156  enemyy = plr[pnum]._py;
1157  best_dist = dist;
1158  bestsameroom = sameroom;
1159  }
1160  }
1161  }
1162  for (j = 0; j < nummonsters; j++) {
1163  mi = monstactive[j];
1164  if (mi == i)
1165  continue;
1166  if (monster[mi]._mx == 1 && monster[mi]._my == 0)
1167  continue;
1168  if (M_Talker(mi) && monster[mi].mtalkmsg)
1169  continue;
1170  if (!(Monst->_mFlags & MFLAG_GOLEM)
1171  && ((abs(monster[mi]._mx - Monst->_mx) >= 2 || abs(monster[mi]._my - Monst->_my) >= 2) && !M_Ranged(i)
1172  || (!(Monst->_mFlags & MFLAG_GOLEM) && !(monster[mi]._mFlags & MFLAG_GOLEM)))) {
1173  continue;
1174  }
1175  sameroom = dTransVal[Monst->_mx][Monst->_my] == dTransVal[monster[mi]._mx][monster[mi]._my];
1176  if (abs(Monst->_mx - monster[mi]._mx) > abs(Monst->_my - monster[mi]._my))
1177  dist = Monst->_mx - monster[mi]._mx;
1178  else
1179  dist = Monst->_my - monster[mi]._my;
1180  dist = abs(dist);
1181  if ((sameroom && !bestsameroom)
1182  || ((sameroom || !bestsameroom) && dist < best_dist)
1183  || (_menemy == -1)) {
1184  Monst->_mFlags |= MFLAG_TARGETS_MONSTER;
1185  _menemy = mi;
1186  enemyx = monster[mi]._mfutx;
1187  enemyy = monster[mi]._mfuty;
1188  best_dist = dist;
1189  bestsameroom = sameroom;
1190  }
1191  }
1192  if (_menemy != -1) {
1193  Monst->_mFlags &= ~MFLAG_NO_ENEMY;
1194  Monst->_menemy = _menemy;
1195  Monst->_menemyx = enemyx;
1196  Monst->_menemyy = enemyy;
1197  } else {
1198  Monst->_mFlags |= MFLAG_NO_ENEMY;
1199  }
1200 }
1201 
1202 int M_GetDir(int i)
1203 {
1204  return GetDirection(monster[i]._mx, monster[i]._my, monster[i]._menemyx, monster[i]._menemyy);
1205 }
1206 
1207 void M_StartStand(int i, int md)
1208 {
1209  ClearMVars(i);
1210  if (monster[i].MType->mtype == MT_GOLEM)
1211  NewMonsterAnim(i, &monster[i].MType->Anims[MA_WALK], md);
1212  else
1213  NewMonsterAnim(i, &monster[i].MType->Anims[MA_STAND], md);
1214  monster[i]._mVar1 = monster[i]._mmode;
1215  monster[i]._mVar2 = 0;
1216  monster[i]._mmode = MM_STAND;
1217  monster[i]._mxoff = 0;
1218  monster[i]._myoff = 0;
1219  monster[i]._mfutx = monster[i]._mx;
1220  monster[i]._mfuty = monster[i]._my;
1221  monster[i]._moldx = monster[i]._mx;
1222  monster[i]._moldy = monster[i]._my;
1223  monster[i]._mdir = md;
1224  M_Enemy(i);
1225 }
1226 
1227 void M_StartDelay(int i, int len)
1228 {
1229  if (len <= 0) {
1230  return;
1231  }
1232 
1233  if (monster[i]._mAi != AI_LAZURUS) {
1234  monster[i]._mVar2 = len;
1235  monster[i]._mmode = MM_DELAY;
1236  }
1237 }
1238 
1239 void M_StartSpStand(int i, int md)
1240 {
1241  NewMonsterAnim(i, &monster[i].MType->Anims[MA_SPECIAL], md);
1242  monster[i]._mmode = MM_SPSTAND;
1243  monster[i]._mxoff = 0;
1244  monster[i]._myoff = 0;
1245  monster[i]._mfutx = monster[i]._mx;
1246  monster[i]._mfuty = monster[i]._my;
1247  monster[i]._moldx = monster[i]._mx;
1248  monster[i]._moldy = monster[i]._my;
1249  monster[i]._mdir = md;
1250 }
1251 
1252 void M_StartWalk(int i, int xvel, int yvel, int xadd, int yadd, int EndDir)
1253 {
1254  int fx = xadd + monster[i]._mx;
1255  int fy = yadd + monster[i]._my;
1256 
1257  dMonster[fx][fy] = -(i + 1);
1258  monster[i]._mmode = MM_WALK;
1259  monster[i]._moldx = monster[i]._mx;
1260  monster[i]._moldy = monster[i]._my;
1261  monster[i]._mfutx = fx;
1262  monster[i]._mfuty = fy;
1263  monster[i]._mxvel = xvel;
1264  monster[i]._myvel = yvel;
1265  monster[i]._mVar1 = xadd;
1266  monster[i]._mVar2 = yadd;
1267  monster[i]._mVar3 = EndDir;
1268  monster[i]._mdir = EndDir;
1269  NewMonsterAnim(i, &monster[i].MType->Anims[MA_WALK], EndDir);
1270  monster[i]._mVar6 = 0;
1271  monster[i]._mVar7 = 0;
1272  monster[i]._mVar8 = 0;
1273 }
1274 
1275 void M_StartWalk2(int i, int xvel, int yvel, int xoff, int yoff, int xadd, int yadd, int EndDir)
1276 {
1277  int fx = xadd + monster[i]._mx;
1278  int fy = yadd + monster[i]._my;
1279 
1280  dMonster[monster[i]._mx][monster[i]._my] = -(i + 1);
1281  monster[i]._mVar1 = monster[i]._mx;
1282  monster[i]._mVar2 = monster[i]._my;
1283  monster[i]._moldx = monster[i]._mx;
1284  monster[i]._moldy = monster[i]._my;
1285  monster[i]._mx = fx;
1286  monster[i]._my = fy;
1287  monster[i]._mfutx = fx;
1288  monster[i]._mfuty = fy;
1289  dMonster[fx][fy] = i + 1;
1290  if (monster[i]._uniqtype != 0)
1291  ChangeLightXY(monster[i].mlid, monster[i]._mx, monster[i]._my);
1292  monster[i]._mxoff = xoff;
1293  monster[i]._myoff = yoff;
1294  monster[i]._mmode = MM_WALK2;
1295  monster[i]._mxvel = xvel;
1296  monster[i]._myvel = yvel;
1297  monster[i]._mVar3 = EndDir;
1298  monster[i]._mdir = EndDir;
1299  NewMonsterAnim(i, &monster[i].MType->Anims[MA_WALK], EndDir);
1300  monster[i]._mVar6 = 16 * xoff;
1301  monster[i]._mVar7 = 16 * yoff;
1302  monster[i]._mVar8 = 0;
1303 }
1304 
1305 void M_StartWalk3(int i, int xvel, int yvel, int xoff, int yoff, int xadd, int yadd, int mapx, int mapy, int EndDir)
1306 {
1307  int fx = xadd + monster[i]._mx;
1308  int fy = yadd + monster[i]._my;
1309  int x = mapx + monster[i]._mx;
1310  int y = mapy + monster[i]._my;
1311 
1312  if (monster[i]._uniqtype != 0)
1313  ChangeLightXY(monster[i].mlid, x, y);
1314 
1315  dMonster[monster[i]._mx][monster[i]._my] = -(i + 1);
1316  dMonster[fx][fy] = -(i + 1);
1317  monster[i]._mVar4 = x;
1318  monster[i]._mVar5 = y;
1319  dFlags[x][y] |= BFLAG_MONSTLR;
1320  monster[i]._moldx = monster[i]._mx;
1321  monster[i]._moldy = monster[i]._my;
1322  monster[i]._mfutx = fx;
1323  monster[i]._mfuty = fy;
1324  monster[i]._mxoff = xoff;
1325  monster[i]._myoff = yoff;
1326  monster[i]._mmode = MM_WALK3;
1327  monster[i]._mxvel = xvel;
1328  monster[i]._myvel = yvel;
1329  monster[i]._mVar1 = fx;
1330  monster[i]._mVar2 = fy;
1331  monster[i]._mVar3 = EndDir;
1332  monster[i]._mdir = EndDir;
1333  NewMonsterAnim(i, &monster[i].MType->Anims[MA_WALK], EndDir);
1334  monster[i]._mVar6 = 16 * xoff;
1335  monster[i]._mVar7 = 16 * yoff;
1336  monster[i]._mVar8 = 0;
1337 }
1338 
1339 void M_StartAttack(int i)
1340 {
1341  int md = M_GetDir(i);
1342  NewMonsterAnim(i, &monster[i].MType->Anims[MA_ATTACK], md);
1343  monster[i]._mmode = MM_ATTACK;
1344  monster[i]._mxoff = 0;
1345  monster[i]._myoff = 0;
1346  monster[i]._mfutx = monster[i]._mx;
1347  monster[i]._mfuty = monster[i]._my;
1348  monster[i]._moldx = monster[i]._mx;
1349  monster[i]._moldy = monster[i]._my;
1350  monster[i]._mdir = md;
1351 }
1352 
1353 void M_StartRAttack(int i, int missile_type, int dam)
1354 {
1355  int md = M_GetDir(i);
1356  NewMonsterAnim(i, &monster[i].MType->Anims[MA_ATTACK], md);
1357  monster[i]._mmode = MM_RATTACK;
1358  monster[i]._mVar1 = missile_type;
1359  monster[i]._mVar2 = dam;
1360  monster[i]._mxoff = 0;
1361  monster[i]._myoff = 0;
1362  monster[i]._mfutx = monster[i]._mx;
1363  monster[i]._mfuty = monster[i]._my;
1364  monster[i]._moldx = monster[i]._mx;
1365  monster[i]._moldy = monster[i]._my;
1366  monster[i]._mdir = md;
1367 }
1368 
1369 void M_StartRSpAttack(int i, int missile_type, int dam)
1370 {
1371  int md = M_GetDir(i);
1372  NewMonsterAnim(i, &monster[i].MType->Anims[MA_SPECIAL], md);
1374  monster[i]._mVar1 = missile_type;
1375  monster[i]._mVar2 = 0;
1376  monster[i]._mVar3 = dam;
1377  monster[i]._mxoff = 0;
1378  monster[i]._myoff = 0;
1379  monster[i]._mfutx = monster[i]._mx;
1380  monster[i]._mfuty = monster[i]._my;
1381  monster[i]._moldx = monster[i]._mx;
1382  monster[i]._moldy = monster[i]._my;
1383  monster[i]._mdir = md;
1384 }
1385 
1386 void M_StartSpAttack(int i)
1387 {
1388  int md = M_GetDir(i);
1389  NewMonsterAnim(i, &monster[i].MType->Anims[MA_SPECIAL], md);
1390  monster[i]._mmode = MM_SATTACK;
1391  monster[i]._mxoff = 0;
1392  monster[i]._myoff = 0;
1393  monster[i]._mfutx = monster[i]._mx;
1394  monster[i]._mfuty = monster[i]._my;
1395  monster[i]._moldx = monster[i]._mx;
1396  monster[i]._moldy = monster[i]._my;
1397  monster[i]._mdir = md;
1398 }
1399 
1400 void M_StartEat(int i)
1401 {
1402  NewMonsterAnim(i, &monster[i].MType->Anims[MA_SPECIAL], monster[i]._mdir);
1403  monster[i]._mmode = MM_SATTACK;
1404  monster[i]._mxoff = 0;
1405  monster[i]._myoff = 0;
1406  monster[i]._mfutx = monster[i]._mx;
1407  monster[i]._mfuty = monster[i]._my;
1408  monster[i]._moldx = monster[i]._mx;
1409  monster[i]._moldy = monster[i]._my;
1410 }
1411 
1412 void M_ClearSquares(int i)
1413 {
1414  int x, y, mx, my, m1, m2;
1415 
1416  mx = monster[i]._moldx;
1417  my = monster[i]._moldy;
1418  m1 = -1 - i;
1419  m2 = i + 1;
1420 
1421  for (y = my - 1; y <= my + 1; y++) {
1422  if (y >= 0 && y < MAXDUNY) {
1423  for (x = mx - 1; x <= mx + 1; x++) {
1424  if (x >= 0 && x < MAXDUNX && (dMonster[x][y] == m1 || dMonster[x][y] == m2))
1425  dMonster[x][y] = 0;
1426  }
1427  }
1428  }
1429 
1430  if (mx + 1 < MAXDUNX)
1431  dFlags[mx + 1][my] &= ~BFLAG_MONSTLR;
1432  if (my + 1 < MAXDUNY)
1433  dFlags[mx][my + 1] &= ~BFLAG_MONSTLR;
1434 }
1435 
1436 void M_GetKnockback(int i)
1437 {
1438  int d = (monster[i]._mdir - 4) & 7;
1439  if (DirOK(i, d)) {
1440  M_ClearSquares(i);
1441  monster[i]._moldx += offset_x[d];
1442  monster[i]._moldy += offset_y[d];
1443  NewMonsterAnim(i, &monster[i].MType->Anims[MA_GOTHIT], monster[i]._mdir);
1444  monster[i]._mmode = MM_GOTHIT;
1445  monster[i]._mxoff = 0;
1446  monster[i]._myoff = 0;
1447  monster[i]._mx = monster[i]._moldx;
1448  monster[i]._my = monster[i]._moldy;
1449  monster[i]._mfutx = monster[i]._mx;
1450  monster[i]._mfuty = monster[i]._my;
1451  // BUGFIX useless assignment
1452  monster[i]._moldx = monster[i]._mx;
1453  monster[i]._moldy = monster[i]._my;
1454  M_ClearSquares(i);
1455  dMonster[monster[i]._mx][monster[i]._my] = i + 1;
1456  }
1457 }
1458 
1459 void M_StartHit(int i, int pnum, int dam)
1460 {
1461  if (pnum >= 0)
1462  monster[i].mWhoHit |= 1 << pnum;
1463  if (pnum == myplr) {
1464  delta_monster_hp(i, monster[i]._mhitpoints, currlevel);
1465  NetSendCmdParam2(FALSE, CMD_MONSTDAMAGE, i, dam);
1466  }
1467  PlayEffect(i, 1);
1468  if (monster[i].MType->mtype >= MT_SNEAK && monster[i].MType->mtype <= MT_ILLWEAV || dam >> 6 >= monster[i].mLevel + 3) {
1469  if (pnum >= 0) {
1471  monster[i]._menemy = pnum;
1472  monster[i]._menemyx = plr[pnum]._px;
1473  monster[i]._menemyy = plr[pnum]._py;
1474  monster[i]._mdir = M_GetDir(i);
1475  }
1476  if (monster[i].MType->mtype == MT_BLINK) {
1477  M_Teleport(i);
1478  } else if (monster[i].MType->mtype >= MT_NSCAV && monster[i].MType->mtype <= MT_YSCAV) {
1480  }
1481  if (monster[i]._mmode != MM_STONE) {
1482  NewMonsterAnim(i, &monster[i].MType->Anims[MA_GOTHIT], monster[i]._mdir);
1483  monster[i]._mmode = MM_GOTHIT;
1484  monster[i]._mxoff = 0;
1485  monster[i]._myoff = 0;
1486  monster[i]._mx = monster[i]._moldx;
1487  monster[i]._my = monster[i]._moldy;
1488  monster[i]._mfutx = monster[i]._moldx;
1489  monster[i]._mfuty = monster[i]._moldy;
1490  M_ClearSquares(i);
1491  dMonster[monster[i]._mx][monster[i]._my] = i + 1;
1492  }
1493  }
1494 }
1495 
1496 void M_DiabloDeath(int i, BOOL sendmsg)
1497 {
1498  MonsterStruct *Monst, *pmonster;
1499  int dist;
1500  int j, k;
1501  int _moldx, _moldy;
1502 
1503  Monst = monster + i;
1504 #ifndef SPAWN
1506 #endif
1508  if (sendmsg)
1509  NetSendCmdQuest(TRUE, Q_DIABLO);
1510  gbProcessPlayers = FALSE;
1512  for (j = 0; j < nummonsters; j++) {
1513  k = monstactive[j];
1514  if (k == i || monster[i]._msquelch == 0)
1515  continue;
1516 
1517  pmonster = monster + k;
1518  NewMonsterAnim(k, &pmonster->MType->Anims[MA_DEATH], pmonster->_mdir);
1519  monster[k]._mxoff = 0;
1520  monster[k]._myoff = 0;
1521  monster[k]._mVar1 = 0;
1522  _moldx = monster[k]._moldx;
1523  _moldy = monster[k]._moldy;
1524  monster[k]._my = _moldy;
1525  monster[k]._mfuty = _moldy;
1526  monster[k]._mmode = MM_DEATH;
1527  monster[k]._mx = _moldx;
1528  monster[k]._mfutx = _moldx;
1529  M_ClearSquares(k);
1530  dMonster[pmonster->_mx][pmonster->_my] = k + 1;
1531  }
1532  AddLight(Monst->_mx, Monst->_my, 8);
1533  DoVision(Monst->_mx, Monst->_my, 8, FALSE, TRUE);
1534  if (abs(ViewX - Monst->_mx) > abs(ViewY - Monst->_my))
1535  dist = abs(ViewX - Monst->_mx);
1536  else
1537  dist = abs(ViewY - Monst->_my);
1538  if (dist > 20)
1539  dist = 20;
1540  j = ViewX << 16;
1541  k = ViewY << 16;
1542  Monst->_mVar3 = j;
1543  Monst->_mVar4 = k;
1544  Monst->_mVar5 = (int)((j - (Monst->_mx << 16)) / (double)dist);
1545  Monst->_mVar6 = (int)((k - (Monst->_my << 16)) / (double)dist);
1546 }
1547 
1548 void M2MStartHit(int mid, int i, int dam)
1549 {
1550  if ((DWORD)mid >= MAXMONSTERS) {
1551  app_fatal("Invalid monster %d getting hit by monster", mid);
1552  }
1553 
1554  if (monster[mid].MType == NULL) {
1555  app_fatal("Monster %d \"%s\" getting hit by monster: MType NULL", mid, monster[mid].mName);
1556  }
1557 
1558  if (i >= 0)
1559  monster[i].mWhoHit |= 1 << i;
1560 
1561  delta_monster_hp(mid, monster[mid]._mhitpoints, currlevel);
1562  NetSendCmdParam2(FALSE, CMD_MONSTDAMAGE, mid, dam);
1563  PlayEffect(mid, 1);
1564 
1565  if (monster[mid].MType->mtype >= MT_SNEAK && monster[mid].MType->mtype <= MT_ILLWEAV || dam >> 6 >= monster[mid].mLevel + 3) {
1566  if (i >= 0)
1567  monster[mid]._mdir = (monster[i]._mdir - 4) & 7;
1568 
1569  if (monster[mid].MType->mtype == MT_BLINK) {
1570  M_Teleport(mid);
1571  } else if (monster[mid].MType->mtype >= MT_NSCAV && monster[mid].MType->mtype <= MT_YSCAV) {
1572  monster[mid]._mgoal = MGOAL_NORMAL;
1573  }
1574 
1575  if (monster[mid]._mmode != MM_STONE) {
1576  if (monster[mid].MType->mtype != MT_GOLEM) {
1577  NewMonsterAnim(mid, &monster[mid].MType->Anims[MA_GOTHIT], monster[mid]._mdir);
1578  monster[mid]._mmode = MM_GOTHIT;
1579  }
1580 
1581  monster[mid]._mxoff = 0;
1582  monster[mid]._myoff = 0;
1583  monster[mid]._mx = monster[mid]._moldx;
1584  monster[mid]._my = monster[mid]._moldy;
1585  monster[mid]._mfutx = monster[mid]._moldx;
1586  monster[mid]._mfuty = monster[mid]._moldy;
1587  M_ClearSquares(mid);
1588  dMonster[monster[mid]._mx][monster[mid]._my] = mid + 1;
1589  }
1590  }
1591 }
1592 
1593 void MonstStartKill(int i, int pnum, BOOL sendmsg)
1594 {
1595  int md;
1596 
1597  if ((DWORD)i >= MAXMONSTERS) {
1598  app_fatal("MonstStartKill: Invalid monster %d", i);
1599  }
1600  if (!monster[i].MType) {
1601  app_fatal("MonstStartKill: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1602  }
1603 
1604  if (pnum >= 0)
1605  monster[i].mWhoHit |= 1 << pnum;
1606  if (pnum < MAX_PLRS && i > MAX_PLRS)
1607  AddPlrMonstExper(monster[i].mLevel, monster[i].mExp, monster[i].mWhoHit);
1608  monstkills[monster[i].MType->mtype]++;
1609  monster[i]._mhitpoints = 0;
1610  SetRndSeed(monster[i]._mRndSeed);
1611  if (QuestStatus(Q_GARBUD) && monster[i].mName == UniqMonst[UMT_GARBUD].mName) {
1612  CreateTypeItem(monster[i]._mx + 1, monster[i]._my + 1, TRUE, ITYPE_MACE, IMISC_NONE, TRUE, FALSE);
1613  } else if (i > MAX_PLRS - 1) { // Golems should not spawn items
1614  SpawnItem(i, monster[i]._mx, monster[i]._my, sendmsg);
1615  }
1616  if (monster[i].MType->mtype == MT_DIABLO)
1617  M_DiabloDeath(i, TRUE);
1618  else
1619  PlayEffect(i, 2);
1620 
1621  if (pnum >= 0)
1622  md = M_GetDir(i);
1623  else
1624  md = monster[i]._mdir;
1625  monster[i]._mdir = md;
1626  NewMonsterAnim(i, &monster[i].MType->Anims[MA_DEATH], md);
1627  monster[i]._mmode = MM_DEATH;
1628  monster[i]._mxoff = 0;
1629  monster[i]._myoff = 0;
1630  monster[i]._mVar1 = 0;
1631  monster[i]._mx = monster[i]._moldx;
1632  monster[i]._my = monster[i]._moldy;
1633  monster[i]._mfutx = monster[i]._moldx;
1634  monster[i]._mfuty = monster[i]._moldy;
1635  M_ClearSquares(i);
1636  dMonster[monster[i]._mx][monster[i]._my] = i + 1;
1637  CheckQuestKill(i, sendmsg);
1638  M_FallenFear(monster[i]._mx, monster[i]._my);
1639  if (monster[i].MType->mtype >= MT_NACID && monster[i].MType->mtype <= MT_XACID)
1640  AddMissile(monster[i]._mx, monster[i]._my, 0, 0, 0, MIS_ACIDPUD, 1, i, monster[i]._mint + 1, 0);
1641 }
1642 
1643 void M2MStartKill(int i, int mid)
1644 {
1645  int md;
1646 
1647  if ((DWORD)i >= MAXMONSTERS) {
1648  app_fatal("M2MStartKill: Invalid monster (attacker) %d", i);
1649  }
1650  if ((DWORD)i >= MAXMONSTERS) {
1651  app_fatal("M2MStartKill: Invalid monster (killed) %d", mid);
1652  }
1653  if (!monster[i].MType)
1654  app_fatal("M2MStartKill: Monster %d \"%s\" MType NULL", mid, monster[mid].mName);
1655 
1656  delta_kill_monster(mid, monster[mid]._mx, monster[mid]._my, currlevel);
1657  NetSendCmdLocParam1(FALSE, CMD_MONSTDEATH, monster[mid]._mx, monster[mid]._my, mid);
1658 
1659  monster[mid].mWhoHit |= 1 << i;
1660  if (i < MAX_PLRS)
1661  AddPlrMonstExper(monster[mid].mLevel, monster[mid].mExp, monster[mid].mWhoHit);
1662 
1663  monstkills[monster[mid].MType->mtype]++;
1664  monster[mid]._mhitpoints = 0;
1665  SetRndSeed(monster[mid]._mRndSeed);
1666 
1667  if (mid >= MAX_PLRS)
1668  SpawnItem(mid, monster[mid]._mx, monster[mid]._my, TRUE);
1669 
1670  if (monster[mid].MType->mtype == MT_DIABLO)
1671  M_DiabloDeath(mid, TRUE);
1672  else
1673  PlayEffect(i, 2);
1674 
1675  PlayEffect(mid, 2);
1676 
1677  md = (monster[i]._mdir - 4) & 7;
1678  if (monster[mid].MType->mtype == MT_GOLEM)
1679  md = 0;
1680 
1681  monster[mid]._mdir = md;
1682  NewMonsterAnim(mid, &monster[mid].MType->Anims[MA_DEATH], md);
1683  monster[mid]._mmode = MM_DEATH;
1684  monster[mid]._mxoff = 0;
1685  monster[mid]._myoff = 0;
1686  monster[mid]._mx = monster[mid]._moldx;
1687  monster[mid]._my = monster[mid]._moldy;
1688  monster[mid]._mfutx = monster[mid]._moldx;
1689  monster[mid]._mfuty = monster[mid]._moldy;
1690  M_ClearSquares(mid);
1691  dMonster[monster[mid]._mx][monster[mid]._my] = mid + 1;
1692  CheckQuestKill(mid, TRUE);
1693  M_FallenFear(monster[mid]._mx, monster[mid]._my);
1694  if (monster[mid].MType->mtype >= MT_NACID && monster[mid].MType->mtype <= MT_XACID)
1695  AddMissile(monster[mid]._mx, monster[mid]._my, 0, 0, 0, MIS_ACIDPUD, 1, mid, monster[mid]._mint + 1, 0);
1696 }
1697 
1698 void M_StartKill(int i, int pnum)
1699 {
1700  if ((DWORD)i >= MAXMONSTERS) {
1701  app_fatal("M_StartKill: Invalid monster %d", i);
1702  }
1703 
1704  if (myplr == pnum) {
1705  delta_kill_monster(i, monster[i]._mx, monster[i]._my, currlevel);
1706  if (i != pnum) {
1707  NetSendCmdLocParam1(FALSE, CMD_MONSTDEATH, monster[i]._mx, monster[i]._my, i);
1708  } else {
1709  NetSendCmdLocParam1(FALSE, CMD_KILLGOLEM, monster[i]._mx, monster[i]._my, currlevel);
1710  }
1711  }
1712 
1713  MonstStartKill(i, pnum, TRUE);
1714 }
1715 
1716 void M_SyncStartKill(int i, int x, int y, int pnum)
1717 {
1718  if ((DWORD)i >= MAXMONSTERS)
1719  app_fatal("M_SyncStartKill: Invalid monster %d", i);
1720 
1721  if (monster[i]._mhitpoints == 0 || monster[i]._mmode == MM_DEATH) {
1722  return;
1723  }
1724 
1725  if (dMonster[x][y] == 0) {
1726  M_ClearSquares(i);
1727  monster[i]._mx = x;
1728  monster[i]._my = y;
1729  monster[i]._moldx = x;
1730  monster[i]._moldy = y;
1731  }
1732 
1733  if (monster[i]._mmode == MM_STONE) {
1734  MonstStartKill(i, pnum, FALSE);
1735  monster[i]._mmode = MM_STONE;
1736  } else {
1737  MonstStartKill(i, pnum, FALSE);
1738  }
1739 }
1740 
1741 void M_StartFadein(int i, int md, BOOL backwards)
1742 {
1743  if ((DWORD)i >= MAXMONSTERS)
1744  app_fatal("M_StartFadein: Invalid monster %d", i);
1745  if (monster[i].MType == NULL)
1746  app_fatal("M_StartFadein: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1747 
1748  NewMonsterAnim(i, &monster[i].MType->Anims[MA_SPECIAL], md);
1749  monster[i]._mmode = MM_FADEIN;
1750  monster[i]._mxoff = 0;
1751  monster[i]._myoff = 0;
1752  monster[i]._mfutx = monster[i]._mx;
1753  monster[i]._mfuty = monster[i]._my;
1754  monster[i]._moldx = monster[i]._mx;
1755  monster[i]._moldy = monster[i]._my;
1756  monster[i]._mdir = md;
1757  monster[i]._mFlags &= ~MFLAG_HIDDEN;
1758  if (backwards) {
1761  }
1762 }
1763 
1764 void M_StartFadeout(int i, int md, BOOL backwards)
1765 {
1766  if ((DWORD)i >= MAXMONSTERS)
1767  app_fatal("M_StartFadeout: Invalid monster %d", i);
1768  if (monster[i].MType == NULL)
1769  app_fatal("M_StartFadeout: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1770 
1771  NewMonsterAnim(i, &monster[i].MType->Anims[MA_SPECIAL], md);
1772  monster[i]._mmode = MM_FADEOUT;
1773  monster[i]._mxoff = 0;
1774  monster[i]._myoff = 0;
1775  monster[i]._mfutx = monster[i]._mx;
1776  monster[i]._mfuty = monster[i]._my;
1777  monster[i]._moldx = monster[i]._mx;
1778  monster[i]._moldy = monster[i]._my;
1779  monster[i]._mdir = md;
1780  if (backwards) {
1783  }
1784 }
1785 
1786 void M_StartHeal(int i)
1787 {
1788  MonsterStruct *Monst;
1789 
1790  if ((DWORD)i >= MAXMONSTERS)
1791  app_fatal("M_StartHeal: Invalid monster %d", i);
1792  if (monster[i].MType == NULL)
1793  app_fatal("M_StartHeal: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1794 
1795  Monst = &monster[i];
1796  Monst->_mAnimData = Monst->MType->Anims[MA_SPECIAL].Data[Monst->_mdir];
1797  Monst->_mAnimFrame = Monst->MType->Anims[MA_SPECIAL].Frames;
1798  Monst->_mFlags |= MFLAG_LOCK_ANIMATION;
1799  Monst->_mmode = MM_HEAL;
1800  Monst->_mVar1 = Monst->_mmaxhp / (16 * (random_(97, 5) + 4));
1801 }
1802 
1803 void M_ChangeLightOffset(int monst)
1804 {
1805  int lx, ly, _mxoff, _myoff, sign;
1806 
1807  if ((DWORD)monst >= MAXMONSTERS)
1808  app_fatal("M_ChangeLightOffset: Invalid monster %d", monst);
1809 
1810  lx = monster[monst]._mxoff + 2 * monster[monst]._myoff;
1811  ly = 2 * monster[monst]._myoff - monster[monst]._mxoff;
1812 
1813  if (lx < 0) {
1814  sign = -1;
1815  lx = -lx;
1816  } else {
1817  sign = 1;
1818  }
1819 
1820  _mxoff = sign * (lx >> 3);
1821  if (ly < 0) {
1822  _myoff = -1;
1823  ly = -ly;
1824  } else {
1825  _myoff = 1;
1826  }
1827 
1828  _myoff *= (ly >> 3);
1829  ChangeLightOff(monster[monst].mlid, _mxoff, _myoff);
1830 }
1831 
1832 BOOL M_DoStand(int i)
1833 {
1834  MonsterStruct *Monst;
1835 
1836  if ((DWORD)i >= MAXMONSTERS)
1837  app_fatal("M_DoStand: Invalid monster %d", i);
1838  if (monster[i].MType == NULL)
1839  app_fatal("M_DoStand: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1840 
1841  Monst = &monster[i];
1842  if (Monst->MType->mtype == MT_GOLEM)
1843  Monst->_mAnimData = Monst->MType->Anims[MA_WALK].Data[Monst->_mdir];
1844  else
1845  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[Monst->_mdir];
1846 
1847  if (Monst->_mAnimFrame == Monst->_mAnimLen)
1848  M_Enemy(i);
1849 
1850  Monst->_mVar2++;
1851 
1852  return FALSE;
1853 }
1854 
1855 BOOL M_DoWalk(int i)
1856 {
1857  BOOL rv;
1858 
1859  if ((DWORD)i >= MAXMONSTERS)
1860  app_fatal("M_DoWalk: Invalid monster %d", i);
1861  if (monster[i].MType == NULL)
1862  app_fatal("M_DoWalk: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1863 
1864  rv = FALSE;
1865  if (monster[i]._mVar8 == monster[i].MType->Anims[MA_WALK].Frames) {
1866  dMonster[monster[i]._mx][monster[i]._my] = 0;
1867  monster[i]._mx += monster[i]._mVar1;
1868  monster[i]._my += monster[i]._mVar2;
1869  dMonster[monster[i]._mx][monster[i]._my] = i + 1;
1870  if (monster[i]._uniqtype != 0)
1871  ChangeLightXY(monster[i].mlid, monster[i]._mx, monster[i]._my);
1872  M_StartStand(i, monster[i]._mdir);
1873  rv = TRUE;
1874  } else if (!monster[i]._mAnimCnt) {
1875  monster[i]._mVar8++;
1876  monster[i]._mVar6 += monster[i]._mxvel;
1877  monster[i]._mVar7 += monster[i]._myvel;
1878  monster[i]._mxoff = monster[i]._mVar6 >> 4;
1879  monster[i]._myoff = monster[i]._mVar7 >> 4;
1880  }
1881 
1882  if (monster[i]._uniqtype != 0)
1884 
1885  return rv;
1886 }
1887 
1888 BOOL M_DoWalk2(int i)
1889 {
1890  BOOL rv;
1891 
1892  if ((DWORD)i >= MAXMONSTERS)
1893  app_fatal("M_DoWalk2: Invalid monster %d", i);
1894  if (monster[i].MType == NULL)
1895  app_fatal("M_DoWalk2: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1896 
1897  if (monster[i]._mVar8 == monster[i].MType->Anims[MA_WALK].Frames) {
1898  dMonster[monster[i]._mVar1][monster[i]._mVar2] = 0;
1899  if (monster[i]._uniqtype != 0)
1900  ChangeLightXY(monster[i].mlid, monster[i]._mx, monster[i]._my);
1901  M_StartStand(i, monster[i]._mdir);
1902  rv = TRUE;
1903  } else {
1904  if (!monster[i]._mAnimCnt) {
1905  monster[i]._mVar8++;
1906  monster[i]._mVar6 += monster[i]._mxvel;
1907  monster[i]._mVar7 += monster[i]._myvel;
1908  monster[i]._mxoff = monster[i]._mVar6 >> 4;
1909  monster[i]._myoff = monster[i]._mVar7 >> 4;
1910  }
1911  rv = FALSE;
1912  }
1913  if (monster[i]._uniqtype != 0)
1915 
1916  return rv;
1917 }
1918 
1919 BOOL M_DoWalk3(int i)
1920 {
1921  BOOL rv;
1922 
1923  if ((DWORD)i >= MAXMONSTERS)
1924  app_fatal("M_DoWalk3: Invalid monster %d", i);
1925  if (monster[i].MType == NULL)
1926  app_fatal("M_DoWalk3: Monster %d \"%s\" MType NULL", i, monster[i].mName);
1927 
1928  if (monster[i]._mVar8 == monster[i].MType->Anims[MA_WALK].Frames) {
1929  dMonster[monster[i]._mx][monster[i]._my] = 0;
1930  monster[i]._mx = monster[i]._mVar1;
1931  monster[i]._my = monster[i]._mVar2;
1933  dMonster[monster[i]._mx][monster[i]._my] = i + 1;
1934  if (monster[i]._uniqtype)
1935  ChangeLightXY(monster[i].mlid, monster[i]._mx, monster[i]._my);
1936  M_StartStand(i, monster[i]._mdir);
1937  rv = TRUE;
1938  } else {
1939  if (!monster[i]._mAnimCnt) {
1940  monster[i]._mVar8++;
1941  monster[i]._mVar6 += monster[i]._mxvel;
1942  monster[i]._mVar7 += monster[i]._myvel;
1943  monster[i]._mxoff = monster[i]._mVar6 >> 4;
1944  monster[i]._myoff = monster[i]._mVar7 >> 4;
1945  }
1946  rv = FALSE;
1947  }
1948  if (monster[i]._uniqtype != 0)
1950 
1951  return rv;
1952 }
1953 
1954 void M_TryM2MHit(int i, int mid, int hper, int mind, int maxd)
1955 {
1956  BOOL ret;
1957 
1958  if ((DWORD)mid >= MAXMONSTERS) {
1959  app_fatal("M_TryM2MHit: Invalid monster %d", mid);
1960  }
1961  if (monster[mid].MType == NULL)
1962  app_fatal("M_TryM2MHit: Monster %d \"%s\" MType NULL", mid, monster[mid].mName);
1963  if (monster[mid]._mhitpoints >> 6 > 0 && (monster[mid].MType->mtype != MT_ILLWEAV || monster[mid]._mgoal != MGOAL_RETREAT)) {
1964  int hit = random_(4, 100);
1965  if (monster[mid]._mmode == MM_STONE)
1966  hit = 0;
1967  if (!CheckMonsterHit(mid, &ret) && hit < hper) {
1968  int dam = (mind + random_(5, maxd - mind + 1)) << 6;
1969  monster[mid]._mhitpoints -= dam;
1970  if (monster[mid]._mhitpoints >> 6 <= 0) {
1971  if (monster[mid]._mmode == MM_STONE) {
1972  M2MStartKill(i, mid);
1973  monster[mid]._mmode = MM_STONE;
1974  } else {
1975  M2MStartKill(i, mid);
1976  }
1977  } else {
1978  if (monster[mid]._mmode == MM_STONE) {
1979  M2MStartHit(mid, i, dam);
1980  monster[mid]._mmode = MM_STONE;
1981  } else {
1982  M2MStartHit(mid, i, dam);
1983  }
1984  }
1985  }
1986  }
1987 }
1988 
1989 void M_TryH2HHit(int i, int pnum, int Hit, int MinDam, int MaxDam)
1990 {
1991  int hit, hper;
1992  int dx, dy;
1993  int blk, blkper;
1994  int dam, mdam;
1995  int newx, newy;
1996  int j, misnum, ms_num, cur_ms_num, new_hp;
1997 
1998  if ((DWORD)i >= MAXMONSTERS)
1999  app_fatal("M_TryH2HHit: Invalid monster %d", i);
2000  if (monster[i].MType == NULL)
2001  app_fatal("M_TryH2HHit: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2002  if (monster[i]._mFlags & MFLAG_TARGETS_MONSTER) {
2003  M_TryM2MHit(i, pnum, Hit, MinDam, MaxDam);
2004  return;
2005  }
2006  if (plr[pnum]._pHitPoints >> 6 <= 0 || plr[pnum]._pInvincible || plr[pnum]._pSpellFlags & 1)
2007  return;
2008  dx = abs(monster[i]._mx - plr[pnum].WorldX);
2009  dy = abs(monster[i]._my - plr[pnum].WorldY);
2010  if (dx >= 2 || dy >= 2)
2011  return;
2012 
2013  hper = random_(98, 100);
2014 #ifdef _DEBUG
2015  if (debug_mode_dollar_sign || debug_mode_key_inverted_v)
2016  hper = 1000;
2017 #endif
2018  hit = Hit
2019  + 2 * (monster[i].mLevel - plr[pnum]._pLevel)
2020  + 30
2021  - plr[pnum]._pIBonusAC
2022  - plr[pnum]._pIAC
2023  - plr[pnum]._pDexterity / 5;
2024  if (hit < 15)
2025  hit = 15;
2026  if (currlevel == 14 && hit < 20)
2027  hit = 20;
2028  if (currlevel == 15 && hit < 25)
2029  hit = 25;
2030  if (currlevel == 16 && hit < 30)
2031  hit = 30;
2032  if ((plr[pnum]._pmode == PM_STAND || plr[pnum]._pmode == PM_ATTACK) && plr[pnum]._pBlockFlag) {
2033  blkper = random_(98, 100);
2034  } else {
2035  blkper = 100;
2036  }
2037  blk = plr[pnum]._pDexterity
2038  + plr[pnum]._pBaseToBlk
2039  - (monster[i].mLevel << 1)
2040  + (plr[pnum]._pLevel << 1);
2041  if (blk < 0)
2042  blk = 0;
2043  if (blk > 100)
2044  blk = 100;
2045  if (hper >= hit)
2046  return;
2047  if (blkper < blk) {
2048  StartPlrBlock(pnum, GetDirection(plr[pnum].WorldX, plr[pnum].WorldY, monster[i]._mx, monster[i]._my));
2049  return;
2050  }
2051  if (monster[i].MType->mtype == MT_YZOMBIE && pnum == myplr) {
2052  ms_num = -1;
2053  cur_ms_num = -1;
2054  for (j = 0; j < nummissiles; j++) {
2055  misnum = missileactive[j];
2056  if (missile[misnum]._mitype != MIS_MANASHIELD)
2057  continue;
2058  if (missile[misnum]._misource == pnum)
2059  cur_ms_num = misnum;
2060  else
2061  ms_num = misnum;
2062  }
2063  if (plr[pnum]._pMaxHP > 64) {
2064  if (plr[pnum]._pMaxHPBase > 64) {
2065  new_hp = plr[pnum]._pMaxHP - 64;
2066  plr[pnum]._pMaxHP = new_hp;
2067  if (plr[pnum]._pHitPoints > new_hp) {
2068  plr[pnum]._pHitPoints = new_hp;
2069  if (cur_ms_num >= 0)
2070  missile[cur_ms_num]._miVar1 = new_hp;
2071  }
2072  new_hp = plr[pnum]._pMaxHPBase - 64;
2073  plr[pnum]._pMaxHPBase = new_hp;
2074  if (plr[pnum]._pHPBase > new_hp) {
2075  plr[pnum]._pHPBase = new_hp;
2076  if (cur_ms_num >= 0)
2077  missile[cur_ms_num]._miVar2 = new_hp;
2078  }
2079  }
2080  }
2081  }
2082  dam = (MinDam << 6) + random_(99, (MaxDam - MinDam + 1) << 6);
2083  dam += (plr[pnum]._pIGetHit << 6);
2084  if (dam < 64)
2085  dam = 64;
2086  if (pnum == myplr) {
2087  plr[pnum]._pHitPoints -= dam;
2088  plr[pnum]._pHPBase -= dam;
2089  }
2090  if (plr[pnum]._pIFlags & ISPL_THORNS) {
2091  mdam = (random_(99, 3) + 1) << 6;
2092  monster[i]._mhitpoints -= mdam;
2093  if (monster[i]._mhitpoints >> 6 <= 0)
2094  M_StartKill(i, pnum);
2095  else
2096  M_StartHit(i, pnum, mdam);
2097  }
2098  if (!(monster[i]._mFlags & MFLAG_NOLIFESTEAL) && monster[i].MType->mtype == MT_SKING && gbMaxPlayers != 1)
2099  monster[i]._mhitpoints += dam;
2100  if (plr[pnum]._pHitPoints > plr[pnum]._pMaxHP) {
2101  plr[pnum]._pHitPoints = plr[pnum]._pMaxHP;
2102  plr[pnum]._pHPBase = plr[pnum]._pMaxHPBase;
2103  }
2104  if (plr[pnum]._pHitPoints >> 6 <= 0) {
2105  SyncPlrKill(pnum, 0);
2106  return;
2107  }
2108  StartPlrHit(pnum, dam, FALSE);
2109  if (monster[i]._mFlags & MFLAG_KNOCKBACK) {
2110  if (plr[pnum]._pmode != PM_GOTHIT)
2111  StartPlrHit(pnum, 0, TRUE);
2112  newx = plr[pnum].WorldX + offset_x[monster[i]._mdir];
2113  newy = plr[pnum].WorldY + offset_y[monster[i]._mdir];
2114  if (PosOkPlayer(pnum, newx, newy)) {
2115  plr[pnum].WorldX = newx;
2116  plr[pnum].WorldY = newy;
2117  FixPlayerLocation(pnum, plr[pnum]._pdir);
2118  FixPlrWalkTags(pnum);
2119  dPlayer[newx][newy] = pnum + 1;
2120  SetPlayerOld(pnum);
2121  }
2122  }
2123 }
2124 
2125 BOOL M_DoAttack(int i)
2126 {
2127  MonsterStruct *Monst;
2128 
2129  if ((DWORD)i >= MAXMONSTERS)
2130  app_fatal("M_DoAttack: Invalid monster %d", i);
2131 
2132  Monst = &monster[i];
2133  if (Monst->MType == NULL)
2134  app_fatal("M_DoAttack: Monster %d \"%s\" MType NULL", i, Monst->mName);
2135  if (Monst->MType == NULL) // BUGFIX: should check MData
2136  app_fatal("M_DoAttack: Monster %d \"%s\" MData NULL", i, Monst->mName);
2137 
2138  if (monster[i]._mAnimFrame == monster[i].MData->mAFNum) {
2139  M_TryH2HHit(i, monster[i]._menemy, monster[i].mHit, monster[i].mMinDamage, monster[i].mMaxDamage);
2140  if (monster[i]._mAi != AI_SNAKE)
2141  PlayEffect(i, 0);
2142  }
2143  if (monster[i].MType->mtype >= MT_NMAGMA && monster[i].MType->mtype <= MT_WMAGMA && monster[i]._mAnimFrame == 9) {
2144  M_TryH2HHit(i, monster[i]._menemy, monster[i].mHit + 10, monster[i].mMinDamage - 2, monster[i].mMaxDamage - 2);
2145  PlayEffect(i, 0);
2146  }
2147  if (monster[i].MType->mtype >= MT_STORM && monster[i].MType->mtype <= MT_MAEL && monster[i]._mAnimFrame == 13) {
2148  M_TryH2HHit(i, monster[i]._menemy, monster[i].mHit - 20, monster[i].mMinDamage + 4, monster[i].mMaxDamage + 4);
2149  PlayEffect(i, 0);
2150  }
2151  if (monster[i]._mAi == AI_SNAKE && monster[i]._mAnimFrame == 1)
2152  PlayEffect(i, 0);
2153  if (monster[i]._mAnimFrame == monster[i]._mAnimLen) {
2154  M_StartStand(i, monster[i]._mdir);
2155  return TRUE;
2156  }
2157 
2158  return FALSE;
2159 }
2160 
2161 BOOL M_DoRAttack(int i)
2162 {
2163  int multimissiles, mi;
2164 
2165  if ((DWORD)i >= MAXMONSTERS)
2166  app_fatal("M_DoRAttack: Invalid monster %d", i);
2167  if (monster[i].MType == NULL)
2168  app_fatal("M_DoRAttack: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2169  if (monster[i].MType == NULL) // BUGFIX: should check MData
2170  app_fatal("M_DoRAttack: Monster %d \"%s\" MData NULL", i, monster[i].mName);
2171 
2172  if (monster[i]._mAnimFrame == monster[i].MData->mAFNum) {
2173  if (monster[i]._mVar1 != -1) {
2174  if (monster[i]._mVar1 != MIS_CBOLT)
2175  multimissiles = 1;
2176  else
2177  multimissiles = 3;
2178  for (mi = 0; mi < multimissiles; mi++) {
2179  AddMissile(
2180  monster[i]._mx,
2181  monster[i]._my,
2182  monster[i]._menemyx,
2183  monster[i]._menemyy,
2184  monster[i]._mdir,
2185  monster[i]._mVar1,
2186  1,
2187  i,
2188  monster[i]._mVar2,
2189  0);
2190  }
2191  }
2192  PlayEffect(i, 0);
2193  }
2194 
2195  if (monster[i]._mAnimFrame == monster[i]._mAnimLen) {
2196  M_StartStand(i, monster[i]._mdir);
2197  return TRUE;
2198  }
2199 
2200  return FALSE;
2201 }
2202 
2203 int M_DoRSpAttack(int i)
2204 {
2205  if ((DWORD)i >= MAXMONSTERS)
2206  app_fatal("M_DoRSpAttack: Invalid monster %d", i);
2207  if (monster[i].MType == NULL)
2208  app_fatal("M_DoRSpAttack: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2209  if (monster[i].MType == NULL) // BUGFIX: should check MData
2210  app_fatal("M_DoRSpAttack: Monster %d \"%s\" MData NULL", i, monster[i].mName);
2211 
2212  if (monster[i]._mAnimFrame == monster[i].MData->mAFNum2 && !monster[i]._mAnimCnt) {
2213  AddMissile(
2214  monster[i]._mx,
2215  monster[i]._my,
2216  monster[i]._menemyx,
2217  monster[i]._menemyy,
2218  monster[i]._mdir,
2219  monster[i]._mVar1,
2220  1,
2221  i,
2222  monster[i]._mVar3,
2223  0);
2224  PlayEffect(i, 3);
2225  }
2226 
2227  if (monster[i]._mAi == AI_MEGA && monster[i]._mAnimFrame == 3) {
2228  int hadV2 = monster[i]._mVar2;
2229  monster[i]._mVar2++;
2230  if (hadV2 == 0) {
2232  } else if (monster[i]._mVar2 == 15) {
2234  }
2235  }
2236 
2237  if (monster[i]._mAnimFrame == monster[i]._mAnimLen) {
2238  M_StartStand(i, monster[i]._mdir);
2239  return TRUE;
2240  }
2241 
2242  return FALSE;
2243 }
2244 
2245 BOOL M_DoSAttack(int i)
2246 {
2247  if ((DWORD)i >= MAXMONSTERS)
2248  app_fatal("M_DoSAttack: Invalid monster %d", i);
2249  if (monster[i].MType == NULL)
2250  app_fatal("M_DoSAttack: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2251  if (monster[i].MType == NULL) // BUGFIX: should check MData
2252  app_fatal("M_DoSAttack: Monster %d \"%s\" MData NULL", i, monster[i].mName);
2253 
2254  if (monster[i]._mAnimFrame == monster[i].MData->mAFNum2)
2255  M_TryH2HHit(i, monster[i]._menemy, monster[i].mHit2, monster[i].mMinDamage2, monster[i].mMaxDamage2);
2256 
2257  if (monster[i]._mAnimFrame == monster[i]._mAnimLen) {
2258  M_StartStand(i, monster[i]._mdir);
2259  return TRUE;
2260  }
2261 
2262  return FALSE;
2263 }
2264 
2265 BOOL M_DoFadein(int i)
2266 {
2267  if ((DWORD)i >= MAXMONSTERS)
2268  app_fatal("M_DoFadein: Invalid monster %d", i);
2269 
2270  if ((!(monster[i]._mFlags & MFLAG_LOCK_ANIMATION) || monster[i]._mAnimFrame != 1)
2271  && (monster[i]._mFlags & MFLAG_LOCK_ANIMATION || monster[i]._mAnimFrame != monster[i]._mAnimLen)) {
2272  return FALSE;
2273  }
2274 
2275  M_StartStand(i, monster[i]._mdir);
2277 
2278  return TRUE;
2279 }
2280 
2281 BOOL M_DoFadeout(int i)
2282 {
2283  int mt;
2284 
2285  if ((DWORD)i >= MAXMONSTERS)
2286  app_fatal("M_DoFadeout: Invalid monster %d", i);
2287 
2288  if ((!(monster[i]._mFlags & MFLAG_LOCK_ANIMATION) || monster[i]._mAnimFrame != 1)
2289  && (monster[i]._mFlags & MFLAG_LOCK_ANIMATION || monster[i]._mAnimFrame != monster[i]._mAnimLen)) {
2290  return FALSE;
2291  }
2292 
2293  mt = monster[i].MType->mtype;
2294  if (mt < MT_INCIN || mt > MT_HELLBURN) {
2297  } else {
2299  }
2300 
2301  M_StartStand(i, monster[i]._mdir);
2302 
2303  return TRUE;
2304 }
2305 
2306 int M_DoHeal(int i)
2307 {
2308  MonsterStruct *Monst;
2309 
2310  if ((DWORD)i >= MAXMONSTERS)
2311  app_fatal("M_DoHeal: Invalid monster %d", i);
2312  Monst = monster + i;
2313  if (monster[i]._mFlags & MFLAG_NOHEAL) {
2314  Monst->_mFlags &= ~MFLAG_ALLOW_SPECIAL;
2315  Monst->_mmode = MM_SATTACK;
2316  return FALSE;
2317  }
2318 
2319  if (Monst->_mAnimFrame == 1) {
2320  Monst->_mFlags &= ~MFLAG_LOCK_ANIMATION;
2321  Monst->_mFlags |= MFLAG_ALLOW_SPECIAL;
2322  if (Monst->_mVar1 + Monst->_mhitpoints < Monst->_mmaxhp) {
2323  Monst->_mhitpoints = Monst->_mVar1 + Monst->_mhitpoints;
2324  } else {
2325  Monst->_mhitpoints = Monst->_mmaxhp;
2326  Monst->_mFlags &= ~MFLAG_ALLOW_SPECIAL;
2327  Monst->_mmode = MM_SATTACK;
2328  }
2329  }
2330  return FALSE;
2331 }
2332 
2333 int M_DoTalk(int i)
2334 {
2335  MonsterStruct *Monst;
2336  int tren;
2337 
2338  if ((DWORD)i >= MAXMONSTERS)
2339  app_fatal("M_DoTalk: Invalid monster %d", i);
2340 
2341  Monst = monster + i;
2342  M_StartStand(i, Monst->_mdir);
2344  if (effect_is_playing(alltext[Monst->mtalkmsg].sfxnr))
2345  return FALSE;
2346  InitQTextMsg(Monst->mtalkmsg);
2347  if (Monst->mName == UniqMonst[UMT_GARBUD].mName) {
2348  if (Monst->mtalkmsg == TEXT_GARBUD1)
2350  quests[Q_GARBUD]._qlog = TRUE;
2351  if (Monst->mtalkmsg == TEXT_GARBUD2 && !(Monst->_mFlags & MFLAG_QUEST_COMPLETE)) {
2352  SpawnItem(i, Monst->_mx + 1, Monst->_my + 1, TRUE);
2353  Monst->_mFlags |= MFLAG_QUEST_COMPLETE;
2354  }
2355  }
2356  if (Monst->mName == UniqMonst[UMT_ZHAR].mName
2357  && Monst->mtalkmsg == TEXT_ZHAR1
2358  && !(Monst->_mFlags & MFLAG_QUEST_COMPLETE)) {
2360  quests[Q_ZHAR]._qlog = TRUE;
2361  CreateTypeItem(Monst->_mx + 1, Monst->_my + 1, FALSE, ITYPE_MISC, IMISC_BOOK, TRUE, FALSE);
2362  Monst->_mFlags |= MFLAG_QUEST_COMPLETE;
2363  }
2364  if (Monst->mName == UniqMonst[UMT_SNOTSPIL].mName) {
2365  if (Monst->mtalkmsg == TEXT_BANNER10 && !(Monst->_mFlags & MFLAG_QUEST_COMPLETE)) {
2366  ObjChangeMap(setpc_x, setpc_y, (setpc_w >> 1) + setpc_x + 2, (setpc_h >> 1) + setpc_y - 2);
2367  tren = TransVal;
2368  TransVal = 9;
2369  DRLG_MRectTrans(setpc_x, setpc_y, (setpc_w >> 1) + setpc_x + 4, setpc_y + (setpc_h >> 1));
2370  TransVal = tren;
2371  quests[Q_LTBANNER]._qvar1 = 2;
2372  if (quests[Q_LTBANNER]._qactive == QUEST_INIT)
2374  Monst->_mFlags |= MFLAG_QUEST_COMPLETE;
2375  }
2376  if (quests[Q_LTBANNER]._qvar1 < 2) {
2377  sprintf(tempstr, "SS Talk = %i, Flags = %i", Monst->mtalkmsg, Monst->_mFlags);
2378  app_fatal(tempstr);
2379  }
2380  }
2381  if (Monst->mName == UniqMonst[UMT_LACHDAN].mName) {
2382  if (Monst->mtalkmsg == TEXT_VEIL9) {
2384  quests[Q_VEIL]._qlog = TRUE;
2385  }
2386  if (Monst->mtalkmsg == TEXT_VEIL11 && !(Monst->_mFlags & MFLAG_QUEST_COMPLETE)) {
2387  SpawnUnique(UITEM_STEELVEIL, Monst->_mx + 1, Monst->_my + 1);
2388  Monst->_mFlags |= MFLAG_QUEST_COMPLETE;
2389  }
2390  }
2391  if (Monst->mName == UniqMonst[UMT_WARLORD].mName)
2392  quests[Q_WARLORD]._qvar1 = 2;
2393  if (Monst->mName == UniqMonst[UMT_LAZURUS].mName && gbMaxPlayers != 1) {
2394  Monst->_msquelch = UCHAR_MAX;
2395  Monst->mtalkmsg = 0;
2396  quests[Q_BETRAYER]._qvar1 = 6;
2397  Monst->_mgoal = MGOAL_NORMAL;
2398  }
2399  return FALSE;
2400 }
2401 
2402 void M_Teleport(int i)
2403 {
2404  BOOL tren;
2405  MonsterStruct *Monst;
2406  int k, j, x, y, _mx, _my, rx, ry;
2407 
2408  if ((DWORD)i >= MAXMONSTERS)
2409  app_fatal("M_Teleport: Invalid monster %d", i);
2410 
2411  tren = FALSE;
2412 
2413  Monst = &monster[i];
2414  if (Monst->_mmode != MM_STONE) {
2415  _mx = Monst->_menemyx;
2416  _my = Monst->_menemyy;
2417  rx = 2 * random_(100, 2) - 1;
2418  ry = 2 * random_(100, 2) - 1;
2419 
2420  for (j = -1; j <= 1 && !tren; j++) {
2421  for (k = -1; k < 1 && !tren; k++) {
2422  if (j != 0 || k != 0) {
2423  x = _mx + rx * j;
2424  y = _my + ry * k;
2425  if (y >= 0 && y < MAXDUNY && x >= 0 && x < MAXDUNX && x != Monst->_mx && y != Monst->_my) {
2426  if (PosOkMonst(i, x, y))
2427  tren = TRUE;
2428  }
2429  }
2430  }
2431  }
2432  }
2433 
2434  if (tren) {
2435  M_ClearSquares(i);
2436  dMonster[Monst->_mx][Monst->_my] = 0;
2437  dMonster[x][y] = i + 1;
2438  Monst->_moldx = x;
2439  Monst->_moldy = y;
2440  Monst->_mdir = M_GetDir(i);
2441  }
2442 }
2443 
2444 BOOL M_DoGotHit(int i)
2445 {
2446  if ((DWORD)i >= MAXMONSTERS)
2447  app_fatal("M_DoGotHit: Invalid monster %d", i);
2448 
2449  if (monster[i].MType == NULL)
2450  app_fatal("M_DoGotHit: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2451  if (monster[i]._mAnimFrame == monster[i]._mAnimLen) {
2452  M_StartStand(i, monster[i]._mdir);
2453 
2454  return TRUE;
2455  }
2456 
2457  return FALSE;
2458 }
2459 
2460 void M_UpdateLeader(int i)
2461 {
2462  int ma, j;
2463 
2464  if ((DWORD)i >= MAXMONSTERS)
2465  app_fatal("M_UpdateLeader: Invalid monster %d", i);
2466 
2467  for (j = 0; j < nummonsters; j++) {
2468  ma = monstactive[j];
2469  if (monster[ma].leaderflag == 1 && monster[ma].leader == i)
2470  monster[ma].leaderflag = 0;
2471  }
2472 
2473  if (monster[i].leaderflag == 1) {
2475  }
2476 }
2477 
2478 void DoEnding()
2479 {
2480  BOOL bMusicOn;
2481  int musicVolume;
2482 
2483  if (gbMaxPlayers > 1) {
2484  SNetLeaveGame(0x40000004);
2485  }
2486 
2487  music_stop();
2488 
2489  if (gbMaxPlayers > 1) {
2490  SDL_Delay(1000);
2491  }
2492 
2493 #ifndef SPAWN
2494  if (plr[myplr]._pClass == PC_WARRIOR) {
2495  play_movie("gendata\\DiabVic2.smk", FALSE);
2496  } else if (plr[myplr]._pClass == PC_SORCERER) {
2497  play_movie("gendata\\DiabVic1.smk", FALSE);
2498  } else {
2499  play_movie("gendata\\DiabVic3.smk", FALSE);
2500  }
2501  play_movie("gendata\\Diabend.smk", FALSE);
2502 
2503  bMusicOn = gbMusicOn;
2504  gbMusicOn = TRUE;
2505 
2506  musicVolume = sound_get_or_set_music_volume(1);
2508 
2510  loop_movie = TRUE;
2511  play_movie("gendata\\loopdend.smk", TRUE);
2512  loop_movie = FALSE;
2513  music_stop();
2514 
2515  sound_get_or_set_music_volume(musicVolume);
2516  gbMusicOn = bMusicOn;
2517 #endif
2518 }
2519 
2521 {
2522  int newKillLevel, i;
2523  DWORD *killLevel;
2524 
2526  gbRunGame = FALSE;
2527  deathflag = FALSE;
2528  cineflag = TRUE;
2529 
2530  killLevel = &plr[myplr].pDiabloKillLevel;
2531  newKillLevel = gnDifficulty + 1;
2532  if (*killLevel > newKillLevel)
2533  newKillLevel = *killLevel;
2534  plr[myplr].pDiabloKillLevel = newKillLevel;
2535 
2536  for (i = 0; i < MAX_PLRS; i++) {
2537  plr[i]._pmode = PM_QUIT;
2538  plr[i]._pInvincible = TRUE;
2539  if (gbMaxPlayers > 1) {
2540  if (plr[i]._pHitPoints >> 6 == 0)
2541  plr[i]._pHitPoints = 64;
2542  if (plr[i]._pMana >> 6 == 0)
2543  plr[i]._pMana = 64;
2544  }
2545  }
2546 }
2547 
2548 BOOL M_DoDeath(int i)
2549 {
2550  int var1;
2551  int x, y;
2552 
2553  if ((DWORD)i >= MAXMONSTERS)
2554  app_fatal("M_DoDeath: Invalid monster %d", i);
2555  if (monster[i].MType == NULL)
2556  app_fatal("M_DoDeath: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2557 
2558  monster[i]._mVar1++;
2559  var1 = monster[i]._mVar1;
2560  if (monster[i].MType->mtype == MT_DIABLO) {
2561  x = monster[i]._mx - ViewX;
2562  if (x < 0)
2563  x = -1;
2564  else
2565  x = x > 0;
2566  ViewX += x;
2567 
2568  y = monster[i]._my - ViewY;
2569  if (y < 0) {
2570  y = -1;
2571  } else {
2572  y = y > 0;
2573  }
2574  ViewY += y;
2575 
2576  if (var1 == 140)
2577  PrepDoEnding();
2578  } else if (monster[i]._mAnimFrame == monster[i]._mAnimLen) {
2579  if (monster[i]._uniqtype == 0)
2580  AddDead(monster[i]._mx, monster[i]._my, monster[i].MType->mdeadval, (direction)monster[i]._mdir);
2581  else
2582  AddDead(monster[i]._mx, monster[i]._my, monster[i]._udeadval, (direction)monster[i]._mdir);
2583 
2584  dMonster[monster[i]._mx][monster[i]._my] = 0;
2585  monster[i]._mDelFlag = TRUE;
2586 
2587  M_UpdateLeader(i);
2588  }
2589  return FALSE;
2590 }
2591 
2592 BOOL M_DoSpStand(int i)
2593 {
2594  if ((DWORD)i >= MAXMONSTERS)
2595  app_fatal("M_DoSpStand: Invalid monster %d", i);
2596  if (monster[i].MType == NULL)
2597  app_fatal("M_DoSpStand: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2598 
2599  if (monster[i]._mAnimFrame == monster[i].MData->mAFNum2)
2600  PlayEffect(i, 3);
2601 
2602  if (monster[i]._mAnimFrame == monster[i]._mAnimLen) {
2603  M_StartStand(i, monster[i]._mdir);
2604  return TRUE;
2605  }
2606 
2607  return FALSE;
2608 }
2609 
2610 BOOL M_DoDelay(int i)
2611 {
2612  int mVar2;
2613  int oFrame;
2614 
2615  if ((DWORD)i >= MAXMONSTERS)
2616  app_fatal("M_DoDelay: Invalid monster %d", i);
2617  if (monster[i].MType == NULL)
2618  app_fatal("M_DoDelay: Monster %d \"%s\" MType NULL", i, monster[i].mName);
2619 
2621  if (monster[i]._mAi == AI_LAZURUS) {
2622  if (monster[i]._mVar2 > 8 || monster[i]._mVar2 < 0)
2623  monster[i]._mVar2 = 8;
2624  }
2625 
2626  mVar2 = monster[i]._mVar2;
2627  monster[i]._mVar2--;
2628 
2629  if (!mVar2) {
2630  oFrame = monster[i]._mAnimFrame;
2631  M_StartStand(i, monster[i]._mdir);
2632  monster[i]._mAnimFrame = oFrame;
2633  return TRUE;
2634  }
2635 
2636  return FALSE;
2637 }
2638 
2639 BOOL M_DoStone(int i)
2640 {
2641  if ((DWORD)i >= MAXMONSTERS)
2642  app_fatal("M_DoStone: Invalid monster %d", i);
2643 
2644  if (!monster[i]._mhitpoints) {
2645  dMonster[monster[i]._mx][monster[i]._my] = 0;
2646  monster[i]._mDelFlag = TRUE;
2647  }
2648 
2649  return FALSE;
2650 }
2651 
2652 void M_WalkDir(int i, int md)
2653 {
2654  int mwi;
2655 
2656  if ((DWORD)i >= MAXMONSTERS)
2657  app_fatal("M_WalkDir: Invalid monster %d", i);
2658 
2659  mwi = monster[i].MType->Anims[MA_WALK].Frames - 1;
2660  switch (md) {
2661  case DIR_N:
2662  M_StartWalk(i, 0, -MWVel[mwi][1], -1, -1, DIR_N);
2663  break;
2664  case DIR_NE:
2665  M_StartWalk(i, MWVel[mwi][1], -MWVel[mwi][0], 0, -1, DIR_NE);
2666  break;
2667  case DIR_E:
2668  M_StartWalk3(i, MWVel[mwi][2], 0, -32, -16, 1, -1, 1, 0, DIR_E);
2669  break;
2670  case DIR_SE:
2671  M_StartWalk2(i, MWVel[mwi][1], MWVel[mwi][0], -32, -16, 1, 0, DIR_SE);
2672  break;
2673  case DIR_S:
2674  M_StartWalk2(i, 0, MWVel[mwi][1], 0, -32, 1, 1, DIR_S);
2675  break;
2676  case DIR_SW:
2677  M_StartWalk2(i, -MWVel[mwi][1], MWVel[mwi][0], 32, -16, 0, 1, DIR_SW);
2678  break;
2679  case DIR_W:
2680  M_StartWalk3(i, -MWVel[mwi][2], 0, 32, -16, -1, 1, 0, 1, DIR_W);
2681  break;
2682  case DIR_NW:
2683  M_StartWalk(i, -MWVel[mwi][1], -MWVel[mwi][0], -1, 0, DIR_NW);
2684  break;
2685  }
2686 }
2687 
2688 void GroupUnity(int i)
2689 {
2690  int leader, m, j;
2691  BOOL clear;
2692 
2693  if ((DWORD)i >= MAXMONSTERS)
2694  app_fatal("GroupUnity: Invalid monster %d", i);
2695 
2696  if (monster[i].leaderflag) {
2697  leader = monster[i].leader;
2698  clear = LineClearF(CheckNoSolid, monster[i]._mx, monster[i]._my, monster[leader]._mfutx, monster[leader]._mfuty);
2699  if (clear || monster[i].leaderflag != 1) {
2700  if (clear
2701  && monster[i].leaderflag == 2
2702  && abs(monster[i]._mx - monster[leader]._mfutx) < 4
2703  && abs(monster[i]._my - monster[leader]._mfuty) < 4) {
2704  monster[leader].packsize++;
2705  monster[i].leaderflag = 1;
2706  }
2707  } else {
2708  monster[leader].packsize--;
2709  monster[i].leaderflag = 2;
2710  }
2711  }
2712 
2713  if (monster[i].leaderflag == 1) {
2714  if (monster[i]._msquelch > monster[leader]._msquelch) {
2715  monster[leader]._lastx = monster[i]._mx;
2716  monster[leader]._lasty = monster[i]._my;
2717  monster[leader]._msquelch = monster[i]._msquelch - 1;
2718  }
2719  if (monster[leader]._mAi == AI_GARG) {
2720  if (monster[leader]._mFlags & MFLAG_ALLOW_SPECIAL) {
2721  monster[leader]._mmode = MM_SATTACK;
2722  monster[leader]._mFlags &= ~MFLAG_ALLOW_SPECIAL;
2723  }
2724  }
2725  return;
2726  }
2727  if (monster[i]._uniqtype != 0) {
2728  if (UniqMonst[monster[i]._uniqtype - 1].mUnqAttr & 2) {
2729  for (j = 0; j < nummonsters; j++) {
2730  m = monstactive[j];
2731  if (monster[m].leaderflag == 1 && monster[m].leader == i) {
2732  if (monster[i]._msquelch > monster[m]._msquelch) {
2733  monster[m]._lastx = monster[i]._mx;
2734  monster[m]._lasty = monster[i]._my;
2735  monster[m]._msquelch = monster[i]._msquelch - 1;
2736  }
2737  if (monster[m]._mAi == AI_GARG) {
2738  if (monster[m]._mFlags & MFLAG_ALLOW_SPECIAL) {
2739  monster[m]._mmode = MM_SATTACK;
2741  }
2742  }
2743  }
2744  }
2745  }
2746  }
2747 }
2748 
2749 BOOL M_CallWalk(int i, int md)
2750 {
2751  int mdtemp;
2752  BOOL ok;
2753 
2754  mdtemp = md;
2755  ok = DirOK(i, md);
2756  if (random_(101, 2))
2757  ok = ok || (md = left[mdtemp], DirOK(i, md)) || (md = right[mdtemp], DirOK(i, md));
2758  else
2759  ok = ok || (md = right[mdtemp], DirOK(i, md)) || (md = left[mdtemp], DirOK(i, md));
2760  if (random_(102, 2))
2761  ok = ok
2762  || (md = right[right[mdtemp]], DirOK(i, md))
2763  || (md = left[left[mdtemp]], DirOK(i, md));
2764  else
2765  ok = ok
2766  || (md = left[left[mdtemp]], DirOK(i, md))
2767  || (md = right[right[mdtemp]], DirOK(i, md));
2768  if (ok)
2769  M_WalkDir(i, md);
2770  return ok;
2771 }
2772 
2773 BOOL M_PathWalk(int i)
2774 {
2775  char path[25];
2776  BOOL(*Check)
2777  (int, int, int);
2778 
2779  if ((DWORD)i >= MAXMONSTERS)
2780  app_fatal("M_PathWalk: Invalid monster %d", i);
2781 
2782  Check = PosOkMonst3;
2783  if (!(monster[i]._mFlags & MFLAG_CAN_OPEN_DOOR))
2784  Check = PosOkMonst;
2785 
2786  if (FindPath(Check, i, monster[i]._mx, monster[i]._my, monster[i]._menemyx, monster[i]._menemyy, path)) {
2787  M_CallWalk(i, plr2monst[path[0]]); /* plr2monst is local */
2788  return TRUE;
2789  }
2790 
2791  return FALSE;
2792 }
2793 
2794 BOOL M_CallWalk2(int i, int md)
2795 {
2796  BOOL ok;
2797  int mdtemp;
2798 
2799  mdtemp = md;
2800  ok = DirOK(i, md); // Can we continue in the same direction
2801  if (random_(101, 2)) { // Randomly go left or right
2802  ok = ok || (mdtemp = left[md], DirOK(i, left[md])) || (mdtemp = right[md], DirOK(i, right[md]));
2803  } else {
2804  ok = ok || (mdtemp = right[md], DirOK(i, right[md])) || (mdtemp = left[md], DirOK(i, left[md]));
2805  }
2806 
2807  if (ok)
2808  M_WalkDir(i, mdtemp);
2809 
2810  return ok;
2811 }
2812 
2813 BOOL M_DumbWalk(int i, int md)
2814 {
2815  BOOL ok;
2816  ok = DirOK(i, md);
2817  if (ok)
2818  M_WalkDir(i, md);
2819 
2820  return ok;
2821 }
2822 
2823 BOOL M_RoundWalk(int i, int md, int *dir)
2824 {
2825  int mdtemp;
2826  BOOL ok;
2827  if (*dir)
2828  md = left[left[md]];
2829  else
2830  md = right[right[md]];
2831 
2832  ok = DirOK(i, md);
2833  mdtemp = md;
2834  if (!ok) {
2835  if (*dir) {
2836  md = right[mdtemp];
2837  ok = DirOK(i, md) || (md = right[right[mdtemp]], DirOK(i, md));
2838  } else {
2839  md = left[mdtemp];
2840  ok = (DirOK(i, md) || (md = left[left[mdtemp]], DirOK(i, md)));
2841  }
2842  }
2843  if (ok) {
2844  M_WalkDir(i, md);
2845  } else {
2846  *dir = !*dir;
2847  ok = M_CallWalk(i, opposite[mdtemp]);
2848  }
2849  return ok;
2850 }
2851 
2852 void MAI_Zombie(int i)
2853 {
2854  MonsterStruct *Monst;
2855  int mx, my;
2856  int md, v;
2857 
2858  if ((DWORD)i >= MAXMONSTERS) {
2859  app_fatal("MAI_Zombie: Invalid monster %d", i);
2860  }
2861 
2862  Monst = &monster[i];
2863  if (Monst->_mmode != MM_STAND) {
2864  return;
2865  }
2866 
2867  mx = Monst->_mx;
2868  my = Monst->_my;
2869  if (!(dFlags[mx][my] & BFLAG_VISIBLE)) {
2870  return;
2871  }
2872 
2873  mx = mx - Monst->_menemyx;
2874  my = my - Monst->_menemyy;
2875  md = Monst->_mdir;
2876  v = random_(103, 100);
2877  if (abs(mx) >= 2 || abs(my) >= 2) {
2878  if (v < 2 * Monst->_mint + 10) {
2879  if (abs(mx) >= 2 * Monst->_mint + 4 || abs(my) >= 2 * Monst->_mint + 4) {
2880  if (random_(104, 100) < 2 * Monst->_mint + 20) {
2881  md = random_(104, 8);
2882  }
2883  M_DumbWalk(i, md);
2884  } else {
2885  md = M_GetDir(i);
2886  M_CallWalk(i, md);
2887  }
2888  }
2889  } else if (v < 2 * Monst->_mint + 10) {
2890  M_StartAttack(i);
2891  }
2892 
2893  if (Monst->_mmode == MM_STAND)
2894  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[Monst->_mdir];
2895 }
2896 
2897 void MAI_SkelSd(int i)
2898 {
2899  MonsterStruct *Monst;
2900  int mx, my, x, y, md;
2901 
2902  if ((DWORD)i >= MAXMONSTERS)
2903  app_fatal("MAI_SkelSd: Invalid monster %d", i);
2904 
2905  Monst = &monster[i];
2906  if (Monst->_mmode != MM_STAND || Monst->_msquelch == 0) {
2907  return;
2908  }
2909 
2910  mx = Monst->_mx;
2911  my = Monst->_my;
2912  x = mx - Monst->_menemyx;
2913  y = my - Monst->_menemyy;
2914  md = GetDirection(mx, my, Monst->_lastx, Monst->_lasty);
2915  Monst->_mdir = md;
2916  if (abs(x) >= 2 || abs(y) >= 2) {
2917  if (Monst->_mVar1 == MM_DELAY || (random_(106, 100) >= 35 - 4 * Monst->_mint)) {
2918  M_CallWalk(i, md);
2919  } else {
2920  M_StartDelay(i, 15 - 2 * Monst->_mint + random_(106, 10));
2921  }
2922  } else {
2923  if (Monst->_mVar1 == MM_DELAY || (random_(105, 100) < 2 * Monst->_mint + 20)) {
2924  M_StartAttack(i);
2925  } else {
2926  M_StartDelay(i, 2 * (5 - Monst->_mint) + random_(105, 10));
2927  }
2928  }
2929 
2930  if (Monst->_mmode == MM_STAND)
2931  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
2932 }
2933 
2934 BOOL MAI_Path(int i)
2935 {
2936  MonsterStruct *Monst;
2937  BOOL clear;
2938 
2939  if ((DWORD)i >= MAXMONSTERS)
2940  app_fatal("MAI_Path: Invalid monster %d", i);
2941 
2942  Monst = &monster[i];
2943  if (Monst->MType->mtype != MT_GOLEM) {
2944  if (Monst->_msquelch == 0)
2945  return FALSE;
2946  if (Monst->_mmode != MM_STAND)
2947  return FALSE;
2948  if (Monst->_mgoal != MGOAL_NORMAL && Monst->_mgoal != MGOAL_MOVE && Monst->_mgoal != MGOAL_SHOOT)
2949  return FALSE;
2950  if (Monst->_mx == 1 && Monst->_my == 0)
2951  return FALSE;
2952  }
2953 
2954  clear = LineClearF1(
2955  PosOkMonst2,
2956  i,
2957  Monst->_mx,
2958  Monst->_my,
2959  Monst->_menemyx,
2960  Monst->_menemyy);
2961  if (!clear || Monst->_pathcount >= 5 && Monst->_pathcount < 8) {
2962  if (Monst->_mFlags & MFLAG_CAN_OPEN_DOOR)
2963  MonstCheckDoors(i);
2964  Monst->_pathcount++;
2965  if (Monst->_pathcount < 5)
2966  return FALSE;
2967  if (M_PathWalk(i))
2968  return TRUE;
2969  }
2970 
2971  if (Monst->MType->mtype != MT_GOLEM)
2972  Monst->_pathcount = 0;
2973 
2974  return FALSE;
2975 }
2976 
2977 void MAI_Snake(int i)
2978 {
2979  MonsterStruct *Monst;
2980  int fx, fy, mx, my, md;
2981  int pnum;
2982  int tmp;
2983 
2984  if ((DWORD)i >= MAXMONSTERS) {
2985  app_fatal("MAI_Snake: Invalid monster %d", i);
2986  }
2987  char pattern[6] = { 1, 1, 0, -1, -1, 0 };
2988  Monst = monster + i;
2989  pnum = Monst->_menemy;
2990  if (Monst->_mmode != MM_STAND || Monst->_msquelch == 0)
2991  return;
2992  fx = Monst->_menemyx;
2993  fy = Monst->_menemyy;
2994  mx = Monst->_mx - fx;
2995  my = Monst->_my - fy;
2996  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
2997  Monst->_mdir = md;
2998  if (abs(mx) >= 2 || abs(my) >= 2) {
2999  if (abs(mx) < 3 && abs(my) < 3 && LineClearF1(PosOkMonst, i, Monst->_mx, Monst->_my, fx, fy) && Monst->_mVar1 != MM_CHARGE) {
3000  if (AddMissile(Monst->_mx, Monst->_my, fx, fy, md, MIS_RHINO, pnum, i, 0, 0) != -1) {
3001  PlayEffect(i, 0);
3002  dMonster[Monst->_mx][Monst->_my] = -(i + 1);
3003  Monst->_mmode = MM_CHARGE;
3004  }
3005  } else if (Monst->_mVar1 == MM_DELAY || random_(106, 100) >= 35 - 2 * Monst->_mint) {
3006  if (md + pattern[Monst->_mgoalvar1] < 0) {
3007  tmp = md + pattern[Monst->_mgoalvar1] + 8;
3008  } else {
3009  tmp = md + pattern[Monst->_mgoalvar1] - 8;
3010  if (md + pattern[Monst->_mgoalvar1] < 8)
3011  tmp = md + pattern[Monst->_mgoalvar1];
3012  }
3013  Monst->_mgoalvar1++;
3014  if (Monst->_mgoalvar1 > 5)
3015  Monst->_mgoalvar1 = 0;
3016  if (tmp - Monst->_mgoalvar2 < 0) {
3017  md = tmp - Monst->_mgoalvar2 + 8;
3018  } else if (tmp - Monst->_mgoalvar2 >= 8) {
3019  md = tmp - Monst->_mgoalvar2 - 8;
3020  } else
3021  md = tmp - Monst->_mgoalvar2;
3022  if (md > 0) {
3023  if (md < 4) {
3024  if (Monst->_mgoalvar2 + 1 < 0) {
3025  md = Monst->_mgoalvar2 + 9;
3026  } else if (Monst->_mgoalvar2 + 1 >= 8) {
3027  md = Monst->_mgoalvar2 - 7;
3028  } else
3029  md = Monst->_mgoalvar2 + 1;
3030  Monst->_mgoalvar2 = md;
3031  } else if (md == 4) {
3032  Monst->_mgoalvar2 = tmp;
3033  } else {
3034  if (Monst->_mgoalvar2 - 1 < 0) {
3035  md = Monst->_mgoalvar2 + 7;
3036  } else if (Monst->_mgoalvar2 - 1 >= 8) {
3037  md = Monst->_mgoalvar2 - 9;
3038  } else
3039  md = Monst->_mgoalvar2 - 1;
3040  Monst->_mgoalvar2 = md;
3041  }
3042  }
3043  if (!M_DumbWalk(i, Monst->_mgoalvar2))
3044  M_CallWalk2(i, Monst->_mdir);
3045  } else {
3046  M_StartDelay(i, 15 - Monst->_mint + random_(106, 10));
3047  }
3048  } else {
3049  if (Monst->_mVar1 == MM_DELAY
3050  || Monst->_mVar1 == MM_CHARGE
3051  || (random_(105, 100) < Monst->_mint + 20)) {
3052  M_StartAttack(i);
3053  } else
3054  M_StartDelay(i, 10 - Monst->_mint + random_(105, 10));
3055  }
3056  if (Monst->_mmode == MM_STAND)
3057  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[Monst->_mdir];
3058 }
3059 
3060 void MAI_Bat(int i)
3061 {
3062  MonsterStruct *Monst;
3063  int md, v, pnum;
3064  int fx, fy, xd, yd;
3065 
3066  if ((DWORD)i >= MAXMONSTERS)
3067  app_fatal("MAI_Bat: Invalid monster %d", i);
3068 
3069  Monst = &monster[i];
3070  pnum = Monst->_menemy;
3071  if (Monst->_mmode != MM_STAND || Monst->_msquelch == 0) {
3072  return;
3073  }
3074 
3075  xd = Monst->_mx - Monst->_menemyx;
3076  yd = Monst->_my - Monst->_menemyy;
3077  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
3078  Monst->_mdir = md;
3079  v = random_(107, 100);
3080  if (Monst->_mgoal == MGOAL_RETREAT) {
3081  if (!Monst->_mgoalvar1) {
3082  M_CallWalk(i, opposite[md]);
3083  Monst->_mgoalvar1++;
3084  } else {
3085  if (random_(108, 2))
3086  M_CallWalk(i, left[md]);
3087  else
3088  M_CallWalk(i, right[md]);
3089  Monst->_mgoal = MGOAL_NORMAL;
3090  }
3091  return;
3092  }
3093 
3094  fx = Monst->_menemyx;
3095  fy = Monst->_menemyy;
3096  if (Monst->MType->mtype == MT_GLOOM
3097  && (abs(xd) >= 5 || abs(yd) >= 5)
3098  && v < 4 * Monst->_mint + 33
3099  && LineClearF1(PosOkMonst, i, Monst->_mx, Monst->_my, fx, fy)) {
3100  if (AddMissile(Monst->_mx, Monst->_my, fx, fy, md, MIS_RHINO, pnum, i, 0, 0) != -1) {
3101  dMonster[Monst->_mx][Monst->_my] = -(i + 1);
3102  Monst->_mmode = MM_CHARGE;
3103  }
3104  } else if (abs(xd) >= 2 || abs(yd) >= 2) {
3105  if (Monst->_mVar2 > 20 && v < Monst->_mint + 13
3106  || (Monst->_mVar1 == MM_WALK || Monst->_mVar1 == MM_WALK2 || Monst->_mVar1 == MM_WALK3)
3107  && Monst->_mVar2 == 0
3108  && v < Monst->_mint + 63) {
3109  M_CallWalk(i, md);
3110  }
3111  } else if (v < 4 * Monst->_mint + 8) {
3112  M_StartAttack(i);
3113  Monst->_mgoal = MGOAL_RETREAT;
3114  Monst->_mgoalvar1 = 0;
3115  if (Monst->MType->mtype == MT_FAMILIAR) {
3116  AddMissile(Monst->_menemyx, Monst->_menemyy, Monst->_menemyx + 1, 0, -1, MIS_LIGHTNING, 1, i, random_(109, 10) + 1, 0);
3117  }
3118  }
3119 
3120  if (Monst->_mmode == MM_STAND)
3121  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3122 }
3123 
3124 void MAI_SkelBow(int i)
3125 {
3126  MonsterStruct *Monst;
3127  int mx, my, md, v;
3128  BOOL walking;
3129 
3130  walking = FALSE;
3131  if ((DWORD)i >= MAXMONSTERS)
3132  app_fatal("MAI_SkelBow: Invalid monster %d", i);
3133 
3134  Monst = &monster[i];
3135  if (Monst->_mmode != MM_STAND || Monst->_msquelch == 0) {
3136  return;
3137  }
3138 
3139  mx = Monst->_mx - Monst->_menemyx;
3140  my = Monst->_my - Monst->_menemyy;
3141 
3142  md = M_GetDir(i);
3143  Monst->_mdir = md;
3144  v = random_(110, 100);
3145 
3146  if (abs(mx) < 4 && abs(my) < 4) {
3147  if (Monst->_mVar2 > 20 && v < 2 * Monst->_mint + 13
3148  || (Monst->_mVar1 == MM_WALK || Monst->_mVar1 == MM_WALK2 || Monst->_mVar1 == MM_WALK3)
3149  && Monst->_mVar2 == 0
3150  && v < 2 * Monst->_mint + 63) {
3151  walking = M_DumbWalk(i, opposite[md]);
3152  }
3153  }
3154 
3155  mx = Monst->_menemyx;
3156  my = Monst->_menemyy;
3157  if (!walking) {
3158  if (random_(110, 100) < 2 * Monst->_mint + 3) {
3159  if (LineClear(Monst->_mx, Monst->_my, mx, my))
3160  M_StartRAttack(i, MIS_ARROW, 4);
3161  }
3162  }
3163 
3164  if (Monst->_mmode == MM_STAND)
3165  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3166 }
3167 
3168 void MAI_Fat(int i)
3169 {
3170  MonsterStruct *Monst;
3171  int mx, my, md, v;
3172 
3173  if ((DWORD)i >= MAXMONSTERS)
3174  app_fatal("MAI_Fat: Invalid monster %d", i);
3175 
3176  Monst = &monster[i];
3177  if (Monst->_mmode != MM_STAND || Monst->_msquelch == 0) {
3178  return;
3179  }
3180 
3181  mx = Monst->_mx - Monst->_menemyx;
3182  my = Monst->_my - Monst->_menemyy;
3183  md = M_GetDir(i);
3184  Monst->_mdir = md;
3185  v = random_(111, 100);
3186  if (abs(mx) >= 2 || abs(my) >= 2) {
3187  if (Monst->_mVar2 > 20 && v < 4 * Monst->_mint + 20
3188  || (Monst->_mVar1 == MM_WALK || Monst->_mVar1 == MM_WALK2 || Monst->_mVar1 == MM_WALK3)
3189  && Monst->_mVar2 == 0
3190  && v < 4 * Monst->_mint + 70) {
3191  M_CallWalk(i, md);
3192  }
3193  } else if (v < 4 * Monst->_mint + 15) {
3194  M_StartAttack(i);
3195  } else if (v < 4 * Monst->_mint + 20) {
3196  M_StartSpAttack(i);
3197  }
3198 
3199  if (Monst->_mmode == MM_STAND)
3200  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3201 }
3202 
3203 void MAI_Sneak(int i)
3204 {
3205  MonsterStruct *Monst;
3206  int mx, my, md;
3207  int dist, v;
3208 
3209  if ((DWORD)i >= MAXMONSTERS) {
3210  app_fatal("MAI_Sneak: Invalid monster %d", i);
3211  }
3212 
3213  Monst = monster + i;
3214  if (Monst->_mmode == MM_STAND) {
3215  mx = Monst->_mx;
3216  my = Monst->_my;
3217  if (dLight[mx][my] != lightmax) {
3218  mx -= Monst->_menemyx;
3219  my -= Monst->_menemyy;
3220 
3221  md = M_GetDir(i);
3222  dist = 5 - Monst->_mint;
3223  if (Monst->_mVar1 == MM_GOTHIT) {
3224  Monst->_mgoalvar1 = 0;
3225  Monst->_mgoal = MGOAL_RETREAT;
3226  } else {
3227  if (abs(mx) >= dist + 3 || abs(my) >= dist + 3 || Monst->_mgoalvar1 > 8) {
3228  Monst->_mgoalvar1 = 0;
3229  Monst->_mgoal = MGOAL_NORMAL;
3230  }
3231  }
3232  if (Monst->_mgoal == MGOAL_RETREAT) {
3233  if (Monst->_mFlags & MFLAG_TARGETS_MONSTER)
3234  md = GetDirection(Monst->_mx, Monst->_my, plr[Monst->_menemy]._pownerx, plr[Monst->_menemy]._pownery);
3235  md = opposite[md];
3236  if (Monst->MType->mtype == MT_UNSEEN) {
3237  if (random_(112, 2))
3238  md = left[md];
3239  else
3240  md = right[md];
3241  }
3242  }
3243  Monst->_mdir = md;
3244  v = random_(112, 100);
3245  if (abs(mx) < dist && abs(my) < dist && Monst->_mFlags & MFLAG_HIDDEN) {
3246  M_StartFadein(i, md, FALSE);
3247  } else {
3248  if ((abs(mx) >= dist + 1 || abs(my) >= dist + 1) && !(Monst->_mFlags & MFLAG_HIDDEN)) {
3249  M_StartFadeout(i, md, TRUE);
3250  } else {
3251  if (Monst->_mgoal == MGOAL_RETREAT
3252  || (abs(mx) >= 2 || abs(my) >= 2) && (Monst->_mVar2 > 20 && v < 4 * Monst->_mint + 14 || (Monst->_mVar1 == MM_WALK || Monst->_mVar1 == MM_WALK2 || Monst->_mVar1 == MM_WALK3) && Monst->_mVar2 == 0 && v < 4 * Monst->_mint + 64)) {
3253  Monst->_mgoalvar1++;
3254  M_CallWalk(i, md);
3255  }
3256  }
3257  }
3258  if (Monst->_mmode == MM_STAND) {
3259  if (abs(mx) >= 2 || abs(my) >= 2 || v >= 4 * Monst->_mint + 10)
3260  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3261  else
3262  M_StartAttack(i);
3263  }
3264  }
3265  }
3266 }
3267 
3268 void MAI_Fireman(int i)
3269 {
3270  int xd, yd;
3271  int md, pnum;
3272  int fx, fy;
3273  MonsterStruct *Monst;
3274 
3275  if ((DWORD)i >= MAXMONSTERS)
3276  app_fatal("MAI_Fireman: Invalid monster %d", i);
3277 
3278  Monst = &monster[i];
3279  if (monster[i]._mmode != MM_STAND || Monst->_msquelch == 0)
3280  return;
3281 
3282  pnum = monster[i]._menemy;
3283  fx = monster[i]._menemyx;
3284  fy = monster[i]._menemyy;
3285  xd = monster[i]._mx - fx;
3286  yd = monster[i]._my - fy;
3287 
3288  md = M_GetDir(i);
3289  if (Monst->_mgoal == MGOAL_NORMAL) {
3290  if (LineClear(Monst->_mx, Monst->_my, fx, fy)
3291  && AddMissile(Monst->_mx, Monst->_my, fx, fy, md, MIS_FIREMAN, pnum, i, 0, 0) != -1) {
3292  Monst->_mmode = MM_CHARGE;
3293  Monst->_mgoal = MGOAL_SHOOT;
3294  Monst->_mgoalvar1 = 0;
3295  }
3296  } else if (Monst->_mgoal == MGOAL_SHOOT) {
3297  if (Monst->_mgoalvar1 == 3) {
3298  Monst->_mgoal = MGOAL_NORMAL;
3299  M_StartFadeout(i, md, TRUE);
3300  } else if (LineClear(Monst->_mx, Monst->_my, fx, fy)) {
3301  M_StartRAttack(i, MIS_KRULL, 4);
3302  Monst->_mgoalvar1++;
3303  } else {
3304  M_StartDelay(i, random_(112, 10) + 5);
3305  Monst->_mgoalvar1++;
3306  }
3307  } else if (Monst->_mgoal == MGOAL_RETREAT) {
3308  M_StartFadein(i, md, FALSE);
3309  Monst->_mgoal = MGOAL_SHOOT;
3310  }
3311  Monst->_mdir = md;
3312  random_(112, 100);
3313  if (Monst->_mmode != MM_STAND)
3314  return;
3315 
3316  if (abs(xd) < 2 && abs(yd) < 2 && Monst->_mgoal == MGOAL_NORMAL) {
3317  M_TryH2HHit(i, monster[i]._menemy, monster[i].mHit, monster[i].mMinDamage, monster[i].mMaxDamage);
3318  Monst->_mgoal = MGOAL_RETREAT;
3319  if (!M_CallWalk(i, opposite[md])) {
3320  M_StartFadein(i, md, FALSE);
3321  Monst->_mgoal = MGOAL_SHOOT;
3322  }
3323  } else if (!M_CallWalk(i, md) && (Monst->_mgoal == MGOAL_NORMAL || Monst->_mgoal == MGOAL_RETREAT)) {
3324  M_StartFadein(i, md, FALSE);
3325  Monst->_mgoal = MGOAL_SHOOT;
3326  }
3327 }
3328 
3329 void MAI_Fallen(int i)
3330 {
3331  int x, y, xpos, ypos;
3332  int m, rad, md;
3333  MonsterStruct *Monst;
3334 
3335  if ((DWORD)i >= MAXMONSTERS) {
3336  app_fatal("MAI_Fallen: Invalid monster %d", i);
3337  }
3338  if (monster[i]._mgoal == MGOAL_SHOOT) {
3339  if (monster[i]._mgoalvar1)
3340  monster[i]._mgoalvar1--;
3341  else
3343  }
3344 
3345  Monst = &monster[i];
3346  if (monster[i]._mmode != MM_STAND || monster[i]._msquelch == 0) {
3347  return;
3348  }
3349 
3350  if (Monst->_mgoal == MGOAL_RETREAT) {
3351  if (!Monst->_mgoalvar1--) {
3352  Monst->_mgoal = MGOAL_NORMAL;
3353  M_StartStand(i, opposite[Monst->_mdir]);
3354  }
3355  }
3356 
3357  if (Monst->_mAnimFrame == Monst->_mAnimLen) {
3358  if (random_(113, 4)) {
3359  return;
3360  }
3361  if (!(Monst->_mFlags & MFLAG_NOHEAL)) {
3362  M_StartSpStand(i, Monst->_mdir);
3363  rad = 2 * Monst->_mint + 2;
3364  if (Monst->_mmaxhp - rad >= Monst->_mhitpoints)
3365  Monst->_mhitpoints = rad + Monst->_mhitpoints;
3366  else
3367  Monst->_mhitpoints = Monst->_mmaxhp;
3368  }
3369  rad = 2 * Monst->_mint + 4;
3370  for (y = -rad; y <= rad; y++) {
3371  for (x = -rad; x <= rad; x++) {
3372  if (y >= 0 && y < MAXDUNY && x >= 0 && x < MAXDUNX) {
3373  m = dMonster[x + Monst->_mx][y + Monst->_my];
3374  if (m > 0) {
3375  m--;
3376  if (monster[m]._mAi == AI_FALLEN) {
3377  monster[m]._mgoal = MGOAL_SHOOT;
3378  monster[m]._mgoalvar1 = 30 * Monst->_mint + 105;
3379  }
3380  }
3381  }
3382  }
3383  }
3384  } else if (Monst->_mgoal == MGOAL_RETREAT) {
3385  md = Monst->_mdir;
3386  M_CallWalk(i, md);
3387  } else if (Monst->_mgoal == MGOAL_SHOOT) {
3388  xpos = Monst->_mx - Monst->_menemyx;
3389  ypos = Monst->_my - Monst->_menemyy;
3390  if (abs(xpos) < 2 && abs(ypos) < 2) {
3391  M_StartAttack(i);
3392  } else {
3393  md = M_GetDir(i);
3394  M_CallWalk(i, md);
3395  }
3396  } else {
3397  MAI_SkelSd(i);
3398  }
3399 }
3400 
3401 void MAI_Cleaver(int i)
3402 {
3403  MonsterStruct *Monst;
3404  int x, y, mx, my, md;
3405 
3406  if ((DWORD)i >= MAXMONSTERS)
3407  app_fatal("MAI_Cleaver: Invalid monster %d", i);
3408 
3409  Monst = &monster[i];
3410  if (Monst->_mmode != MM_STAND || Monst->_msquelch == 0) {
3411  return;
3412  }
3413 
3414  mx = Monst->_mx;
3415  my = Monst->_my;
3416  x = mx - Monst->_menemyx;
3417  y = my - Monst->_menemyy;
3418 
3419  md = GetDirection(mx, my, Monst->_lastx, Monst->_lasty);
3420  Monst->_mdir = md;
3421 
3422  if (abs(x) >= 2 || abs(y) >= 2)
3423  M_CallWalk(i, md);
3424  else
3425  M_StartAttack(i);
3426 
3427  if (Monst->_mmode == MM_STAND)
3428  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3429 }
3430 
3431 void MAI_Round(int i, BOOL special)
3432 {
3433  MonsterStruct *Monst;
3434  int fx, fy;
3435  int mx, my, md;
3436  int dist, v;
3437 
3438  if ((DWORD)i >= MAXMONSTERS)
3439  app_fatal("MAI_Round: Invalid monster %d", i);
3440  Monst = monster + i;
3441  if (Monst->_mmode == MM_STAND && Monst->_msquelch != 0) {
3442  fy = Monst->_menemyy;
3443  fx = Monst->_menemyx;
3444  mx = Monst->_mx - fx;
3445  my = Monst->_my - fy;
3446  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
3447  if (Monst->_msquelch < UCHAR_MAX)
3448  MonstCheckDoors(i);
3449  v = random_(114, 100);
3450  if ((abs(mx) >= 2 || abs(my) >= 2) && Monst->_msquelch == UCHAR_MAX && dTransVal[Monst->_mx][Monst->_my] == dTransVal[fx][fy]) {
3451  if (Monst->_mgoal == MGOAL_MOVE || (abs(mx) >= 4 || abs(my) >= 4) && random_(115, 4) == 0) {
3452  if (Monst->_mgoal != MGOAL_MOVE) {
3453  Monst->_mgoalvar1 = 0;
3454  Monst->_mgoalvar2 = random_(116, 2);
3455  }
3456  Monst->_mgoal = MGOAL_MOVE;
3457  if (abs(mx) > abs(my))
3458  dist = abs(mx);
3459  else
3460  dist = abs(my);
3461  if (Monst->_mgoalvar1++ >= 2 * dist && DirOK(i, md) || dTransVal[Monst->_mx][Monst->_my] != dTransVal[fx][fy]) {
3462  Monst->_mgoal = MGOAL_NORMAL;
3463  } else if (!M_RoundWalk(i, md, &Monst->_mgoalvar2)) {
3464  M_StartDelay(i, random_(125, 10) + 10);
3465  }
3466  }
3467  } else
3468  Monst->_mgoal = MGOAL_NORMAL;
3469  if (Monst->_mgoal == MGOAL_NORMAL) {
3470  if (abs(mx) >= 2 || abs(my) >= 2) {
3471  if (Monst->_mVar2 > 20 && v < 2 * Monst->_mint + 28
3472  || (Monst->_mVar1 == MM_WALK || Monst->_mVar1 == MM_WALK2 || Monst->_mVar1 == MM_WALK3)
3473  && Monst->_mVar2 == 0
3474  && v < 2 * Monst->_mint + 78) {
3475  M_CallWalk(i, md);
3476  }
3477  } else if (v < 2 * Monst->_mint + 23) {
3478  Monst->_mdir = md;
3479  if (special && Monst->_mhitpoints < (Monst->_mmaxhp >> 1) && random_(117, 2) != 0)
3480  M_StartSpAttack(i);
3481  else
3482  M_StartAttack(i);
3483  }
3484  }
3485  if (Monst->_mmode == MM_STAND)
3486  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3487  }
3488 }
3489 
3490 void MAI_GoatMc(int i)
3491 {
3492  MAI_Round(i, TRUE);
3493 }
3494 
3495 void MAI_Ranged(int i, int missile_type, BOOL special)
3496 {
3497  int md;
3498  int fx, fy, mx, my;
3499  MonsterStruct *Monst;
3500 
3501  if ((DWORD)i >= MAXMONSTERS)
3502  app_fatal("MAI_Ranged: Invalid monster %d", i);
3503 
3504  if (monster[i]._mmode != MM_STAND) {
3505  return;
3506  }
3507 
3508  Monst = monster + i;
3509  if (Monst->_msquelch == UCHAR_MAX || Monst->_mFlags & MFLAG_TARGETS_MONSTER) {
3510  fx = Monst->_menemyx;
3511  fy = Monst->_menemyy;
3512  mx = Monst->_mx - fx;
3513  my = Monst->_my - fy;
3514  md = M_GetDir(i);
3515  if (Monst->_msquelch < UCHAR_MAX)
3516  MonstCheckDoors(i);
3517  Monst->_mdir = md;
3518  if (Monst->_mVar1 == MM_RATTACK) {
3519  M_StartDelay(i, random_(118, 20));
3520  } else if (abs(mx) < 4 && abs(my) < 4) {
3521  if (random_(119, 100) < 10 * (Monst->_mint + 7))
3522  M_CallWalk(i, opposite[md]);
3523  }
3524  if (Monst->_mmode == MM_STAND) {
3525  if (LineClear(Monst->_mx, Monst->_my, fx, fy)) {
3526  if (special)
3527  M_StartRSpAttack(i, missile_type, 4);
3528  else
3529  M_StartRAttack(i, missile_type, 4);
3530  } else {
3531  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3532  }
3533  }
3534  } else if (Monst->_msquelch != 0) {
3535  fx = Monst->_lastx;
3536  fy = Monst->_lasty;
3537  md = GetDirection(Monst->_mx, Monst->_my, fx, fy);
3538  M_CallWalk(i, md);
3539  }
3540 }
3541 
3542 void MAI_GoatBow(int i)
3543 {
3544  MAI_Ranged(i, MIS_ARROW, FALSE);
3545 }
3546 
3547 void MAI_Succ(int i)
3548 {
3549  MAI_Ranged(i, MIS_FLARE, FALSE);
3550 }
3551 
3552 void MAI_AcidUniq(int i)
3553 {
3554  MAI_Ranged(i, MIS_ACID, TRUE);
3555 }
3556 
3557 void MAI_Scav(int i)
3558 {
3559  BOOL done;
3560  int x, y;
3561  int _mx, _my;
3562  MonsterStruct *Monst;
3563 
3564  if ((DWORD)i >= MAXMONSTERS)
3565  app_fatal("MAI_Scav: Invalid monster %d", i);
3566  Monst = monster + i;
3567  _mx = Monst->_mx;
3568  _my = Monst->_my;
3569  done = FALSE;
3570  if (monster[i]._mmode != MM_STAND)
3571  return;
3572  if (Monst->_mhitpoints < (Monst->_mmaxhp >> 1) && Monst->_mgoal != MGOAL_HEALING) {
3573  if (Monst->leaderflag) {
3574  monster[Monst->leader].packsize--;
3575  Monst->leaderflag = 0;
3576  }
3577  Monst->_mgoal = MGOAL_HEALING;
3578  Monst->_mgoalvar3 = 10;
3579  }
3580  if (Monst->_mgoal == MGOAL_HEALING && Monst->_mgoalvar3 != 0) {
3581  Monst->_mgoalvar3--;
3582  if (dDead[Monst->_mx][Monst->_my]) {
3583  M_StartEat(i);
3584  if (!(Monst->_mFlags & MFLAG_NOHEAL))
3585  Monst->_mhitpoints += 64;
3586  if (Monst->_mhitpoints >= (Monst->_mmaxhp >> 1) + (Monst->_mmaxhp >> 2)) {
3587  Monst->_mgoal = MGOAL_NORMAL;
3588  Monst->_mgoalvar1 = 0;
3589  Monst->_mgoalvar2 = 0;
3590  }
3591  } else {
3592  if (Monst->_mgoalvar1 == 0) {
3593  if (random_(120, 2) != 0) {
3594  for (y = -4; y <= 4 && !done; y++) {
3595  for (x = -4; x <= 4 && !done; x++) {
3596  // BUGFIX: incorrect check of offset against limits of the dungeon
3597  if (y < 0 || y >= MAXDUNY || x < 0 || x >= MAXDUNX)
3598  continue;
3599  done = dDead[Monst->_mx + x][Monst->_my + y] != 0
3600  && LineClearF(
3601  CheckNoSolid,
3602  Monst->_mx,
3603  Monst->_my,
3604  Monst->_mx + x,
3605  Monst->_my + y);
3606  }
3607  }
3608  x--;
3609  y--;
3610  } else {
3611  for (y = 4; y >= -4 && !done; y--) {
3612  for (x = 4; x >= -4 && !done; x--) {
3613  // BUGFIX: incorrect check of offset against limits of the dungeon
3614  if (y < 0 || y >= MAXDUNY || x < 0 || x >= MAXDUNX)
3615  continue;
3616  done = dDead[Monst->_mx + x][Monst->_my + y] != 0
3617  && LineClearF(
3618  CheckNoSolid,
3619  Monst->_mx,
3620  Monst->_my,
3621  Monst->_mx + x,
3622  Monst->_my + y);
3623  }
3624  }
3625  x++;
3626  y++;
3627  }
3628  if (done) {
3629  Monst->_mgoalvar1 = x + Monst->_mx + 1;
3630  Monst->_mgoalvar2 = y + Monst->_my + 1;
3631  }
3632  }
3633  if (Monst->_mgoalvar1) {
3634  x = Monst->_mgoalvar1 - 1;
3635  y = Monst->_mgoalvar2 - 1;
3636  Monst->_mdir = GetDirection(Monst->_mx, Monst->_my, x, y);
3637  M_CallWalk(i, Monst->_mdir);
3638  }
3639  }
3640  }
3641  if (Monst->_mmode == MM_STAND)
3642  MAI_SkelSd(i);
3643 }
3644 
3645 void MAI_Garg(int i)
3646 {
3647  MonsterStruct *Monst;
3648  int mx, my, dx, dy, md;
3649 
3650  if ((DWORD)i >= MAXMONSTERS)
3651  app_fatal("MAI_Garg: Invalid monster %d", i);
3652 
3653  Monst = &monster[i];
3654  dx = Monst->_mx - Monst->_lastx;
3655  dy = Monst->_my - Monst->_lasty;
3656  md = M_GetDir(i);
3657  if (Monst->_msquelch != 0 && Monst->_mFlags & MFLAG_ALLOW_SPECIAL) {
3658  M_Enemy(i);
3659  mx = Monst->_mx - Monst->_menemyx;
3660  my = Monst->_my - Monst->_menemyy;
3661  if (abs(mx) < Monst->_mint + 2 && abs(my) < Monst->_mint + 2) {
3662  Monst->_mFlags &= ~MFLAG_ALLOW_SPECIAL;
3663  }
3664  return;
3665  }
3666 
3667  if (Monst->_mmode != MM_STAND || Monst->_msquelch == 0) {
3668  return;
3669  }
3670 
3671  if (Monst->_mhitpoints<Monst->_mmaxhp>> 1 && !(Monst->_mFlags & MFLAG_NOHEAL))
3672  Monst->_mgoal = MGOAL_RETREAT;
3673  if (Monst->_mgoal == MGOAL_RETREAT) {
3674  if (abs(dx) >= Monst->_mint + 2 || abs(dy) >= Monst->_mint + 2) {
3675  Monst->_mgoal = MGOAL_NORMAL;
3676  M_StartHeal(i);
3677  } else if (!M_CallWalk(i, opposite[md])) {
3678  Monst->_mgoal = MGOAL_NORMAL;
3679  }
3680  }
3681  MAI_Round(i, FALSE);
3682 }
3683 
3684 void MAI_RoundRanged(int i, int missile_type, BOOL checkdoors, int dam, int lessmissiles)
3685 {
3686  MonsterStruct *Monst;
3687  int mx, my;
3688  int fx, fy;
3689  int md, dist, v;
3690 
3691  if ((DWORD)i >= MAXMONSTERS)
3692  app_fatal("MAI_RoundRanged: Invalid monster %d", i);
3693  Monst = monster + i;
3694  if (Monst->_mmode == MM_STAND && Monst->_msquelch != 0) {
3695  fx = Monst->_menemyx;
3696  fy = Monst->_menemyy;
3697  mx = Monst->_mx - fx;
3698  my = Monst->_my - fy;
3699  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
3700  if (checkdoors && Monst->_msquelch < UCHAR_MAX)
3701  MonstCheckDoors(i);
3702  v = random_(121, 10000);
3703  if ((abs(mx) >= 2 || abs(my) >= 2) && Monst->_msquelch == UCHAR_MAX && dTransVal[Monst->_mx][Monst->_my] == dTransVal[fx][fy]) {
3704  if (Monst->_mgoal == MGOAL_MOVE || ((abs(mx) >= 3 || abs(my) >= 3) && random_(122, 4 << lessmissiles) == 0)) {
3705  if (Monst->_mgoal != MGOAL_MOVE) {
3706  Monst->_mgoalvar1 = 0;
3707  Monst->_mgoalvar2 = random_(123, 2);
3708  }
3709  Monst->_mgoal = MGOAL_MOVE;
3710  if (abs(mx) > abs(my)) {
3711  dist = abs(mx);
3712  } else {
3713  dist = abs(my);
3714  }
3715  if (Monst->_mgoalvar1++ >= 2 * dist && DirOK(i, md)) {
3716  Monst->_mgoal = MGOAL_NORMAL;
3717  } else if (v<500 * (Monst->_mint + 1)>> lessmissiles
3718  && (LineClear(Monst->_mx, Monst->_my, fx, fy))) {
3719  M_StartRSpAttack(i, missile_type, dam);
3720  } else {
3721  M_RoundWalk(i, md, &Monst->_mgoalvar2);
3722  }
3723  }
3724  } else {
3725  Monst->_mgoal = MGOAL_NORMAL;
3726  }
3727  if (Monst->_mgoal == MGOAL_NORMAL) {
3728  if (((abs(mx) >= 3 || abs(my) >= 3) && v < ((500 * (Monst->_mint + 2)) >> lessmissiles)
3729  || v < ((500 * (Monst->_mint + 1)) >> lessmissiles))
3730  && LineClear(Monst->_mx, Monst->_my, fx, fy)) {
3731  M_StartRSpAttack(i, missile_type, dam);
3732  } else if (abs(mx) >= 2 || abs(my) >= 2) {
3733  v = random_(124, 100);
3734  if (v < 1000 * (Monst->_mint + 5)
3735  || (Monst->_mVar1 == MM_WALK || Monst->_mVar1 == MM_WALK2 || Monst->_mVar1 == MM_WALK3) && Monst->_mVar2 == 0 && v < 1000 * (Monst->_mint + 8)) {
3736  M_CallWalk(i, md);
3737  }
3738  } else if (v < 1000 * (Monst->_mint + 6)) {
3739  Monst->_mdir = md;
3740  M_StartAttack(i);
3741  }
3742  }
3743  if (Monst->_mmode == MM_STAND) {
3744  M_StartDelay(i, random_(125, 10) + 5);
3745  }
3746  }
3747 }
3748 
3749 void MAI_Magma(int i)
3750 {
3751  MAI_RoundRanged(i, MIS_MAGMABALL, TRUE, 4, 0);
3752 }
3753 
3754 void MAI_Storm(int i)
3755 {
3756  MAI_RoundRanged(i, MIS_LIGHTCTRL2, TRUE, 4, 0);
3757 }
3758 
3759 void MAI_Acid(int i)
3760 {
3761  MAI_RoundRanged(i, MIS_ACID, FALSE, 4, 1);
3762 }
3763 
3764 void MAI_Diablo(int i)
3765 {
3766  MAI_RoundRanged(i, MIS_DIABAPOCA, FALSE, 40, 0);
3767 }
3768 
3769 void MAI_RR2(int i, int mistype, int dam)
3770 {
3771  MonsterStruct *Monst;
3772  int mx, my, fx, fy;
3773  int dist, v, md;
3774 
3775  if ((DWORD)i >= MAXMONSTERS)
3776  app_fatal("MAI_RR2: Invalid monster %d", i);
3777 
3778  Monst = monster + i;
3779  mx = Monst->_mx - Monst->_menemyx;
3780  my = Monst->_my - Monst->_menemyy;
3781  if (abs(mx) >= 5 || abs(my) >= 5) {
3782  MAI_SkelSd(i);
3783  return;
3784  }
3785 
3786  if (Monst->_mmode == MM_STAND && Monst->_msquelch != 0) {
3787  fx = Monst->_menemyx;
3788  fy = Monst->_menemyy;
3789  mx = Monst->_mx - fx;
3790  my = Monst->_my - fy;
3791  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
3792  if (Monst->_msquelch < UCHAR_MAX)
3793  MonstCheckDoors(i);
3794  v = random_(121, 100);
3795  if ((abs(mx) >= 2 || abs(my) >= 2) && Monst->_msquelch == UCHAR_MAX && dTransVal[Monst->_mx][Monst->_my] == dTransVal[fx][fy]) {
3796  if (Monst->_mgoal == MGOAL_MOVE || (abs(mx) >= 3 || abs(my) >= 3)) {
3797  if (Monst->_mgoal != MGOAL_MOVE) {
3798  Monst->_mgoalvar1 = 0;
3799  Monst->_mgoalvar2 = random_(123, 2);
3800  }
3801  Monst->_mgoal = MGOAL_MOVE;
3802  Monst->_mgoalvar3 = 4;
3803  if (abs(mx) > abs(my)) {
3804  dist = abs(mx);
3805  } else {
3806  dist = abs(my);
3807  }
3808  if (Monst->_mgoalvar1++ < 2 * dist || !DirOK(i, md)) {
3809  if (v < 5 * (Monst->_mint + 16))
3810  M_RoundWalk(i, md, &Monst->_mgoalvar2);
3811  } else
3812  Monst->_mgoal = MGOAL_NORMAL;
3813  }
3814  } else
3815  Monst->_mgoal = MGOAL_NORMAL;
3816  if (Monst->_mgoal == MGOAL_NORMAL) {
3817  if (((abs(mx) >= 3 || abs(my) >= 3) && v < 5 * (Monst->_mint + 2) || v < 5 * (Monst->_mint + 1) || Monst->_mgoalvar3 == 4) && LineClear(Monst->_mx, Monst->_my, fx, fy)) {
3818  M_StartRSpAttack(i, mistype, dam);
3819  } else if (abs(mx) >= 2 || abs(my) >= 2) {
3820  v = random_(124, 100);
3821  if (v < 2 * (5 * Monst->_mint + 25)
3822  || (Monst->_mVar1 == MM_WALK || Monst->_mVar1 == MM_WALK2 || Monst->_mVar1 == MM_WALK3)
3823  && Monst->_mVar2 == 0
3824  && v < 2 * (5 * Monst->_mint + 40)) {
3825  M_CallWalk(i, md);
3826  }
3827  } else {
3828  if (random_(124, 100) < 10 * (Monst->_mint + 4)) {
3829  Monst->_mdir = md;
3830  if (random_(124, 2) != 0)
3831  M_StartAttack(i);
3832  else
3833  M_StartRSpAttack(i, mistype, dam);
3834  }
3835  }
3836  Monst->_mgoalvar3 = 1;
3837  }
3838  if (Monst->_mmode == MM_STAND) {
3839  M_StartDelay(i, random_(125, 10) + 5);
3840  }
3841  }
3842 }
3843 
3844 void MAI_Mega(int i)
3845 {
3846  MAI_RR2(i, MIS_FLAMEC, 0);
3847 }
3848 
3849 void MAI_Golum(int i)
3850 {
3851  int mx, my, _mex, _mey;
3852  int md, j, k, _menemy;
3853  MonsterStruct *Monst;
3854  BOOL have_enemy, ok;
3855 
3856  if ((DWORD)i >= MAXMONSTERS)
3857  app_fatal("MAI_Golum: Invalid monster %d", i);
3858 
3859  Monst = &monster[i];
3860  if (Monst->_mx == 1 && Monst->_my == 0) {
3861  return;
3862  }
3863 
3864  if (Monst->_mmode == MM_DEATH
3865  || Monst->_mmode == MM_SPSTAND
3866  || (Monst->_mmode >= MM_WALK && Monst->_mmode <= MM_WALK3)) {
3867  return;
3868  }
3869 
3870  if (!(Monst->_mFlags & MFLAG_TARGETS_MONSTER))
3871  M_Enemy(i);
3872 
3873  have_enemy = !(monster[i]._mFlags & MFLAG_NO_ENEMY);
3874 
3875  if (Monst->_mmode == MM_ATTACK) {
3876  return;
3877  }
3878 
3879  _menemy = monster[i]._menemy;
3880 
3881  mx = monster[i]._mx;
3882  my = monster[i]._my;
3883  _mex = mx - monster[_menemy]._mfutx;
3884  _mey = my - monster[_menemy]._mfuty;
3885  md = GetDirection(mx, my, monster[_menemy]._mx, monster[_menemy]._my);
3886  monster[i]._mdir = md;
3887  if (abs(_mex) >= 2 || abs(_mey) >= 2) {
3888  if (have_enemy && MAI_Path(i))
3889  return;
3890  } else if (have_enemy) {
3891  _menemy = monster[i]._menemy;
3892  monster[i]._menemyx = monster[_menemy]._mx;
3893  monster[i]._menemyy = monster[_menemy]._my;
3894  if (monster[_menemy]._msquelch == 0) {
3895  monster[_menemy]._msquelch = UCHAR_MAX;
3898  for (j = 0; j < 5; j++) {
3899  for (k = 0; k < 5; k++) {
3900  _menemy = dMonster[monster[i]._mx + k - 2][monster[i]._my + j - 2];
3901  if (_menemy > 0)
3902  monster[_menemy]._msquelch = UCHAR_MAX;
3903  }
3904  }
3905  }
3906  M_StartAttack(i);
3907  return;
3908  }
3909 
3910  monster[i]._pathcount++;
3911  if (monster[i]._pathcount > 8)
3912  monster[i]._pathcount = 5;
3913 
3914  ok = M_CallWalk(i, plr[i]._pdir);
3915  if (!ok) {
3916  md = (md - 1) & 7;
3917  for (j = 0; j < 8 && !ok; j++) {
3918  md = (md + 1) & 7;
3919  ok = DirOK(i, md);
3920  }
3921  if (!ok) {
3922  return;
3923  }
3924  M_WalkDir(i, md);
3925  }
3926 }
3927 
3928 void MAI_SkelKing(int i)
3929 {
3930  MonsterStruct *Monst;
3931  int mx, my, fx, fy, nx, ny;
3932  int dist, v, md;
3933 
3934  if ((DWORD)i >= MAXMONSTERS)
3935  app_fatal("MAI_SkelKing: Invalid monster %d", i);
3936  Monst = monster + i;
3937  if (Monst->_mmode == MM_STAND && Monst->_msquelch != 0) {
3938  fx = Monst->_menemyx;
3939  fy = Monst->_menemyy;
3940  mx = Monst->_mx - fx;
3941  my = Monst->_my - fy;
3942  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
3943  if (Monst->_msquelch < UCHAR_MAX)
3944  MonstCheckDoors(i);
3945  v = random_(126, 100);
3946  if ((abs(mx) >= 2 || abs(my) >= 2) && Monst->_msquelch == UCHAR_MAX && dTransVal[Monst->_mx][Monst->_my] == dTransVal[fx][fy]) {
3947  if (Monst->_mgoal == MGOAL_MOVE || (abs(mx) >= 3 || abs(my) >= 3) && random_(127, 4) == 0) {
3948  if (Monst->_mgoal != MGOAL_MOVE) {
3949  Monst->_mgoalvar1 = 0;
3950  Monst->_mgoalvar2 = random_(128, 2);
3951  }
3952  Monst->_mgoal = MGOAL_MOVE;
3953  if (abs(mx) > abs(my)) {
3954  dist = abs(mx);
3955  } else {
3956  dist = abs(my);
3957  }
3958  if (Monst->_mgoalvar1++ >= 2 * dist && DirOK(i, md) || dTransVal[Monst->_mx][Monst->_my] != dTransVal[fx][fy]) {
3959  Monst->_mgoal = MGOAL_NORMAL;
3960  } else if (!M_RoundWalk(i, md, &Monst->_mgoalvar2)) {
3961  M_StartDelay(i, random_(125, 10) + 10);
3962  }
3963  }
3964  } else
3965  Monst->_mgoal = MGOAL_NORMAL;
3966  if (Monst->_mgoal == MGOAL_NORMAL) {
3967  if (gbMaxPlayers == 1
3968  && ((abs(mx) >= 3 || abs(my) >= 3) && v < 4 * Monst->_mint + 35 || v < 6)
3969  && LineClear(Monst->_mx, Monst->_my, fx, fy)) {
3970  nx = Monst->_mx + offset_x[md];
3971  ny = Monst->_my + offset_y[md];
3972  if (PosOkMonst(i, nx, ny) && nummonsters < MAXMONSTERS) {
3973  M_SpawnSkel(nx, ny, md);
3974  M_StartSpStand(i, md);
3975  }
3976  } else {
3977  if (abs(mx) >= 2 || abs(my) >= 2) {
3978  v = random_(129, 100);
3979  if (v >= Monst->_mint + 25
3980  && (Monst->_mVar1 != MM_WALK && Monst->_mVar1 != MM_WALK2 && Monst->_mVar1 != MM_WALK3 || Monst->_mVar2 != 0 || (v >= Monst->_mint + 75))) {
3981  M_StartDelay(i, random_(130, 10) + 10);
3982  } else {
3983  M_CallWalk(i, md);
3984  }
3985  } else if (v < Monst->_mint + 20) {
3986  Monst->_mdir = md;
3987  M_StartAttack(i);
3988  }
3989  }
3990  }
3991  if (Monst->_mmode == MM_STAND)
3992  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
3993  }
3994 }
3995 
3996 void MAI_Rhino(int i)
3997 {
3998  MonsterStruct *Monst;
3999  int mx, my, fx, fy;
4000  int v, dist, md;
4001 
4002  if ((DWORD)i >= MAXMONSTERS)
4003  app_fatal("MAI_Rhino: Invalid monster %d", i);
4004  Monst = monster + i;
4005  if (Monst->_mmode == MM_STAND && Monst->_msquelch != 0) {
4006  fx = Monst->_menemyx;
4007  fy = Monst->_menemyy;
4008  mx = Monst->_mx - fx;
4009  my = Monst->_my - fy;
4010  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
4011  if (Monst->_msquelch < UCHAR_MAX)
4012  MonstCheckDoors(i);
4013  v = random_(131, 100);
4014  if (abs(mx) >= 2 || abs(my) >= 2) {
4015  if (Monst->_mgoal == MGOAL_MOVE || (abs(mx) >= 5 || abs(my) >= 5) && random_(132, 4) != 0) {
4016  if (Monst->_mgoal != MGOAL_MOVE) {
4017  Monst->_mgoalvar1 = 0;
4018  Monst->_mgoalvar2 = random_(133, 2);
4019  }
4020  Monst->_mgoal = MGOAL_MOVE;
4021  if (abs(mx) > abs(my)) {
4022  dist = abs(mx);
4023  } else {
4024  dist = abs(my);
4025  }
4026  if (Monst->_mgoalvar1++ >= 2 * dist || dTransVal[Monst->_mx][Monst->_my] != dTransVal[fx][fy]) {
4027  Monst->_mgoal = MGOAL_NORMAL;
4028  } else if (!M_RoundWalk(i, md, &Monst->_mgoalvar2)) {
4029  M_StartDelay(i, random_(125, 10) + 10);
4030  }
4031  }
4032  } else
4033  Monst->_mgoal = MGOAL_NORMAL;
4034  if (Monst->_mgoal == MGOAL_NORMAL) {
4035  if ((abs(mx) >= 5 || abs(my) >= 5)
4036  && v < 2 * Monst->_mint + 43
4037  && LineClearF1(PosOkMonst, i, Monst->_mx, Monst->_my, fx, fy)) {
4038  if (AddMissile(Monst->_mx, Monst->_my, fx, fy, md, MIS_RHINO, Monst->_menemy, i, 0, 0) != -1) {
4039  if (Monst->MData->snd_special)
4040  PlayEffect(i, 3);
4041  Monst->_mmode = MM_CHARGE;
4042  dMonster[Monst->_mx][Monst->_my] = -1 - i;
4043  }
4044  } else {
4045  if (abs(mx) >= 2 || abs(my) >= 2) {
4046  v = random_(134, 100);
4047  if (v >= 2 * Monst->_mint + 33
4048  && (Monst->_mVar1 != MM_WALK && Monst->_mVar1 != MM_WALK2 && Monst->_mVar1 != MM_WALK3
4049  || Monst->_mVar2
4050  || v >= 2 * Monst->_mint + 83)) {
4051  M_StartDelay(i, random_(135, 10) + 10);
4052  } else {
4053  M_CallWalk(i, md);
4054  }
4055  } else if (v < 2 * Monst->_mint + 28) {
4056  Monst->_mdir = md;
4057  M_StartAttack(i);
4058  }
4059  }
4060  }
4061  if (Monst->_mmode == MM_STAND)
4062  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[Monst->_mdir];
4063  }
4064 }
4065 
4066 void MAI_Counselor(int i)
4067 {
4068  int mx, my, fx, fy;
4069  int dist, md, v;
4070  MonsterStruct *Monst;
4071 
4072  if ((DWORD)i >= MAXMONSTERS)
4073  app_fatal("MAI_Counselor: Invalid monster %d", i);
4074  if (monster[i]._mmode == MM_STAND && monster[i]._msquelch != 0) {
4075  Monst = monster + i;
4076  fx = Monst->_menemyx;
4077  fy = Monst->_menemyy;
4078  mx = Monst->_mx - fx;
4079  my = Monst->_my - fy;
4080  md = GetDirection(Monst->_mx, Monst->_my, Monst->_lastx, Monst->_lasty);
4081  if (Monst->_msquelch < UCHAR_MAX)
4082  MonstCheckDoors(i);
4083  v = random_(121, 100);
4084  if (Monst->_mgoal == MGOAL_RETREAT) {
4085  if (Monst->_mgoalvar1++ <= 3)
4086  M_CallWalk(i, opposite[md]);
4087  else {
4088  Monst->_mgoal = MGOAL_NORMAL;
4089  M_StartFadein(i, md, TRUE);
4090  }
4091  } else if (Monst->_mgoal == MGOAL_MOVE) {
4092  if (abs(mx) > abs(my))
4093  dist = abs(mx);
4094  else
4095  dist = abs(my);
4096  if ((abs(mx) >= 2 || abs(my) >= 2) && Monst->_msquelch == UCHAR_MAX && dTransVal[Monst->_mx][Monst->_my] == dTransVal[fx][fy]) {
4097  if (Monst->_mgoalvar1++ < 2 * dist || !DirOK(i, md)) {
4098  M_RoundWalk(i, md, &Monst->_mgoalvar2);
4099  } else {
4100  Monst->_mgoal = MGOAL_NORMAL;
4101  M_StartFadein(i, md, TRUE);
4102  }
4103  } else {
4104  Monst->_mgoal = MGOAL_NORMAL;
4105  M_StartFadein(i, md, TRUE);
4106  }
4107  } else if (Monst->_mgoal == MGOAL_NORMAL) {
4108  if (abs(mx) >= 2 || abs(my) >= 2) {
4109  if (v < 5 * (Monst->_mint + 10) && LineClear(Monst->_mx, Monst->_my, fx, fy)) {
4110  M_StartRAttack(i, counsmiss[Monst->_mint], Monst->mMinDamage + random_(77, Monst->mMaxDamage - Monst->mMinDamage + 1));
4111  } else if (random_(124, 100) < 30) {
4112  Monst->_mgoal = MGOAL_MOVE;
4113  Monst->_mgoalvar1 = 0;
4114  M_StartFadeout(i, md, FALSE);
4115  } else
4116  M_StartDelay(i, random_(105, 10) + 2 * (5 - Monst->_mint));
4117  } else {
4118  Monst->_mdir = md;
4119  if (Monst->_mhitpoints < (Monst->_mmaxhp >> 1)) {
4120  Monst->_mgoal = MGOAL_RETREAT;
4121  Monst->_mgoalvar1 = 0;
4122  M_StartFadeout(i, md, FALSE);
4123  } else if (Monst->_mVar1 == MM_DELAY
4124  || random_(105, 100) < 2 * Monst->_mint + 20) {
4125  M_StartRAttack(i, -1, 0);
4126  AddMissile(Monst->_mx, Monst->_my, 0, 0, Monst->_mdir, MIS_FLASH, 1, i, 4, 0);
4127  AddMissile(Monst->_mx, Monst->_my, 0, 0, Monst->_mdir, MIS_FLASH2, 1, i, 4, 0);
4128  } else
4129  M_StartDelay(i, random_(105, 10) + 2 * (5 - Monst->_mint));
4130  }
4131  }
4132  if (Monst->_mmode == MM_STAND) {
4133  M_StartDelay(i, random_(125, 10) + 5);
4134  }
4135  }
4136 }
4137 
4138 void MAI_Garbud(int i)
4139 {
4140  int _mx, _my, md;
4141  MonsterStruct *Monst;
4142 
4143  if ((DWORD)i >= MAXMONSTERS)
4144  app_fatal("MAI_Garbud: Invalid monster %d", i);
4145 
4146  Monst = &monster[i];
4147  if (Monst->_mmode != MM_STAND) {
4148  return;
4149  }
4150 
4151  _mx = Monst->_mx;
4152  _my = Monst->_my;
4153  md = M_GetDir(i);
4154 
4155  if (Monst->mtalkmsg < TEXT_GARBUD4
4156  && Monst->mtalkmsg > TEXT_DOOM10
4157  && !(dFlags[_mx][_my] & BFLAG_VISIBLE)
4158  && Monst->_mgoal == MGOAL_TALKING) {
4159  Monst->_mgoal = MGOAL_INQUIRING;
4160  Monst->mtalkmsg++;
4161  }
4162 
4163  if (dFlags[_mx][_my] & BFLAG_VISIBLE) {
4164 #ifndef SPAWN
4165  if (Monst->mtalkmsg == TEXT_GARBUD4) {
4166  if (!effect_is_playing(USFX_GARBUD4) && Monst->_mgoal == MGOAL_TALKING) {
4167  Monst->_mgoal = MGOAL_NORMAL;
4168  Monst->_msquelch = UCHAR_MAX;
4169  Monst->mtalkmsg = 0;
4170  }
4171  }
4172 #endif
4173  }
4174 
4175  if (Monst->_mgoal == MGOAL_NORMAL || Monst->_mgoal == MGOAL_MOVE)
4176  MAI_Round(i, TRUE);
4177 
4178  monster[i]._mdir = md;
4179 
4180  if (Monst->_mmode == MM_STAND)
4181  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
4182 }
4183 
4184 void MAI_Zhar(int i)
4185 {
4186  int mx, my, _mx, _my, md;
4187  MonsterStruct *Monst;
4188 
4189  if ((DWORD)i >= MAXMONSTERS)
4190  app_fatal("MAI_Zhar: Invalid monster %d", i);
4191 
4192  Monst = &monster[i];
4193  if (monster[i]._mmode != MM_STAND) {
4194  return;
4195  }
4196 
4197  mx = Monst->_mx;
4198  my = Monst->_my;
4199  md = M_GetDir(i);
4200  if (Monst->mtalkmsg == TEXT_ZHAR1 && !(dFlags[mx][my] & BFLAG_VISIBLE) && Monst->_mgoal == MGOAL_TALKING) {
4201  Monst->mtalkmsg = TEXT_ZHAR2;
4202  Monst->_mgoal = MGOAL_INQUIRING;
4203  }
4204 
4205  if (dFlags[mx][my] & BFLAG_VISIBLE) {
4206  _mx = Monst->_mx - Monst->_menemyx;
4207  _my = Monst->_my - Monst->_menemyy;
4208  if (abs(_mx) > abs(_my))
4209  abs(_mx);
4210  else
4211  abs(_my);
4212 #ifndef SPAWN
4213  if (Monst->mtalkmsg == TEXT_ZHAR2) {
4214  if (!effect_is_playing(USFX_ZHAR2) && Monst->_mgoal == MGOAL_TALKING) {
4215  Monst->_msquelch = UCHAR_MAX;
4216  Monst->mtalkmsg = 0;
4217  Monst->_mgoal = MGOAL_NORMAL;
4218  }
4219  }
4220 #endif
4221  }
4222 
4223  if (Monst->_mgoal == MGOAL_NORMAL || Monst->_mgoal == MGOAL_RETREAT || Monst->_mgoal == MGOAL_MOVE)
4224  MAI_Counselor(i);
4225 
4226  Monst->_mdir = md;
4227 
4228  if (monster[i]._mmode == MM_STAND)
4229  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
4230 }
4231 
4232 void MAI_SnotSpil(int i)
4233 {
4234  int mx, my, md;
4235  MonsterStruct *Monst;
4236 
4237  if ((DWORD)i >= MAXMONSTERS)
4238  app_fatal("MAI_SnotSpil: Invalid monster %d", i);
4239 
4240  Monst = &monster[i];
4241  if (monster[i]._mmode != MM_STAND) {
4242  return;
4243  }
4244 
4245  mx = Monst->_mx;
4246  my = Monst->_my;
4247  md = M_GetDir(i);
4248 
4249  if (Monst->mtalkmsg == TEXT_BANNER10 && !(dFlags[mx][my] & BFLAG_VISIBLE) && Monst->_mgoal == MGOAL_TALKING) {
4250  Monst->mtalkmsg = TEXT_BANNER11;
4251  Monst->_mgoal = MGOAL_INQUIRING;
4252  }
4253 
4254  if (Monst->mtalkmsg == TEXT_BANNER11 && quests[Q_LTBANNER]._qvar1 == 3) {
4255  Monst->mtalkmsg = 0;
4256  Monst->_mgoal = MGOAL_NORMAL;
4257  }
4258 
4259  if (dFlags[mx][my] & BFLAG_VISIBLE) {
4260 #ifndef SPAWN
4261  if (Monst->mtalkmsg == TEXT_BANNER12) {
4262  if (!effect_is_playing(USFX_SNOT3) && Monst->_mgoal == MGOAL_TALKING) {
4264  quests[Q_LTBANNER]._qvar1 = 3;
4265  RedoPlayerVision();
4266  Monst->_msquelch = UCHAR_MAX;
4267  Monst->mtalkmsg = 0;
4268  Monst->_mgoal = MGOAL_NORMAL;
4269  }
4270  }
4271 #endif
4272  if (quests[Q_LTBANNER]._qvar1 == 3) {
4273  if (Monst->_mgoal == MGOAL_NORMAL || Monst->_mgoal == MGOAL_SHOOT)
4274  MAI_Fallen(i);
4275  }
4276  }
4277 
4278  Monst->_mdir = md;
4279 
4280  if (monster[i]._mmode == MM_STAND)
4281  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
4282 }
4283 
4284 void MAI_Lazurus(int i)
4285 {
4286  int mx, my, md;
4287  MonsterStruct *Monst;
4288 
4289  if ((DWORD)i >= MAXMONSTERS)
4290  app_fatal("MAI_Lazurus: Invalid monster %d", i);
4291 
4292  Monst = &monster[i];
4293  if (monster[i]._mmode != MM_STAND) {
4294  return;
4295  }
4296 
4297  mx = Monst->_mx;
4298  my = Monst->_my;
4299  md = M_GetDir(i);
4300  if (dFlags[mx][my] & BFLAG_VISIBLE) {
4301  if (gbMaxPlayers == 1) {
4302  if (Monst->mtalkmsg == TEXT_VILE13 && Monst->_mgoal == MGOAL_INQUIRING && plr[myplr].WorldX == TEXT_VILE13 && plr[myplr].WorldY == 46) {
4303  PlayInGameMovie("gendata\\fprst3.smk");
4304  Monst->_mmode = MM_TALK;
4305  quests[Q_BETRAYER]._qvar1 = 5;
4306  }
4307 
4308 #ifndef SPAWN
4309  if (Monst->mtalkmsg == TEXT_VILE13 && !effect_is_playing(USFX_LAZ1) && Monst->_mgoal == MGOAL_TALKING) {
4310  ObjChangeMapResync(1, 18, 20, 24);
4311  RedoPlayerVision();
4312  Monst->_msquelch = UCHAR_MAX;
4313  Monst->mtalkmsg = 0;
4314  quests[Q_BETRAYER]._qvar1 = 6;
4315  Monst->_mgoal = MGOAL_NORMAL;
4316  }
4317 #endif
4318  }
4319 
4320  if (gbMaxPlayers != 1 && Monst->mtalkmsg == TEXT_VILE13 && Monst->_mgoal == MGOAL_INQUIRING && quests[Q_BETRAYER]._qvar1 <= 3) {
4321  Monst->_mmode = MM_TALK;
4322  }
4323  }
4324 
4325  if (Monst->_mgoal == MGOAL_NORMAL || Monst->_mgoal == MGOAL_RETREAT || Monst->_mgoal == MGOAL_MOVE) {
4326  Monst->mtalkmsg = 0;
4327  MAI_Counselor(i);
4328  }
4329 
4330  Monst->_mdir = md;
4331 
4332  if (monster[i]._mmode == MM_STAND || monster[i]._mmode == MM_TALK)
4333  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
4334 }
4335 
4336 void MAI_Lazhelp(int i)
4337 {
4338  int _mx, _my;
4339  volatile int md; // BUGFIX: very questionable volatile
4340  MonsterStruct *Monst;
4341 
4342  if ((DWORD)i >= MAXMONSTERS)
4343  app_fatal("MAI_Lazhelp: Invalid monster %d", i);
4344  if (monster[i]._mmode != MM_STAND)
4345  return;
4346 
4347  Monst = monster + i;
4348  _mx = Monst->_mx;
4349  _my = Monst->_my;
4350  md = M_GetDir(i);
4351 
4352  if (dFlags[_mx][_my] & BFLAG_VISIBLE) {
4353  if (gbMaxPlayers == 1) {
4354  if (quests[Q_BETRAYER]._qvar1 <= 5) {
4355  Monst->_mgoal = MGOAL_INQUIRING;
4356  } else {
4357  Monst->mtalkmsg = 0;
4358  Monst->_mgoal = MGOAL_NORMAL;
4359  }
4360  } else
4361  Monst->_mgoal = MGOAL_NORMAL;
4362  }
4363  if (Monst->_mgoal == MGOAL_NORMAL)
4364  MAI_Succ(i);
4365  Monst->_mdir = md;
4366  if (monster[i]._mmode == MM_STAND)
4367  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
4368 }
4369 
4370 void MAI_Lachdanan(int i)
4371 {
4372  int _mx, _my, md;
4373  MonsterStruct *Monst;
4374 
4375  if ((DWORD)i >= MAXMONSTERS)
4376  app_fatal("MAI_Lachdanan: Invalid monster %d", i);
4377 
4378  Monst = &monster[i];
4379  if (monster[i]._mmode != MM_STAND) {
4380  return;
4381  }
4382 
4383  _mx = Monst->_mx;
4384  _my = Monst->_my;
4385  md = M_GetDir(i);
4386 #ifndef SPAWN
4387  if (Monst->mtalkmsg == TEXT_VEIL9 && !(dFlags[_mx][_my] & BFLAG_VISIBLE) && monster[i]._mgoal == MGOAL_TALKING) {
4388  Monst->mtalkmsg = TEXT_VEIL10;
4390  }
4391 
4392  if (dFlags[_mx][_my] & BFLAG_VISIBLE) {
4393  if (Monst->mtalkmsg == TEXT_VEIL11) {
4394  if (!effect_is_playing(USFX_LACH3) && Monst->_mgoal == MGOAL_TALKING) {
4395  Monst->mtalkmsg = 0;
4397  M_StartKill(i, -1);
4398  }
4399  }
4400  }
4401 #endif
4402 
4403  Monst->_mdir = md;
4404 
4405  if (monster[i]._mmode == MM_STAND)
4406  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[md];
4407 }
4408 
4409 void MAI_Warlord(int i)
4410 {
4411  MonsterStruct *Monst;
4412  int mx, my, md;
4413 
4414  if ((DWORD)i >= MAXMONSTERS)
4415  app_fatal("MAI_Warlord: Invalid monster %d", i);
4416 
4417  Monst = &monster[i];
4418  if (monster[i]._mmode != MM_STAND) {
4419  return;
4420  }
4421 
4422  mx = Monst->_mx;
4423  my = Monst->_my;
4424  md = M_GetDir(i);
4425  if (dFlags[mx][my] & BFLAG_VISIBLE) {
4426  if (Monst->mtalkmsg == TEXT_WARLRD9 && Monst->_mgoal == MGOAL_INQUIRING)
4427  Monst->_mmode = MM_TALK;
4428 #ifndef SPAWN
4429  if (Monst->mtalkmsg == TEXT_WARLRD9 && !effect_is_playing(USFX_WARLRD1) && Monst->_mgoal == MGOAL_TALKING) {
4430  Monst->_msquelch = UCHAR_MAX;
4431  Monst->mtalkmsg = 0;
4432  Monst->_mgoal = MGOAL_NORMAL;
4433  }
4434 #endif
4435  }
4436 
4437  if (Monst->_mgoal == MGOAL_NORMAL)
4438  MAI_SkelSd(i);
4439 
4440  Monst->_mdir = md;
4441 
4442  if (monster[i]._mmode == MM_STAND || monster[i]._mmode == MM_TALK)
4443  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[Monst->_mdir];
4444 }
4445 
4447 {
4448  int i;
4449  for (i = 0; i < MAX_PLRS; i++) {
4450  if (monster[i]._mDelFlag) {
4451  monster[i]._mx = 1;
4452  monster[i]._my = 0;
4453  monster[i]._mfutx = 0;
4454  monster[i]._mfuty = 0;
4455  monster[i]._moldx = 0;
4456  monster[i]._moldy = 0;
4457  monster[i]._mDelFlag = FALSE;
4458  }
4459  }
4460 
4461  i = MAX_PLRS;
4462  while (i < nummonsters) {
4463  if (monster[monstactive[i]]._mDelFlag) {
4464  DeleteMonster(i);
4465  i = 0; // TODO: check if this should be MAX_PLRS.
4466  } else {
4467  i++;
4468  }
4469  }
4470 }
4471 
4473 {
4474  int i, mi, mx, my, _menemy;
4475  BOOL raflag;
4476  MonsterStruct *Monst;
4477 
4479 
4480  assert((DWORD)nummonsters <= MAXMONSTERS);
4481  for (i = 0; i < nummonsters; i++) {
4482  mi = monstactive[i];
4483  Monst = &monster[mi];
4484  raflag = FALSE;
4485  if (gbMaxPlayers > 1) {
4486  SetRndSeed(Monst->_mAISeed);
4487  Monst->_mAISeed = GetRndSeed();
4488  }
4489  if (!(monster[mi]._mFlags & MFLAG_NOHEAL) && Monst->_mhitpoints < Monst->_mmaxhp && Monst->_mhitpoints >> 6 > 0) {
4490  if (Monst->mLevel > 1) {
4491  Monst->_mhitpoints += Monst->mLevel >> 1;
4492  } else {
4493  Monst->_mhitpoints += Monst->mLevel;
4494  }
4495  }
4496  mx = Monst->_mx;
4497  my = Monst->_my;
4498 #ifndef SPAWN
4499  if (dFlags[mx][my] & BFLAG_VISIBLE && Monst->_msquelch == 0) {
4500  if (Monst->MType->mtype == MT_CLEAVER) {
4502  }
4503  }
4504 #endif
4505  if (Monst->_mFlags & MFLAG_TARGETS_MONSTER) {
4506  _menemy = Monst->_menemy;
4507  if ((DWORD)_menemy >= MAXMONSTERS) {
4508  app_fatal("Illegal enemy monster %d for monster \"%s\"", _menemy, Monst->mName);
4509  }
4510  Monst->_lastx = monster[Monst->_menemy]._mfutx;
4511  Monst->_menemyx = Monst->_lastx;
4512  Monst->_lasty = monster[Monst->_menemy]._mfuty;
4513  Monst->_menemyy = Monst->_lasty;
4514  } else {
4515  _menemy = Monst->_menemy;
4516  if ((DWORD)_menemy >= MAX_PLRS) {
4517  app_fatal("Illegal enemy player %d for monster \"%s\"", _menemy, Monst->mName);
4518  }
4519  Monst->_menemyx = plr[Monst->_menemy]._px;
4520  Monst->_menemyy = plr[Monst->_menemy]._py;
4521  if (dFlags[mx][my] & BFLAG_VISIBLE) {
4522  Monst->_msquelch = 255;
4523  Monst->_lastx = plr[Monst->_menemy]._px;
4524  Monst->_lasty = plr[Monst->_menemy]._py;
4525  } else if (Monst->_msquelch != 0 && Monst->_mAi != MT_DIABLO) {
4526  Monst->_msquelch--;
4527  }
4528  }
4529  do {
4530  if (!(Monst->_mFlags & MFLAG_SEARCH)) {
4531  AiProc[Monst->_mAi](mi);
4532  } else if (!MAI_Path(mi)) {
4533  AiProc[Monst->_mAi](mi);
4534  }
4535  switch (Monst->_mmode) {
4536  case MM_STAND:
4537  raflag = M_DoStand(mi);
4538  break;
4539  case MM_WALK:
4540  raflag = M_DoWalk(mi);
4541  break;
4542  case MM_WALK2:
4543  raflag = M_DoWalk2(mi);
4544  break;
4545  case MM_WALK3:
4546  raflag = M_DoWalk3(mi);
4547  break;
4548  case MM_ATTACK:
4549  raflag = M_DoAttack(mi);
4550  break;
4551  case MM_GOTHIT:
4552  raflag = M_DoGotHit(mi);
4553  break;
4554  case MM_DEATH:
4555  raflag = M_DoDeath(mi);
4556  break;
4557  case MM_SATTACK:
4558  raflag = M_DoSAttack(mi);
4559  break;
4560  case MM_FADEIN:
4561  raflag = M_DoFadein(mi);
4562  break;
4563  case MM_FADEOUT:
4564  raflag = M_DoFadeout(mi);
4565  break;
4566  case MM_RATTACK:
4567  raflag = M_DoRAttack(mi);
4568  break;
4569  case MM_SPSTAND:
4570  raflag = M_DoSpStand(mi);
4571  break;
4572  case MM_RSPATTACK:
4573  raflag = M_DoRSpAttack(mi);
4574  break;
4575  case MM_DELAY:
4576  raflag = M_DoDelay(mi);
4577  break;
4578  case MM_CHARGE:
4579  raflag = FALSE;
4580  break;
4581  case MM_STONE:
4582  raflag = M_DoStone(mi);
4583  break;
4584  case MM_HEAL:
4585  raflag = M_DoHeal(mi);
4586  break;
4587  case MM_TALK:
4588  raflag = M_DoTalk(mi);
4589  break;
4590  }
4591  if (raflag) {
4592  GroupUnity(mi);
4593  }
4594  } while (raflag);
4595  if (Monst->_mmode != MM_STONE) {
4596  Monst->_mAnimCnt++;
4597  if (!(Monst->_mFlags & MFLAG_ALLOW_SPECIAL) && Monst->_mAnimCnt >= Monst->_mAnimDelay) {
4598  Monst->_mAnimCnt = 0;
4599  if (Monst->_mFlags & MFLAG_LOCK_ANIMATION) {
4600  Monst->_mAnimFrame--;
4601  if (Monst->_mAnimFrame == 0) {
4602  Monst->_mAnimFrame = Monst->_mAnimLen;
4603  }
4604  } else {
4605  Monst->_mAnimFrame++;
4606  if (Monst->_mAnimFrame > Monst->_mAnimLen) {
4607  Monst->_mAnimFrame = 1;
4608  }
4609  }
4610  }
4611  }
4612  }
4613 
4615 }
4616 
4618 {
4619  int mtype;
4620  int i, j;
4621 
4622  for (i = 0; i < nummtypes; i++) {
4623  mtype = Monsters[i].mtype;
4624  for (j = 0; j < 6; j++) {
4625  if (animletter[j] != 's' || monsterdata[mtype].has_special) {
4626  MemFreeDbg(Monsters[i].Anims[j].CMem);
4627  }
4628  }
4629  }
4630 
4631  FreeMissiles2();
4632 }
4633 
4634 BOOL DirOK(int i, int mdir)
4635 {
4636  int fx, fy;
4637  int x, y;
4638  int mcount, mi;
4639 
4640  if ((DWORD)i >= MAXMONSTERS)
4641  app_fatal("DirOK: Invalid monster %d", i);
4642  fx = monster[i]._mx + offset_x[mdir];
4643  fy = monster[i]._my + offset_y[mdir];
4644  if (fy < 0 || fy >= MAXDUNY || fx < 0 || fx >= MAXDUNX || !PosOkMonst(i, fx, fy))
4645  return FALSE;
4646  if (mdir == DIR_E) {
4647  if (SolidLoc(fx, fy + 1) || dFlags[fx][fy + 1] & BFLAG_MONSTLR)
4648  return FALSE;
4649  }
4650  if (mdir == DIR_W) {
4651  if (SolidLoc(fx + 1, fy) || dFlags[fx + 1][fy] & BFLAG_MONSTLR)
4652  return FALSE;
4653  }
4654  if (mdir == DIR_N) {
4655  if (SolidLoc(fx + 1, fy) || SolidLoc(fx, fy + 1))
4656  return FALSE;
4657  }
4658  if (mdir == DIR_S)
4659  if (SolidLoc(fx - 1, fy) || SolidLoc(fx, fy - 1))
4660  return FALSE;
4661  if (monster[i].leaderflag == 1) {
4662  if (abs(fx - monster[monster[i].leader]._mfutx) >= 4
4663  || abs(fy - monster[monster[i].leader]._mfuty) >= 4) {
4664  return FALSE;
4665  }
4666  return TRUE;
4667  }
4668  if (monster[i]._uniqtype == 0 || !(UniqMonst[monster[i]._uniqtype - 1].mUnqAttr & 2))
4669  return TRUE;
4670  mcount = 0;
4671  for (x = fx - 3; x <= fx + 3; x++) {
4672  for (y = fy - 3; y <= fy + 3; y++) {
4673  if (y < 0 || y >= MAXDUNY || x < 0 || x >= MAXDUNX)
4674  continue;
4675  mi = dMonster[x][y];
4676  if (mi < 0)
4677  mi = -mi;
4678  if (mi != 0)
4679  mi--;
4680  if (monster[mi].leaderflag == 1
4681  && monster[mi].leader == i
4682  && monster[mi]._mfutx == x
4683  && monster[mi]._mfuty == y) {
4684  mcount++;
4685  }
4686  }
4687  }
4688  return mcount == monster[i].packsize;
4689 }
4690 
4691 BOOL PosOkMissile(int x, int y)
4692 {
4693  return !nMissileTable[dPiece[x][y]] && !(dFlags[x][y] & BFLAG_MONSTLR);
4694 }
4695 
4696 BOOL CheckNoSolid(int x, int y)
4697 {
4698  return nSolidTable[dPiece[x][y]] == FALSE;
4699 }
4700 
4701 BOOL LineClearF(BOOL (*Clear)(int, int), int x1, int y1, int x2, int y2)
4702 {
4703  int xorg, yorg;
4704  int dx, dy;
4705  int d;
4706  int xincD, yincD, dincD, dincH;
4707  int tmp;
4708 
4709  xorg = x1;
4710  yorg = y1;
4711  dx = x2 - x1;
4712  dy = y2 - y1;
4713  if (abs(dx) > abs(dy)) {
4714  if (dx < 0) {
4715  tmp = x1;
4716  x1 = x2;
4717  x2 = tmp;
4718  tmp = y1;
4719  y1 = y2;
4720  y2 = tmp;
4721  dx = -dx;
4722  dy = -dy;
4723  }
4724  if (dy > 0) {
4725  d = 2 * dy - dx;
4726  dincH = 2 * (dy - dx);
4727  dincD = 2 * dy;
4728  yincD = 1;
4729  } else {
4730  d = 2 * dy + dx;
4731  dincH = 2 * (dx + dy);
4732  dincD = 2 * dy;
4733  yincD = -1;
4734  }
4735  while (x1 != x2 || y1 != y2) {
4736  if ((d <= 0) ^ (yincD < 0)) {
4737  d += dincD;
4738  } else {
4739  d += dincH;
4740  y1 += yincD;
4741  }
4742  x1++;
4743  if ((x1 != xorg || y1 != yorg) && !Clear(x1, y1))
4744  break;
4745  }
4746  } else {
4747  if (dy < 0) {
4748  tmp = y1;
4749  y1 = y2;
4750  y2 = tmp;
4751  tmp = x1;
4752  x1 = x2;
4753  x2 = tmp;
4754  dy = -dy;
4755  dx = -dx;
4756  }
4757  if (dx > 0) {
4758  d = 2 * dx - dy;
4759  dincH = 2 * (dx - dy);
4760  dincD = 2 * dx;
4761  xincD = 1;
4762  } else {
4763  d = 2 * dx + dy;
4764  dincH = 2 * (dy + dx);
4765  dincD = 2 * dx;
4766  xincD = -1;
4767  }
4768  while (y1 != y2 || x1 != x2) {
4769  if ((d <= 0) ^ (xincD < 0)) {
4770  d += dincD;
4771  } else {
4772  d += dincH;
4773  x1 += xincD;
4774  }
4775  y1++;
4776  if ((y1 != yorg || x1 != xorg) && !Clear(x1, y1))
4777  break;
4778  }
4779  }
4780  return x1 == x2 && y1 == y2;
4781 }
4782 
4783 BOOL LineClear(int x1, int y1, int x2, int y2)
4784 {
4785  return LineClearF(PosOkMissile, x1, y1, x2, y2);
4786 }
4787 
4788 BOOL LineClearF1(BOOL (*Clear)(int, int, int), int monst, int x1, int y1, int x2, int y2)
4789 {
4790  int xorg, yorg;
4791  int dx, dy;
4792  int d;
4793  int xincD, yincD, dincD, dincH;
4794  int tmp;
4795 
4796  xorg = x1;
4797  yorg = y1;
4798  dx = x2 - x1;
4799  dy = y2 - y1;
4800  if (abs(dx) > abs(dy)) {
4801  if (dx < 0) {
4802  tmp = x1;
4803  x1 = x2;
4804  x2 = tmp;
4805  tmp = y1;
4806  y1 = y2;
4807  y2 = tmp;
4808  dx = -dx;
4809  dy = -dy;
4810  }
4811  if (dy > 0) {
4812  d = 2 * dy - dx;
4813  dincH = 2 * (dy - dx);
4814  dincD = 2 * dy;
4815  yincD = 1;
4816  } else {
4817  d = 2 * dy + dx;
4818  dincH = 2 * (dx + dy);
4819  dincD = 2 * dy;
4820  yincD = -1;
4821  }
4822  while (x1 != x2 || y1 != y2) {
4823  if ((d <= 0) ^ (yincD < 0)) {
4824  d += dincD;
4825  } else {
4826  d += dincH;
4827  y1 += yincD;
4828  }
4829  x1++;
4830  if ((x1 != xorg || y1 != yorg) && !Clear(monst, x1, y1))
4831  break;
4832  }
4833  } else {
4834  if (dy < 0) {
4835  tmp = y1;
4836  y1 = y2;
4837  y2 = tmp;
4838  tmp = x1;
4839  x1 = x2;
4840  x2 = tmp;
4841  dy = -dy;
4842  dx = -dx;
4843  }
4844  if (dx > 0) {
4845  d = 2 * dx - dy;
4846  dincH = 2 * (dx - dy);
4847  dincD = 2 * dx;
4848  xincD = 1;
4849  } else {
4850  d = 2 * dx + dy;
4851  dincH = 2 * (dy + dx);
4852  dincD = 2 * dx;
4853  xincD = -1;
4854  }
4855  while (y1 != y2 || x1 != x2) {
4856  if ((d <= 0) ^ (xincD < 0)) {
4857  d += dincD;
4858  } else {
4859  d += dincH;
4860  x1 += xincD;
4861  }
4862  y1++;
4863  if ((y1 != yorg || x1 != xorg) && !Clear(monst, x1, y1))
4864  break;
4865  }
4866  }
4867  return x1 == x2 && y1 == y2;
4868 }
4869 
4870 void SyncMonsterAnim(int i)
4871 {
4872  MonsterData *MData;
4873  int _mdir;
4874  MonsterStruct *Monst;
4875 
4876  if ((DWORD)i >= MAXMONSTERS)
4877  app_fatal("SyncMonsterAnim: Invalid monster %d", i);
4878  Monst = monster + i;
4879  Monst->MType = Monsters + Monst->_mMTidx;
4880  MData = Monsters[Monst->_mMTidx].MData;
4881  Monst->MData = MData;
4882  if (Monst->_uniqtype != 0)
4883  Monst->mName = UniqMonst[Monst->_uniqtype - 1].mName;
4884  else
4885  Monst->mName = MData->mName;
4886  _mdir = monster[i]._mdir;
4887 
4888  switch (Monst->_mmode) {
4889  case MM_WALK:
4890  case MM_WALK2:
4891  case MM_WALK3:
4892  Monst->_mAnimData = Monst->MType->Anims[MA_WALK].Data[_mdir];
4893  return;
4894  case MM_ATTACK:
4895  case MM_RATTACK:
4896  Monst->_mAnimData = Monst->MType->Anims[MA_ATTACK].Data[_mdir];
4897  return;
4898  case MM_GOTHIT:
4899  Monst->_mAnimData = Monst->MType->Anims[MA_GOTHIT].Data[_mdir];
4900  return;
4901  case MM_DEATH:
4902  Monst->_mAnimData = Monst->MType->Anims[MA_DEATH].Data[_mdir];
4903  return;
4904  case MM_SATTACK:
4905  case MM_FADEIN:
4906  case MM_FADEOUT:
4907  Monst->_mAnimData = Monst->MType->Anims[MA_SPECIAL].Data[_mdir];
4908  return;
4909  case MM_SPSTAND:
4910  case MM_RSPATTACK:
4911  Monst->_mAnimData = Monst->MType->Anims[MA_SPECIAL].Data[_mdir];
4912  return;
4913  case MM_HEAL:
4914  Monst->_mAnimData = Monst->MType->Anims[MA_SPECIAL].Data[_mdir];
4915  return;
4916  case MM_STAND:
4917  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[_mdir];
4918  return;
4919  case MM_DELAY:
4920  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[_mdir];
4921  return;
4922  case MM_TALK:
4923  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[_mdir];
4924  return;
4925  case MM_CHARGE:
4926  Monst->_mAnimData = Monst->MType->Anims[MA_ATTACK].Data[_mdir];
4927  Monst->_mAnimFrame = 1;
4928  Monst->_mAnimLen = Monst->MType->Anims[MA_ATTACK].Frames;
4929  break;
4930  default:
4931  Monst->_mAnimData = Monst->MType->Anims[MA_STAND].Data[_mdir];
4932  Monst->_mAnimFrame = 1;
4933  Monst->_mAnimLen = Monst->MType->Anims[MA_STAND].Frames;
4934  break;
4935  }
4936 }
4937 
4938 void M_FallenFear(int x, int y)
4939 {
4940  int i, mi, rundist, aitype;
4941 
4942  for (i = 0; i < nummonsters; i++) {
4943  rundist = 0;
4944  mi = monstactive[i];
4945 
4946  switch (monster[mi].MType->mtype) {
4947  case MT_RFALLSP:
4948  case MT_RFALLSD:
4949  rundist = 7;
4950  break;
4951  case MT_DFALLSP:
4952  case MT_DFALLSD:
4953  rundist = 5;
4954  break;
4955  case MT_YFALLSP:
4956  case MT_YFALLSD:
4957  rundist = 3;
4958  break;
4959  case MT_BFALLSP:
4960  case MT_BFALLSD:
4961  rundist = 2;
4962  break;
4963  }
4964  aitype = monster[mi]._mAi;
4965  if (aitype == AI_FALLEN
4966  && rundist
4967  && abs(x - monster[mi]._mx) < 5
4968  && abs(y - monster[mi]._my) < 5
4969  && monster[mi]._mhitpoints >> 6 > 0) {
4971  monster[mi]._mgoalvar1 = rundist;
4972  monster[mi]._mdir = GetDirection(x, y, monster[i]._mx, monster[i]._my);
4973  }
4974  }
4975 }
4976 
4977 void PrintMonstHistory(int mt)
4978 {
4979  int minHP, maxHP, res;
4980 
4981  sprintf(tempstr, "Total kills : %i", monstkills[mt]);
4982  AddPanelString(tempstr, TRUE);
4983  if (monstkills[mt] >= 30) {
4984  minHP = monsterdata[mt].mMinHP;
4985  maxHP = monsterdata[mt].mMaxHP;
4986  if (gbMaxPlayers == 1) {
4987  minHP = monsterdata[mt].mMinHP >> 1;
4988  maxHP = monsterdata[mt].mMaxHP >> 1;
4989  }
4990  if (minHP < 1)
4991  minHP = 1;
4992  if (maxHP < 1)
4993  maxHP = 1;
4994  if (gnDifficulty == DIFF_NIGHTMARE) {
4995  minHP = 3 * minHP + 1;
4996  maxHP = 3 * maxHP + 1;
4997  }
4998  if (gnDifficulty == DIFF_HELL) {
4999  minHP = 4 * minHP + 3;
5000  maxHP = 4 * maxHP + 3;
5001  }
5002  sprintf(tempstr, "Hit Points : %i-%i", minHP, maxHP);
5003  AddPanelString(tempstr, TRUE);
5004  }
5005  if (monstkills[mt] >= 15) {
5006  if (gnDifficulty != DIFF_HELL)
5007  res = monsterdata[mt].mMagicRes;
5008  else
5009  res = monsterdata[mt].mMagicRes2;
5011  if (!res) {
5012  strcpy(tempstr, "No magic resistance");
5013  AddPanelString(tempstr, TRUE);
5014  } else {
5015  if (res & (RESIST_MAGIC | RESIST_FIRE | RESIST_LIGHTNING)) {
5016  strcpy(tempstr, "Resists : ");
5017  if (res & RESIST_MAGIC)
5018  strcat(tempstr, "Magic ");
5019  if (res & RESIST_FIRE)
5020  strcat(tempstr, "Fire ");
5021  if (res & RESIST_LIGHTNING)
5022  strcat(tempstr, "Lightning ");
5023  tempstr[strlen(tempstr) - 1] = '\0';
5024  AddPanelString(tempstr, TRUE);
5025  }
5026  if (res & (IMUNE_MAGIC | IMUNE_FIRE | IMUNE_LIGHTNING)) {
5027  strcpy(tempstr, "Immune : ");
5028  if (res & IMUNE_MAGIC)
5029  strcat(tempstr, "Magic ");
5030  if (res & IMUNE_FIRE)
5031  strcat(tempstr, "Fire ");
5032  if (res & IMUNE_LIGHTNING)
5033  strcat(tempstr, "Lightning ");
5034  tempstr[strlen(tempstr) - 1] = '\0';
5035  AddPanelString(tempstr, TRUE);
5036  }
5037  }
5038  }
5039  pinfoflag = TRUE;
5040 }
5041 
5043 {
5044  int res;
5045 
5047  if (!res) {
5048  strcpy(tempstr, "No resistances");
5049  AddPanelString(tempstr, TRUE);
5050  strcpy(tempstr, "No Immunities");
5051  } else {
5052  if (res & (RESIST_MAGIC | RESIST_FIRE | RESIST_LIGHTNING))
5053  strcpy(tempstr, "Some Magic Resistances");
5054  else
5055  strcpy(tempstr, "No resistances");
5056  AddPanelString(tempstr, TRUE);
5057  if (res & (IMUNE_MAGIC | IMUNE_FIRE | IMUNE_LIGHTNING)) {
5058  strcpy(tempstr, "Some Magic Immunities");
5059  } else {
5060  strcpy(tempstr, "No Immunities");
5061  }
5062  }
5063  AddPanelString(tempstr, TRUE);
5064  pinfoflag = TRUE;
5065 }
5066 
5067 void MissToMonst(int i, int x, int y)
5068 {
5069  int oldx, oldy;
5070  int newx, newy;
5071  int m, pnum;
5072  MissileStruct *Miss;
5073  MonsterStruct *Monst;
5074 
5075  if ((DWORD)i >= MAXMISSILES)
5076  app_fatal("MissToMonst: Invalid missile %d", i);
5077 
5078  Miss = &missile[i];
5079  m = Miss->_misource;
5080 
5081  if ((DWORD)m >= MAXMONSTERS)
5082  app_fatal("MissToMonst: Invalid monster %d", m);
5083 
5084  Monst = &monster[m];
5085  oldx = Miss->_mix;
5086  oldy = Miss->_miy;
5087  dMonster[x][y] = m + 1;
5088  Monst->_mdir = Miss->_mimfnum;
5089  Monst->_mx = x;
5090  Monst->_my = y;
5091  M_StartStand(m, Monst->_mdir);
5092  if (Monst->MType->mtype < MT_INCIN || Monst->MType->mtype > MT_HELLBURN) {
5093  if (!(Monst->_mFlags & MFLAG_TARGETS_MONSTER))
5094  M_StartHit(m, -1, 0);
5095  else
5096  M2MStartHit(m, -1, 0);
5097  } else {
5098  M_StartFadein(m, Monst->_mdir, FALSE);
5099  }
5100 
5101  if (!(Monst->_mFlags & MFLAG_TARGETS_MONSTER)) {
5102  pnum = dPlayer[oldx][oldy] - 1;
5103  if (dPlayer[oldx][oldy] > 0) {
5104  if (Monst->MType->mtype != MT_GLOOM && (Monst->MType->mtype < MT_INCIN || Monst->MType->mtype > MT_HELLBURN)) {
5105  M_TryH2HHit(m, dPlayer[oldx][oldy] - 1, 500, Monst->mMinDamage2, Monst->mMaxDamage2);
5106  if (pnum == dPlayer[oldx][oldy] - 1 && (Monst->MType->mtype < MT_NSNAKE || Monst->MType->mtype > MT_GSNAKE)) {
5107  if (plr[pnum]._pmode != 7 && plr[pnum]._pmode != 8)
5108  StartPlrHit(pnum, 0, TRUE);
5109  newx = oldx + offset_x[Monst->_mdir];
5110  newy = oldy + offset_y[Monst->_mdir];
5111  if (PosOkPlayer(pnum, newx, newy)) {
5112  plr[pnum].WorldX = newx;
5113  plr[pnum].WorldY = newy;
5114  FixPlayerLocation(pnum, plr[pnum]._pdir);
5115  FixPlrWalkTags(pnum);
5116  dPlayer[newx][newy] = pnum + 1;
5117  SetPlayerOld(pnum);
5118  }
5119  }
5120  }
5121  }
5122  } else {
5123  if (dMonster[oldx][oldy] > 0) {
5124  if (Monst->MType->mtype != MT_GLOOM && (Monst->MType->mtype < MT_INCIN || Monst->MType->mtype > MT_HELLBURN)) {
5125  M_TryM2MHit(m, dMonster[oldx][oldy] - 1, 500, Monst->mMinDamage2, Monst->mMaxDamage2);
5126  if (Monst->MType->mtype < MT_NSNAKE || Monst->MType->mtype > MT_GSNAKE) {
5127  newx = oldx + offset_x[Monst->_mdir];
5128  newy = oldy + offset_y[Monst->_mdir];
5129  if (PosOkMonst(dMonster[oldx][oldy] - 1, newx, newy)) {
5130  m = dMonster[oldx][oldy];
5131  dMonster[newx][newy] = m;
5132  dMonster[oldx][oldy] = 0;
5133  m--;
5134  monster[m]._mx = newx;
5135  monster[m]._mfutx = newx;
5136  monster[m]._my = newy;
5137  monster[m]._mfuty = newy;
5138  }
5139  }
5140  }
5141  }
5142  }
5143 }
5144 
5145 BOOL PosOkMonst(int i, int x, int y)
5146 {
5147  int oi, mi, j;
5148  BOOL ret, fire;
5149 
5150  fire = FALSE;
5151  ret = !SolidLoc(x, y) && !dPlayer[x][y] && !dMonster[x][y];
5152  if (ret && dObject[x][y]) {
5153  oi = dObject[x][y] > 0 ? dObject[x][y] - 1 : -(dObject[x][y] + 1);
5154  if (object[oi]._oSolidFlag)
5155  ret = FALSE;
5156  }
5157 
5158  if (ret && dMissile[x][y] && i >= 0) {
5159  mi = dMissile[x][y];
5160  if (mi > 0) {
5161  if (missile[mi - 1]._mitype == MIS_FIREWALL) { // BUGFIX: Change 'mi' to 'mi - 1' (fixed)
5162  fire = TRUE;
5163  } else {
5164  for (j = 0; j < nummissiles; j++) {
5165  if (missile[missileactive[j]]._mitype == MIS_FIREWALL)
5166  fire = TRUE;
5167  }
5168  }
5169  }
5170  if (fire && (!(monster[i].mMagicRes & IMUNE_FIRE) || monster[i].MType->mtype == MT_DIABLO))
5171  ret = FALSE;
5172  }
5173 
5174  return ret;
5175 }
5176 
5177 BOOL PosOkMonst2(int i, int x, int y)
5178 {
5179  int oi, mi, j;
5180  BOOL ret, fire;
5181 
5182  fire = FALSE;
5183  ret = !SolidLoc(x, y);
5184  if (ret && dObject[x][y]) {
5185  oi = dObject[x][y] > 0 ? dObject[x][y] - 1 : -(dObject[x][y] + 1);
5186  if (object[oi]._oSolidFlag)
5187  ret = FALSE;
5188  }
5189 
5190  if (ret && dMissile[x][y] && i >= 0) {
5191  mi = dMissile[x][y];
5192  if (mi > 0) {
5193  if (missile[mi - 1]._mitype == MIS_FIREWALL) { // BUGFIX: Change 'mi' to 'mi - 1' (fixed)
5194  fire = TRUE;
5195  } else {
5196  for (j = 0; j < nummissiles; j++) {
5197  if (missile[missileactive[j]]._mitype == MIS_FIREWALL)
5198  fire = TRUE;
5199  }
5200  }
5201  }
5202  if (fire && (!(monster[i].mMagicRes & IMUNE_FIRE) || monster[i].MType->mtype == MT_DIABLO))
5203  ret = FALSE;
5204  }
5205 
5206  return ret;
5207 }
5208 
5209 BOOL PosOkMonst3(int i, int x, int y)
5210 {
5211  int j, oi, objtype, mi;
5212  BOOL ret, fire, isdoor;
5213 
5214  fire = FALSE;
5215  ret = TRUE;
5216  isdoor = FALSE;
5217 
5218  if (ret && dObject[x][y] != 0) {
5219  oi = dObject[x][y] > 0 ? dObject[x][y] - 1 : -(dObject[x][y] + 1);
5220  objtype = object[oi]._otype;
5221  isdoor = objtype == OBJ_L1LDOOR || objtype == OBJ_L1RDOOR
5222  || objtype == OBJ_L2LDOOR || objtype == OBJ_L2RDOOR
5223  || objtype == OBJ_L3LDOOR || objtype == OBJ_L3RDOOR;
5224  if (object[oi]._oSolidFlag && !isdoor) {
5225  ret = FALSE;
5226  }
5227  }
5228  if (ret) {
5229  ret = (!SolidLoc(x, y) || isdoor) && dPlayer[x][y] == 0 && dMonster[x][y] == 0;
5230  }
5231  if (ret && dMissile[x][y] != 0 && i >= 0) {
5232  mi = dMissile[x][y];
5233  if (mi > 0) {
5234  if (missile[mi - 1]._mitype == MIS_FIREWALL) { // BUGFIX: Change 'mi' to 'mi - 1' (fixed)
5235  fire = TRUE;
5236  } else {
5237  for (j = 0; j < nummissiles; j++) {
5238  mi = missileactive[j];
5239  if (missile[mi]._mitype == MIS_FIREWALL) {
5240  fire = TRUE;
5241  }
5242  }
5243  }
5244  }
5245  if (fire && (!(monster[i].mMagicRes & IMUNE_FIRE) || monster[i].MType->mtype == MT_DIABLO)) {
5246  ret = FALSE;
5247  }
5248  }
5249 
5250  return ret;
5251 }
5252 
5253 BOOL IsSkel(int mt)
5254 {
5255  return mt >= MT_WSKELAX && mt <= MT_XSKELAX
5256  || mt >= MT_WSKELBW && mt <= MT_XSKELBW
5257  || mt >= MT_WSKELSD && mt <= MT_XSKELSD;
5258 }
5259 
5260 BOOL IsGoat(int mt)
5261 {
5262  return mt >= MT_NGOATMC && mt <= MT_GGOATMC
5263  || mt >= MT_NGOATBW && mt <= MT_GGOATBW;
5264 }
5265 
5266 int M_SpawnSkel(int x, int y, int dir)
5267 {
5268  int i, j, skeltypes, skel;
5269 
5270  j = 0;
5271  for (i = 0; i < nummtypes; i++) {
5272  if (IsSkel(Monsters[i].mtype))
5273  j++;
5274  }
5275 
5276  if (j) {
5277  skeltypes = random_(136, j);
5278  j = 0;
5279  for (i = 0; i < nummtypes && j <= skeltypes; i++) {
5280  if (IsSkel(Monsters[i].mtype))
5281  j++;
5282  }
5283  skel = AddMonster(x, y, dir, i - 1, TRUE);
5284  if (skel != -1)
5285  M_StartSpStand(skel, dir);
5286 
5287  return skel;
5288  }
5289 
5290  return -1;
5291 }
5292 
5293 void ActivateSpawn(int i, int x, int y, int dir)
5294 {
5295  dMonster[x][y] = i + 1;
5296  monster[i]._mx = x;
5297  monster[i]._my = y;
5298  monster[i]._mfutx = x;
5299  monster[i]._mfuty = y;
5300  monster[i]._moldx = x;
5301  monster[i]._moldy = y;
5302  M_StartSpStand(i, dir);
5303 }
5304 
5305 BOOL SpawnSkeleton(int ii, int x, int y)
5306 {
5307  int dx, dy, xx, yy, dir, j, k, rs;
5308  BOOL savail;
5309  int monstok[3][3];
5310 
5311  if (ii == -1)
5312  return FALSE;
5313 
5314  if (PosOkMonst(-1, x, y)) {
5315  dir = GetDirection(x, y, x, y);
5316  ActivateSpawn(ii, x, y, dir);
5317  return TRUE;
5318  }
5319 
5320  savail = FALSE;
5321  yy = 0;
5322  for (j = y - 1; j <= y + 1; j++) {
5323  xx = 0;
5324  for (k = x - 1; k <= x + 1; k++) {
5325  monstok[xx][yy] = PosOkMonst(-1, k, j);
5326  savail |= monstok[xx][yy];
5327  xx++;
5328  }
5329  yy++;
5330  }
5331  if (!savail) {
5332  return FALSE;
5333  }
5334 
5335  rs = random_(137, 15) + 1;
5336  xx = 0;
5337  yy = 0;
5338  while (rs > 0) {
5339  if (monstok[xx][yy])
5340  rs--;
5341  if (rs > 0) {
5342  xx++;
5343  if (xx == 3) {
5344  xx = 0;
5345  yy++;
5346  if (yy == 3)
5347  yy = 0;
5348  }
5349  }
5350  }
5351 
5352  dx = x - 1 + xx;
5353  dy = y - 1 + yy;
5354  dir = GetDirection(dx, dy, x, y);
5355  ActivateSpawn(ii, dx, dy, dir);
5356 
5357  return TRUE;
5358 }
5359 
5361 {
5362  int i, j, skeltypes, skel;
5363 
5364  j = 0;
5365 
5366  for (i = 0; i < nummtypes; i++) {
5367  if (IsSkel(Monsters[i].mtype))
5368  j++;
5369  }
5370 
5371  if (j) {
5372  skeltypes = random_(136, j);
5373  j = 0;
5374  for (i = 0; i < nummtypes && j <= skeltypes; i++) {
5375  if (IsSkel(Monsters[i].mtype))
5376  j++;
5377  }
5378  skel = AddMonster(0, 0, 0, i - 1, FALSE);
5379  if (skel != -1)
5380  M_StartStand(skel, 0);
5381 
5382  return skel;
5383  }
5384 
5385  return -1;
5386 }
5387 
5388 void TalktoMonster(int i)
5389 {
5390  MonsterStruct *Monst;
5391  int pnum, itm;
5392 
5393  if ((DWORD)i >= MAXMONSTERS)
5394  app_fatal("TalktoMonster: Invalid monster %d", i);
5395 
5396  Monst = &monster[i];
5397  pnum = Monst->_menemy;
5398  Monst->_mmode = MM_TALK;
5399  if (Monst->_mAi == AI_SNOTSPIL || Monst->_mAi == AI_LACHDAN) {
5400  if (QuestStatus(Q_LTBANNER) && quests[Q_LTBANNER]._qvar1 == 2 && PlrHasItem(pnum, IDI_BANNER, &itm)) {
5401  RemoveInvItem(pnum, itm);
5403  Monst->mtalkmsg = TEXT_BANNER12;
5404  Monst->_mgoal = MGOAL_INQUIRING;
5405  }
5406  if (QuestStatus(Q_VEIL) && Monst->mtalkmsg >= TEXT_VEIL9) {
5407  if (PlrHasItem(pnum, IDI_GLDNELIX, &itm)) {
5408  RemoveInvItem(pnum, itm);
5409  Monst->mtalkmsg = TEXT_VEIL11;
5410  Monst->_mgoal = MGOAL_INQUIRING;
5411  }
5412  }
5413  }
5414 }
5415 
5416 void SpawnGolum(int i, int x, int y, int mi)
5417 {
5418  if ((DWORD)i >= MAXMONSTERS)
5419  app_fatal("SpawnGolum: Invalid monster %d", i);
5420 
5421  dMonster[x][y] = i + 1;
5422  monster[i]._mx = x;
5423  monster[i]._my = y;
5424  monster[i]._mfutx = x;
5425  monster[i]._mfuty = y;
5426  monster[i]._moldx = x;
5427  monster[i]._moldy = y;
5428  monster[i]._pathcount = 0;
5429  monster[i]._mFlags |= MFLAG_GOLEM;
5430  monster[i].mArmorClass = 25;
5431  monster[i]._mmaxhp = 2 * (320 * missile[mi]._mispllvl + plr[i]._pMaxMana / 3);
5433  monster[i].mHit = 5 * (missile[mi]._mispllvl + 8) + 2 * plr[i]._pLevel;
5434  monster[i].mMinDamage = 2 * (missile[mi]._mispllvl + 4);
5435  monster[i].mMaxDamage = 2 * (missile[mi]._mispllvl + 8);
5436  M_StartSpStand(i, 0);
5437  M_Enemy(i);
5438  if (i == myplr) {
5440  monster[i]._mx,
5441  monster[i]._my,
5442  monster[i]._mdir,
5443  monster[i]._menemy,
5444  monster[i]._mhitpoints,
5445  currlevel);
5446  }
5447 }
5448 
5449 BOOL CanTalkToMonst(int m)
5450 {
5451  if ((DWORD)m >= MAXMONSTERS) {
5452  app_fatal("CanTalkToMonst: Invalid monster %d", m);
5453  }
5454 
5455  if (monster[m]._mgoal == MGOAL_INQUIRING) {
5456  return TRUE;
5457  }
5458 
5459  return monster[m]._mgoal == MGOAL_TALKING;
5460 }
5461 
5462 BOOL CheckMonsterHit(int m, BOOL *ret)
5463 {
5464  if ((DWORD)m >= MAXMONSTERS) {
5465  app_fatal("CheckMonsterHit: Invalid monster %d", m);
5466  }
5467 
5468  if (monster[m]._mAi == AI_GARG && monster[m]._mFlags & MFLAG_ALLOW_SPECIAL) {
5469  monster[m]._mmode = MM_SATTACK;
5471  *ret = TRUE;
5472  return TRUE;
5473  }
5474 
5475  if (monster[m].MType->mtype < MT_COUNSLR || monster[m].MType->mtype > MT_ADVOCATE || monster[m]._mgoal == MGOAL_NORMAL) {
5476  return FALSE;
5477  } else {
5478  *ret = FALSE;
5479  }
5480 
5481  return TRUE;
5482 }
5483 
5484 int encode_enemy(int m)
5485 {
5486  if (monster[m]._mFlags & MFLAG_TARGETS_MONSTER)
5487  return monster[m]._menemy + MAX_PLRS;
5488  else
5489  return monster[m]._menemy;
5490 }
5491 
5492 void decode_enemy(int m, int enemy)
5493 {
5494  if (enemy < MAX_PLRS) {
5496  monster[m]._menemy = enemy;
5497  monster[m]._menemyx = plr[enemy]._px;
5498  monster[m]._menemyy = plr[enemy]._py;
5499  } else {
5501  enemy -= MAX_PLRS;
5502  monster[m]._menemy = enemy;
5503  monster[m]._menemyx = monster[enemy]._mfutx;
5504  monster[m]._menemyy = monster[enemy]._mfuty;
5505  }
5506 }
5507 
MT_WSKELAX
@ MT_WSKELAX
Definition: enums.h:1382
MonsterStruct::_mVar1
int _mVar1
Definition: structs.h:570
MIS_ACID
@ MIS_ACID
Definition: enums.h:1251
M_Ranged
BOOL M_Ranged(int i)
Definition: monster.cpp:1105
MFLAG_TARGETS_MONSTER
@ MFLAG_TARGETS_MONSTER
Definition: enums.h:1507
MonsterStruct::mArmorClass
unsigned char mArmorClass
Definition: structs.h:603
MAI_Garbud
void MAI_Garbud(int i)
Definition: monster.cpp:4138
LineClear
BOOL LineClear(int x1, int y1, int x2, int y2)
Definition: monster.cpp:4783
PlaceQuestMonsters
void PlaceQuestMonsters()
Definition: monster.cpp:761
AI_LAZURUS
@ AI_LAZURUS
Definition: enums.h:1345
MAI_Fallen
void MAI_Fallen(int i)
Definition: monster.cpp:3329
MIS_MAGMABALL
@ MIS_MAGMABALL
Definition: enums.h:1215
M_DiabloDeath
void M_DiabloDeath(int i, BOOL sendmsg)
Definition: monster.cpp:1496
MFILE_ACIDPUD
@ MFILE_ACIDPUD
Definition: enums.h:1298
SyncMonsterAnim
void SyncMonsterAnim(int i)
Definition: monster.cpp:4870
MAI_Storm
void MAI_Storm(int i)
Definition: monster.cpp:3754
AddLight
int AddLight(int x, int y, int r)
Definition: lighting.cpp:1021
AddDead
void AddDead(int dx, int dy, char dv, int ddir)
Definition: dead.cpp:74
MissileStruct::_mispllvl
int _mispllvl
Definition: structs.h:420
MonsterData::has_special
BOOL has_special
Definition: structs.h:487
MT_RBLACK
@ MT_RBLACK
Definition: enums.h:1470
PosOkPlayer
BOOL PosOkPlayer(int pnum, int x, int y)
Definition: player.cpp:3475
quests
QuestStruct quests[MAXQUESTS]
Definition: quests.cpp:8
Q_ANVIL
@ Q_ANVIL
Definition: enums.h:2558
MonsterStruct::_moldx
int _moldx
Definition: structs.h:552
MFILE_ACIDBF
@ MFILE_ACIDBF
Definition: enums.h:1296
UniqMonst
UniqMonstStruct UniqMonst[]
Definition: monstdat.cpp:264
MonsterData
Definition: structs.h:483
StartPlrHit
void StartPlrHit(int pnum, int dam, BOOL forcehit)
Definition: player.cpp:1554
MonsterStruct::_mAISeed
int _mAISeed
Definition: structs.h:589
MAI_Ranged
void MAI_Ranged(int i, int missile_type, BOOL special)
Definition: monster.cpp:3495
diabquad1y
int diabquad1y
Definition: drlg_l4.cpp:11
play_movie
void play_movie(char *pszMovie, BOOL user_can_close)
Definition: movie.cpp:17
MM_WALK
@ MM_WALK
Definition: enums.h:2014
MonsterData::snd_special
BOOL snd_special
Definition: structs.h:489
MonsterStruct::_mAnimDelay
int _mAnimDelay
Definition: structs.h:564
MonsterStruct::_mVar5
int _mVar5
Definition: structs.h:574
Monsters
CMonster Monsters[MAX_LVLMTYPES]
Definition: monster.cpp:21
setpc_w
int setpc_w
Definition: gendung.cpp:71
RedoPlayerVision
void RedoPlayerVision()
Definition: objects.cpp:1979
TEXT_VEIL10
@ TEXT_VEIL10
Definition: enums.h:1629
MT_ADVOCATE
@ MT_ADVOCATE
Definition: enums.h:1482
UMT_GARBUD
@ UMT_GARBUD
Definition: enums.h:1490
PlayerStruct::_pIGetHit
int _pIGetHit
Definition: structs.h:328
CMonster::trans_file
BYTE * trans_file
Definition: structs.h:536
UniqMonstStruct::mlevel
unsigned char mlevel
Definition: structs.h:620
MonsterStruct::_mAnimLen
int _mAnimLen
Definition: structs.h:566
MonsterStruct::_mint
unsigned char _mint
Definition: structs.h:581
currlevel
BYTE currlevel
Definition: gendung.cpp:40
PlayerStruct::_pMaxHP
int _pMaxHP
Definition: structs.h:259
AI_LACHDAN
@ AI_LACHDAN
Definition: enums.h:1347
PlayerStruct::_pMana
int _pMana
Definition: structs.h:263
MT_GLOOM
@ MT_GLOOM
Definition: enums.h:1414
NetSendCmdLocParam1
void NetSendCmdLocParam1(BOOL bHiPri, BYTE bCmd, BYTE x, BYTE y, WORD wParam1)
Definition: msg.cpp:653
AI_FALLEN
@ AI_FALLEN
Definition: enums.h:1325
diabquad2y
int diabquad2y
Definition: drlg_l4.cpp:15
Q_BUTCHER
@ Q_BUTCHER
Definition: enums.h:2554
gbActivePlayers
BYTE gbActivePlayers
Definition: multi.cpp:21
MM_SPSTAND
@ MM_SPSTAND
Definition: enums.h:2024
TEXT_BANNER12
@ TEXT_BANNER12
Definition: enums.h:1569
TEXT_DOOM10
@ TEXT_DOOM10
Definition: enums.h:1690
DIR_W
@ DIR_W
Definition: enums.h:2084
MonsterStruct::mHit
unsigned char mHit
Definition: structs.h:597
ProcessMonsters
void ProcessMonsters()
Definition: monster.cpp:4472
M_GetDir
int M_GetDir(int i)
Definition: monster.cpp:1202
MonsterStruct::_mAi
unsigned char _mAi
Definition: structs.h:580
ClearMVars
void ClearMVars(int i)
Definition: monster.cpp:372
M_UpdateLeader
void M_UpdateLeader(int i)
Definition: monster.cpp:2460
UMT_WARLORD
@ UMT_WARLORD
Definition: enums.h:1498
AddMonster
int AddMonster(int x, int y, int dir, int mtype, BOOL InMap)
Definition: monster.cpp:1080
setpc_h
int setpc_h
Definition: gendung.cpp:70
M_PathWalk
BOOL M_PathWalk(int i)
Definition: monster.cpp:2773
diabquad3x
int diabquad3x
Definition: drlg_l4.cpp:12
MIS_FLASH
@ MIS_FLASH
Definition: enums.h:1205
CMonster::mAFNum
unsigned char mAFNum
Definition: structs.h:531
MAI_Golum
void MAI_Golum(int i)
Definition: monster.cpp:3849
MGOAL_HEALING
@ MGOAL_HEALING
Definition: enums.h:1521
QuestStatus
BOOL QuestStatus(int i)
Definition: quests.cpp:239
setpc_x
int setpc_x
Definition: gendung.cpp:72
UniqMonstStruct::mmaxhp
unsigned short mmaxhp
Definition: structs.h:621
MAX_PLRS
#define MAX_PLRS
Definition: defs.h:16
MGOAL_NORMAL
@ MGOAL_NORMAL
Definition: enums.h:1519
pLightTbl
BYTE * pLightTbl
Definition: lighting.cpp:21
MT_DFALLSP
@ MT_DFALLSP
Definition: enums.h:1379
MT_SNEAK
@ MT_SNEAK
Definition: enums.h:1403
MAXMISSILES
#define MAXMISSILES
Definition: defs.h:30
USFX_LACH3
@ USFX_LACH3
Definition: enums.h:1154
DIFF_HELL
@ DIFF_HELL
Definition: enums.h:2008
MonsterStruct::mlid
unsigned char mlid
Definition: structs.h:610
SetRndSeed
void SetRndSeed(int s)
Set the RNG seed.
Definition: engine.cpp:728
M_GetKnockback
void M_GetKnockback(int i)
Definition: monster.cpp:1436
TextDataStruct::sfxnr
int sfxnr
Definition: structs.h:367
MT_XSKELAX
@ MT_XSKELAX
Definition: enums.h:1385
MonstCheckDoors
void MonstCheckDoors(int m)
Definition: objects.cpp:2265
MFILE_SCUBMISC
@ MFILE_SCUBMISC
Definition: enums.h:1307
M_StartSpAttack
void M_StartSpAttack(int i)
Definition: monster.cpp:1386
effect_is_playing
BOOL effect_is_playing(int nSFX)
Definition: effects.cpp:920
CelGetFrameStart
BYTE * CelGetFrameStart(BYTE *pCelBuff, int nCel)
Definition: engine.h:30
AI_SKELBOW
@ AI_SKELBOW
Definition: enums.h:1320
ChangeLightOff
void ChangeLightOff(int i, int x, int y)
Definition: lighting.cpp:1085
MonsterStruct::_mmaxhp
int _mmaxhp
Definition: structs.h:578
GetLevelMTypes
void GetLevelMTypes()
Definition: monster.cpp:178
MonsterStruct::_mRndSeed
int _mRndSeed
Definition: structs.h:588
MonsterStruct::_mVar8
int _mVar8
Definition: structs.h:577
M_StartEat
void M_StartEat(int i)
Definition: monster.cpp:1400
MT_GGOATBW
@ MT_GGOATBW
Definition: enums.h:1419
offset_x
int offset_x[8]
Definition: monster.cpp:61
PlayerStruct::_pMaxHPBase
int _pMaxHPBase
Definition: structs.h:257
MonsterStruct::_mVar2
int _mVar2
Definition: structs.h:571
UITEM_STEELVEIL
@ UITEM_STEELVEIL
Definition: enums.h:2618
THEME_LOC::x
int x
Definition: structs.h:1114
MT_WMAGMA
@ MT_WMAGMA
Definition: enums.h:1437
MonsterStruct::mMaxDamage2
unsigned char mMaxDamage2
Definition: structs.h:602
MA_DEATH
@ MA_DEATH
Definition: enums.h:2038
MIS_CBOLT
@ MIS_CBOLT
Definition: enums.h:1246
UMT_ZHAR
@ UMT_ZHAR
Definition: enums.h:1492
PlayEffect
void PlayEffect(int i, int mode)
Definition: effects.cpp:992
UMT_SNOTSPIL
@ UMT_SNOTSPIL
Definition: enums.h:1493
MonsterStruct::mExp
unsigned short mExp
Definition: structs.h:596
MissileStruct::_miVar2
int _miVar2
Definition: structs.h:445
MT_NSNAKE
@ MT_NSNAKE
Definition: enums.h:1463
OBJ_L1RDOOR
@ OBJ_L1RDOOR
Definition: enums.h:2331
PlaceMonster
void PlaceMonster(int i, int mtype, int x, int y)
Definition: monster.cpp:545
TEXT_ZHAR1
@ TEXT_ZHAR1
Definition: enums.h:1695
Cl2ApplyTrans
void Cl2ApplyTrans(BYTE *p, BYTE *ttbl, int nCel)
Apply the color swaps to a CL2 sprite.
Definition: engine.cpp:859
SL_VILEBETRAYER
@ SL_VILEBETRAYER
Definition: enums.h:2544
MissileStruct::_mimfnum
int _mimfnum
Definition: structs.h:419
PlayerStruct::WorldX
int WorldX
Definition: structs.h:188
M_DoFadeout
BOOL M_DoFadeout(int i)
Definition: monster.cpp:2281
MAI_Zombie
void MAI_Zombie(int i)
Definition: monster.cpp:2852
PlayerStruct::_pHPBase
int _pHPBase
Definition: structs.h:256
MA_SPECIAL
@ MA_SPECIAL
Definition: enums.h:2039
FixPlrWalkTags
void FixPlrWalkTags(int pnum)
Definition: player.cpp:1507
MAI_Scav
void MAI_Scav(int i)
Definition: monster.cpp:3557
CheckNoSolid
BOOL CheckNoSolid(int x, int y)
Definition: monster.cpp:4696
M_TryH2HHit
void M_TryH2HHit(int i, int pnum, int Hit, int MinDam, int MaxDam)
Definition: monster.cpp:1989
M_DoGotHit
BOOL M_DoGotHit(int i)
Definition: monster.cpp:2444
MonsterData::mHit
unsigned char mHit
Definition: structs.h:503
MAXDUNX
#define MAXDUNX
Definition: defs.h:25
IMUNE_FIRE
@ IMUNE_FIRE
Definition: enums.h:1533
MAI_SkelSd
void MAI_SkelSd(int i)
Definition: monster.cpp:2897
M_CallWalk
BOOL M_CallWalk(int i, int md)
Definition: monster.cpp:2749
OBJ_L3RDOOR
@ OBJ_L3RDOOR
Definition: enums.h:2404
debugmonsttypes
int debugmonsttypes
Definition: diablo.cpp:40
UMT_BUTCHER
@ UMT_BUTCHER
Definition: enums.h:1499
MAI_Fireman
void MAI_Fireman(int i)
Definition: monster.cpp:3268
MonsterData::mAFNum
unsigned char mAFNum
Definition: structs.h:504
MAI_RR2
void MAI_RR2(int i, int mistype, int dam)
Definition: monster.cpp:3769
music_start
void music_start(int nTrack)
PlayerStruct::_pBaseToBlk
int _pBaseToBlk
Definition: structs.h:255
AI_GARG
@ AI_GARG
Definition: enums.h:1329
OBJ_L2LDOOR
@ OBJ_L2LDOOR
Definition: enums.h:2371
MonsterStruct::_mfutx
int _mfutx
Definition: structs.h:550
MAI_Counselor
void MAI_Counselor(int i)
Definition: monster.cpp:4066
GroupUnity
void GroupUnity(int i)
Definition: monster.cpp:2688
GetDirection
int GetDirection(int x1, int y1, int x2, int y2)
Calculate the best fit direction between two points.
Definition: engine.cpp:683
MonsterData::mMinDamage
unsigned char mMinDamage
Definition: structs.h:505
UMT_RED_VEX
@ UMT_RED_VEX
Definition: enums.h:1495
MonsterStruct::_mAnimFrame
int _mAnimFrame
Definition: structs.h:567
FixPlayerLocation
void FixPlayerLocation(int pnum, int bDir)
Definition: player.cpp:1034
MonstPlace
BOOL MonstPlace(int xp, int yp)
Definition: monster.cpp:521
MIS_MANASHIELD
@ MIS_MANASHIELD
Definition: enums.h:1207
MonsterStruct::_mDelFlag
BOOL _mDelFlag
Definition: structs.h:569
MonsterData::mMinDLvl
char mMinDLvl
Definition: structs.h:495
MonsterStruct::_mgoal
unsigned char _mgoal
Definition: structs.h:542
MT_GOLEM
@ MT_GOLEM
Definition: enums.h:1483
MFILE_ACIDSPLA
@ MFILE_ACIDSPLA
Definition: enums.h:1297
DIR_N
@ DIR_N
Definition: enums.h:2086
PM_GOTHIT
@ PM_GOTHIT
Definition: enums.h:2050
M_TryM2MHit
void M_TryM2MHit(int i, int mid, int hper, int mind, int maxd)
Definition: monster.cpp:1954
M_CallWalk2
BOOL M_CallWalk2(int i, int md)
Definition: monster.cpp:2794
CMonster::width
int width
Definition: structs.h:526
ViewX
int ViewX
Definition: gendung.cpp:73
missile
MissileStruct missile[MAXMISSILES]
Definition: missiles.cpp:12
DIR_NW
@ DIR_NW
Definition: enums.h:2085
M_StartWalk3
void M_StartWalk3(int i, int xvel, int yvel, int xoff, int yoff, int xadd, int yadd, int mapx, int mapy, int EndDir)
Definition: monster.cpp:1305
PosOkMonst
BOOL PosOkMonst(int i, int x, int y)
Definition: monster.cpp:5145
PlayInGameMovie
void PlayInGameMovie(char *pszMovie)
Fade to black and play a video.
Definition: engine.cpp:1259
MAX_LVLMTYPES
#define MAX_LVLMTYPES
Definition: defs.h:19
MonsterStruct::_mmode
int _mmode
Definition: structs.h:541
M_DoAttack
BOOL M_DoAttack(int i)
Definition: monster.cpp:2125
RESIST_MAGIC
@ RESIST_MAGIC
Definition: enums.h:1529
USFX_WARLRD1
@ USFX_WARLRD1
Definition: enums.h:1161
MonsterStruct::mLevel
char mLevel
Definition: structs.h:595
CanTalkToMonst
BOOL CanTalkToMonst(int m)
Definition: monster.cpp:5449
MT_NGOATMC
@ MT_NGOATMC
Definition: enums.h:1408
MonsterData::width
int width
Definition: structs.h:484
MonsterStruct::_uniqtype
unsigned char _uniqtype
Definition: structs.h:591
PlaceUniques
void PlaceUniques()
Definition: monster.cpp:999
MonstStartKill
void MonstStartKill(int i, int pnum, BOOL sendmsg)
Definition: monster.cpp:1593
AI_GOATBOW
@ AI_GOATBOW
Definition: enums.h:1324
MemFreeDbg
#define MemFreeDbg(p)
Definition: defs.h:157
MonsterData::mMinDamage2
unsigned char mMinDamage2
Definition: structs.h:509
CheckQuestKill
void CheckQuestKill(int m, BOOL sendmsg)
Definition: quests.cpp:252
CMonster
Definition: structs.h:520
MonsterData::mMaxHP
int mMaxHP
Definition: structs.h:499
FreeMonsters
void FreeMonsters()
Definition: monster.cpp:4617
ChangeLightXY
void ChangeLightXY(int i, int x, int y)
Definition: lighting.cpp:1070
MIS_FIREBALL
@ MIS_FIREBALL
Definition: enums.h:1200
FreeMissiles2
void FreeMissiles2()
Definition: missiles.cpp:1168
MAI_Garg
void MAI_Garg(int i)
Definition: monster.cpp:3645
gnDifficulty
int gnDifficulty
Definition: gendung.cpp:31
MonsterStruct::mtalkmsg
int mtalkmsg
Definition: structs.h:606
UniqMonstStruct::mName
char * mName
Definition: structs.h:618
MIS_LIGHTCTRL2
@ MIS_LIGHTCTRL2
Definition: enums.h:1216
AI_SNAKE
@ AI_SNAKE
Definition: enums.h:1341
MT_XSKELBW
@ MT_XSKELBW
Definition: enums.h:1397
MT_BFALLSP
@ MT_BFALLSP
Definition: enums.h:1381
AI_SNOTSPIL
@ AI_SNOTSPIL
Definition: enums.h:1340
MAI_SnotSpil
void MAI_SnotSpil(int i)
Definition: monster.cpp:4232
PosOkMissile
BOOL PosOkMissile(int x, int y)
Definition: monster.cpp:4691
rnd20
int rnd20[4]
Definition: monster.cpp:67
UniqMonstStruct::mAi
unsigned char mAi
Definition: structs.h:622
MAI_SkelKing
void MAI_SkelKing(int i)
Definition: monster.cpp:3928
MonsterStruct::_mx
int _mx
Definition: structs.h:548
PlayerStruct::_pLevel
char _pLevel
Definition: structs.h:266
MonsterStruct::_mMTidx
int _mMTidx
Definition: structs.h:540
M_DoDelay
BOOL M_DoDelay(int i)
Definition: monster.cpp:2610
delta_kill_monster
void delta_kill_monster(int mi, BYTE x, BYTE y, BYTE bLevel)
Definition: msg.cpp:310
MAI_Acid
void MAI_Acid(int i)
Definition: monster.cpp:3759
SyncPlrKill
void SyncPlrKill(int pnum, int earflag)
Definition: player.cpp:1884
SpawnUnique
void SpawnUnique(int uid, int x, int y)
Definition: items.cpp:2011
UniqMonstStruct::mint
unsigned char mint
Definition: structs.h:623
all.h
DIR_SE
@ DIR_SE
Definition: enums.h:2089
ViewY
int ViewY
Definition: gendung.cpp:74
QuestStruct::_qactive
unsigned char _qactive
Definition: structs.h:973
uniquetrans
int uniquetrans
Definition: monster.cpp:23
MissileStruct::_miy
int _miy
Definition: structs.h:410
CheckDungeonClear
void CheckDungeonClear()
Definition: debug.cpp:34
AnimStruct::Data
BYTE * Data[8]
Definition: structs.h:478
MT_NSCAV
@ MT_NSCAV
Definition: enums.h:1390
nummtypes
int nummtypes
Definition: monster.cpp:24
MFLAG_KNOCKBACK
@ MFLAG_KNOCKBACK
Definition: enums.h:1510
InitMonster
void InitMonster(int i, int rd, int mtype, int x, int y)
Definition: monster.cpp:384
OBJ_L1LDOOR
@ OBJ_L1LDOOR
Definition: enums.h:2330
M_Teleport
void M_Teleport(int i)
Definition: monster.cpp:2402
UniqMonstStruct::mtype
char mtype
Definition: structs.h:617
UniqMonstStruct
Definition: structs.h:616
MIS_FIREMAN
@ MIS_FIREMAN
Definition: enums.h:1244
MGOAL_MOVE
@ MGOAL_MOVE
Definition: enums.h:1522
USFX_ZHAR2
@ USFX_ZHAR2
Definition: enums.h:1164
UniqMonstStruct::mMagicRes
unsigned short mMagicRes
Definition: structs.h:626
dObject
char dObject[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:19
M_DoWalk2
BOOL M_DoWalk2(int i)
Definition: monster.cpp:1888
MGOAL_INQUIRING
@ MGOAL_INQUIRING
Definition: enums.h:1524
InitMonsterGFX
void InitMonsterGFX(int monst)
Definition: monster.cpp:283
MM_FADEIN
@ MM_FADEIN
Definition: enums.h:2021
themeCount
int themeCount
Definition: gendung.cpp:11
MonsterStruct::mMinDamage
unsigned char mMinDamage
Definition: structs.h:598
DIR_SW
@ DIR_SW
Definition: enums.h:2083
MonsterData::mMagicRes
unsigned short mMagicRes
Definition: structs.h:513
loop_movie
BOOL loop_movie
Should the movie play in a loop.
Definition: movie.cpp:15
SolidLoc
BOOL SolidLoc(int x, int y)
Definition: player.cpp:959
M_DoStone
BOOL M_DoStone(int i)
Definition: monster.cpp:2639
MonsterStruct::_myvel
int _myvel
Definition: structs.h:557
UMT_LAZURUS
@ UMT_LAZURUS
Definition: enums.h:1494
DeleteMonster
void DeleteMonster(int i)
Definition: monster.cpp:1070
LoadFileWithMem
DWORD LoadFileWithMem(const char *pszName, void *p)
Load a file in to the given buffer.
Definition: engine.cpp:830
MGOAL_SHOOT
@ MGOAL_SHOOT
Definition: enums.h:1523
gbMaxPlayers
BYTE gbMaxPlayers
Specifies the maximum number of players in a game, where 1 represents a single player game and 4 repr...
Definition: multi.cpp:34
MIS_FLARE
@ MIS_FLARE
Definition: enums.h:1218
NetSendCmdQuest
void NetSendCmdQuest(BOOL bHiPri, BYTE q)
Definition: msg.cpp:737
USFX_SNOT3
@ USFX_SNOT3
Definition: enums.h:1160
MFILE_SCUBMISB
@ MFILE_SCUBMISB
Definition: enums.h:1305
PlayerStruct::_pownerx
int _pownerx
Definition: structs.h:194
MonsterStruct::_lasty
int _lasty
Definition: structs.h:587
MonsterStruct::_mgoalvar3
int _mgoalvar3
Definition: structs.h:545
MonstConvTbl
char MonstConvTbl[128]
Definition: monstdat.cpp:128
SetPlayerOld
void SetPlayerOld(int pnum)
Definition: player.cpp:1024
AddMissile
int AddMissile(int sx, int sy, int dx, int dy, int midir, int mitype, char micaster, int id, int midam, int spllvl)
Definition: missiles.cpp:2455
MT_RFALLSP
@ MT_RFALLSP
Definition: enums.h:1378
SpawnItem
void SpawnItem(int m, int x, int y, BOOL sendmsg)
Definition: items.cpp:2100
MAXDUNY
#define MAXDUNY
Definition: defs.h:26
PlayerStruct::_px
int _px
Definition: structs.h:190
MAI_AcidUniq
void MAI_AcidUniq(int i)
Definition: monster.cpp:3552
M_StartKill
void M_StartKill(int i, int pnum)
Definition: monster.cpp:1698
MIS_FIREWALL
@ MIS_FIREWALL
Definition: enums.h:1199
NewMonsterAnim
void NewMonsterAnim(int i, AnimStruct *anim, int md)
Definition: monster.cpp:1093
dLight
char dLight[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:27
MonsterData::mName
char * mName
Definition: structs.h:494
LIGHTSIZE
#define LIGHTSIZE
Definition: defs.h:10
zharlib
int zharlib
Definition: themes.cpp:18
sound_get_or_set_music_volume
int sound_get_or_set_music_volume(int volume)
DIR_NE
@ DIR_NE
Definition: enums.h:2087
TEXT_VEIL11
@ TEXT_VEIL11
Definition: enums.h:1630
UniqMonstStruct::mUnqVar1
unsigned char mUnqVar1
Definition: structs.h:628
PlayerStruct::_pMaxMana
int _pMaxMana
Definition: structs.h:264
dPiece
int dPiece[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:26
CheckMonsterHit
BOOL CheckMonsterHit(int m, BOOL *ret)
Definition: monster.cpp:5462
gbProcessPlayers
BOOL gbProcessPlayers
Definition: diablo.cpp:25
MT_HLSPWN
@ MT_HLSPWN
Definition: enums.h:1477
nSolidTable
BOOLEAN nSolidTable[2049]
List of path blocking dPieces.
Definition: gendung.cpp:45
DRLG_MRectTrans
void DRLG_MRectTrans(int x1, int y1, int x2, int y2)
Definition: gendung.cpp:178
M_DoSpStand
BOOL M_DoSpStand(int i)
Definition: monster.cpp:2592
PreSpawnSkeleton
int PreSpawnSkeleton()
Definition: monster.cpp:5360
M_SyncStartKill
void M_SyncStartKill(int i, int x, int y, int pnum)
Definition: monster.cpp:1716
MonsterStruct::_mdir
int _mdir
Definition: structs.h:558
MT_NACID
@ MT_NACID
Definition: enums.h:1420
MAI_Warlord
void MAI_Warlord(int i)
Definition: monster.cpp:4409
RESIST_FIRE
@ RESIST_FIRE
Definition: enums.h:1530
MonsterData::Frames
int Frames[6]
Definition: structs.h:492
assert
#define assert(exp)
Definition: defs.h:168
MT_ILLWEAV
@ MT_ILLWEAV
Definition: enums.h:1406
direction
direction
Definition: enums.h:2081
MissToMonst
void MissToMonst(int i, int x, int y)
Definition: monster.cpp:5067
AddPlrMonstExper
void AddPlrMonstExper(int lvl, int exp, char pmask)
Definition: player.cpp:819
MonsterStruct::_mxoff
int _mxoff
Definition: structs.h:554
TEXT_WARLRD9
@ TEXT_WARLRD9
Definition: enums.h:1657
DoEnding
void DoEnding()
Definition: monster.cpp:2478
TEXT_GARBUD4
@ TEXT_GARBUD4
Definition: enums.h:1694
M_StartHeal
void M_StartHeal(int i)
Definition: monster.cpp:1786
Q_BETRAYER
@ Q_BETRAYER
Definition: enums.h:2563
MonsterStruct::_mgoalvar1
int _mgoalvar1
Definition: structs.h:543
ObjChangeMapResync
void ObjChangeMapResync(int x1, int y1, int x2, int y2)
Definition: objects.cpp:2330
TransVal
char TransVal
Definition: gendung.cpp:23
NUM_MTYPES
@ NUM_MTYPES
Definition: enums.h:1485
MonsterStruct::_mVar4
int _mVar4
Definition: structs.h:573
MAI_Diablo
void MAI_Diablo(int i)
Definition: monster.cpp:3764
AiProc
void(* AiProc[])(int i)
Definition: monster.cpp:70
MonsterData::mArmorClass
unsigned char mArmorClass
Definition: structs.h:511
TEXT_BANNER10
@ TEXT_BANNER10
Definition: enums.h:1567
DebugMonsters
int DebugMonsters[10]
Definition: diablo.cpp:28
TMUSIC_L2
@ TMUSIC_L2
Definition: enums.h:2287
app_fatal
void app_fatal(const char *pszFmt,...)
Definition: appfat.cpp:18
gbRunGame
BOOL gbRunGame
Definition: diablo.cpp:21
MFLAG_NOHEAL
@ MFLAG_NOHEAL
Definition: enums.h:1506
MT_SUCCUBUS
@ MT_SUCCUBUS
Definition: enums.h:1475
DIR_E
@ DIR_E
Definition: enums.h:2088
DEVILUTION_END_NAMESPACE
#define DEVILUTION_END_NAMESPACE
Definition: types.h:10
MM_RATTACK
@ MM_RATTACK
Definition: enums.h:2023
MonsterStruct::packsize
unsigned char packsize
Definition: structs.h:609
nMissileTable
BOOLEAN nMissileTable[2049]
List of missile blocking dPieces.
Definition: gendung.cpp:62
PrintMonstHistory
void PrintMonstHistory(int mt)
Definition: monster.cpp:4977
M_DoRAttack
BOOL M_DoRAttack(int i)
Definition: monster.cpp:2161
MAI_SkelBow
void MAI_SkelBow(int i)
Definition: monster.cpp:3124
plr2monst
const char plr2monst[9]
Definition: monster.cpp:26
TEXT_ZHAR2
@ TEXT_ZHAR2
Definition: enums.h:1696
MIS_ACIDPUD
@ MIS_ACIDPUD
Definition: enums.h:1253
MT_COUNSLR
@ MT_COUNSLR
Definition: enums.h:1479
MonsterData::mMaxDamage
unsigned char mMaxDamage
Definition: structs.h:506
M_Talker
BOOL M_Talker(int i)
Definition: monster.cpp:1111
M_DoRSpAttack
int M_DoRSpAttack(int i)
Definition: monster.cpp:2203
M_DoWalk
BOOL M_DoWalk(int i)
Definition: monster.cpp:1855
monster
MonsterStruct monster[MAXMONSTERS]
Definition: monster.cpp:19
MonsterStruct::_mhitpoints
int _mhitpoints
Definition: structs.h:579
MAI_Snake
void MAI_Snake(int i)
Definition: monster.cpp:2977
left
int left[8]
Definition: monster.cpp:58
MonsterStruct::MData
MonsterData * MData
Definition: structs.h:613
ClrAllMonsters
void ClrAllMonsters()
Definition: monster.cpp:486
PosOkMonst2
BOOL PosOkMonst2(int i, int x, int y)
Definition: monster.cpp:5177
SL_SKELKING
@ SL_SKELKING
Definition: enums.h:2540
rnd5
int rnd5[4]
unused
Definition: monster.cpp:65
AddPanelString
void AddPanelString(char *str, BOOL just)
Definition: control.cpp:550
SpawnGolum
void SpawnGolum(int i, int x, int y, int mi)
Definition: monster.cpp:5416
MIS_DIABAPOCA
@ MIS_DIABAPOCA
Definition: enums.h:1261
MT_SOLBRNR
@ MT_SOLBRNR
Definition: enums.h:1478
MIS_FLAMEC
@ MIS_FLAMEC
Definition: enums.h:1243
Q_VEIL
@ Q_VEIL
Definition: enums.h:2552
PrintUniqueHistory
void PrintUniqueHistory()
Definition: monster.cpp:5042
MonsterStruct::leaderflag
unsigned char leaderflag
Definition: structs.h:608
MonsterStruct::MType
CMonster * MType
Definition: structs.h:612
trigs
TriggerStruct trigs[MAXTRIGGERS]
Definition: trigs.cpp:8
monstimgtot
int monstimgtot
Definition: monster.cpp:22
sgbSaveSoundOn
BOOLEAN sgbSaveSoundOn
Definition: monster.cpp:18
MT_YSCAV
@ MT_YSCAV
Definition: enums.h:1393
M_WalkDir
void M_WalkDir(int i, int md)
Definition: monster.cpp:2652
MWVel
int MWVel[24][3]
Definition: monster.cpp:31
opposite
int opposite[8]
Definition: monster.cpp:60
MT_DIABLO
@ MT_DIABLO
Definition: enums.h:1484
PlayerStruct::_pInvincible
BOOLEAN _pInvincible
Definition: structs.h:234
MT_YZOMBIE
@ MT_YZOMBIE
Definition: enums.h:1377
MT_CLEAVER
@ MT_CLEAVER
Definition: enums.h:1425
themeLoc
THEME_LOC themeLoc[MAXTHEMES]
Definition: gendung.cpp:48
MonstAvailTbl
BYTE MonstAvailTbl[]
0 = Never avalible 1 = Avalible in retail and shareware 2 = avalible in retail only
Definition: monstdat.cpp:149
LoadFileInMem
BYTE * LoadFileInMem(char *pszName, DWORD *pdwFileLen)
Load a file in to a buffer.
Definition: engine.cpp:801
OBJ_L3LDOOR
@ OBJ_L3LDOOR
Definition: enums.h:2403
IMUNE_MAGIC
@ IMUNE_MAGIC
Definition: enums.h:1532
M_StartSpStand
void M_StartSpStand(int i, int md)
Definition: monster.cpp:1239
RemoveInvItem
void RemoveInvItem(int pnum, int iv)
Definition: inv.cpp:1276
UniqMonstStruct::mMaxDamage
unsigned char mMaxDamage
Definition: structs.h:625
PlayerStruct::pDiabloKillLevel
DWORD pDiabloKillLevel
Definition: structs.h:345
MAI_Lazhelp
void MAI_Lazhelp(int i)
Definition: monster.cpp:4336
M_StartWalk
void M_StartWalk(int i, int xvel, int yvel, int xadd, int yadd, int EndDir)
Definition: monster.cpp:1252
MonsterStruct::mMinDamage2
unsigned char mMinDamage2
Definition: structs.h:601
CMonster::Anims
AnimStruct Anims[6]
Definition: structs.h:524
diabquad2x
int diabquad2x
Definition: drlg_l4.cpp:14
MonsterStruct::_mxvel
int _mxvel
Definition: structs.h:556
USFX_DIABLOD
@ USFX_DIABLOD
Definition: enums.h:1165
IMISC_BOOK
@ IMISC_BOOK
Definition: enums.h:2455
CMD_MONSTDAMAGE
@ CMD_MONSTDAMAGE
Definition: enums.h:2208
dMissile
char dMissile[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:76
M_DoSAttack
BOOL M_DoSAttack(int i)
Definition: monster.cpp:2245
AI_WARLORD
@ AI_WARLORD
Definition: enums.h:1348
IDI_BANNER
@ IDI_BANNER
Definition: enums.h:2512
DeleteMonsterList
void DeleteMonsterList()
Definition: monster.cpp:4446
M_StartRSpAttack
void M_StartRSpAttack(int i, int missile_type, int dam)
Definition: monster.cpp:1369
MA_WALK
@ MA_WALK
Definition: enums.h:2035
TEXT_VEIL9
@ TEXT_VEIL9
Definition: enums.h:1628
MonsterData::mAi
char mAi
Definition: structs.h:500
MonsterStruct::mWhoHit
char mWhoHit
Definition: structs.h:594
MT_YFALLSP
@ MT_YFALLSP
Definition: enums.h:1380
MAI_Rhino
void MAI_Rhino(int i)
Definition: monster.cpp:3996
MissileStruct::_miVar1
int _miVar1
Definition: structs.h:444
CMonster::MData
MonsterData * MData
Definition: structs.h:533
MonsterStruct::_menemyx
unsigned char _menemyx
Definition: structs.h:560
MonsterStruct::mMaxDamage
unsigned char mMaxDamage
Definition: structs.h:599
music_stop
void music_stop()
UniqMonstStruct::mTrnName
char * mTrnName
Definition: structs.h:619
MM_ATTACK
@ MM_ATTACK
Definition: enums.h:2017
MA_ATTACK
@ MA_ATTACK
Definition: enums.h:2036
M_StartStand
void M_StartStand(int i, int md)
Definition: monster.cpp:1207
MT_SNOWWICH
@ MT_SNOWWICH
Definition: enums.h:1476
totalmonsters
int totalmonsters
Definition: monster.cpp:20
AddMonsterType
int AddMonsterType(int type, int placeflag)
Definition: monster.cpp:154
M_DoHeal
int M_DoHeal(int i)
Definition: monster.cpp:2306
TalktoMonster
void TalktoMonster(int i)
Definition: monster.cpp:5388
MFLAG_ALLOW_SPECIAL
@ MFLAG_ALLOW_SPECIAL
Definition: enums.h:1505
QUEST_DONE
@ QUEST_DONE
Definition: enums.h:2570
decode_enemy
void decode_enemy(int m, int enemy)
Definition: monster.cpp:5492
CreateTypeItem
void CreateTypeItem(int x, int y, BOOL onlygood, int itype, int imisc, BOOL sendmsg, BOOL delta)
Definition: items.cpp:2226
missileactive
DEVILUTION_BEGIN_NAMESPACE int missileactive[MAXMISSILES]
Definition: missiles.cpp:10
M_DumbWalk
BOOL M_DumbWalk(int i, int md)
Definition: monster.cpp:2813
LoadDiabMonsts
void LoadDiabMonsts()
Definition: monster.cpp:905
GetRndSeed
int GetRndSeed()
Get the current RNG seed.
Definition: engine.cpp:739
M_ChangeLightOffset
void M_ChangeLightOffset(int monst)
Definition: monster.cpp:1803
QUEST_NOTAVAIL
@ QUEST_NOTAVAIL
Definition: enums.h:2567
MonsterStruct::leader
unsigned char leader
Definition: structs.h:607
PlayerStruct::_pownery
int _pownery
Definition: structs.h:195
MissileStruct::_mix
int _mix
Definition: structs.h:409
M_StartAttack
void M_StartAttack(int i)
Definition: monster.cpp:1339
MFILE_SCBSEXPB
@ MFILE_SCBSEXPB
Definition: enums.h:1306
MT_BLINK
@ MT_BLINK
Definition: enums.h:1413
MonsterData::mFlags
int mFlags
Definition: structs.h:501
MonsterStruct::_mVar3
int _mVar3
Definition: structs.h:572
MonsterStruct::_mFlags
int _mFlags
Definition: structs.h:583
MFLAG_GOLEM
@ MFLAG_GOLEM
Definition: enums.h:1508
diabquad4y
int diabquad4y
Definition: drlg_l4.cpp:17
MT_XSKELSD
@ MT_XSKELSD
Definition: enums.h:1401
Q_BLIND
@ Q_BLIND
Definition: enums.h:2556
AI_MEGA
@ AI_MEGA
Definition: enums.h:1343
MAI_Round
void MAI_Round(int i, BOOL special)
Definition: monster.cpp:3431
MonsterStruct::_menemyy
unsigned char _menemyy
Definition: structs.h:561
M_StartRAttack
void M_StartRAttack(int i, int missile_type, int dam)
Definition: monster.cpp:1353
M_RoundWalk
BOOL M_RoundWalk(int i, int md, int *dir)
Definition: monster.cpp:2823
MAI_Zhar
void MAI_Zhar(int i)
Definition: monster.cpp:4184
gbMusicOn
BOOLEAN gbMusicOn
M2MStartKill
void M2MStartKill(int i, int mid)
Definition: monster.cpp:1643
Q_LTBANNER
@ Q_LTBANNER
Definition: enums.h:2555
offset_y
int offset_y[8]
Definition: monster.cpp:62
MAI_Path
BOOL MAI_Path(int i)
Definition: monster.cpp:2934
PlayerStruct::_pBlockFlag
BOOLEAN _pBlockFlag
Definition: structs.h:233
MM_FADEOUT
@ MM_FADEOUT
Definition: enums.h:2022
MT_DFALLSD
@ MT_DFALLSD
Definition: enums.h:1387
M_DoFadein
BOOL M_DoFadein(int i)
Definition: monster.cpp:2265
PlaySFX
void PlaySFX(int psfx)
Definition: effects.cpp:1043
StartPlrBlock
void StartPlrBlock(int pnum, int dir)
Definition: player.cpp:1438
MonsterStruct::_pathcount
unsigned char _pathcount
Definition: structs.h:547
MT_SKING
@ MT_SKING
Definition: enums.h:1424
MAI_Lachdanan
void MAI_Lachdanan(int i)
Definition: monster.cpp:4370
MFLAG_NO_ENEMY
@ MFLAG_NO_ENEMY
Definition: enums.h:1513
AnimStruct::CMem
BYTE * CMem
Definition: structs.h:477
UMT_SKELKING
@ UMT_SKELKING
Definition: enums.h:1491
MGOAL_TALKING
@ MGOAL_TALKING
Definition: enums.h:1525
InitLevelMonsters
void InitLevelMonsters()
Definition: monster.cpp:131
MFLAG_SEARCH
@ MFLAG_SEARCH
Definition: enums.h:1511
setlevel
BOOLEAN setlevel
Definition: gendung.cpp:65
pcursmonst
int pcursmonst
Definition: cursor.cpp:13
MonsterData::mMinHP
int mMinHP
Definition: structs.h:498
MonsterData::mLevel
char mLevel
Definition: structs.h:497
MonsterStruct::field_18
int field_18
Definition: structs.h:546
CMD_KILLGOLEM
@ CMD_KILLGOLEM
Definition: enums.h:2258
MissileStruct
Definition: structs.h:407
InitQTextMsg
void InitQTextMsg(int m)
Definition: minitext.cpp:70
Q_WARLORD
@ Q_WARLORD
Definition: enums.h:2559
MT_STORM
@ MT_STORM
Definition: enums.h:1450
pinfoflag
BOOL pinfoflag
Definition: control.cpp:33
MFLAG_CAN_OPEN_DOOR
@ MFLAG_CAN_OPEN_DOOR
Definition: enums.h:1512
MT_RFALLSD
@ MT_RFALLSD
Definition: enums.h:1386
MAI_Fat
void MAI_Fat(int i)
Definition: monster.cpp:3168
MFLAG_NOLIFESTEAL
@ MFLAG_NOLIFESTEAL
Definition: enums.h:1515
MAI_GoatMc
void MAI_GoatMc(int i)
Definition: monster.cpp:3490
MonsterData::mHit2
unsigned char mHit2
Definition: structs.h:507
MAI_Magma
void MAI_Magma(int i)
Definition: monster.cpp:3749
cineflag
BOOLEAN cineflag
Definition: diablo.cpp:29
MAI_Lazurus
void MAI_Lazurus(int i)
Definition: monster.cpp:4284
monstkills
int monstkills[MAXMONSTERS]
Definition: monster.cpp:15
MFILE_MAGBALL
@ MFILE_MAGBALL
Definition: enums.h:1289
M_ClearSquares
void M_ClearSquares(int i)
Definition: monster.cpp:1412
tempstr
char tempstr[256]
Definition: control.cpp:41
AI_LAZHELP
@ AI_LAZHELP
Definition: enums.h:1346
monstactive
int monstactive[MAXMONSTERS]
Definition: monster.cpp:16
MonsterStruct::_mVar7
int _mVar7
Definition: structs.h:576
MIS_LIGHTNING
@ MIS_LIGHTNING
Definition: enums.h:1202
MonsterStruct::_msquelch
BYTE _msquelch
Definition: structs.h:584
DoVision
void DoVision(int nXPos, int nYPos, int nRadius, BOOL doautomap, BOOL visible)
Definition: lighting.cpp:696
IMISC_NONE
@ IMISC_NONE
Definition: enums.h:2431
FindPath
int FindPath(BOOL(*PosOk)(int, int, int), int PosOkArg, int sx, int sy, int dx, int dy, char *path)
find the shortest path from (sx,sy) to (dx,dy), using PosOk(PosOkArg,x,y) to check that each step is ...
Definition: path.cpp:51
DIFF_NIGHTMARE
@ DIFF_NIGHTMARE
Definition: enums.h:2007
MT_YFALLSD
@ MT_YFALLSD
Definition: enums.h:1388
InitMonsters
void InitMonsters()
Definition: monster.cpp:924
THEME_LOC::y
int y
Definition: structs.h:1115
PlaceGroup
void PlaceGroup(int mtype, int num, int leaderf, int leader)
Definition: monster.cpp:832
nummonsters
int nummonsters
Definition: monster.cpp:17
MT_GGOATMC
@ MT_GGOATMC
Definition: enums.h:1411
MonsterData::Rate
int Rate[6]
Definition: structs.h:493
M_StartFadeout
void M_StartFadeout(int i, int md, BOOL backwards)
Definition: monster.cpp:1764
M_StartDelay
void M_StartDelay(int i, int len)
Definition: monster.cpp:1227
dTransVal
char dTransVal[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:37
MM_SATTACK
@ MM_SATTACK
Definition: enums.h:2020
MFILE_SCBSEXPC
@ MFILE_SCBSEXPC
Definition: enums.h:1308
SetMapMonsters
void SetMapMonsters(BYTE *pMap, int startx, int starty)
Definition: monster.cpp:1029
MFILE_THINLGHT
@ MFILE_THINLGHT
Definition: enums.h:1286
PosOkMonst3
BOOL PosOkMonst3(int i, int x, int y)
Definition: monster.cpp:5209
TEXT_BANNER11
@ TEXT_BANNER11
Definition: enums.h:1568
CMonster::width2
int width2
Definition: structs.h:527
MT_MAEL
@ MT_MAEL
Definition: enums.h:1453
CMonster::mPlaceFlags
unsigned char mPlaceFlags
Definition: structs.h:523
MonsterStruct::_moldy
int _moldy
Definition: structs.h:553
MFILE_SCUBMISD
@ MFILE_SCUBMISD
Definition: enums.h:1309
MM_HEAL
@ MM_HEAL
Definition: enums.h:2029
PlrHasItem
ItemStruct * PlrHasItem(int pnum, int item, int *i)
Definition: towners.cpp:558
setlvlnum
BYTE setlvlnum
Definition: gendung.cpp:58
MM_STONE
@ MM_STONE
Definition: enums.h:2028
MonsterStruct
Definition: structs.h:539
DIR_S
@ DIR_S
Definition: enums.h:2082
MT_UNSEEN
@ MT_UNSEEN
Definition: enums.h:1405
CMonster::mMaxHP
unsigned char mMaxHP
Definition: structs.h:529
MAI_GoatBow
void MAI_GoatBow(int i)
Definition: monster.cpp:3542
M_StartHit
void M_StartHit(int i, int pnum, int dam)
Definition: monster.cpp:1459
alltext
const DEVILUTION_BEGIN_NAMESPACE TextDataStruct alltext[]
Contains the data related to each speech ID.
Definition: textdat.cpp:8
MFILE_FLAREEXP
@ MFILE_FLAREEXP
Definition: enums.h:1288
MT_WSKELSD
@ MT_WSKELSD
Definition: enums.h:1398
IDI_GLDNELIX
@ IDI_GLDNELIX
Definition: enums.h:2515
myplr
int myplr
Definition: player.cpp:9
MM_WALK2
@ MM_WALK2
Definition: enums.h:2015
delta_monster_hp
void delta_monster_hp(int mi, int hp, BYTE bLevel)
Definition: msg.cpp:324
M_FallenFear
void M_FallenFear(int x, int y)
Definition: monster.cpp:4938
MonsterStruct::_uniqtrans
unsigned char _uniqtrans
Definition: structs.h:592
MM_GOTHIT
@ MM_GOTHIT
Definition: enums.h:2018
random_
int random_(BYTE idx, int v)
Main RNG function.
Definition: engine.cpp:752
PlayerStruct::_pHitPoints
int _pHitPoints
Definition: structs.h:258
MM_RSPATTACK
@ MM_RSPATTACK
Definition: enums.h:2025
Q_SKELKING
@ Q_SKELKING
Definition: enums.h:2560
OBJ_L2RDOOR
@ OBJ_L2RDOOR
Definition: enums.h:2372
M_DoStand
BOOL M_DoStand(int i)
Definition: monster.cpp:1832
RESIST_LIGHTNING
@ RESIST_LIGHTNING
Definition: enums.h:1531
MM_TALK
@ MM_TALK
Definition: enums.h:2030
mem_free_dbg
void mem_free_dbg(void *p)
Multithreaded safe memfree.
Definition: engine.cpp:786
ITYPE_MISC
@ ITYPE_MISC
Definition: enums.h:2480
PC_WARRIOR
@ PC_WARRIOR
Definition: enums.h:2706
MFLAG_LOCK_ANIMATION
@ MFLAG_LOCK_ANIMATION
Definition: enums.h:1504
nummissiles
int nummissiles
Definition: missiles.cpp:13
Q_ZHAR
@ Q_ZHAR
Definition: enums.h:2551
USFX_CLEAVER
@ USFX_CLEAVER
Definition: enums.h:1146
DirOK
BOOL DirOK(int i, int mdir)
Definition: monster.cpp:4634
UniqMonstStruct::mtalkmsg
int mtalkmsg
Definition: structs.h:630
M_DoWalk3
BOOL M_DoWalk3(int i)
Definition: monster.cpp:1919
M_SpawnSkel
int M_SpawnSkel(int x, int y, int dir)
Definition: monster.cpp:5266
QUEST_ACTIVE
@ QUEST_ACTIVE
Definition: enums.h:2569
QuestStruct::_qvar1
unsigned char _qvar1
Definition: structs.h:980
MFLAG_QUEST_COMPLETE
@ MFLAG_QUEST_COMPLETE
Definition: enums.h:1509
MFILE_KRULL
@ MFILE_KRULL
Definition: enums.h:1290
MonsterData::mInt
unsigned char mInt
Definition: structs.h:502
LoadMissileGFX
void LoadMissileGFX(BYTE mi)
Definition: missiles.cpp:1098
SpawnSkeleton
BOOL SpawnSkeleton(int ii, int x, int y)
Definition: monster.cpp:5305
AnimStruct
Definition: structs.h:476
MIS_FLASH2
@ MIS_FLASH2
Definition: enums.h:1206
MIS_LIGHTCTRL
@ MIS_LIGHTCTRL
Definition: enums.h:1201
MT_NGOATBW
@ MT_NGOATBW
Definition: enums.h:1416
UniqMonstStruct::mMinDamage
unsigned char mMinDamage
Definition: structs.h:624
MonsterStruct::_myoff
int _myoff
Definition: structs.h:555
CMonster::mMinHP
unsigned char mMinHP
Definition: structs.h:528
M_StartFadein
void M_StartFadein(int i, int md, BOOL backwards)
Definition: monster.cpp:1741
dMonster
int dMonster[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:17
PrepDoEnding
void PrepDoEnding()
Definition: monster.cpp:2520
rnd60
int rnd60[4]
Definition: monster.cpp:68
DEVILUTION_BEGIN_NAMESPACE
Definition: sha.cpp:10
NetSendCmdParam2
void NetSendCmdParam2(BOOL bHiPri, BYTE bCmd, WORD wParam1, WORD wParam2)
Definition: msg.cpp:710
QuestStruct::_qlog
int _qlog
Definition: structs.h:982
MonsterStruct::_mfuty
int _mfuty
Definition: structs.h:551
MissileStruct::_misource
int _misource
Definition: structs.h:437
M_Enemy
void M_Enemy(int i)
Definition: monster.cpp:1123
M_DoDeath
BOOL M_DoDeath(int i)
Definition: monster.cpp:2548
MonsterStruct::mName
char * mName
Definition: structs.h:611
UniqMonstStruct::mUnqAttr
unsigned short mUnqAttr
Definition: structs.h:627
MT_NMAGMA
@ MT_NMAGMA
Definition: enums.h:1434
Q_DIABLO
@ Q_DIABLO
Definition: enums.h:2553
BFLAG_POPULATED
@ BFLAG_POPULATED
Definition: enums.h:1880
ObjChangeMap
void ObjChangeMap(int x1, int y1, int x2, int y2)
Definition: objects.cpp:2310
MT_WSKELBW
@ MT_WSKELBW
Definition: enums.h:1394
PlayerStruct::_py
int _py
Definition: structs.h:191
MAI_Succ
void MAI_Succ(int i)
Definition: monster.cpp:3547
MonsterStruct::_lastx
int _lastx
Definition: structs.h:586
MonsterData::mImage
int mImage
Definition: structs.h:485
MonsterData::mMagicRes2
unsigned short mMagicRes2
Definition: structs.h:514
MonsterStruct::mMagicRes
unsigned short mMagicRes
Definition: structs.h:605
gbSoundOn
BOOLEAN gbSoundOn
MT_HELLBURN
@ MT_HELLBURN
Definition: enums.h:1449
USFX_GARBUD4
@ USFX_GARBUD4
Definition: enums.h:1150
MIS_ARROW
@ MIS_ARROW
Definition: enums.h:1194
diabquad4x
int diabquad4x
Definition: drlg_l4.cpp:16
PM_QUIT
@ PM_QUIT
Definition: enums.h:2054
dPlayer
char dPlayer[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:49
NetSendCmdGolem
void NetSendCmdGolem(BYTE mx, BYTE my, BYTE dir, BYTE menemy, int hp, BYTE cl)
Definition: msg.cpp:626
diabquad1x
DEVILUTION_BEGIN_NAMESPACE int diabquad1x
Definition: drlg_l4.cpp:10
dFlags
char dFlags[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:56
MGOAL_RETREAT
@ MGOAL_RETREAT
Definition: enums.h:1520
MonsterStruct::_mAnimCnt
int _mAnimCnt
Definition: structs.h:565
TEXT_GARBUD2
@ TEXT_GARBUD2
Definition: enums.h:1692
ITYPE_MACE
@ ITYPE_MACE
Definition: enums.h:2484
AnimStruct::Rate
int Rate
Definition: structs.h:480
MonsterStruct::mHit2
unsigned char mHit2
Definition: structs.h:600
right
int right[8]
Definition: monster.cpp:59
QUEST_INIT
@ QUEST_INIT
Definition: enums.h:2568
InitMonsterSND
void InitMonsterSND(int monst)
Definition: effects.cpp:942
numtrigs
int numtrigs
Definition: trigs.cpp:7
PlayerStruct::_pmode
int _pmode
Definition: structs.h:179
AI_SUCC
@ AI_SUCC
Definition: enums.h:1331
MonsterStruct::_mgoalvar2
int _mgoalvar2
Definition: structs.h:544
PlayerStruct::WorldY
int WorldY
Definition: structs.h:189
MonsterStruct::_my
int _my
Definition: structs.h:549
PM_STAND
@ PM_STAND
Definition: enums.h:2043
AnimStruct::Frames
int Frames
Definition: structs.h:479
MFLAG_HIDDEN
@ MFLAG_HIDDEN
Definition: enums.h:1503
PlaceUniqueMonst
void PlaceUniqueMonst(int uniqindex, int miniontype, int unpackfilesize)
Definition: monster.cpp:556
MonsterStruct::_mAnimData
unsigned char * _mAnimData
Definition: structs.h:563
ActivateSpawn
void ActivateSpawn(int i, int x, int y, int dir)
Definition: monster.cpp:5293
MFILE_FIREPLAR
@ MFILE_FIREPLAR
Definition: enums.h:1304
monsterdata
DEVILUTION_BEGIN_NAMESPACE MonsterData monsterdata[]
Definition: monstdat.cpp:10
USFX_LAZ1
@ USFX_LAZ1
Definition: enums.h:1155
MFILE_SCBSEXPD
@ MFILE_SCBSEXPD
Definition: enums.h:1310
MFILE_FLARE
@ MFILE_FLARE
Definition: enums.h:1287
M2MStartHit
void M2MStartHit(int mid, int i, int dam)
Definition: monster.cpp:1548
MAI_Sneak
void MAI_Sneak(int i)
Definition: monster.cpp:3203
setpc_y
int setpc_y
Definition: gendung.cpp:75
MIS_RHINO
@ MIS_RHINO
Definition: enums.h:1214
MissileFileFlag
DEVILUTION_BEGIN_NAMESPACE int MissileFileFlag
Tracks which missile files are already loaded.
Definition: monster.cpp:12
MM_DELAY
@ MM_DELAY
Definition: enums.h:2026
BFLAG_MONSTLR
@ BFLAG_MONSTLR
Definition: enums.h:1881
IsGoat
BOOL IsGoat(int mt)
Definition: monster.cpp:5260
AI_ZHAR
@ AI_ZHAR
Definition: enums.h:1339
M_DoTalk
int M_DoTalk(int i)
Definition: monster.cpp:2333
MA_GOTHIT
@ MA_GOTHIT
Definition: enums.h:2037
CMonster::mtype
unsigned char mtype
Definition: structs.h:521
UMT_BLACKJADE
@ UMT_BLACKJADE
Definition: enums.h:1496
MIS_KRULL
@ MIS_KRULL
Definition: enums.h:1245
AI_GARBUD
@ AI_GARBUD
Definition: enums.h:1335
LineClearF
BOOL LineClearF(BOOL(*Clear)(int, int), int x1, int y1, int x2, int y2)
Definition: monster.cpp:4701
MAXMONSTERS
#define MAXMONSTERS
Definition: defs.h:31
MonsterStruct::_mVar6
int _mVar6
Definition: structs.h:575
MT_GSNAKE
@ MT_GSNAKE
Definition: enums.h:1466
TEXT_GARBUD1
@ TEXT_GARBUD1
Definition: enums.h:1691
diabquad3y
int diabquad3y
Definition: drlg_l4.cpp:13
dDead
char dDead[MAXDUNX][MAXDUNY]
Definition: gendung.cpp:21
animletter
char animletter[7]
Definition: monster.cpp:57
plr
PlayerStruct plr[MAX_PLRS]
Definition: player.cpp:10
MT_XACID
@ MT_XACID
Definition: enums.h:1423
BFLAG_VISIBLE
@ BFLAG_VISIBLE
Definition: enums.h:1878
MIS_FIREBOLT
@ MIS_FIREBOLT
Definition: enums.h:1195
PC_SORCERER
@ PC_SORCERER
Definition: enums.h:2708
MA_STAND
@ MA_STAND
Definition: enums.h:2034
InitMonsterTRN
void InitMonsterTRN(int monst, BOOL special)
Definition: monster.cpp:105
rnd10
int rnd10[4]
Definition: monster.cpp:66
MonsterData::mMaxDLvl
char mMaxDLvl
Definition: structs.h:496
lightmax
char lightmax
Definition: lighting.cpp:17
counsmiss
const BYTE counsmiss[4]
Definition: monster.cpp:27
MAI_Bat
void MAI_Bat(int i)
Definition: monster.cpp:3060
IMUNE_LIGHTNING
@ IMUNE_LIGHTNING
Definition: enums.h:1534
MM_STAND
@ MM_STAND
Definition: enums.h:2013
encode_enemy
int encode_enemy(int m)
Definition: monster.cpp:5484
Q_BLOOD
@ Q_BLOOD
Definition: enums.h:2557
LineClearF1
BOOL LineClearF1(BOOL(*Clear)(int, int, int), int monst, int x1, int y1, int x2, int y2)
Definition: monster.cpp:4788
monstdebug
BOOL monstdebug
Definition: diablo.cpp:36
MM_CHARGE
@ MM_CHARGE
Definition: enums.h:2027
ISPL_THORNS
@ ISPL_THORNS
Definition: enums.h:2779
MonsterData::mExp
unsigned short mExp
Definition: structs.h:517
MAI_RoundRanged
void MAI_RoundRanged(int i, int missile_type, BOOL checkdoors, int dam, int lessmissiles)
Definition: monster.cpp:3684
DoUnVision
void DoUnVision(int nXPos, int nYPos, int nRadius)
Definition: lighting.cpp:666
IsSkel
BOOL IsSkel(int mt)
Definition: monster.cpp:5253
TEXT_VILE13
@ TEXT_VILE13
Definition: enums.h:1582
deathflag
BOOL deathflag
Definition: player.cpp:13
MonsterStruct::_menemy
int _menemy
Definition: structs.h:559
Q_GARBUD
@ Q_GARBUD
Definition: enums.h:2550
CMD_MONSTDEATH
@ CMD_MONSTDEATH
Definition: enums.h:2207
M_StartWalk2
void M_StartWalk2(int i, int xvel, int yvel, int xoff, int yoff, int xadd, int yadd, int EndDir)
Definition: monster.cpp:1275
MAI_Mega
void MAI_Mega(int i)
Definition: monster.cpp:3844
PM_ATTACK
@ PM_ATTACK
Definition: enums.h:2047
CMonster::has_special
BOOL has_special
Definition: structs.h:530
MT_FAMILIAR
@ MT_FAMILIAR
Definition: enums.h:1415
MM_WALK3
@ MM_WALK3
Definition: enums.h:2016
MT_INCIN
@ MT_INCIN
Definition: enums.h:1446
MM_DEATH
@ MM_DEATH
Definition: enums.h:2019
MonsterData::mMaxDamage2
unsigned char mMaxDamage2
Definition: structs.h:510
MAI_Cleaver
void MAI_Cleaver(int i)
Definition: monster.cpp:3401
PlayerStruct::_pDexterity
int _pDexterity
Definition: structs.h:249
MT_BFALLSD
@ MT_BFALLSD
Definition: enums.h:1389
UMT_LACHDAN
@ UMT_LACHDAN
Definition: enums.h:1497