wolf3d

The original open source release of Wolfenstein 3D
Log | Files | Refs

WL_AGENT.C (22576B)


      1 // WL_AGENT.C
      2 
      3 #include "WL_DEF.H"
      4 #pragma hdrstop
      5 
      6 
      7 /*
      8 =============================================================================
      9 
     10 						 LOCAL CONSTANTS
     11 
     12 =============================================================================
     13 */
     14 
     15 #define MAXMOUSETURN	10
     16 
     17 
     18 #define MOVESCALE		150l
     19 #define BACKMOVESCALE	100l
     20 #define ANGLESCALE		20
     21 
     22 /*
     23 =============================================================================
     24 
     25 						 GLOBAL VARIABLES
     26 
     27 =============================================================================
     28 */
     29 
     30 
     31 
     32 //
     33 // player state info
     34 //
     35 boolean		running;
     36 long		thrustspeed;
     37 
     38 unsigned	plux,pluy;			// player coordinates scaled to unsigned
     39 
     40 int			anglefrac;
     41 int			gotgatgun;	// JR
     42 
     43 objtype		*LastAttacker;
     44 
     45 /*
     46 =============================================================================
     47 
     48 						 LOCAL VARIABLES
     49 
     50 =============================================================================
     51 */
     52 
     53 
     54 void	T_Player (objtype *ob);
     55 void	T_Attack (objtype *ob);
     56 
     57 statetype s_player = {false,0,0,T_Player,NULL,NULL};
     58 statetype s_attack = {false,0,0,T_Attack,NULL,NULL};
     59 
     60 
     61 long	playerxmove,playerymove;
     62 
     63 struct atkinf
     64 {
     65 	char	tics,attack,frame;		// attack is 1 for gun, 2 for knife
     66 } attackinfo[4][14] =
     67 
     68 {
     69 { {6,0,1},{6,2,2},{6,0,3},{6,-1,4} },
     70 { {6,0,1},{6,1,2},{6,0,3},{6,-1,4} },
     71 { {6,0,1},{6,1,2},{6,3,3},{6,-1,4} },
     72 { {6,0,1},{6,1,2},{6,4,3},{6,-1,4} },
     73 };
     74 
     75 
     76 int	strafeangle[9] = {0,90,180,270,45,135,225,315,0};
     77 
     78 void DrawWeapon (void);
     79 void GiveWeapon (int weapon);
     80 void	GiveAmmo (int ammo);
     81 
     82 //===========================================================================
     83 
     84 //----------
     85 
     86 void Attack (void);
     87 void Use (void);
     88 void Search (objtype *ob);
     89 void SelectWeapon (void);
     90 void SelectItem (void);
     91 
     92 //----------
     93 
     94 boolean TryMove (objtype *ob);
     95 void T_Player (objtype *ob);
     96 
     97 void ClipMove (objtype *ob, long xmove, long ymove);
     98 
     99 /*
    100 =============================================================================
    101 
    102 						CONTROL STUFF
    103 
    104 =============================================================================
    105 */
    106 
    107 /*
    108 ======================
    109 =
    110 = CheckWeaponChange
    111 =
    112 = Keys 1-4 change weapons
    113 =
    114 ======================
    115 */
    116 
    117 void CheckWeaponChange (void)
    118 {
    119 	int	i,buttons;
    120 
    121 	if (!gamestate.ammo)		// must use knife with no ammo
    122 		return;
    123 
    124 	for (i=wp_knife ; i<=gamestate.bestweapon ; i++)
    125 		if (buttonstate[bt_readyknife+i-wp_knife])
    126 		{
    127 			gamestate.weapon = gamestate.chosenweapon = i;
    128 			DrawWeapon ();
    129 			return;
    130 		}
    131 }
    132 
    133 
    134 /*
    135 =======================
    136 =
    137 = ControlMovement
    138 =
    139 = Takes controlx,controly, and buttonstate[bt_strafe]
    140 =
    141 = Changes the player's angle and position
    142 =
    143 = There is an angle hack because when going 70 fps, the roundoff becomes
    144 = significant
    145 =
    146 =======================
    147 */
    148 
    149 void ControlMovement (objtype *ob)
    150 {
    151 	long	oldx,oldy;
    152 	int		angle,maxxmove;
    153 	int		angleunits;
    154 	long	speed;
    155 
    156 	thrustspeed = 0;
    157 
    158 	oldx = player->x;
    159 	oldy = player->y;
    160 
    161 //
    162 // side to side move
    163 //
    164 	if (buttonstate[bt_strafe])
    165 	{
    166 	//
    167 	// strafing
    168 	//
    169 	//
    170 		if (controlx > 0)
    171 		{
    172 			angle = ob->angle - ANGLES/4;
    173 			if (angle < 0)
    174 				angle += ANGLES;
    175 			Thrust (angle,controlx*MOVESCALE);	// move to left
    176 		}
    177 		else if (controlx < 0)
    178 		{
    179 			angle = ob->angle + ANGLES/4;
    180 			if (angle >= ANGLES)
    181 				angle -= ANGLES;
    182 			Thrust (angle,-controlx*MOVESCALE);	// move to right
    183 		}
    184 	}
    185 	else
    186 	{
    187 	//
    188 	// not strafing
    189 	//
    190 		anglefrac += controlx;
    191 		angleunits = anglefrac/ANGLESCALE;
    192 		anglefrac -= angleunits*ANGLESCALE;
    193 		ob->angle -= angleunits;
    194 
    195 		if (ob->angle >= ANGLES)
    196 			ob->angle -= ANGLES;
    197 		if (ob->angle < 0)
    198 			ob->angle += ANGLES;
    199 
    200 	}
    201 
    202 //
    203 // forward/backwards move
    204 //
    205 	if (controly < 0)
    206 	{
    207 		Thrust (ob->angle,-controly*MOVESCALE);	// move forwards
    208 	}
    209 	else if (controly > 0)
    210 	{
    211 		angle = ob->angle + ANGLES/2;
    212 		if (angle >= ANGLES)
    213 			angle -= ANGLES;
    214 		Thrust (angle,controly*BACKMOVESCALE);		// move backwards
    215 	}
    216 
    217 	if (gamestate.victoryflag)		// watching the BJ actor
    218 		return;
    219 
    220 //
    221 // calculate total move
    222 //
    223 	playerxmove = player->x - oldx;
    224 	playerymove = player->y - oldy;
    225 }
    226 
    227 /*
    228 =============================================================================
    229 
    230 					STATUS WINDOW STUFF
    231 
    232 =============================================================================
    233 */
    234 
    235 
    236 /*
    237 ==================
    238 =
    239 = StatusDrawPic
    240 =
    241 ==================
    242 */
    243 
    244 void StatusDrawPic (unsigned x, unsigned y, unsigned picnum)
    245 {
    246 	unsigned	temp;
    247 
    248 	temp = bufferofs;
    249 	bufferofs = 0;
    250 
    251 	bufferofs = PAGE1START+(200-STATUSLINES)*SCREENWIDTH;
    252 	LatchDrawPic (x,y,picnum);
    253 	bufferofs = PAGE2START+(200-STATUSLINES)*SCREENWIDTH;
    254 	LatchDrawPic (x,y,picnum);
    255 	bufferofs = PAGE3START+(200-STATUSLINES)*SCREENWIDTH;
    256 	LatchDrawPic (x,y,picnum);
    257 
    258 	bufferofs = temp;
    259 }
    260 
    261 
    262 /*
    263 ==================
    264 =
    265 = DrawFace
    266 =
    267 ==================
    268 */
    269 
    270 void DrawFace (void)
    271 {
    272 	if (gamestate.health)
    273 	{
    274 		#ifdef SPEAR
    275 		if (godmode)
    276 			StatusDrawPic (17,4,GODMODEFACE1PIC+gamestate.faceframe);
    277 		else
    278 		#endif
    279 		StatusDrawPic (17,4,FACE1APIC+3*((100-gamestate.health)/16)+gamestate.faceframe);
    280 	}
    281 	else
    282 	{
    283 #ifndef SPEAR
    284 	 if (LastAttacker->obclass == needleobj)
    285 	   StatusDrawPic (17,4,MUTANTBJPIC);
    286 	 else
    287 #endif
    288 	   StatusDrawPic (17,4,FACE8APIC);
    289 	}
    290 }
    291 
    292 
    293 /*
    294 ===============
    295 =
    296 = UpdateFace
    297 =
    298 = Calls draw face if time to change
    299 =
    300 ===============
    301 */
    302 
    303 #define FACETICS	70
    304 
    305 int	facecount;
    306 
    307 void	UpdateFace (void)
    308 {
    309 
    310 	if (SD_SoundPlaying() == GETGATLINGSND)
    311 	  return;
    312 
    313 	facecount += tics;
    314 	if (facecount > US_RndT())
    315 	{
    316 		gamestate.faceframe = (US_RndT()>>6);
    317 		if (gamestate.faceframe==3)
    318 			gamestate.faceframe = 1;
    319 
    320 		facecount = 0;
    321 		DrawFace ();
    322 	}
    323 }
    324 
    325 
    326 
    327 /*
    328 ===============
    329 =
    330 = LatchNumber
    331 =
    332 = right justifies and pads with blanks
    333 =
    334 ===============
    335 */
    336 
    337 void	LatchNumber (int x, int y, int width, long number)
    338 {
    339 	unsigned	length,c;
    340 	char	str[20];
    341 
    342 	ltoa (number,str,10);
    343 
    344 	length = strlen (str);
    345 
    346 	while (length<width)
    347 	{
    348 		StatusDrawPic (x,y,N_BLANKPIC);
    349 		x++;
    350 		width--;
    351 	}
    352 
    353 	c= length <= width ? 0 : length-width;
    354 
    355 	while (c<length)
    356 	{
    357 		StatusDrawPic (x,y,str[c]-'0'+ N_0PIC);
    358 		x++;
    359 		c++;
    360 	}
    361 }
    362 
    363 
    364 /*
    365 ===============
    366 =
    367 = DrawHealth
    368 =
    369 ===============
    370 */
    371 
    372 void	DrawHealth (void)
    373 {
    374 	LatchNumber (21,16,3,gamestate.health);
    375 }
    376 
    377 
    378 /*
    379 ===============
    380 =
    381 = TakeDamage
    382 =
    383 ===============
    384 */
    385 
    386 void	TakeDamage (int points,objtype *attacker)
    387 {
    388 	LastAttacker = attacker;
    389 
    390 	if (gamestate.victoryflag)
    391 		return;
    392 	if (gamestate.difficulty==gd_baby)
    393 	  points>>=2;
    394 
    395 	if (!godmode)
    396 		gamestate.health -= points;
    397 
    398 	if (gamestate.health<=0)
    399 	{
    400 		gamestate.health = 0;
    401 		playstate = ex_died;
    402 		killerobj = attacker;
    403 	}
    404 
    405 	StartDamageFlash (points);
    406 
    407 	gotgatgun=0;
    408 
    409 	DrawHealth ();
    410 	DrawFace ();
    411 
    412 	//
    413 	// MAKE BJ'S EYES BUG IF MAJOR DAMAGE!
    414 	//
    415 	#ifdef SPEAR
    416 	if (points > 30 && gamestate.health!=0 && !godmode)
    417 	{
    418 		StatusDrawPic (17,4,BJOUCHPIC);
    419 		facecount = 0;
    420 	}
    421 	#endif
    422 
    423 }
    424 
    425 
    426 /*
    427 ===============
    428 =
    429 = HealSelf
    430 =
    431 ===============
    432 */
    433 
    434 void	HealSelf (int points)
    435 {
    436 	gamestate.health += points;
    437 	if (gamestate.health>100)
    438 		gamestate.health = 100;
    439 
    440 	DrawHealth ();
    441 	gotgatgun = 0;	// JR
    442 	DrawFace ();
    443 }
    444 
    445 
    446 //===========================================================================
    447 
    448 
    449 /*
    450 ===============
    451 =
    452 = DrawLevel
    453 =
    454 ===============
    455 */
    456 
    457 void	DrawLevel (void)
    458 {
    459 #ifdef SPEAR
    460 	if (gamestate.mapon == 20)
    461 		LatchNumber (2,16,2,18);
    462 	else
    463 #endif
    464 	LatchNumber (2,16,2,gamestate.mapon+1);
    465 }
    466 
    467 //===========================================================================
    468 
    469 
    470 /*
    471 ===============
    472 =
    473 = DrawLives
    474 =
    475 ===============
    476 */
    477 
    478 void	DrawLives (void)
    479 {
    480 	LatchNumber (14,16,1,gamestate.lives);
    481 }
    482 
    483 
    484 /*
    485 ===============
    486 =
    487 = GiveExtraMan
    488 =
    489 ===============
    490 */
    491 
    492 void	GiveExtraMan (void)
    493 {
    494 	if (gamestate.lives<9)
    495 		gamestate.lives++;
    496 	DrawLives ();
    497 	SD_PlaySound (BONUS1UPSND);
    498 }
    499 
    500 //===========================================================================
    501 
    502 /*
    503 ===============
    504 =
    505 = DrawScore
    506 =
    507 ===============
    508 */
    509 
    510 void	DrawScore (void)
    511 {
    512 	LatchNumber (6,16,6,gamestate.score);
    513 }
    514 
    515 /*
    516 ===============
    517 =
    518 = GivePoints
    519 =
    520 ===============
    521 */
    522 
    523 void	GivePoints (long points)
    524 {
    525 	gamestate.score += points;
    526 	while (gamestate.score >= gamestate.nextextra)
    527 	{
    528 		gamestate.nextextra += EXTRAPOINTS;
    529 		GiveExtraMan ();
    530 	}
    531 	DrawScore ();
    532 }
    533 
    534 //===========================================================================
    535 
    536 /*
    537 ==================
    538 =
    539 = DrawWeapon
    540 =
    541 ==================
    542 */
    543 
    544 void DrawWeapon (void)
    545 {
    546 	StatusDrawPic (32,8,KNIFEPIC+gamestate.weapon);
    547 }
    548 
    549 
    550 /*
    551 ==================
    552 =
    553 = DrawKeys
    554 =
    555 ==================
    556 */
    557 
    558 void DrawKeys (void)
    559 {
    560 	if (gamestate.keys & 1)
    561 		StatusDrawPic (30,4,GOLDKEYPIC);
    562 	else
    563 		StatusDrawPic (30,4,NOKEYPIC);
    564 
    565 	if (gamestate.keys & 2)
    566 		StatusDrawPic (30,20,SILVERKEYPIC);
    567 	else
    568 		StatusDrawPic (30,20,NOKEYPIC);
    569 }
    570 
    571 
    572 
    573 /*
    574 ==================
    575 =
    576 = GiveWeapon
    577 =
    578 ==================
    579 */
    580 
    581 void GiveWeapon (int weapon)
    582 {
    583 	GiveAmmo (6);
    584 
    585 	if (gamestate.bestweapon<weapon)
    586 		gamestate.bestweapon = gamestate.weapon
    587 		= gamestate.chosenweapon = weapon;
    588 
    589 	DrawWeapon ();
    590 }
    591 
    592 
    593 //===========================================================================
    594 
    595 /*
    596 ===============
    597 =
    598 = DrawAmmo
    599 =
    600 ===============
    601 */
    602 
    603 void	DrawAmmo (void)
    604 {
    605 	LatchNumber (27,16,2,gamestate.ammo);
    606 }
    607 
    608 
    609 /*
    610 ===============
    611 =
    612 = GiveAmmo
    613 =
    614 ===============
    615 */
    616 
    617 void	GiveAmmo (int ammo)
    618 {
    619 	if (!gamestate.ammo)				// knife was out
    620 	{
    621 		if (!gamestate.attackframe)
    622 		{
    623 			gamestate.weapon = gamestate.chosenweapon;
    624 			DrawWeapon ();
    625 		}
    626 	}
    627 	gamestate.ammo += ammo;
    628 	if (gamestate.ammo > 99)
    629 		gamestate.ammo = 99;
    630 	DrawAmmo ();
    631 }
    632 
    633 //===========================================================================
    634 
    635 /*
    636 ==================
    637 =
    638 = GiveKey
    639 =
    640 ==================
    641 */
    642 
    643 void GiveKey (int key)
    644 {
    645 	gamestate.keys |= (1<<key);
    646 	DrawKeys ();
    647 }
    648 
    649 
    650 
    651 /*
    652 =============================================================================
    653 
    654 							MOVEMENT
    655 
    656 =============================================================================
    657 */
    658 
    659 
    660 /*
    661 ===================
    662 =
    663 = GetBonus
    664 =
    665 ===================
    666 */
    667 void GetBonus (statobj_t *check)
    668 {
    669 	switch (check->itemnumber)
    670 	{
    671 	case	bo_firstaid:
    672 		if (gamestate.health == 100)
    673 			return;
    674 
    675 		SD_PlaySound (HEALTH2SND);
    676 		HealSelf (25);
    677 		break;
    678 
    679 	case	bo_key1:
    680 	case	bo_key2:
    681 	case	bo_key3:
    682 	case	bo_key4:
    683 		GiveKey (check->itemnumber - bo_key1);
    684 		SD_PlaySound (GETKEYSND);
    685 		break;
    686 
    687 	case	bo_cross:
    688 		SD_PlaySound (BONUS1SND);
    689 		GivePoints (100);
    690 		gamestate.treasurecount++;
    691 		break;
    692 	case	bo_chalice:
    693 		SD_PlaySound (BONUS2SND);
    694 		GivePoints (500);
    695 		gamestate.treasurecount++;
    696 		break;
    697 	case	bo_bible:
    698 		SD_PlaySound (BONUS3SND);
    699 		GivePoints (1000);
    700 		gamestate.treasurecount++;
    701 		break;
    702 	case	bo_crown:
    703 		SD_PlaySound (BONUS4SND);
    704 		GivePoints (5000);
    705 		gamestate.treasurecount++;
    706 		break;
    707 
    708 	case	bo_clip:
    709 		if (gamestate.ammo == 99)
    710 			return;
    711 
    712 		SD_PlaySound (GETAMMOSND);
    713 		GiveAmmo (8);
    714 		break;
    715 	case	bo_clip2:
    716 		if (gamestate.ammo == 99)
    717 			return;
    718 
    719 		SD_PlaySound (GETAMMOSND);
    720 		GiveAmmo (4);
    721 		break;
    722 
    723 #ifdef SPEAR
    724 	case	bo_25clip:
    725 		if (gamestate.ammo == 99)
    726 		  return;
    727 
    728 		SD_PlaySound (GETAMMOBOXSND);
    729 		GiveAmmo (25);
    730 		break;
    731 #endif
    732 
    733 	case	bo_machinegun:
    734 		SD_PlaySound (GETMACHINESND);
    735 		GiveWeapon (wp_machinegun);
    736 		break;
    737 	case	bo_chaingun:
    738 		SD_PlaySound (GETGATLINGSND);
    739 		GiveWeapon (wp_chaingun);
    740 
    741 		StatusDrawPic (17,4,GOTGATLINGPIC);
    742 		facecount = 0;
    743 		gotgatgun = 1;
    744 		break;
    745 
    746 	case	bo_fullheal:
    747 		SD_PlaySound (BONUS1UPSND);
    748 		HealSelf (99);
    749 		GiveAmmo (25);
    750 		GiveExtraMan ();
    751 		gamestate.treasurecount++;
    752 		break;
    753 
    754 	case	bo_food:
    755 		if (gamestate.health == 100)
    756 			return;
    757 
    758 		SD_PlaySound (HEALTH1SND);
    759 		HealSelf (10);
    760 		break;
    761 
    762 	case	bo_alpo:
    763 		if (gamestate.health == 100)
    764 			return;
    765 
    766 		SD_PlaySound (HEALTH1SND);
    767 		HealSelf (4);
    768 		break;
    769 
    770 	case	bo_gibs:
    771 		if (gamestate.health >10)
    772 			return;
    773 
    774 		SD_PlaySound (SLURPIESND);
    775 		HealSelf (1);
    776 		break;
    777 
    778 	case	bo_spear:
    779 		spearflag = true;
    780 		spearx = player->x;
    781 		speary = player->y;
    782 		spearangle = player->angle;
    783 		playstate = ex_completed;
    784 	}
    785 
    786 	StartBonusFlash ();
    787 	check->shapenum = -1;			// remove from list
    788 }
    789 
    790 
    791 /*
    792 ===================
    793 =
    794 = TryMove
    795 =
    796 = returns true if move ok
    797 = debug: use pointers to optimize
    798 ===================
    799 */
    800 
    801 boolean TryMove (objtype *ob)
    802 {
    803 	int			xl,yl,xh,yh,x,y;
    804 	objtype		*check;
    805 	long		deltax,deltay;
    806 
    807 	xl = (ob->x-PLAYERSIZE) >>TILESHIFT;
    808 	yl = (ob->y-PLAYERSIZE) >>TILESHIFT;
    809 
    810 	xh = (ob->x+PLAYERSIZE) >>TILESHIFT;
    811 	yh = (ob->y+PLAYERSIZE) >>TILESHIFT;
    812 
    813 //
    814 // check for solid walls
    815 //
    816 	for (y=yl;y<=yh;y++)
    817 		for (x=xl;x<=xh;x++)
    818 		{
    819 			check = actorat[x][y];
    820 			if (check && check<objlist)
    821 				return false;
    822 		}
    823 
    824 //
    825 // check for actors
    826 //
    827 	if (yl>0)
    828 		yl--;
    829 	if (yh<MAPSIZE-1)
    830 		yh++;
    831 	if (xl>0)
    832 		xl--;
    833 	if (xh<MAPSIZE-1)
    834 		xh++;
    835 
    836 	for (y=yl;y<=yh;y++)
    837 		for (x=xl;x<=xh;x++)
    838 		{
    839 			check = actorat[x][y];
    840 			if (check > objlist
    841 			&& (check->flags & FL_SHOOTABLE) )
    842 			{
    843 				deltax = ob->x - check->x;
    844 				if (deltax < -MINACTORDIST || deltax > MINACTORDIST)
    845 					continue;
    846 				deltay = ob->y - check->y;
    847 				if (deltay < -MINACTORDIST || deltay > MINACTORDIST)
    848 					continue;
    849 
    850 				return false;
    851 			}
    852 		}
    853 
    854 	return true;
    855 }
    856 
    857 
    858 /*
    859 ===================
    860 =
    861 = ClipMove
    862 =
    863 ===================
    864 */
    865 
    866 void ClipMove (objtype *ob, long xmove, long ymove)
    867 {
    868 	long	basex,basey;
    869 
    870 	basex = ob->x;
    871 	basey = ob->y;
    872 
    873 	ob->x = basex+xmove;
    874 	ob->y = basey+ymove;
    875 	if (TryMove (ob))
    876 		return;
    877 
    878 	if (noclip && ob->x > 2*TILEGLOBAL && ob->y > 2*TILEGLOBAL &&
    879 	ob->x < (((long)(mapwidth-1))<<TILESHIFT)
    880 	&& ob->y < (((long)(mapheight-1))<<TILESHIFT) )
    881 		return;		// walk through walls
    882 
    883 	if (!SD_SoundPlaying())
    884 		SD_PlaySound (HITWALLSND);
    885 
    886 	ob->x = basex+xmove;
    887 	ob->y = basey;
    888 	if (TryMove (ob))
    889 		return;
    890 
    891 	ob->x = basex;
    892 	ob->y = basey+ymove;
    893 	if (TryMove (ob))
    894 		return;
    895 
    896 	ob->x = basex;
    897 	ob->y = basey;
    898 }
    899 
    900 //==========================================================================
    901 
    902 /*
    903 ===================
    904 =
    905 = VictoryTile
    906 =
    907 ===================
    908 */
    909 
    910 void VictoryTile (void)
    911 {
    912 #ifndef SPEAR
    913 	SpawnBJVictory ();
    914 #endif
    915 
    916 	gamestate.victoryflag = true;
    917 }
    918 
    919 
    920 /*
    921 ===================
    922 =
    923 = Thrust
    924 =
    925 ===================
    926 */
    927 
    928 void Thrust (int angle, long speed)
    929 {
    930 	long xmove,ymove;
    931 	long	slowmax;
    932 	unsigned	offset;
    933 
    934 
    935 	//
    936 	// ZERO FUNNY COUNTER IF MOVED!
    937 	//
    938 	#ifdef SPEAR
    939 	if (speed)
    940 		funnyticount = 0;
    941 	#endif
    942 
    943 	thrustspeed += speed;
    944 //
    945 // moving bounds speed
    946 //
    947 	if (speed >= MINDIST*2)
    948 		speed = MINDIST*2-1;
    949 
    950 	xmove = FixedByFrac(speed,costable[angle]);
    951 	ymove = -FixedByFrac(speed,sintable[angle]);
    952 
    953 	ClipMove(player,xmove,ymove);
    954 
    955 	player->tilex = player->x >> TILESHIFT;		// scale to tile values
    956 	player->tiley = player->y >> TILESHIFT;
    957 
    958 	offset = farmapylookup[player->tiley]+player->tilex;
    959 	player->areanumber = *(mapsegs[0] + offset) -AREATILE;
    960 
    961 	if (*(mapsegs[1] + offset) == EXITTILE)
    962 		VictoryTile ();
    963 }
    964 
    965 
    966 /*
    967 =============================================================================
    968 
    969 								ACTIONS
    970 
    971 =============================================================================
    972 */
    973 
    974 
    975 /*
    976 ===============
    977 =
    978 = Cmd_Fire
    979 =
    980 ===============
    981 */
    982 
    983 void Cmd_Fire (void)
    984 {
    985 	buttonheld[bt_attack] = true;
    986 
    987 	gamestate.weaponframe = 0;
    988 
    989 	player->state = &s_attack;
    990 
    991 	gamestate.attackframe = 0;
    992 	gamestate.attackcount =
    993 		attackinfo[gamestate.weapon][gamestate.attackframe].tics;
    994 	gamestate.weaponframe =
    995 		attackinfo[gamestate.weapon][gamestate.attackframe].frame;
    996 }
    997 
    998 //===========================================================================
    999 
   1000 /*
   1001 ===============
   1002 =
   1003 = Cmd_Use
   1004 =
   1005 ===============
   1006 */
   1007 
   1008 void Cmd_Use (void)
   1009 {
   1010 	objtype 	*check;
   1011 	int			checkx,checky,doornum,dir;
   1012 	boolean		elevatorok;
   1013 
   1014 
   1015 //
   1016 // find which cardinal direction the player is facing
   1017 //
   1018 	if (player->angle < ANGLES/8 || player->angle > 7*ANGLES/8)
   1019 	{
   1020 		checkx = player->tilex + 1;
   1021 		checky = player->tiley;
   1022 		dir = di_east;
   1023 		elevatorok = true;
   1024 	}
   1025 	else if (player->angle < 3*ANGLES/8)
   1026 	{
   1027 		checkx = player->tilex;
   1028 		checky = player->tiley-1;
   1029 		dir = di_north;
   1030 		elevatorok = false;
   1031 	}
   1032 	else if (player->angle < 5*ANGLES/8)
   1033 	{
   1034 		checkx = player->tilex - 1;
   1035 		checky = player->tiley;
   1036 		dir = di_west;
   1037 		elevatorok = true;
   1038 	}
   1039 	else
   1040 	{
   1041 		checkx = player->tilex;
   1042 		checky = player->tiley + 1;
   1043 		dir = di_south;
   1044 		elevatorok = false;
   1045 	}
   1046 
   1047 	doornum = tilemap[checkx][checky];
   1048 	if (*(mapsegs[1]+farmapylookup[checky]+checkx) == PUSHABLETILE)
   1049 	{
   1050 	//
   1051 	// pushable wall
   1052 	//
   1053 
   1054 		PushWall (checkx,checky,dir);
   1055 		return;
   1056 	}
   1057 	if (!buttonheld[bt_use] && doornum == ELEVATORTILE && elevatorok)
   1058 	{
   1059 	//
   1060 	// use elevator
   1061 	//
   1062 		buttonheld[bt_use] = true;
   1063 
   1064 		tilemap[checkx][checky]++;		// flip switch
   1065 		if (*(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) == ALTELEVATORTILE)
   1066 			playstate = ex_secretlevel;
   1067 		else
   1068 			playstate = ex_completed;
   1069 		SD_PlaySound (LEVELDONESND);
   1070 		SD_WaitSoundDone();
   1071 	}
   1072 	else if (!buttonheld[bt_use] && doornum & 0x80)
   1073 	{
   1074 		buttonheld[bt_use] = true;
   1075 		OperateDoor (doornum & ~0x80);
   1076 	}
   1077 	else
   1078 		SD_PlaySound (DONOTHINGSND);
   1079 
   1080 }
   1081 
   1082 /*
   1083 =============================================================================
   1084 
   1085 						   PLAYER CONTROL
   1086 
   1087 =============================================================================
   1088 */
   1089 
   1090 
   1091 
   1092 /*
   1093 ===============
   1094 =
   1095 = SpawnPlayer
   1096 =
   1097 ===============
   1098 */
   1099 
   1100 void SpawnPlayer (int tilex, int tiley, int dir)
   1101 {
   1102 	player->obclass = playerobj;
   1103 	player->active = true;
   1104 	player->tilex = tilex;
   1105 	player->tiley = tiley;
   1106 	player->areanumber =
   1107 		*(mapsegs[0] + farmapylookup[player->tiley]+player->tilex);
   1108 	player->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
   1109 	player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
   1110 	player->state = &s_player;
   1111 	player->angle = (1-dir)*90;
   1112 	if (player->angle<0)
   1113 		player->angle += ANGLES;
   1114 	player->flags = FL_NEVERMARK;
   1115 	Thrust (0,0);				// set some variables
   1116 
   1117 	InitAreas ();
   1118 }
   1119 
   1120 
   1121 //===========================================================================
   1122 
   1123 /*
   1124 ===============
   1125 =
   1126 = T_KnifeAttack
   1127 =
   1128 = Update player hands, and try to do damage when the proper frame is reached
   1129 =
   1130 ===============
   1131 */
   1132 
   1133 void	KnifeAttack (objtype *ob)
   1134 {
   1135 	objtype *check,*closest;
   1136 	long	dist;
   1137 
   1138 	SD_PlaySound (ATKKNIFESND);
   1139 // actually fire
   1140 	dist = 0x7fffffff;
   1141 	closest = NULL;
   1142 	for (check=ob->next ; check ; check=check->next)
   1143 		if ( (check->flags & FL_SHOOTABLE)
   1144 		&& (check->flags & FL_VISABLE)
   1145 		&& abs (check->viewx-centerx) < shootdelta
   1146 		)
   1147 		{
   1148 			if (check->transx < dist)
   1149 			{
   1150 				dist = check->transx;
   1151 				closest = check;
   1152 			}
   1153 		}
   1154 
   1155 	if (!closest || dist> 0x18000l)
   1156 	{
   1157 	// missed
   1158 
   1159 		return;
   1160 	}
   1161 
   1162 // hit something
   1163 	DamageActor (closest,US_RndT() >> 4);
   1164 }
   1165 
   1166 
   1167 
   1168 void	GunAttack (objtype *ob)
   1169 {
   1170 	objtype *check,*closest,*oldclosest;
   1171 	int		damage;
   1172 	int		dx,dy,dist;
   1173 	long	viewdist;
   1174 
   1175 	switch (gamestate.weapon)
   1176 	{
   1177 	case wp_pistol:
   1178 		SD_PlaySound (ATKPISTOLSND);
   1179 		break;
   1180 	case wp_machinegun:
   1181 		SD_PlaySound (ATKMACHINEGUNSND);
   1182 		break;
   1183 	case wp_chaingun:
   1184 		SD_PlaySound (ATKGATLINGSND);
   1185 		break;
   1186 	}
   1187 
   1188 	madenoise = true;
   1189 
   1190 //
   1191 // find potential targets
   1192 //
   1193 	viewdist = 0x7fffffffl;
   1194 	closest = NULL;
   1195 
   1196 	while (1)
   1197 	{
   1198 		oldclosest = closest;
   1199 
   1200 		for (check=ob->next ; check ; check=check->next)
   1201 			if ( (check->flags & FL_SHOOTABLE)
   1202 			&& (check->flags & FL_VISABLE)
   1203 			&& abs (check->viewx-centerx) < shootdelta
   1204 			)
   1205 			{
   1206 				if (check->transx < viewdist)
   1207 				{
   1208 					viewdist = check->transx;
   1209 					closest = check;
   1210 				}
   1211 			}
   1212 
   1213 		if (closest == oldclosest)
   1214 			return;						// no more targets, all missed
   1215 
   1216 	//
   1217 	// trace a line from player to enemey
   1218 	//
   1219 		if (CheckLine(closest))
   1220 			break;
   1221 
   1222 	}
   1223 
   1224 //
   1225 // hit something
   1226 //
   1227 	dx = abs(closest->tilex - player->tilex);
   1228 	dy = abs(closest->tiley - player->tiley);
   1229 	dist = dx>dy ? dx:dy;
   1230 
   1231 	if (dist<2)
   1232 		damage = US_RndT() / 4;
   1233 	else if (dist<4)
   1234 		damage = US_RndT() / 6;
   1235 	else
   1236 	{
   1237 		if ( (US_RndT() / 12) < dist)		// missed
   1238 			return;
   1239 		damage = US_RndT() / 6;
   1240 	}
   1241 
   1242 	DamageActor (closest,damage);
   1243 }
   1244 
   1245 //===========================================================================
   1246 
   1247 /*
   1248 ===============
   1249 =
   1250 = VictorySpin
   1251 =
   1252 ===============
   1253 */
   1254 
   1255 void VictorySpin (void)
   1256 {
   1257 	long	desty;
   1258 
   1259 	if (player->angle > 270)
   1260 	{
   1261 		player->angle -= tics * 3;
   1262 		if (player->angle < 270)
   1263 			player->angle = 270;
   1264 	}
   1265 	else if (player->angle < 270)
   1266 	{
   1267 		player->angle += tics * 3;
   1268 		if (player->angle > 270)
   1269 			player->angle = 270;
   1270 	}
   1271 
   1272 	desty = (((long)player->tiley-5)<<TILESHIFT)-0x3000;
   1273 
   1274 	if (player->y > desty)
   1275 	{
   1276 		player->y -= tics*4096;
   1277 		if (player->y < desty)
   1278 			player->y = desty;
   1279 	}
   1280 }
   1281 
   1282 
   1283 //===========================================================================
   1284 
   1285 /*
   1286 ===============
   1287 =
   1288 = T_Attack
   1289 =
   1290 ===============
   1291 */
   1292 
   1293 void	T_Attack (objtype *ob)
   1294 {
   1295 	struct	atkinf	*cur;
   1296 
   1297 	UpdateFace ();
   1298 
   1299 	if (gamestate.victoryflag)		// watching the BJ actor
   1300 	{
   1301 		VictorySpin ();
   1302 		return;
   1303 	}
   1304 
   1305 	if ( buttonstate[bt_use] && !buttonheld[bt_use] )
   1306 		buttonstate[bt_use] = false;
   1307 
   1308 	if ( buttonstate[bt_attack] && !buttonheld[bt_attack])
   1309 		buttonstate[bt_attack] = false;
   1310 
   1311 	ControlMovement (ob);
   1312 	if (gamestate.victoryflag)		// watching the BJ actor
   1313 		return;
   1314 
   1315 	plux = player->x >> UNSIGNEDSHIFT;			// scale to fit in unsigned
   1316 	pluy = player->y >> UNSIGNEDSHIFT;
   1317 	player->tilex = player->x >> TILESHIFT;		// scale to tile values
   1318 	player->tiley = player->y >> TILESHIFT;
   1319 
   1320 //
   1321 // change frame and fire
   1322 //
   1323 	gamestate.attackcount -= tics;
   1324 	while (gamestate.attackcount <= 0)
   1325 	{
   1326 		cur = &attackinfo[gamestate.weapon][gamestate.attackframe];
   1327 		switch (cur->attack)
   1328 		{
   1329 		case -1:
   1330 			ob->state = &s_player;
   1331 			if (!gamestate.ammo)
   1332 			{
   1333 				gamestate.weapon = wp_knife;
   1334 				DrawWeapon ();
   1335 			}
   1336 			else
   1337 			{
   1338 				if (gamestate.weapon != gamestate.chosenweapon)
   1339 				{
   1340 					gamestate.weapon = gamestate.chosenweapon;
   1341 					DrawWeapon ();
   1342 				}
   1343 			};
   1344 			gamestate.attackframe = gamestate.weaponframe = 0;
   1345 			return;
   1346 
   1347 		case 4:
   1348 			if (!gamestate.ammo)
   1349 				break;
   1350 			if (buttonstate[bt_attack])
   1351 				gamestate.attackframe -= 2;
   1352 		case 1:
   1353 			if (!gamestate.ammo)
   1354 			{	// can only happen with chain gun
   1355 				gamestate.attackframe++;
   1356 				break;
   1357 			}
   1358 			GunAttack (ob);
   1359 			gamestate.ammo--;
   1360 			DrawAmmo ();
   1361 			break;
   1362 
   1363 		case 2:
   1364 			KnifeAttack (ob);
   1365 			break;
   1366 
   1367 		case 3:
   1368 			if (gamestate.ammo && buttonstate[bt_attack])
   1369 				gamestate.attackframe -= 2;
   1370 			break;
   1371 		}
   1372 
   1373 		gamestate.attackcount += cur->tics;
   1374 		gamestate.attackframe++;
   1375 		gamestate.weaponframe =
   1376 			attackinfo[gamestate.weapon][gamestate.attackframe].frame;
   1377 	}
   1378 
   1379 }
   1380 
   1381 
   1382 
   1383 //===========================================================================
   1384 
   1385 /*
   1386 ===============
   1387 =
   1388 = T_Player
   1389 =
   1390 ===============
   1391 */
   1392 
   1393 void	T_Player (objtype *ob)
   1394 {
   1395 	if (gamestate.victoryflag)		// watching the BJ actor
   1396 	{
   1397 		VictorySpin ();
   1398 		return;
   1399 	}
   1400 
   1401 	UpdateFace ();
   1402 	CheckWeaponChange ();
   1403 
   1404 	if ( buttonstate[bt_use] )
   1405 		Cmd_Use ();
   1406 
   1407 	if ( buttonstate[bt_attack] && !buttonheld[bt_attack])
   1408 		Cmd_Fire ();
   1409 
   1410 	ControlMovement (ob);
   1411 	if (gamestate.victoryflag)		// watching the BJ actor
   1412 		return;
   1413 
   1414 
   1415 	plux = player->x >> UNSIGNEDSHIFT;			// scale to fit in unsigned
   1416 	pluy = player->y >> UNSIGNEDSHIFT;
   1417 	player->tilex = player->x >> TILESHIFT;		// scale to tile values
   1418 	player->tiley = player->y >> TILESHIFT;
   1419 }
   1420 
   1421