1 /* Catacomb Armageddon Source Code
\r
2 * Copyright (C) 1993-2014 Flat Rock Software
\r
4 * This program is free software; you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation; either version 2 of the License, or
\r
7 * (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License along
\r
15 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
25 =============================================================================
\r
29 =============================================================================
\r
34 =============================================================================
\r
38 =============================================================================
\r
44 =============================================================================
\r
48 =============================================================================
\r
52 dirtype opposite[9] =
\r
53 {south,west,north,east,southwest,northwest,northeast,southeast,nodir};
\r
57 //===========================================================================
\r
63 = Internal_SpawnNewObj
\r
67 void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat)
\r
69 extern objtype dummyobj;
\r
71 GetNewObj(UseDummy);
\r
74 new->ticcount = random (state->tictime)+1;
\r
78 new->x = ((long)x<<TILESHIFT)+TILEGLOBAL/2;
\r
79 new->y = ((long)y<<TILESHIFT)+TILEGLOBAL/2;
\r
82 new->active = noalways;
\r
84 if (new != &dummyobj && PutInActorat)
\r
85 actorat[new->tilex][new->tiley] = new;
\r
88 void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy)
\r
90 GetNewObj(UseDummy);
\r
93 new->ticcount = random (state->tictime)+1;
\r
94 new->active = noalways;
\r
98 new->tilex = x>>TILESHIFT;
\r
99 new->tiley = y>>TILESHIFT;
\r
101 new->distance = 100;
\r
109 ===================
\r
113 = If the object can move next to the player, it will return true
\r
115 ===================
\r
118 boolean CheckHandAttack (objtype *ob)
\r
120 long deltax,deltay,size;
\r
122 size = (long)ob->size + player->size + ob->speed*tics + SIZE_TEST;
\r
123 deltax = ob->x - player->x;
\r
124 deltay = ob->y - player->y;
\r
126 if (deltax > size || deltax < -size || deltay > size || deltay < -size)
\r
134 ===================
\r
138 = Attacks the player if still nearby, then immediately changes to next state
\r
140 ===================
\r
143 void T_DoDamage (objtype *ob)
\r
148 if (CheckHandAttack(ob) && (!(ob->flags & of_damagedone)))
\r
152 switch (ob->obclass)
\r
179 TakeDamage (points);
\r
180 ob->flags |= of_damagedone;
\r
185 //==========================================================================
\r
188 ==================================
\r
192 ==================================
\r
195 boolean Walk (objtype *ob)
\r
200 if (actorat[ob->tilex][ob->tiley-1])
\r
203 ob->distance = TILEGLOBAL;
\r
207 if (actorat[ob->tilex+1][ob->tiley-1])
\r
211 ob->distance = TILEGLOBAL;
\r
215 if (actorat[ob->tilex+1][ob->tiley])
\r
218 ob->distance = TILEGLOBAL;
\r
222 if (actorat[ob->tilex+1][ob->tiley+1])
\r
226 ob->distance = TILEGLOBAL;
\r
230 if (actorat[ob->tilex][ob->tiley+1])
\r
233 ob->distance = TILEGLOBAL;
\r
237 if (actorat[ob->tilex-1][ob->tiley+1])
\r
241 ob->distance = TILEGLOBAL;
\r
245 if (actorat[ob->tilex-1][ob->tiley])
\r
248 ob->distance = TILEGLOBAL;
\r
252 if (actorat[ob->tilex-1][ob->tiley-1])
\r
256 ob->distance = TILEGLOBAL;
\r
263 Quit ("Walk: Bad dir");
\r
270 ==================================
\r
273 = have the current monster go after the player,
\r
274 = either diagonally or straight on
\r
276 ==================================
\r
279 void ChaseThink (objtype *obj, boolean diagonal)
\r
281 int deltax,deltay,i;
\r
283 dirtype tdir, olddir, turnaround;
\r
287 turnaround=opposite[olddir];
\r
289 deltax=player->tilex - obj->tilex;
\r
290 deltay=player->tiley - obj->tiley;
\r
304 if (abs(deltay)>abs(deltax))
\r
311 if (d[1]==turnaround)
\r
313 if (d[2]==turnaround)
\r
318 { /*ramdiagonals try the best dir first*/
\r
323 return; /*either moved forward or attacked*/
\r
334 { /*ramstraights try the second best dir first*/
\r
351 /* there is no direct path to the player, so pick another direction */
\r
357 if (US_RndT()>128) /*randomly determine direction of search*/
\r
359 for (tdir=north;tdir<=west;tdir++)
\r
361 if (tdir!=turnaround)
\r
371 for (tdir=west;tdir>=north;tdir--)
\r
373 if (tdir!=turnaround)
\r
382 obj->dir=turnaround;
\r
383 Walk(obj); /*last chance, don't worry about returned value*/
\r
395 void MoveObj (objtype *ob, long move)
\r
397 ob->distance -=move;
\r
441 = returns true if hand attack range
\r
446 boolean Chase (objtype *ob, boolean diagonal)
\r
449 long deltax,deltay,size;
\r
451 ob->flags &= ~of_damagedone;
\r
453 move = ob->speed*tics;
\r
454 size = (long)ob->size + player->size + move + SIZE_TEST;
\r
458 deltax = ob->x - player->x;
\r
459 deltay = ob->y - player->y;
\r
461 if (deltax <= size && deltax >= -size
\r
462 && deltay <= size && deltay >= -size)
\r
468 if (move < ob->distance)
\r
473 actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal
\r
474 if (ob->dir == nodir)
\r
477 ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;
\r
478 ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
\r
479 move -= ob->distance;
\r
481 ChaseThink (ob,diagonal);
\r
483 break; // no possible move
\r
484 actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker
\r
490 //===========================================================================
\r
494 ===================
\r
498 ===================
\r
501 void ShootActor (objtype *ob, unsigned damage)
\r
503 ob->hitpoints -= damage;
\r
505 if (ob->hitpoints<=0)
\r
507 switch (ob->obclass)
\r
510 ob->state = &s_red_demondie1;
\r
513 ob->state = &s_succubus_death1;
\r
516 ob->state = &s_fatdemon_blowup1;
\r
519 ob->state = &s_godessdie1;
\r
522 ob->state = &s_magedie1;
\r
525 ob->state = &s_batdie1;
\r
527 ob->obclass = solidobj; // don't add this obj to inert list
\r
531 ob->state = &s_greldie1;
\r
535 ob->state = &s_zombie_death1;
\r
539 ob->state = &s_skel_die1;
\r
543 ob->state = &s_ant_die1;
\r
547 ob->state = &s_wet_die1;
\r
549 ob->obclass = solidobj; // don't add this obj to inert list
\r
554 ob->state = &s_eye_die1;
\r
560 ob->state = &s_bonus_die;
\r
562 ob->obclass = solidobj; // don't add these objs to inert list
\r
567 ob->state = &s_tree_death1;
\r
568 ob->obclass = solidobj;
\r
570 ob->flags &= ~of_damagedone;
\r
575 ob->state = &s_bunny_death1;
\r
586 ob->state = &s_pshot_exp1;
\r
587 ob->obclass = expobj;
\r
588 ob->ticcount = ob->state->tictime;
\r
589 SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L));
\r
590 bordertime = FLASHTICS<<2;
\r
592 VW_ColorBorder(14 | 56);
\r
593 DisplaySMsg("Item destroyed", NULL);
\r
594 status_flag = S_NONE;
\r
599 ob->obclass = solidobj; // don't add this obj to inert list
\r
605 if (ob->obclass != solidobj && ob->obclass != realsolidobj)
\r
607 ob->obclass = inertobj;
\r
608 ob->flags &= ~of_shootable;
\r
609 actorat[ob->tilex][ob->tiley] = NULL;
\r
611 MoveObjToInert(ob);
\r
616 if (ob->flags & of_forcefield)
\r
618 ob->state = &s_force_field_die;
\r
619 ob->flags &= ~of_shootable;
\r
625 switch (ob->obclass)
\r
629 ob->state = &s_red_demonouch;
\r
634 ob->state = &s_succubus_ouch;
\r
637 ob->state = &s_fatdemon_ouch;
\r
640 ob->state = &s_godessouch;
\r
643 ob->state = &s_mageouch;
\r
647 ob->state = &s_grelouch;
\r
651 ob->state = &s_zombie_ouch;
\r
655 ob->state = &s_ant_ouch;
\r
659 ob->state = &s_skel_ouch;
\r
663 ob->state = &s_wet_ouch;
\r
667 ob->state = &s_eye_ouch;
\r
671 ob->state = &s_tree_ouch;
\r
675 ob->state = &s_bunny_ouch;
\r
679 ob->ticcount = ob->state->tictime;
\r