Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
298 serge 1
// Emacs style mode select   -*- C++ -*-
2
//-----------------------------------------------------------------------------
3
//
4
// $Id:$
5
//
6
// Copyright (C) 1993-1996 by id Software, Inc.
7
//
8
// This source is available for distribution and/or modification
9
// only under the terms of the DOOM Source Code License as
10
// published by id Software. All rights reserved.
11
//
12
// The source is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15
// for more details.
16
//
17
// $Log:$
18
//
19
// DESCRIPTION:
20
//	Weapon sprite animation, weapon objects.
21
//	Action functions for weapons.
22
//
23
//-----------------------------------------------------------------------------
24
 
25
static const char
26
rcsid[] = "$Id: p_pspr.c,v 1.5 1997/02/03 22:45:12 b1 Exp $";
27
 
28
#include "doomdef.h"
29
#include "d_event.h"
30
 
31
 
32
#include "m_random.h"
33
#include "p_local.h"
34
#include "s_sound.h"
35
 
36
// State.
37
#include "doomstat.h"
38
 
39
// Data.
40
#include "sounds.h"
41
 
42
#include "p_pspr.h"
43
 
44
#define LOWERSPEED		FRACUNIT*6
45
#define RAISESPEED		FRACUNIT*6
46
 
47
#define WEAPONBOTTOM	128*FRACUNIT
48
#define WEAPONTOP		32*FRACUNIT
49
 
50
 
51
// plasma cells for a bfg attack
52
#define BFGCELLS		40
53
 
54
 
55
//
56
// P_SetPsprite
57
//
58
void
59
P_SetPsprite
60
( player_t*	player,
61
  int		position,
62
  statenum_t	stnum )
63
{
64
    pspdef_t*	psp;
65
    state_t*	state;
66
 
67
    psp = &player->psprites[position];
68
 
69
    do
70
    {
71
	if (!stnum)
72
	{
73
	    // object removed itself
74
	    psp->state = NULL;
75
	    break;
76
	}
77
 
78
	state = &states[stnum];
79
	psp->state = state;
80
	psp->tics = state->tics;	// could be 0
81
 
82
	if (state->misc1)
83
	{
84
	    // coordinate set
85
	    psp->sx = state->misc1 << FRACBITS;
86
	    psp->sy = state->misc2 << FRACBITS;
87
	}
88
 
89
	// Call action routine.
90
	// Modified handling.
91
	if (state->action.acp2)
92
	{
93
	    state->action.acp2(player, psp);
94
	    if (!psp->state)
95
		break;
96
	}
97
 
98
	stnum = psp->state->nextstate;
99
 
100
    } while (!psp->tics);
101
    // an initial state of 0 could cycle through
102
}
103
 
104
 
105
 
106
//
107
// P_CalcSwing
108
//
109
fixed_t		swingx;
110
fixed_t		swingy;
111
 
112
void P_CalcSwing (player_t*	player)
113
{
114
    fixed_t	swing;
115
    int		angle;
116
 
117
    // OPTIMIZE: tablify this.
118
    // A LUT would allow for different modes,
119
    //  and add flexibility.
120
 
121
    swing = player->bob;
122
 
123
    angle = (FINEANGLES/70*leveltime)&FINEMASK;
124
    swingx = FixedMul ( swing, finesine[angle]);
125
 
126
    angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK;
127
    swingy = -FixedMul ( swingx, finesine[angle]);
128
}
129
 
130
 
131
 
132
//
133
// P_BringUpWeapon
134
// Starts bringing the pending weapon up
135
// from the bottom of the screen.
136
// Uses player
137
//
138
void P_BringUpWeapon (player_t* player)
139
{
140
    statenum_t	newstate;
141
 
142
    if (player->pendingweapon == wp_nochange)
143
	player->pendingweapon = player->readyweapon;
144
 
145
    if (player->pendingweapon == wp_chainsaw)
146
	S_StartSound (player->mo, sfx_sawup);
147
 
148
    newstate = weaponinfo[player->pendingweapon].upstate;
149
 
150
    player->pendingweapon = wp_nochange;
151
    player->psprites[ps_weapon].sy = WEAPONBOTTOM;
152
 
153
    P_SetPsprite (player, ps_weapon, newstate);
154
}
155
 
156
//
157
// P_CheckAmmo
158
// Returns true if there is enough ammo to shoot.
159
// If not, selects the next weapon to use.
160
//
161
boolean P_CheckAmmo (player_t* player)
162
{
163
    ammotype_t		ammo;
164
    int			count;
165
 
166
    ammo = weaponinfo[player->readyweapon].ammo;
167
 
168
    // Minimal amount for one shot varies.
169
    if (player->readyweapon == wp_bfg)
170
	count = BFGCELLS;
171
    else if (player->readyweapon == wp_supershotgun)
172
	count = 2;	// Double barrel.
173
    else
174
	count = 1;	// Regular.
175
 
176
    // Some do not need ammunition anyway.
177
    // Return if current ammunition sufficient.
178
    if (ammo == am_noammo || player->ammo[ammo] >= count)
179
	return true;
180
 
181
    // Out of ammo, pick a weapon to change to.
182
    // Preferences are set here.
183
    do
184
    {
185
	if (player->weaponowned[wp_plasma]
186
	    && player->ammo[am_cell]
187
	    && (gamemode != shareware) )
188
	{
189
	    player->pendingweapon = wp_plasma;
190
	}
191
	else if (player->weaponowned[wp_supershotgun]
192
		 && player->ammo[am_shell]>2
193
		 && (gamemode == commercial) )
194
	{
195
	    player->pendingweapon = wp_supershotgun;
196
	}
197
	else if (player->weaponowned[wp_chaingun]
198
		 && player->ammo[am_clip])
199
	{
200
	    player->pendingweapon = wp_chaingun;
201
	}
202
	else if (player->weaponowned[wp_shotgun]
203
		 && player->ammo[am_shell])
204
	{
205
	    player->pendingweapon = wp_shotgun;
206
	}
207
	else if (player->ammo[am_clip])
208
	{
209
	    player->pendingweapon = wp_pistol;
210
	}
211
	else if (player->weaponowned[wp_chainsaw])
212
	{
213
	    player->pendingweapon = wp_chainsaw;
214
	}
215
	else if (player->weaponowned[wp_missile]
216
		 && player->ammo[am_misl])
217
	{
218
	    player->pendingweapon = wp_missile;
219
	}
220
	else if (player->weaponowned[wp_bfg]
221
		 && player->ammo[am_cell]>40
222
		 && (gamemode != shareware) )
223
	{
224
	    player->pendingweapon = wp_bfg;
225
	}
226
	else
227
	{
228
	    // If everything fails.
229
	    player->pendingweapon = wp_fist;
230
	}
231
 
232
    } while (player->pendingweapon == wp_nochange);
233
 
234
    // Now set appropriate weapon overlay.
235
    P_SetPsprite (player,
236
		  ps_weapon,
237
		  weaponinfo[player->readyweapon].downstate);
238
 
239
    return false;
240
}
241
 
242
 
243
//
244
// P_FireWeapon.
245
//
246
void P_FireWeapon (player_t* player)
247
{
248
    statenum_t	newstate;
249
 
250
    if (!P_CheckAmmo (player))
251
	return;
252
 
253
    P_SetMobjState (player->mo, S_PLAY_ATK1);
254
    newstate = weaponinfo[player->readyweapon].atkstate;
255
    P_SetPsprite (player, ps_weapon, newstate);
256
    P_NoiseAlert (player->mo, player->mo);
257
}
258
 
259
 
260
 
261
//
262
// P_DropWeapon
263
// Player died, so put the weapon away.
264
//
265
void P_DropWeapon (player_t* player)
266
{
267
    P_SetPsprite (player,
268
		  ps_weapon,
269
		  weaponinfo[player->readyweapon].downstate);
270
}
271
 
272
 
273
 
274
//
275
// A_WeaponReady
276
// The player can fire the weapon
277
// or change to another weapon at this time.
278
// Follows after getting weapon up,
279
// or after previous attack/fire sequence.
280
//
281
void
282
A_WeaponReady
283
( player_t*	player,
284
  pspdef_t*	psp )
285
{
286
    statenum_t	newstate;
287
    int		angle;
288
 
289
    // get out of attack state
290
    if (player->mo->state == &states[S_PLAY_ATK1]
291
	|| player->mo->state == &states[S_PLAY_ATK2] )
292
    {
293
	P_SetMobjState (player->mo, S_PLAY);
294
    }
295
 
296
    if (player->readyweapon == wp_chainsaw
297
	&& psp->state == &states[S_SAW])
298
    {
299
	S_StartSound (player->mo, sfx_sawidl);
300
    }
301
 
302
    // check for change
303
    //  if player is dead, put the weapon away
304
    if (player->pendingweapon != wp_nochange || !player->health)
305
    {
306
	// change weapon
307
	//  (pending weapon should allready be validated)
308
	newstate = weaponinfo[player->readyweapon].downstate;
309
	P_SetPsprite (player, ps_weapon, newstate);
310
	return;
311
    }
312
 
313
    // check for fire
314
    //  the missile launcher and bfg do not auto fire
315
    if (player->cmd.buttons & BT_ATTACK)
316
    {
317
	if ( !player->attackdown
318
	     || (player->readyweapon != wp_missile
319
		 && player->readyweapon != wp_bfg) )
320
	{
321
	    player->attackdown = true;
322
	    P_FireWeapon (player);
323
	    return;
324
	}
325
    }
326
    else
327
	player->attackdown = false;
328
 
329
    // bob the weapon based on movement speed
330
    angle = (128*leveltime)&FINEMASK;
331
    psp->sx = FRACUNIT + FixedMul (player->bob, finecosine[angle]);
332
    angle &= FINEANGLES/2-1;
333
    psp->sy = WEAPONTOP + FixedMul (player->bob, finesine[angle]);
334
}
335
 
336
 
337
 
338
//
339
// A_ReFire
340
// The player can re-fire the weapon
341
// without lowering it entirely.
342
//
343
void A_ReFire
344
( player_t*	player,
345
  pspdef_t*	psp )
346
{
347
 
348
    // check for fire
349
    //  (if a weaponchange is pending, let it go through instead)
350
    if ( (player->cmd.buttons & BT_ATTACK)
351
	 && player->pendingweapon == wp_nochange
352
	 && player->health)
353
    {
354
	player->refire++;
355
	P_FireWeapon (player);
356
    }
357
    else
358
    {
359
	player->refire = 0;
360
	P_CheckAmmo (player);
361
    }
362
}
363
 
364
 
365
void
366
A_CheckReload
367
( player_t*	player,
368
  pspdef_t*	psp )
369
{
370
    P_CheckAmmo (player);
371
#if 0
372
    if (player->ammo[am_shell]<2)
373
	P_SetPsprite (player, ps_weapon, S_DSNR1);
374
#endif
375
}
376
 
377
 
378
 
379
//
380
// A_Lower
381
// Lowers current weapon,
382
//  and changes weapon at bottom.
383
//
384
void
385
A_Lower
386
( player_t*	player,
387
  pspdef_t*	psp )
388
{
389
    psp->sy += LOWERSPEED;
390
 
391
    // Is already down.
392
    if (psp->sy < WEAPONBOTTOM )
393
	return;
394
 
395
    // Player is dead.
396
    if (player->playerstate == PST_DEAD)
397
    {
398
	psp->sy = WEAPONBOTTOM;
399
 
400
	// don't bring weapon back up
401
	return;
402
    }
403
 
404
    // The old weapon has been lowered off the screen,
405
    // so change the weapon and start raising it
406
    if (!player->health)
407
    {
408
	// Player is dead, so keep the weapon off screen.
409
	P_SetPsprite (player,  ps_weapon, S_NULL);
410
	return;
411
    }
412
 
413
    player->readyweapon = player->pendingweapon;
414
 
415
    P_BringUpWeapon (player);
416
}
417
 
418
 
419
//
420
// A_Raise
421
//
422
void
423
A_Raise
424
( player_t*	player,
425
  pspdef_t*	psp )
426
{
427
    statenum_t	newstate;
428
 
429
    psp->sy -= RAISESPEED;
430
 
431
    if (psp->sy > WEAPONTOP )
432
	return;
433
 
434
    psp->sy = WEAPONTOP;
435
 
436
    // The weapon has been raised all the way,
437
    //  so change to the ready state.
438
    newstate = weaponinfo[player->readyweapon].readystate;
439
 
440
    P_SetPsprite (player, ps_weapon, newstate);
441
}
442
 
443
 
444
 
445
//
446
// A_GunFlash
447
//
448
void
449
A_GunFlash
450
( player_t*	player,
451
  pspdef_t*	psp )
452
{
453
    P_SetMobjState (player->mo, S_PLAY_ATK2);
454
    P_SetPsprite (player,ps_flash,weaponinfo[player->readyweapon].flashstate);
455
}
456
 
457
 
458
 
459
//
460
// WEAPON ATTACKS
461
//
462
 
463
 
464
//
465
// A_Punch
466
//
467
void
468
A_Punch
469
( player_t*	player,
470
  pspdef_t*	psp )
471
{
472
    angle_t	angle;
473
    int		damage;
474
    int		slope;
475
 
476
    damage = (P_Random ()%10+1)<<1;
477
 
478
    if (player->powers[pw_strength])
479
	damage *= 10;
480
 
481
    angle = player->mo->angle;
482
    angle += (P_Random()-P_Random())<<18;
483
    slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
484
    P_LineAttack (player->mo, angle, MELEERANGE, slope, damage);
485
 
486
    // turn to face target
487
    if (linetarget)
488
    {
489
	S_StartSound (player->mo, sfx_punch);
490
	player->mo->angle = R_PointToAngle2 (player->mo->x,
491
					     player->mo->y,
492
					     linetarget->x,
493
					     linetarget->y);
494
    }
495
}
496
 
497
 
498
//
499
// A_Saw
500
//
501
void
502
A_Saw
503
( player_t*	player,
504
  pspdef_t*	psp )
505
{
506
    angle_t	angle;
507
    int		damage;
508
    int		slope;
509
 
510
    damage = 2*(P_Random ()%10+1);
511
    angle = player->mo->angle;
512
    angle += (P_Random()-P_Random())<<18;
513
 
514
    // use meleerange + 1 se the puff doesn't skip the flash
515
    slope = P_AimLineAttack (player->mo, angle, MELEERANGE+1);
516
    P_LineAttack (player->mo, angle, MELEERANGE+1, slope, damage);
517
 
518
    if (!linetarget)
519
    {
520
	S_StartSound (player->mo, sfx_sawful);
521
	return;
522
    }
523
    S_StartSound (player->mo, sfx_sawhit);
524
 
525
    // turn to face target
526
    angle = R_PointToAngle2 (player->mo->x, player->mo->y,
527
			     linetarget->x, linetarget->y);
528
    if (angle - player->mo->angle > ANG180)
529
    {
530
	if (angle - player->mo->angle < -ANG90/20)
531
	    player->mo->angle = angle + ANG90/21;
532
	else
533
	    player->mo->angle -= ANG90/20;
534
    }
535
    else
536
    {
537
	if (angle - player->mo->angle > ANG90/20)
538
	    player->mo->angle = angle - ANG90/21;
539
	else
540
	    player->mo->angle += ANG90/20;
541
    }
542
    player->mo->flags |= MF_JUSTATTACKED;
543
}
544
 
545
 
546
 
547
//
548
// A_FireMissile
549
//
550
void
551
A_FireMissile
552
( player_t*	player,
553
  pspdef_t*	psp )
554
{
555
    player->ammo[weaponinfo[player->readyweapon].ammo]--;
556
    P_SpawnPlayerMissile (player->mo, MT_ROCKET);
557
}
558
 
559
 
560
//
561
// A_FireBFG
562
//
563
void
564
A_FireBFG
565
( player_t*	player,
566
  pspdef_t*	psp )
567
{
568
    player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS;
569
    P_SpawnPlayerMissile (player->mo, MT_BFG);
570
}
571
 
572
 
573
 
574
//
575
// A_FirePlasma
576
//
577
void
578
A_FirePlasma
579
( player_t*	player,
580
  pspdef_t*	psp )
581
{
582
    player->ammo[weaponinfo[player->readyweapon].ammo]--;
583
 
584
    P_SetPsprite (player,
585
		  ps_flash,
586
		  weaponinfo[player->readyweapon].flashstate+(P_Random ()&1) );
587
 
588
    P_SpawnPlayerMissile (player->mo, MT_PLASMA);
589
}
590
 
591
 
592
 
593
//
594
// P_BulletSlope
595
// Sets a slope so a near miss is at aproximately
596
// the height of the intended target
597
//
598
fixed_t		bulletslope;
599
 
600
 
601
void P_BulletSlope (mobj_t*	mo)
602
{
603
    angle_t	an;
604
 
605
    // see which target is to be aimed at
606
    an = mo->angle;
607
    bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
608
 
609
    if (!linetarget)
610
    {
611
	an += 1<<26;
612
	bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
613
	if (!linetarget)
614
	{
615
	    an -= 2<<26;
616
	    bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
617
	}
618
    }
619
}
620
 
621
 
622
//
623
// P_GunShot
624
//
625
void
626
P_GunShot
627
( mobj_t*	mo,
628
  boolean	accurate )
629
{
630
    angle_t	angle;
631
    int		damage;
632
 
633
    damage = 5*(P_Random ()%3+1);
634
    angle = mo->angle;
635
 
636
    if (!accurate)
637
	angle += (P_Random()-P_Random())<<18;
638
 
639
    P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage);
640
}
641
 
642
 
643
//
644
// A_FirePistol
645
//
646
void
647
A_FirePistol
648
( player_t*	player,
649
  pspdef_t*	psp )
650
{
651
    S_StartSound (player->mo, sfx_pistol);
652
 
653
    P_SetMobjState (player->mo, S_PLAY_ATK2);
654
    player->ammo[weaponinfo[player->readyweapon].ammo]--;
655
 
656
    P_SetPsprite (player,
657
		  ps_flash,
658
		  weaponinfo[player->readyweapon].flashstate);
659
 
660
    P_BulletSlope (player->mo);
661
    P_GunShot (player->mo, !player->refire);
662
}
663
 
664
 
665
//
666
// A_FireShotgun
667
//
668
void
669
A_FireShotgun
670
( player_t*	player,
671
  pspdef_t*	psp )
672
{
673
    int		i;
674
 
675
    S_StartSound (player->mo, sfx_shotgn);
676
    P_SetMobjState (player->mo, S_PLAY_ATK2);
677
 
678
    player->ammo[weaponinfo[player->readyweapon].ammo]--;
679
 
680
    P_SetPsprite (player,
681
		  ps_flash,
682
		  weaponinfo[player->readyweapon].flashstate);
683
 
684
    P_BulletSlope (player->mo);
685
 
686
    for (i=0 ; i<7 ; i++)
687
	P_GunShot (player->mo, false);
688
}
689
 
690
 
691
 
692
//
693
// A_FireShotgun2
694
//
695
void
696
A_FireShotgun2
697
( player_t*	player,
698
  pspdef_t*	psp )
699
{
700
    int		i;
701
    angle_t	angle;
702
    int		damage;
703
 
704
 
705
    S_StartSound (player->mo, sfx_dshtgn);
706
    P_SetMobjState (player->mo, S_PLAY_ATK2);
707
 
708
    player->ammo[weaponinfo[player->readyweapon].ammo]-=2;
709
 
710
    P_SetPsprite (player,
711
		  ps_flash,
712
		  weaponinfo[player->readyweapon].flashstate);
713
 
714
    P_BulletSlope (player->mo);
715
 
716
    for (i=0 ; i<20 ; i++)
717
    {
718
	damage = 5*(P_Random ()%3+1);
719
	angle = player->mo->angle;
720
	angle += (P_Random()-P_Random())<<19;
721
	P_LineAttack (player->mo,
722
		      angle,
723
		      MISSILERANGE,
724
		      bulletslope + ((P_Random()-P_Random())<<5), damage);
725
    }
726
}
727
 
728
 
729
//
730
// A_FireCGun
731
//
732
void
733
A_FireCGun
734
( player_t*	player,
735
  pspdef_t*	psp )
736
{
737
    S_StartSound (player->mo, sfx_pistol);
738
 
739
    if (!player->ammo[weaponinfo[player->readyweapon].ammo])
740
	return;
741
 
742
    P_SetMobjState (player->mo, S_PLAY_ATK2);
743
    player->ammo[weaponinfo[player->readyweapon].ammo]--;
744
 
745
    P_SetPsprite (player,
746
		  ps_flash,
747
		  weaponinfo[player->readyweapon].flashstate
748
		  + psp->state
749
		  - &states[S_CHAIN1] );
750
 
751
    P_BulletSlope (player->mo);
752
 
753
    P_GunShot (player->mo, !player->refire);
754
}
755
 
756
 
757
 
758
//
759
// ?
760
//
761
void A_Light0 (player_t *player, pspdef_t *psp)
762
{
763
    player->extralight = 0;
764
}
765
 
766
void A_Light1 (player_t *player, pspdef_t *psp)
767
{
768
    player->extralight = 1;
769
}
770
 
771
void A_Light2 (player_t *player, pspdef_t *psp)
772
{
773
    player->extralight = 2;
774
}
775
 
776
 
777
//
778
// A_BFGSpray
779
// Spawn a BFG explosion on every monster in view
780
//
781
void A_BFGSpray (mobj_t* mo)
782
{
783
    int			i;
784
    int			j;
785
    int			damage;
786
    angle_t		an;
787
 
788
    // offset angles from its attack angle
789
    for (i=0 ; i<40 ; i++)
790
    {
791
	an = mo->angle - ANG90/2 + ANG90/40*i;
792
 
793
	// mo->target is the originator (player)
794
	//  of the missile
795
	P_AimLineAttack (mo->target, an, 16*64*FRACUNIT);
796
 
797
	if (!linetarget)
798
	    continue;
799
 
800
	P_SpawnMobj (linetarget->x,
801
		     linetarget->y,
802
		     linetarget->z + (linetarget->height>>2),
803
		     MT_EXTRABFG);
804
 
805
	damage = 0;
806
	for (j=0;j<15;j++)
807
	    damage += (P_Random()&7) + 1;
808
 
809
	P_DamageMobj (linetarget, mo->target,mo->target, damage);
810
    }
811
}
812
 
813
 
814
//
815
// A_BFGsound
816
//
817
void
818
A_BFGsound
819
( player_t*	player,
820
  pspdef_t*	psp )
821
{
822
    S_StartSound (player->mo, sfx_bfg);
823
}
824
 
825
 
826
 
827
//
828
// P_SetupPsprites
829
// Called at start of level for each player.
830
//
831
void P_SetupPsprites (player_t* player)
832
{
833
    int	i;
834
 
835
    // remove all psprites
836
    for (i=0 ; i
837
	player->psprites[i].state = NULL;
838
 
839
    // spawn the gun
840
    player->pendingweapon = player->readyweapon;
841
    P_BringUpWeapon (player);
842
}
843
 
844
 
845
 
846
 
847
//
848
// P_MovePsprites
849
// Called every tic by player thinking routine.
850
//
851
void P_MovePsprites (player_t* player)
852
{
853
    int		i;
854
    pspdef_t*	psp;
855
    state_t*	state;
856
 
857
    psp = &player->psprites[0];
858
    for (i=0 ; i
859
    {
860
	// a null state means not active
861
	if ( (state = psp->state) )
862
	{
863
	    // drop tic count and possibly change state
864
 
865
	    // a -1 tic count never changes
866
	    if (psp->tics != -1)
867
	    {
868
		psp->tics--;
869
		if (!psp->tics)
870
		    P_SetPsprite (player, i, psp->state->nextstate);
871
	    }
872
	}
873
    }
874
 
875
    player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
876
    player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
877
}
878
 
879