1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
|
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// bulletFest.CPP
// Project: Postal
//
// History:
// 02/18/97 JMI Started.
//
// 02/19/97 JMI Now Ricochet(), Impact(), and Flare() create animations.
//
// 02/19/97 JMI Now Fire() and FireDeluxe() take all 3 CSmash masks.
//
// 02/19/97 JMI FireDeluxe(), forces impact, if terrain contact is off
// of realm.
//
// 02/27/97 JMI In progress.
//
// 02/27/97 JMI Experimented with various ways of determining the terrain
// angle but it is very difficult considering the attribute
// maps granularity is 8!
//
// 02/27/97 JMI Forgot to remove feedback flag to preprocessor.
//
// 02/27/97 JMI Fongoolishness.
//
// 03/03/97 JMI Now uses QuickCheckClosest() instead of QuickCheck().
//
// 03/05/97 JMI Fixed rspMod360() usage to current.
//
// 03/10/97 JMI Added unimplemented UpdateTarget() stub.
//
// 03/21/97 JMI Added optional tracers.
//
// 03/21/97 BRH Fixed a small divide by zero error check.
//
// 04/02/97 JMI Changed ms_ati to m_ati.
// Also, added ms_u8TracerIndex.
// Also, removed all the edge info crap.
// Also, added two additional ways of displaying tracers.
//
// 04/02/97 JMI Feedback now uses CSpriteLine2d.
// Also, now uses mapping using 4 clusters &'d together
// as an index to determine the terrain angle.
// Does not seem to work.
//
// 04/02/97 JMI Switched back to METHOD 2 and fixed a divide by zero bug
// in there. Also, reduced TRACER_MAX_LENGTH to 3.
//
// 04/10/97 BRH Updated this to work with the new multi layer attribute
// maps.
//
// 04/24/97 JMI Added smid parameter specifying type of ammo noise to
// FireDeluxe() and Flare().
//
// 04/29/97 JMI Fire() now allows bullets to go 10 pixels off the realm
// to make it appear as if the edge of the realm has no
// affect on them (except the bottom edge which is greater
// so that, if the bullet is exceptionally high, it will
// go, hopefully, entirely off screen).
//
// 05/29/97 JMI Removed references to m_pRealm->m_pAttribMap which no longer
// exists.
//
// 06/11/97 JMI Added Preload() for loading assets used during play.
//
// 06/17/97 JMI Converted all occurrences of rand() to GetRand() and
// srand() to SeedRand().
//
// 06/26/97 JMI Now uses pRealm->Map3Dto2D() in Fire() (was using
// the global version defined in reality.h which is no more).
//
// 06/28/97 JMI Missed some Map3Dto2D()'s that were #if 0'ed out.
//
// 06/30/97 JMI Now uses CRealm's new GetRealmWidth() and *Height()
// for dimensions of realm's X/Z plane.
//
// 07/01/97 JMI Now bases ricochets on GetRandom().
//
// 07/09/97 JMI Now uses m_pRealm->Make2dResPath() to get the fullpath
// for 2D image components.
//
// 07/09/97 JMI Changed Preload() to take a pointer to the calling realm
// as a parameter.
//
// 07/18/97 JMI Got rid of bogus immitation PlaySample functions.
// Now there is one PlaySample() function. Also, you now
// MUST specify a category and you don't have to specify a
// SoundInstance ptr to specify a volume.
//
// 07/27/97 JMI No longer calls CRealm::Make2dResPath() on res names
// given to CAnimThing as it already does that
// automatically.
//
// 08/20/97 BRH Changed ricochet sounds to Weapon volume control
// instead of destruction.
//
// 09/18/97 JMI UpdateTracerColor() now takes a realm ptr and now is
// color mapped on the first Fire() call.
//
//////////////////////////////////////////////////////////////////////////////
//
// bulletFest handles launching of bullets. Currently it doesn't seem like
// much more than a function call, but eventually it will need to keep track
// of some per instance data.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// C Headers -- Must be included before RSPiX.h b/c RSPiX utilizes SHMalloc.
//////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// RSPiX Headers.
///////////////////////////////////////////////////////////////////////////////
#include "RSPiX.h"
//////////////////////////////////////////////////////////////////////////////
// Postal headers.
//////////////////////////////////////////////////////////////////////////////
#include "bulletFest.h"
#include "AnimThing.h"
#include "SampleMaster.h"
#include "reality.h"
//////////////////////////////////////////////////////////////////////////////
// Module specific macros.
//////////////////////////////////////////////////////////////////////////////
#define IMPACT_RES_NAME "Ricochet.aan"
#define RICOCHET_RES_NAME "Ricochet.aan"
#define FLARE_RES_NAME "MuzzleFlare.aan"
#define TERRAIN_CHECK_DIST 1
#define TERRAIN_CHECK_GRANULARITY 4
#define TRACER_COLOR_RED 250
#define TRACER_COLOR_GREEN 200
#define TRACER_COLOR_BLUE 50
// The color range that is the same on all hoods.
#define CONSTANT_COLOR_START_INDEX 95
#define NUM_CONSTANT_COLOR_INDICES (256 - CONSTANT_COLOR_START_INDEX - 10)
#define TRACER_WRAP_DISTANCE 10
#define TRACER_MAX_LENGTH 3
#define LEFT_TOP 0x1
#define RIGHT_TOP 0x2
#define LEFT_BOTTOM 0x4
#define RIGHT_BOTTOM 0x8
// This is done as a mask instead of a mod for speed.
// Note that 0xFFFF is 100% of the time, 0x00FF is 50%, etc.
// Chances are number of set bits out of 16.
#define RICOCHET_CHANCE_MASK 0x0001
#define BULLET_SND_HALF_LIFE 1000
//////////////////////////////////////////////////////////////////////////////
// Module specific typedefs.
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Exported (extern) variables.
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Module specific (static) variables / Instantiate class statics.
//////////////////////////////////////////////////////////////////////////////
U8 CBulletFest::ms_u8TracerIndex = 0; // The color index to use for tracers.
// This value is gotten only once per
// execution of this program; therefore,
// it is gotten from the group of colors
// that don't change from hood to hood.
// Maps a combination of bits to an angle.
// Each bit set to 1 indicates the terrain in that location is higher than
// the specified height.
// Less than 2 bits set is considered not enough info.
// 2 diagonally adjacent bits is considered not enough info.
static int16_t ms_asTerrainAngleMap[16] =
{
-1, // 0000 ==
-1, // 0001 == LEFT_TOP
-1, // 0010 == RIGHT_TOP
0, // 0011 == RIGHT_TOP | LEFT_TOP
-1, // 0100 == LEFT_BOTTOM
90, // 0101 == LEFT_BOTTOM | LEFT_TOP
-1, // 0110 == LEFT_BOTTOM | RIGHT_TOP
45, // 0111 == LEFT_BOTTOM | RIGHT_TOP | LEFT_TOP
-1, // 1000 == RIGHT_BOTTOM
-1, // 1001 == RIGHT_BOTTOM | LEFT_TOP
270, // 1010 == RIGHT_BOTTOM | RIGHT_TOP
315, // 1011 == RIGHT_BOTTOM | RIGHT_TOP | LEFT_TOP
180, // 1100 == RIGHT_BOTTOM | LEFT_BOTTOM
135, // 1101 == RIGHT_BOTTOM | LEFT_BOTTOM | LEFT_TOP
225, // 1110 == RIGHT_BOTTOM | LEFT_BOTTOM | RIGHT_TOP
-1, // 1111 == RIGHT_BOTTOM | LEFT_BOTTOM | RIGHT_TOP | LEFT_TOP
};
//////////////////////////////////////////////////////////////////////////////
// Module specific (static) protos.
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Functions.
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Get max height in a rectangle.
//////////////////////////////////////////////////////////////////////////////
inline
int16_t GetMaxHeight(
CRealm* pRealm, // In: Realm in question.
int16_t sX, // In: X position on terrain.
int16_t sZ, // In: Z position on terrain.
int16_t sW, // In: Amount in X direction.
int16_t sH) // In: Amount in Z direction.
{
int16_t sX2 = sX + sW;
int16_t sZ2 = sZ + sH;
int16_t sIterX;
int16_t sIterZ;
int16_t sMaxHeight = -32767;
for (sIterZ = sZ; sIterZ < sZ2; sIterZ++)
{
for (sIterX = sX; sIterX < sX2; sIterX++)
{
// Relying on NON-macro MAX (macro MAX would evaluate one of the paramters
// twice).
sMaxHeight = MAX(sMaxHeight, pRealm->GetHeight(sIterX, sIterZ));
#if 0
// FEEDBACK.
// Create a line sprite.
CSpriteLine2d* psl2d = new CSpriteLine2d;
if (psl2d != NULL)
{
psl2d->m_sX2 = sIterX,
psl2d->m_sY2 = sIterZ;
psl2d->m_sX2End = sIterX + 1;
psl2d->m_sY2End = sIterZ + 1;
psl2d->m_sPriority = 0;
psl2d->m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer(sIterX, sIterZ));
psl2d->m_u8Color = 250;
// Destroy when done.
psl2d->m_sInFlags = CSprite::InDeleteOnRender;
// Put 'er there.
pRealm->m_scene.UpdateSprite(psl2d);
}
#endif
}
}
return sMaxHeight;
}
//////////////////////////////////////////////////////////////////////////////
//
// Take a point inside terrain and find our way out via X/Z plane.
// Once out, scan along terrain to determine the angle of the terrain that
// exceeds sY in height.
// ASSUMPTION: sX, sY, sZ is a point inside terrain.
//
//////////////////////////////////////////////////////////////////////////////
inline
bool GetTerrainAngle( // true, if terrain angle determined.
// false, if not enough iterations available to
// determine angle.
CRealm* pRealm, // In: Realm in question.
int16_t sX, // In: X position on terrain.
int16_t sY, // In: Y position on terrain.
int16_t sZ, // In: Z position on terrain.
int16_t sMaxOutIterations, // In: Max iterations to get out of terrain
// (i.e., find terrain edge).
int16_t sNumScanIterations, // In: Num iterations to scan edge of terrain
// in EACH direction.
int16_t sInAngle, // In: Angle from which object entered terrain.
// This angle + 180 is used to scan out.
int16_t* psAngle) // Out: Angle of terrain at specified location
// on X/Z plane.
{
bool bFoundAngle = false; // Assume not found.
#if 0 // NOT WORKING CORRECTLY.
////////////////////// Get out of terrain /////////////////////////////////
sInAngle = rspMod360(sInAngle);
// Determine rate to get in deeper.
float fInRateX = COSQ[sInAngle];
float fInRateZ = -SINQ[sInAngle];
// Get angles to get out.
short sOutAngle = rspMod360(90 + sInAngle); // 180 + sInAngle - 90
// Determine proper rate to get out of terrain.
float fRateX = COSQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleX*/;
float fRateZ = -SINQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleY*/;
const float fInMult = (sMaxOutIterations - 3);
// Set initial position inside terrain.
float fPosX = sX + fInRateX * fInMult;
float fPosZ = sZ + fInRateZ * fInMult;
// Store extents.
short sMaxX = pRealm->GetRealmWidth();
short sMaxZ = pRealm->GetRealmHeight();
short sIterations = 0;
// Quicker access to attribute map for loop.
RAttributeMap* pattrib = pRealm->m_pAttribMap;
short sTerrainH;
bool bGotOut = false;
// Scan while in realm.
while (
fPosX > 0.0
&& fPosZ > 0.0
&& fPosX < sMaxX
&& fPosZ < sMaxZ
&& sIterations < sMaxOutIterations)
{
sTerrainH = m_pRealm->GetHeight((short) fPosX, (short) fPosZ);
// If above terrain . . .
if (sY > sTerrainH)
{
bGotOut = true;
// fPosX -= fRateX;
// fPosZ -= fRateZ;
break;
}
fPosX += fRateX;
fPosZ += fRateZ;
sIterations++;
}
// If we did not find our way out . . .
if (bGotOut == false)
{
sOutAngle = rspMod360(270 + sInAngle); // 180 + sInAngle + 90
fRateX = COSQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleX*/;
fRateZ = -SINQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleY*/;
fPosX = sX + fInRateX * fInMult;
fPosZ = sZ + fInRateZ * fInMult;
sIterations = 0;
// Scan while in realm.
while (
fPosX > 0.0
&& fPosZ > 0.0
&& fPosX < sMaxX
&& fPosZ < sMaxZ
&& sIterations < sMaxOutIterations)
{
sTerrainH = m_pRealm->GetHeight((short) fPosX, (short) fPosZ);
// If above terrain . . .
if (sY > sTerrainH)
{
bGotOut = true;
// fPosX -= fRateX;
// fPosZ -= fRateZ;
break;
}
fPosX += fRateX;
fPosZ += fRateZ;
sIterations++;
}
}
// If we found our way out in either case . . .
if (bGotOut == true)
{
// Determine angle of a line between the two points.
short sDeltaX = fPosX - (sX - fInRateX);
short sDeltaZ = (sZ - fInRateZ) - fPosZ;
*psAngle = rspATan(sDeltaX, sDeltaZ);
// Found angle.
bFoundAngle = true;
}
#else
// Check 4 surrounding points.
int16_t sIndex = 0;
if (GetMaxHeight(pRealm, sX - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, sZ - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
sIndex = LEFT_TOP;
if (GetMaxHeight(pRealm, sX + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, sZ - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
sIndex |= RIGHT_TOP;
if (GetMaxHeight(pRealm, sX - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, sZ + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
sIndex |= LEFT_BOTTOM;
if (GetMaxHeight(pRealm, sX + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, sZ + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
sIndex |= RIGHT_BOTTOM;
// Get terrain angle.
*psAngle = ms_asTerrainAngleMap[sIndex];
if (*psAngle > -1)
{
bFoundAngle = true;
}
#endif
return bFoundAngle;
}
//////////////////////////////////////////////////////////////////////////////
//
// Launch a bullet, create a muzzle flare at the src (sX, sY, sZ), and,
// if no CThing hit, create a ricochet at where the bullet contacted
// terrain (*psX, *psY, *psZ).
// This same process can be done in pieces with more control to the user
// by calling Fire(), Flare(), and Ricochet().
//
//////////////////////////////////////////////////////////////////////////////
bool CBulletFest::FireDeluxe( // Returns what and as Fire() would.
int16_t sAngleY, // In: Angle of launch in degrees (on X/Z plane).
int16_t sAngleZ, // In: Angle of launch in degrees (on X/Y plane).
int16_t sX, // In: Launch position.
int16_t sY, // In: Launch position.
int16_t sZ, // In: Launch position.
int16_t sRange, // In: Maximum distance.
CRealm* pRealm, // In: Realm in which to fire.
CSmash::Bits bitsInclude, // In: Mask of CSmash masks that this bullet can hit.
CSmash::Bits bitsDontCare, // In: Mask of CSmash masks that this bullet does not care to hit.
CSmash::Bits bitsExclude, // In: Mask of CSmash masks that this bullet cannot hit.
int16_t sMaxRicochetAngle, // In: Maximum angle with terrain that can cause
// a ricochet (on X/Z plane).
int16_t sMaxRicochets, // In: The maximum number of ricochets.
int16_t* psX, // Out: Hit position.
int16_t* psY, // Out: Hit position.
int16_t* psZ, // Out: Hit position.
CThing** ppthing, // Out: Ptr to thing hit or NULL.
bool bTracer /*= true*/, // In: Draw a tracer at random point along path.
SampleMasterID smid /*= g_smidBulletFire*/) // In: Use ammo sample.
{
bool bHit = false; // Assume no collision with CThing within u32ThingMask.
// Create a muzzle flare.
Flare(sAngleY, sX, sY, sZ, pRealm, smid);
int16_t sRicochets = 0;
bool bImpact = false;
int16_t sTerrainAngle = 0;
// While no hits and not out of ricochets . . .
while (bHit == false && sRicochets <= sMaxRicochets && bImpact == false)
{
sAngleY = rspMod360(sAngleY);
bHit = Fire(
sAngleY, // In: Angle of launch in degrees (on X/Z plane).
sAngleZ, // In: Angle of launch in degrees (on X/Y plane).
sX, // In: Launch position.
sY, // In: Launch position.
sZ, // In: Launch position.
sRange, // In: Maximum distance.
pRealm, // In: Realm in which to fire.
bitsInclude, // In: Mask of CSmash masks that this bullet can hit.
bitsDontCare, // In: Mask of CSmash masks that this bullet does not care to hit.
bitsExclude, // In: Mask of CSmash masks that this bullet cannot hit.
psX, // Out: Hit position.
psY, // Out: Hit position.
psZ, // Out: Hit position.
ppthing, // Out: Ptr to thing hit or NULL.
bTracer); // In: Draw a tracer at random point along path.
// If no hit . . .
if (bHit == false)
{
// If on screen . . .
if ( *psX > 0 && *psX < pRealm->GetRealmWidth()
&& *psZ > 0 && *psZ - *psY < pRealm->GetRealmHeight() )
{
#if 0
// If out of ricochets . . .
if (sRicochets >= sMaxRicochets)
{
// Impact.
bImpact = true;
}
else
{
if (GetTerrainAngle(pRealm, *psX, *psY, *psZ, 10, 10, sAngleY, &sTerrainAngle) == true)
{
// Check for ricochet. If our contact angle is within sMaxRicochetAngle
// of terrain angle . . .
short sAngleDif = sTerrainAngle - sAngleY;
if (ABS(sAngleDif) > sMaxRicochetAngle)
{
bImpact = true;
}
else
{
sAngleY = sTerrainAngle + sAngleDif;
}
#if 0
// Might have to break this into four quadrants.
// This works for the case when sAngle is in quadrant 1
// and sTerrainAngle is in quadrant 2.
short sAngleDif = (180 - sTerrainAngle) + sAngleY;
if (sAngleDif > 90)
{
sAngleDif = 180 - sAngleDif;
}
if (sAngleDif > sMaxRicochetAngle)
{
// Impact.
bImpact = true;
}
else // Ricochet.
{
// **** Determine deflection angle here.
sAngleY = 360 - sAngleDif;
}
#endif
}
else
{
bImpact = true;
}
}
// If impacting . . .
if (bImpact == true)
{
// Impact at angle of shot.
Impact(sAngleY, *psX, *psY, *psZ, pRealm);
}
else
{
// Ricochet at angle of deflection.
Ricochet(sAngleY, *psX, *psY, *psZ, pRealm);
sX = *psX;
sY = *psY;
sZ = *psZ;
}
#else
// Ricochet is random.
if (GetRandom() & RICOCHET_CHANCE_MASK)
{
// Impact at angle of shot.
Impact(sAngleY, *psX, *psY, *psZ, pRealm);
}
else
{
// Ricochet at angle of deflection.
Ricochet(sAngleY, *psX, *psY, *psZ, pRealm);
}
// Always stop though.
bImpact = true;
#endif
}
}
sRicochets++;
}
return bHit;
}
//////////////////////////////////////////////////////////////////////////////
//
// Create a muzzle flare effect.
//
//////////////////////////////////////////////////////////////////////////////
void CBulletFest::Flare( // Returns nothing.
int16_t sAngle, // In: Angle of launch in degrees (on X/Z plane).
int16_t sX, // In: Launch position.
int16_t sY, // In: Launch position.
int16_t sZ, // In: Launch position.
CRealm* pRealm, // In: Realm in which to fire.
SampleMasterID smid /*= g_smidBulletFire*/) // In: Use ammo sample.
{
// Create the animator . . .
CAnimThing* pat = new CAnimThing(pRealm);
ASSERT(pat != NULL);
strcpy(pat->m_szResName, FLARE_RES_NAME);
// Start it up:
// No looping.
pat->m_sLoop = FALSE;
// No notification necessary.
// NOTE: sAngle is currently not utilized.
pat->Setup(sX, sY, sZ);
// Start sample.
PlaySample(
smid,
SampleMaster::Weapon,
DistanceToVolume(sX, sY, sZ, BULLET_SND_HALF_LIFE) );
}
//////////////////////////////////////////////////////////////////////////////
//
// Create a impact effect.
//
//////////////////////////////////////////////////////////////////////////////
void CBulletFest::Impact( // Returns nothing.
int16_t sAngle, // In: Angle of launch in degrees (on X/Z plane).
int16_t sX, // In: Launch position.
int16_t sY, // In: Launch position.
int16_t sZ, // In: Launch position.
CRealm* pRealm) // In: Realm in which to fire.
{
// Create the animator . . .
CAnimThing* pat = new CAnimThing(pRealm);
ASSERT(pat != NULL);
strcpy(pat->m_szResName, IMPACT_RES_NAME);
// Start it up:
// No looping.
pat->m_sLoop = FALSE;
// No notification necessary.
// NOTE: sAngle is currently not utilized.
pat->Setup(sX, sY, sZ);
}
//////////////////////////////////////////////////////////////////////////////
//
// Create a ricochet effect.
//
//////////////////////////////////////////////////////////////////////////////
void CBulletFest::Ricochet( // Returns nothing.
int16_t sAngle, // In: Angle of launch in degrees (on X/Z plane).
int16_t sX, // In: Launch position.
int16_t sY, // In: Launch position.
int16_t sZ, // In: Launch position.
CRealm* pRealm) // In: Realm in which to fire.
{
// Create the animator . . .
CAnimThing* pat = new CAnimThing(pRealm);
ASSERT(pat != NULL);
strcpy(pat->m_szResName, RICOCHET_RES_NAME);
// Start it up:
// No looping.
pat->m_sLoop = FALSE;
// No notification necessary.
// NOTE: sAngle is currently not utilized.
pat->Setup(sX, sY, sZ);
// Start sample.
PlaySample(
(GetRand() % 2) ? g_smidRicochet1 : g_smidRicochet2,
SampleMaster::Weapon,
DistanceToVolume(sX, sY, sZ, BULLET_SND_HALF_LIFE) );
}
//////////////////////////////////////////////////////////////////////////////
//
// Launch a bullet.
//
//////////////////////////////////////////////////////////////////////////////
bool CBulletFest::Fire( // Returns true if a hit, false otherwise.
int16_t sAngleY, // In: Angle of launch in degrees (on X/Z plane).
int16_t sAngleZ, // In: Angle of launch in degrees (on X/Y plane).
int16_t sX, // In: Launch position.
int16_t sY, // In: Launch position.
int16_t sZ, // In: Launch position.
int16_t sRange, // In: Maximum distance.
CRealm* pRealm, // In: Realm in which to fire.
CSmash::Bits bitsInclude, // In: Mask of CSmash masks that this bullet can hit.
CSmash::Bits bitsDontCare, // In: Mask of CSmash masks that this bullet does not care to hit.
CSmash::Bits bitsExclude, // In: Mask of CSmash masks that this bullet cannot hit.
int16_t* psX, // Out: Hit position.
int16_t* psY, // Out: Hit position.
int16_t* psZ, // Out: Hit position.
CThing** ppthing, // Out: Ptr to thing hit or NULL.
bool bTracer /*= true*/) // In: Draw a tracer at random point along path.
{
bool bHit = false; // Assume no collision with CThing within u32ThingMask.
// If tracer color not yet color mapped . . .
if (ms_u8TracerIndex == 0)
{
UpdateTracerColor(pRealm);
}
/////////// Determine length of travel if no CThings hit ///////////////////
// Get most efficient increments that won't miss any attributes.
// For the rates we use trig with a hypotenuse of 1 which will give
// us a rate <= 1.0 and then multiply by the scaling of the map for
// a reasonable doubling or so of the speed of this alg.
// sAngle must be between 0 and 359.
sAngleY = rspMod360(sAngleY);
sAngleZ = rspMod360(sAngleZ);
float fRateX = COSQ[sAngleY]/* * pRealm->m_pAttribMap->m_sScaleX*/;
float fRateZ = -SINQ[sAngleY]/* * pRealm->m_pAttribMap->m_sScaleY*/;
float fRateY = SINQ[sAngleZ];
// Determine amount traveled per iteration just once.
float fIterDist = sqrt(ABS2(fRateX, fRateY, fRateZ) );
// Set initial position to first point to check (NEVER checks original position).
float fPosX = sX + fRateX;
float fPosY = sY + fRateY;
float fPosZ = sZ + fRateZ;
float fTotalDist = 0.0F;
// Store extents allowing bullets to go 10 pixels off each boundary.
// This makes them appear to be unhindered by the edge of the realm.
// (except the bottom edge which is greater so that, if the bullet
// is exceptionally high, it will go, hopefully, entirely off screen).
int16_t sMaxX = pRealm->GetRealmWidth() + 10;
int16_t sMaxY = 512; // Robustness: Guard against infinite loop
// in the remote possibility that we shoot
// straight up (currently one can only shoot
// horizontally).
int16_t sMaxZ = pRealm->GetRealmHeight() + sY + 10;
int16_t sMinX = -10;
int16_t sMinY = -512;
int16_t sMinZ = -10;
int16_t sCurH;
// Scan while in realm.
while (
fPosX > sMinX
&& fPosY > sMinY
&& fPosZ > sMinZ
&& fPosX < sMaxX
&& fPosY < sMaxY
&& fPosZ < sMaxZ)
{
sCurH = pRealm->GetHeight((int16_t) fPosX, (int16_t) fPosZ);
// If bullet below or at terrain . . .
if (fPosY <= sCurH)
{
break;
}
// If we've exceeded range . . .
if (fTotalDist > sRange)
{
// Drop bullet via gravity.
fPosY -= 1.0; // FUDGE!
}
fPosX += fRateX;
fPosY += fRateY;
fPosZ += fRateZ;
fTotalDist += fIterDist;
}
// Create 3D line segment.
R3DLine line;
line.X1 = sX;
line.Y1 = sY;
line.Z1 = sZ;
line.X2 = fPosX;
line.Y2 = fPosY;
line.Z2 = fPosZ;
// Determine if anything with mask u32ThingMask was hit . . .
CSmash* psmash;
if (pRealm->m_smashatorium.QuickCheckClosest(
&line,
bitsInclude,
bitsDontCare,
bitsExclude,
&psmash) == true)
{
// Set *ppthing to thing hit.
*ppthing = psmash->m_pThing;
bHit = true;
// Set end pt.
*psX = psmash->m_sphere.sphere.X;
*psY = psmash->m_sphere.sphere.Y;
*psZ = psmash->m_sphere.sphere.Z;
}
else
{
// Clear thing ptr.
*ppthing = NULL;
// Set end pt.
*psX = fPosX;
*psY = fPosY;
*psZ = fPosZ;
}
if (bTracer == true)
{
// Create a line sprite.
CSpriteLine2d* psl2d = new CSpriteLine2d;
if (psl2d != NULL)
{
// Get distance. Since an interception may have occurred, we need to redetermine the
// distance.
// We use one coordinate of the end point against one coordinate of the interception point
// to get a ratio and apply that to the total distance.
int16_t sDistance = 0;
if (fPosX - sX != 0.0)
{
sDistance = fTotalDist * ((*psX - sX) / (fPosX - sX));
}
#define METHOD 2
#define TRACER_ACCUMMULATION_STEP_MAX 80
#define TRACER_ACCUMMULATION_STEP_MIN 10
int16_t sRand = 0;
#if METHOD == 1 // Method 1: Random dist between here and there.
if (sDistance != 0)
{
sRand = GetRand() % sDistance;
}
#elif METHOD == 2 // Method 2: Cummulating position with wrapping.
if (sDistance != 0)
{
sRand = m_sCurTracerPos = (m_sCurTracerPos + GetRand() % TRACER_ACCUMMULATION_STEP_MAX) % sDistance;
}
#else METHOD == 3 // Method 3: Multiple Method 1 and Method 2's.
// Reduce distance.
sDistance -= TRACER_ACCUMMULATION_STEP_MAX;
while (sRand < sDistance)
{
sRand += (GetRand() % (TRACER_ACCUMMULATION_STEP_MAX - TRACER_ACCUMMULATION_STEP_MIN)) + TRACER_ACCUMMULATION_STEP_MIN;
// If no current sprite . . .
if (psl2d == NULL)
{
// Create a line sprite.
psl2d = new CSpriteLine2d;
}
// If we have a sprite . . .
if (psl2d != NULL)
{
#endif
int16_t sRand2 = GetRand() % TRACER_MAX_LENGTH;
float fStartX = sX + fRateX * sRand;
float fStartY = sY + fRateY * sRand;
float fStartZ = sZ + fRateZ * sRand;
pRealm->Map3Dto2D(
fStartX,
fStartY,
fStartZ,
&(psl2d->m_sX2),
&(psl2d->m_sY2) );
pRealm->Map3Dto2D(
fStartX + fRateX * sRand2,
fStartY + fRateY * sRand2,
fStartZ + fRateZ * sRand2,
&(psl2d->m_sX2End),
&(psl2d->m_sY2End) );
psl2d->m_sPriority = fStartZ;
psl2d->m_sLayer = CRealm::GetLayerViaAttrib(pRealm->GetLayer((int16_t) fStartX, (int16_t) fStartZ));
psl2d->m_u8Color = ms_u8TracerIndex;
// Destroy when done.
psl2d->m_sInFlags = CSprite::InDeleteOnRender;
// Put 'er there.
pRealm->m_scene.UpdateSprite(psl2d);
#if METHOD == 3
// Done with this 'sprite'.
psl2d = NULL;
}
}
#endif
}
else
{
TRACE("Fire(): Unable to allocate CSpriteLine2d.\n");
}
}
#if 0
// FEEDBACK.
// Create a line sprite.
CSpriteLine2d* psl2d = new CSpriteLine2d;
if (psl2d != NULL)
{
pRealm->Map3Dto2D(
sX,
sY,
sZ,
&(psl2d->m_sX2),
&(psl2d->m_sY2) );
pRealm->Map3Dto2D(
sX + fRateX * fTotalDist,
sY + fRateY * fTotalDist,
sZ + fRateZ * fTotalDist,
&(psl2d->m_sX2End),
&(psl2d->m_sY2End) );
psl2d->m_sPriority = sZ;
psl2d->m_sLayer = CRealm::GetLayerViaAttrib(pRealm->GetLayer(sX, sZ));
psl2d->m_u8Color = ms_u8TracerIndex;
// Destroy when done.
psl2d->m_sInFlags = CSprite::InDeleteOnRender;
// Put 'er there.
pRealm->m_scene.UpdateSprite(psl2d);
}
#endif
return bHit;
}
///////////////////////////////////////////////////////////////////////////////
// Update current targeting information. This will function will
// continually track the position of a target in order to determine
// how much it is moving. The idea being that targets that move
// irratically(sp?) and more often are harder to hit.
///////////////////////////////////////////////////////////////////////////////
void CBulletFest::UpdateTarget( // Returns nothing.
int16_t sAngle, // In: Angle of aim in degrees (on X/Z plane).
int16_t sX, // In: Aim position.
int16_t sY, // In: Aim position.
int16_t sZ, // In: Aim position.
CRealm* pRealm) // In: Realm in which to target.
{
// NYI!
}
///////////////////////////////////////////////////////////////////////////////
// Updates the static tracer color.
// (static)
///////////////////////////////////////////////////////////////////////////////
void CBulletFest::UpdateTracerColor(
CRealm* prealm) // In: Calling realm.
{
ASSERT(prealm->m_phood);
ASSERT(prealm->m_phood->m_pimBackground);
ASSERT(prealm->m_phood->m_pimBackground->m_pPalette);
RPal* ppal = prealm->m_phood->m_pimBackground->m_pPalette;
ms_u8TracerIndex = rspMatchColorRGB( // Out: Matched index.
TRACER_COLOR_RED, // In: Pixel's red value.
TRACER_COLOR_GREEN, // In: Pixel's green value.
TRACER_COLOR_BLUE, // In: Pixel's blue value.
CONSTANT_COLOR_START_INDEX, // In: Min mappable index (affects source).
// Do NOT include the top 10 windows colors!
NUM_CONSTANT_COLOR_INDICES, // In: Num mappable indices (affects source).
ppal->Red(0), // In: Beginning of red color table.
ppal->Green(0), // In: Beginning of green color table.
ppal->Blue(0), // In: Beginning of blue color table.
ppal->GetPalEntrySize() // In: Size to increment between each index in each table.
);
}
///////////////////////////////////////////////////////////////////////////////
// Preload any assets that may be used.
// (static).
///////////////////////////////////////////////////////////////////////////////
int16_t CBulletFest::Preload(
CRealm* prealm) // In: Calling realm.
{
CAnimThing::ChannelAA* paaCache;
int16_t sResult = 0;
if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(IMPACT_RES_NAME), &paaCache) == 0)
rspReleaseResource(&g_resmgrGame, &paaCache);
else
sResult |= 1;
if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(RICOCHET_RES_NAME), &paaCache) == 0)
rspReleaseResource(&g_resmgrGame, &paaCache);
else
sResult |= 1;
if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(FLARE_RES_NAME), &paaCache) == 0)
rspReleaseResource(&g_resmgrGame, &paaCache);
else
sResult |= 1;
return sResult;
}
///////////////////////////////////////////////////////////////////////////////
// EOF
///////////////////////////////////////////////////////////////////////////////
|