Revisión | 1892b63c7310093f82fb866ac717912aeb07dc89 (tree) |
---|---|
Tiempo | 2022-09-15 15:07:17 |
Autor | Adam Kaminski <kaminskiadam9@gmai...> |
Commiter | Adam Kaminski |
Added the CVar "cl_identifymonsters" which allows monsters to be identified with cl_identifytarget.
@@ -31,6 +31,7 @@ | ||
31 | 31 | + - Added an option to filter the server list by name within the built-in server browser menu. [Kaminsky] |
32 | 32 | + - Added the dmflag "sv_dontoverrideplayercolors", which prevents player colors from being overriden. [Kaminsky] |
33 | 33 | + - Added the EVENT script types for domination: GAMEEVENT_DOMINATION_CONTROL for when a team takes control of a point sector, and GAMEEVENT_DOMINATION_POINT for when a team gets a point for owning a sector. [Kaminsky] |
34 | ++ - Added the CVar "cl_identifymonsters" which allows monsters to be identified with cl_identifytarget. [Kaminsky] | |
34 | 35 | - - Fixed: clients didn't initialize a sector's friction properly in some cases due to a superfluous check that wasn't removed earlier. [Kaminsky] |
35 | 36 | - - Fixed: the server wouldn't initialize compatflags and compatflags2 properly if entered as command line parameters. [Kaminsky] |
36 | 37 | - - Fixed: serverinfo CVars entered on the command line were restored in reverse order. [Kaminsky] |
@@ -83,6 +83,15 @@ | ||
83 | 83 | IDENTIFY_TARGET_CLASS, |
84 | 84 | }; |
85 | 85 | |
86 | +// [AK] Message levels used for cl_identifymonsters. | |
87 | +enum | |
88 | +{ | |
89 | + IDENTIFY_MONSTERS_OFF, | |
90 | + IDENTIFY_MONSTERS_NAME, | |
91 | + IDENTIFY_MONSTERS_DROPITEMS, | |
92 | + IDENTIFY_MONSTERS_GHOST, | |
93 | +}; | |
94 | + | |
86 | 95 | //***************************************************************************** |
87 | 96 | // VARIABLES |
88 | 97 |
@@ -146,6 +155,7 @@ | ||
146 | 155 | // CONSOLE VARIABLES |
147 | 156 | |
148 | 157 | CVAR( Int, cl_identifytarget, IDENTIFY_TARGET_NAME, CVAR_ARCHIVE ) |
158 | +CVAR( Int, cl_identifymonsters, IDENTIFY_MONSTERS_OFF, CVAR_ARCHIVE ) | |
149 | 159 | CVAR( Bool, cl_drawcoopinfo, true, CVAR_ARCHIVE ) |
150 | 160 | CVAR( Bool, r_drawspectatingstring, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG ) |
151 | 161 | CVAR( Bool, r_drawrespawnstring, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG ) |
@@ -396,7 +406,7 @@ | ||
396 | 406 | |
397 | 407 | //***************************************************************************** |
398 | 408 | // |
399 | -static player_t *HUD_ScanForTarget( AActor *pSource ) | |
409 | +static AActor *HUD_ScanForTarget( AActor *pSource ) | |
400 | 410 | { |
401 | 411 | FTraceResults trace; |
402 | 412 |
@@ -431,12 +441,8 @@ | ||
431 | 441 | if ( trace.HitType != TRACE_HitActor ) |
432 | 442 | return ( NULL ); |
433 | 443 | |
434 | - // Return NULL if the actor we hit is not a player. | |
435 | - if ( trace.Actor->player == NULL ) | |
436 | - return ( NULL ); | |
437 | - | |
438 | - // Return the player we found. | |
439 | - return ( trace.Actor->player ); | |
444 | + // Return the actor we found. | |
445 | + return ( trace.Actor ); | |
440 | 446 | } |
441 | 447 | } |
442 | 448 |
@@ -463,32 +469,55 @@ | ||
463 | 469 | // Look for players directly in front of the player. |
464 | 470 | if ( camera ) |
465 | 471 | { |
466 | - FString targetInfoMsg; | |
467 | - | |
468 | - // Search for a player directly in front of the camera. If none are found, exit. | |
469 | - player_t *pTargetPlayer = HUD_ScanForTarget( camera ); | |
470 | - if ( pTargetPlayer == NULL ) | |
472 | + // Search for a player or monster directly in front of the camera. If none are found, exit. | |
473 | + AActor *pTargetActor = HUD_ScanForTarget( camera ); | |
474 | + if (( pTargetActor == NULL ) || (( pTargetActor->player == NULL ) && (( pTargetActor->flags3 & MF3_ISMONSTER ) == false ))) | |
471 | 475 | return; |
472 | 476 | |
473 | - // [CK] If the player shouldn't be identified from decorate flags, ignore them | |
474 | - if ( pTargetPlayer->mo != NULL && ( pTargetPlayer->mo->STFlags & STFL_DONTIDENTIFYTARGET ) != 0 ) | |
477 | + // [CK] If the actor shouldn't be identified from decorate flags, ignore them. | |
478 | + // [AK] Likewise, ignore monsters if we don't want to identify them. | |
479 | + if ((( pTargetActor->STFlags & STFL_DONTIDENTIFYTARGET ) != 0 ) || (( cl_identifymonsters == IDENTIFY_MONSTERS_OFF ) && ( pTargetActor->flags3 & MF3_ISMONSTER ))) | |
475 | 480 | return; |
476 | 481 | |
477 | 482 | // Build the string and text color; |
478 | 483 | EColorRange color = CR_GRAY; |
479 | - targetInfoMsg.Format( "%s", pTargetPlayer->userinfo.GetName( )); | |
484 | + FString targetInfoMsg; | |
485 | + | |
486 | + if ( pTargetActor->player ) | |
487 | + { | |
488 | + targetInfoMsg = pTargetActor->player->userinfo.GetName( ); | |
489 | + } | |
490 | + else | |
491 | + { | |
492 | + targetInfoMsg = pTargetActor->GetTag( ); | |
493 | + | |
494 | + // [AK] Colorize the string in case the actor's name tag contains unformatted color codes. | |
495 | + V_ColorizeString( targetInfoMsg ); | |
496 | + } | |
480 | 497 | |
481 | 498 | // Attempt to use the team color. |
482 | - if (( GAMEMODE_GetCurrentFlags( ) & GMF_PLAYERSONTEAMS ) && ( pTargetPlayer->bOnTeam )) | |
483 | - color = static_cast<EColorRange>( TEAM_GetTextColor( pTargetPlayer->Team )); | |
499 | + if ( GAMEMODE_GetCurrentFlags( ) & GMF_PLAYERSONTEAMS ) | |
500 | + { | |
501 | + ULONG ulTeam = TEAM_None; | |
484 | 502 | |
485 | - // [AK] If this player is our teammate, print more information about them. | |
486 | - if (( pTargetPlayer->mo != NULL ) && ( pTargetPlayer->mo->IsTeammate( players[consoleplayer].mo ))) | |
503 | + // [AK] If the target is not a player, then check their designated team. | |
504 | + if ( pTargetActor->player == NULL ) | |
505 | + ulTeam = pTargetActor->DesignatedTeam; | |
506 | + else if ( pTargetActor->player->bOnTeam ) | |
507 | + ulTeam = pTargetActor->player->Team; | |
508 | + | |
509 | + // [AK] Only change the text color if this actor's team is valid. | |
510 | + if (( ulTeam != TEAM_None ) && ( TEAM_CheckIfValid( ulTeam ))) | |
511 | + color = static_cast<EColorRange>( TEAM_GetTextColor( ulTeam )); | |
512 | + } | |
513 | + | |
514 | + // [AK] If this actor is friendly to us, print more information about them. | |
515 | + if ( pTargetActor->IsFriend( players[consoleplayer].mo )) | |
487 | 516 | { |
488 | - // [AK] Print this player's current health and armor. | |
517 | + // [AK] Print this actor's current health and armor. | |
489 | 518 | if ( cl_identifytarget >= IDENTIFY_TARGET_HEALTH ) |
490 | 519 | { |
491 | - int healthPercentage = ( 100 * pTargetPlayer->mo->health ) / pTargetPlayer->mo->GetMaxHealth( ); | |
520 | + int healthPercentage = ( 100 * pTargetActor->health ) / ( pTargetActor->player ? pTargetActor->player->mo->GetMaxHealth( ) : pTargetActor->SpawnHealth( )); | |
492 | 521 | targetInfoMsg += '\n'; |
493 | 522 | |
494 | 523 | if ( healthPercentage <= 25 ) |
@@ -500,74 +529,126 @@ | ||
500 | 529 | else |
501 | 530 | targetInfoMsg += TEXTCOLOR_GREEN; |
502 | 531 | |
503 | - AInventory *armor = pTargetPlayer->mo->FindInventory( RUNTIME_CLASS( ABasicArmor )); | |
504 | - targetInfoMsg.AppendFormat( "%d" TEXTCOLOR_GREEN " / %d", pTargetPlayer->mo->health, armor ? armor->Amount : 0 ); | |
532 | + AInventory *armor = pTargetActor->FindInventory( RUNTIME_CLASS( ABasicArmor )); | |
533 | + targetInfoMsg.AppendFormat( "%d" TEXTCOLOR_GREEN " / %d", pTargetActor->health, armor ? armor->Amount : 0 ); | |
505 | 534 | } |
506 | 535 | |
507 | - // [AK] Print this player's current weapon if they have one. | |
508 | - if (( cl_identifytarget >= IDENTIFY_TARGET_WEAPON ) && ( pTargetPlayer->ReadyWeapon )) | |
536 | + if ( pTargetActor->player ) | |
509 | 537 | { |
510 | - targetInfoMsg += '\n'; | |
511 | - targetInfoMsg.AppendFormat( TEXTCOLOR_GREEN "%s", pTargetPlayer->ReadyWeapon->GetTag( )); | |
538 | + // [AK] Print this player's current weapon if they have one. | |
539 | + if (( cl_identifytarget >= IDENTIFY_TARGET_WEAPON ) && ( pTargetActor->player->ReadyWeapon )) | |
540 | + { | |
541 | + targetInfoMsg += '\n'; | |
542 | + targetInfoMsg.AppendFormat( TEXTCOLOR_GREEN "%s", pTargetActor->player->ReadyWeapon->GetTag( )); | |
512 | 543 | |
513 | - // [AK] If this weapon uses ammo, print the amount as well. | |
514 | - if ( pTargetPlayer->ReadyWeapon->Ammo1 ) | |
544 | + // [AK] If this weapon uses ammo, print the amount as well. | |
545 | + if ( pTargetActor->player->ReadyWeapon->Ammo1 ) | |
546 | + { | |
547 | + targetInfoMsg.AppendFormat( TEXTCOLOR_GOLD " %d", pTargetActor->player->ReadyWeapon->Ammo1->Amount ); | |
548 | + | |
549 | + // [AK] If this weapon also has a secondary ammo type, print that amount too. | |
550 | + if ( pTargetActor->player->ReadyWeapon->Ammo2 ) | |
551 | + targetInfoMsg.AppendFormat( " %d", pTargetActor->player->ReadyWeapon->Ammo2->Amount ); | |
552 | + } | |
553 | + } | |
554 | + | |
555 | + // [AK] Print this player's class. | |
556 | + if ( cl_identifytarget >= IDENTIFY_TARGET_CLASS ) | |
515 | 557 | { |
516 | - targetInfoMsg.AppendFormat( TEXTCOLOR_GOLD " %d", pTargetPlayer->ReadyWeapon->Ammo1->Amount ); | |
558 | + FString classString; | |
517 | 559 | |
518 | - // [AK] If this weapon also has a secondary ammo type, print that amount too. | |
519 | - if ( pTargetPlayer->ReadyWeapon->Ammo2 ) | |
520 | - targetInfoMsg.AppendFormat( " %d", pTargetPlayer->ReadyWeapon->Ammo2->Amount ); | |
560 | + // [AK] Display the name of the class the player is current playing as. | |
561 | + // If they're supposed to be morphed, don't print the name of their skin. | |
562 | + if ( pTargetActor->player->MorphedPlayerClass ) | |
563 | + { | |
564 | + classString = pTargetActor->player->MorphedPlayerClass->TypeName.GetChars( ); | |
565 | + } | |
566 | + else | |
567 | + { | |
568 | + FString skinString; | |
569 | + | |
570 | + if ( PlayerClasses.Size( ) > 1 ) | |
571 | + classString = GetPrintableDisplayName( pTargetActor->player->cls ); | |
572 | + | |
573 | + if ( classString.IsNotEmpty( )) | |
574 | + classString += " - "; | |
575 | + | |
576 | + // [AK] Get the name of the player's current skin, if skins are enabled. | |
577 | + // Their skin should only be displayed if they're playing the class meant | |
578 | + // for it. Otherwise, print "base" instead. | |
579 | + if ( cl_skins ) | |
580 | + { | |
581 | + const int skin = pTargetActor->player->userinfo.GetSkin( ); | |
582 | + | |
583 | + for ( unsigned int i = 0; i < PlayerClasses.Size( ); i++ ) | |
584 | + { | |
585 | + if (( pTargetActor->player->cls == PlayerClasses[i].Type ) && ( PlayerClasses[i].CheckSkin( skin ))) | |
586 | + { | |
587 | + skinString += skins[skin].name; | |
588 | + break; | |
589 | + } | |
590 | + } | |
591 | + } | |
592 | + | |
593 | + classString += skinString.IsNotEmpty( ) ? skinString : "Base"; | |
594 | + } | |
595 | + | |
596 | + targetInfoMsg += '\n'; | |
597 | + targetInfoMsg.AppendFormat( TEXTCOLOR_GREEN "%s", classString.GetChars( )); | |
598 | + } | |
599 | + } | |
600 | + } | |
601 | + | |
602 | + if ( pTargetActor->flags3 & MF3_ISMONSTER ) | |
603 | + { | |
604 | + // [AK] Print a list of this monster's drop items if we want to. | |
605 | + if ( cl_identifymonsters >= IDENTIFY_MONSTERS_DROPITEMS ) | |
606 | + { | |
607 | + FDropItem *pDropItems = pTargetActor->GetDropItems( ); | |
608 | + FString dropItemList; | |
609 | + | |
610 | + while ( pDropItems ) | |
611 | + { | |
612 | + const PClass *pClass = PClass::FindClass( pDropItems->Name ); | |
613 | + | |
614 | + // [AK] Ignore items that are invalid or have no chance of spawning. | |
615 | + if (( pClass != NULL ) && ( pDropItems->probability > -1 )) | |
616 | + { | |
617 | + if ( dropItemList.IsNotEmpty( )) | |
618 | + dropItemList += ", "; | |
619 | + | |
620 | + dropItemList += pDropItems->Name.GetChars( ); | |
621 | + | |
622 | + // [AK] Include this item's probability if applicable. | |
623 | + if ( pDropItems->probability < 255 ) | |
624 | + { | |
625 | + float fProbabilityPercentage = clamp( static_cast<float>(( pDropItems->probability + 1 ) * 100 ) / 256.f, 0.f, 100.f ); | |
626 | + | |
627 | + // [AK] When the probability is less than 1%, display it with two decimals. | |
628 | + // Otherwise, display it with one decimal. | |
629 | + if ( fProbabilityPercentage < 1.f ) | |
630 | + dropItemList.AppendFormat( " (%.2f%%)", fProbabilityPercentage ); | |
631 | + else | |
632 | + dropItemList.AppendFormat( " (%.1f%%)", fProbabilityPercentage ); | |
633 | + } | |
634 | + } | |
635 | + | |
636 | + pDropItems = pDropItems->Next; | |
637 | + } | |
638 | + | |
639 | + if ( dropItemList.IsNotEmpty( )) | |
640 | + { | |
641 | + targetInfoMsg += '\n'; | |
642 | + targetInfoMsg.AppendFormat( TEXTCOLOR_BLACK "%s", dropItemList.GetChars( )); | |
521 | 643 | } |
522 | 644 | } |
523 | 645 | |
524 | - // [AK] Print this player's class. | |
525 | - if ( cl_identifytarget >= IDENTIFY_TARGET_CLASS ) | |
526 | - { | |
527 | - FString classString; | |
528 | - | |
529 | - // [AK] Display the name of the class the player is current playing as. | |
530 | - // If they're supposed to be morphed, don't print the name of their skin. | |
531 | - if ( pTargetPlayer->MorphedPlayerClass ) | |
532 | - { | |
533 | - classString = pTargetPlayer->MorphedPlayerClass->TypeName.GetChars( ); | |
534 | - } | |
535 | - else | |
536 | - { | |
537 | - FString skinString; | |
538 | - | |
539 | - if ( PlayerClasses.Size( ) > 1 ) | |
540 | - classString = GetPrintableDisplayName( pTargetPlayer->cls ); | |
541 | - | |
542 | - if ( classString.IsNotEmpty( )) | |
543 | - classString += " - "; | |
544 | - | |
545 | - // [AK] Get the name of the player's current skin, if skins are enabled. | |
546 | - // Their skin should only be displayed if they're playing the class meant | |
547 | - // for it. Otherwise, print "base" instead. | |
548 | - if ( cl_skins ) | |
549 | - { | |
550 | - const int skin = pTargetPlayer->userinfo.GetSkin( ); | |
551 | - | |
552 | - for ( unsigned int i = 0; i < PlayerClasses.Size( ); i++ ) | |
553 | - { | |
554 | - if (( pTargetPlayer->cls == PlayerClasses[i].Type ) && ( PlayerClasses[i].CheckSkin( skin ))) | |
555 | - { | |
556 | - skinString += skins[skin].name; | |
557 | - break; | |
558 | - } | |
559 | - } | |
560 | - } | |
561 | - | |
562 | - classString += skinString.IsNotEmpty( ) ? skinString : "Base"; | |
563 | - } | |
564 | - | |
565 | - targetInfoMsg += '\n'; | |
566 | - targetInfoMsg.AppendFormat( TEXTCOLOR_GREEN "%s", classString.GetChars( )); | |
567 | - } | |
646 | + // [AK] Indicate if this monster is a ghost. | |
647 | + if (( cl_identifymonsters >= IDENTIFY_MONSTERS_GHOST ) && ( pTargetActor->flags3 & MF3_GHOST )) | |
648 | + targetInfoMsg += "\n" TEXTCOLOR_DARKGRAY "Is a ghost"; | |
568 | 649 | } |
569 | 650 | |
570 | - if (( pTargetPlayer->mo != NULL ) && ( pTargetPlayer->mo->IsTeammate( camera ))) | |
651 | + if ( pTargetActor->IsFriend( camera )) | |
571 | 652 | { |
572 | 653 | targetInfoMsg += "\n" TEXTCOLOR_DARKGREEN "Ally"; |
573 | 654 | } |
@@ -576,7 +657,7 @@ | ||
576 | 657 | targetInfoMsg += "\n" TEXTCOLOR_DARKRED "Enemy"; |
577 | 658 | |
578 | 659 | // If this player is carrying the terminator artifact, display his name in red. |
579 | - if (( terminator ) && ( pTargetPlayer->cheats2 & CF2_TERMINATORARTIFACT )) | |
660 | + if (( terminator ) && ( pTargetActor->player ) && ( pTargetActor->player->cheats2 & CF2_TERMINATORARTIFACT )) | |
580 | 661 | color = CR_RED; |
581 | 662 | } |
582 | 663 |
@@ -28,6 +28,14 @@ | ||
28 | 28 | 4.0, "Class and skin" |
29 | 29 | } |
30 | 30 | |
31 | +OptionValue ZA_IdentifyMonsterLevels | |
32 | +{ | |
33 | + 0.0, "Off" | |
34 | + 1.0, "Name" | |
35 | + 2.0, "Drop items" | |
36 | + 3.0, "Ghost" | |
37 | +} | |
38 | + | |
31 | 39 | OptionMenu ZA_MultiplayerOptions |
32 | 40 | { |
33 | 41 | Title "MULTIPLAYER OPTIONS" |
@@ -59,6 +67,7 @@ | ||
59 | 67 | Option "Taunts", "cl_taunts", "OnOff" |
60 | 68 | Option "Icons", "cl_icons", "OnOff" |
61 | 69 | Option "Identify player level", "cl_identifytarget", "ZA_IdentifyTargetLevels" |
70 | + Option "Identify monster level", "cl_identifymonsters", "ZA_IdentifyMonsterLevels" | |
62 | 71 | Option "Show spawn spots", "cl_showspawns", "YesNo" |
63 | 72 | Option "Free chasecam", "cl_freechase", "YesNo" |
64 | 73 | Option "Teleport to spied player", "cl_telespy", "YesNo" |