- Small fixes and tweaks (by Sandro)

o Minor feature added: Shift+RMB click on a gun with ammo inside, will now unload its magazine and place it on your cursor (if not holding anything already). You don't have to go to the weapon description box to unload it.
o Added Throwing trait support for NCTH (was left out before) and added a minor tweak to STOMP+NCTH compatibility.
o Fixed a bug in NCTH when aiming levels of very fast-to-aim guns could be lowered below zero during calculation, which resulted in suddenly giving the gun 8 aim levels making it extremely slow to aim instead. This occurs when you use a pistol with reflex sights with a Gunslinger merc. In the "AllowedAimingLevelsNCTH" the value "aimLevels" was defined as UINT8, so when it goes below 0, it becomes 255 and is then capped at 8. Changed it to signed.

- IIS fix (by Sandro)
o Interrupts on hearing now occur only in meaningful situations - either when enemy attacks us or comes very close to us.
o Added some other IIS tweaks and fixes (first of all the top status bar should now work properly).

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5289 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Wanne
2012-05-20 16:30:14 +00:00
parent 2691c22397
commit eb2ca0ba70
12 changed files with 144 additions and 57 deletions
@@ -231,6 +231,8 @@ extern UINT16 gusExternVoSubIndex;
extern MOUSE_REGION gMPanelRegion;
extern void InternalMAPBeginItemPointer( SOLDIERTYPE *pSoldier );
// HEADROCK HAM 5: Because BigItem graphics are not loaded into memory by default, we need to load them to
// display the large map inventory. We save their indexes in this array:
typedef struct
@@ -1206,7 +1208,13 @@ void MapInvenPoolSlots(MOUSE_REGION * pRegion, INT32 iReason )
}
else
{
MAPInternalInitItemDescriptionBox( &twItem->object, 0, MercPtrs[gCharactersList[bSelectedInfoChar].usSolID] );
if ( _KeyDown(SHIFT) && gpItemPointer == NULL && Item[twItem->object.usItem].usItemClass == IC_GUN && !(Item[twItem->object.usItem].singleshotrocketlauncher))
{
EmptyWeaponMagazine( &twItem->object, &gItemPointer );
InternalMAPBeginItemPointer( MercPtrs[gCharactersList[bSelectedInfoChar].usSolID] );
}
else
MAPInternalInitItemDescriptionBox( &twItem->object, 0, MercPtrs[gCharactersList[bSelectedInfoChar].usSolID] );
}
}
else if(fValidPointer)
+15 -5
View File
@@ -582,6 +582,8 @@ extern void SwapGoggles(SOLDIERTYPE *pTeamSoldier);
// HEADROCK HAM B2.8: Function to switch team's goggles uniformly
extern void SwapGogglesUniformly(SOLDIERTYPE *pTeamSoldier, BOOLEAN fToNightVision);
extern void InternalMAPBeginItemPointer( SOLDIERTYPE *pSoldier );
UINT32 guiCHARLIST;
UINT32 guiCHARINFO;
UINT32 guiSleepIcon;
@@ -9683,12 +9685,20 @@ void MAPInvClickCallback( MOUSE_REGION *pRegion, INT32 iReason )
{
if ( !InItemDescriptionBox( ) )
{
//CHRISL: This could return a false in which case we don't want to activate the infobox.
if(InitItemDescriptionBox( pSoldier, (UINT8)uiHandPos, MAP_ITEMDESC_START_X, MAP_ITEMDESC_START_Y, 0 ))
if ( _KeyDown(SHIFT) && gpItemPointer == NULL && Item[pSoldier->inv[ uiHandPos ].usItem].usItemClass == IC_GUN && !(Item[pSoldier->inv[ uiHandPos ].usItem].singleshotrocketlauncher) )
{
fShowDescriptionFlag=TRUE;
fTeamPanelDirty=TRUE;
fInterfacePanelDirty = DIRTYLEVEL2;
EmptyWeaponMagazine( &(pSoldier->inv[ uiHandPos ]), &gItemPointer );
InternalMAPBeginItemPointer( pSoldier );
}
else
{
//CHRISL: This could return a false in which case we don't want to activate the infobox.
if(InitItemDescriptionBox( pSoldier, (UINT8)uiHandPos, MAP_ITEMDESC_START_X, MAP_ITEMDESC_START_Y, 0 ))
{
fShowDescriptionFlag=TRUE;
fTeamPanelDirty=TRUE;
fInterfacePanelDirty = DIRTYLEVEL2;
}
}
}
}
+15 -2
View File
@@ -9636,12 +9636,25 @@ void ItemPopupRegionCallback( MOUSE_REGION * pRegion, INT32 iReason )
}
else
{
MAPInternalInitItemDescriptionBox( gpItemPopupObject, (UINT8)uiItemPos, gpItemPopupSoldier );
if ( _KeyDown(SHIFT) && gpItemPointer == NULL && Item[gpItemPopupObject->usItem].usItemClass == IC_GUN )
{
EmptyWeaponMagazine( gpItemPopupObject, &gItemPointer, uiItemPos );
InternalMAPBeginItemPointer( gpItemPopupSoldier );
}
else
MAPInternalInitItemDescriptionBox( gpItemPopupObject, (UINT8)uiItemPos, gpItemPopupSoldier );
}
}
else
{
InternalInitItemDescriptionBox( gpItemPopupObject, (INT16) ITEMDESC_START_X, (INT16) ITEMDESC_START_Y, (UINT8)uiItemPos, gpItemPopupSoldier );
if ( _KeyDown(SHIFT) && gpItemPointer == NULL && Item[gpItemPopupObject->usItem].usItemClass == IC_GUN && !(Item[gpItemPopupObject->usItem].singleshotrocketlauncher) && !( guiTacticalInterfaceFlags & INTERFACE_SHOPKEEP_INTERFACE ) )
{
EmptyWeaponMagazine( gpItemPopupObject, &gItemPointer, uiItemPos );
gpItemPointer = &gItemPointer;
gpItemPointerSoldier = gpItemPopupSoldier;
}
else
InternalInitItemDescriptionBox( gpItemPopupObject, (INT16) ITEMDESC_START_X, (INT16) ITEMDESC_START_Y, (UINT8)uiItemPos, gpItemPopupSoldier );
}
}
}
+8 -1
View File
@@ -3986,7 +3986,14 @@ void SMInvClickCallback( MOUSE_REGION * pRegion, INT32 iReason )
{
if ( !InItemDescriptionBox( ) )
{
InitItemDescriptionBox( gpSMCurrentMerc, (UINT8)uiHandPos, ITEMDESC_START_X, ITEMDESC_START_Y, 0 );
if ( _KeyDown(SHIFT) && gpItemPointer == NULL && Item[gpSMCurrentMerc->inv[ uiHandPos ].usItem].usItemClass == IC_GUN && !( guiTacticalInterfaceFlags & INTERFACE_SHOPKEEP_INTERFACE ) )
{
EmptyWeaponMagazine( &(gpSMCurrentMerc->inv[ uiHandPos ]), &gItemPointer );
gpItemPointer = &gItemPointer;
gpItemPointerSoldier = gpSMCurrentMerc;
}
else
InitItemDescriptionBox( gpSMCurrentMerc, (UINT8)uiHandPos, ITEMDESC_START_X, ITEMDESC_START_Y, 0 );
}
}
}
+18 -2
View File
@@ -12014,7 +12014,7 @@ FLOAT GetScopeRangeMultiplier( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, FLOAT d2
UINT8 AllowedAimingLevelsNCTH( SOLDIERTYPE *pSoldier, INT32 sGridNo )
{
UINT8 aimLevels = 4;
INT8 aimLevels = 4;
FLOAT iScopeMagFactor = 0.0, rangeMultiplier = 0.0;
BOOLEAN allowed = TRUE;
UINT16 weaponRange;
@@ -12038,6 +12038,22 @@ UINT8 AllowedAimingLevelsNCTH( SOLDIERTYPE *pSoldier, INT32 sGridNo )
weaponType = Weapon[pSoldier->inv[pSoldier->ubAttackingHand].usItem].ubWeaponType;
fUsingBipod = FALSE;
// SANDRO - throwing knives are a special case, allow 2 aiming clicks for them
if( weaponType == NOT_GUN )
{
aimLevels = 2;
if ( Item[pSoldier->inv[pSoldier->ubAttackingHand].usItem].usItemClass == IC_THROWING_KNIFE )
{
// minus the bonus for Throwing trait
if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, THROWING_NT ) )
{
aimLevels -= gSkillTraitValues.ubTHBladesAimClicksAdded;
}
}
return ( __max(1, aimLevels) );
}
// If outside limits...
if (aimLevels < 1 ||
aimLevels > 8 )
@@ -12090,7 +12106,7 @@ UINT8 AllowedAimingLevelsNCTH( SOLDIERTYPE *pSoldier, INT32 sGridNo )
aimLevels = __max(1, aimLevels);
aimLevels = __min(8, aimLevels);
return aimLevels;
return ((UINT8)aimLevels);
}
UINT8 AllowedAimingLevels(SOLDIERTYPE * pSoldier, INT32 sGridNo)
+3 -3
View File
@@ -1928,7 +1928,7 @@ BOOLEAN ExecuteOverhead( )
// "artificially" set lock ui flag in this case
if (pSoldier->bTeam == gbPlayerNum)
{
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
//AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
guiPendingOverrideEvent = LU_BEGINUILOCK;
HandleTacticalUI( );
}
@@ -2859,7 +2859,7 @@ BOOLEAN HandleAtNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving )
{
if ( ResolvePendingInterrupt( pSoldier, MOVEMENT_INTERRUPT ) )
{
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
//AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -8705,7 +8705,7 @@ SOLDIERTYPE *InternalReduceAttackBusyCount( )
// "artificially" set lock ui flag
if (pSoldier->bTeam == gbPlayerNum)
{
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
//AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
guiPendingOverrideEvent = LU_BEGINUILOCK;
HandleTacticalUI( );
}
+7 -3
View File
@@ -919,7 +919,7 @@ void DeductPoints( SOLDIERTYPE *pSoldier, INT16 sAPCost, INT32 iBPCost, UINT8 ub
//INT16 iDistVisible = (pOpponent->GetMaxDistanceVisible(pOpponent->sGridNo, pOpponent->bTargetLevel, CALC_FROM_ALL_DIRS ) * CELL_X_SIZE); // how far do we see
INT16 iDistVisible = pOpponent->GetMaxDistanceVisible(pOpponent->sGridNo, pOpponent->bTargetLevel, CALC_FROM_ALL_DIRS ) * CELL_X_SIZE; // -1% registered by 4% of the difference of how far we can see and how far is the target
iDistVisible = max(iDistVisible, CELL_X_SIZE);
ubPointsRegistered -= ( 25 * GetRangeInCellCoordsFromGridNoDiff( pOpponent->sGridNo, pSoldier->sGridNo ) / iDistVisible );
ubPointsRegistered -= min( 25, ( 25 * GetRangeInCellCoordsFromGridNoDiff( pOpponent->sGridNo, pSoldier->sGridNo ) / iDistVisible ));
if ( gGameOptions.fNewTraitSystem )
{
@@ -936,8 +936,12 @@ void DeductPoints( SOLDIERTYPE *pSoldier, INT16 sAPCost, INT32 iBPCost, UINT8 ub
}
// ALRIGHT! Get final value
ubPointsRegistered = max( 5, min( 100, ubPointsRegistered ) ); // 5% is minimum, 100% maximum
ubPointsRegistered = (UINT8)((sAPCost * ubPointsRegistered / 100) + 0.5); // now calc how many APs we will award and store it in ubPointsRegistered
if (pOpponent->aiData.bOppList[pSoldier->ubID] == SEEN_CURRENTLY )
ubPointsRegistered = max( 20, min( 100, ubPointsRegistered ) ); // 20% is minimum on seeing
else
ubPointsRegistered = max( 10, min( 100, ubPointsRegistered ) ); // 10% is minimum on hearing
ubPointsRegistered = min( 100, ubPointsRegistered ); // 100% is maximum ofc
ubPointsRegistered = (UINT8)((sAPCost * ubPointsRegistered / 100) + 0.5); // now calc how many APs we will award
// increase the counter
if ( ubPointsRegistered )
+2 -2
View File
@@ -618,7 +618,7 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier )
// "artificially" set lock ui flag in this case
if (pSoldier->bTeam == gbPlayerNum)
{
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
//AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
guiPendingOverrideEvent = LU_BEGINUILOCK;
HandleTacticalUI( );
}
@@ -2078,7 +2078,7 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier )
// "artificially" set lock ui flag in this case
if (pSoldier->bTeam == gbPlayerNum)
{
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
//AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
guiPendingOverrideEvent = LU_BEGINUILOCK;
HandleTacticalUI( );
}
+6 -4
View File
@@ -4757,7 +4757,7 @@ void SOLDIERTYPE::EVENT_FireSoldierWeapon( INT32 sTargetGridNo )
// "artificially" set lock ui flag
if (this->bTeam == gbPlayerNum)
{
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
//AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
guiPendingOverrideEvent = LU_BEGINUILOCK;
HandleTacticalUI( );
}
@@ -15371,13 +15371,15 @@ BOOLEAN ResolvePendingInterrupt( SOLDIERTYPE * pSoldier, UINT8 ubInterruptType )
/////////////////////////////////////////////////////////////
// set base value ( interrupt per every X APs an enemy uses )
if ( pInterrupter->aiData.bOppList[pSoldier->ubID] == SEEN_CURRENTLY ) // if we see him
// if not seen but just heard... we interrupt only if they attack us (or if they are very close) in that case
if (( pInterrupter->aiData.bOppList[pSoldier->ubID] == SEEN_CURRENTLY ) || ( pInterrupter->aiData.bOppList[pSoldier->ubID] == HEARD_THIS_TURN && (ubInterruptType == AFTERSHOT_INTERRUPT || ubInterruptType == AFTERACTION_INTERRUPT || PythSpacesAway( pInterrupter->sGridNo, pSoldier->sGridNo) < 3) ))
{
uiReactionTime = gGameExternalOptions.ubBasicReactionTimeLengthIIS;
}
else // if not seen the length is higher (we don't want to interrupt in most cases here)
else
{
uiReactionTime = (gGameExternalOptions.ubBasicReactionTimeLengthIIS * 2);
// not seen or not heard anything worth interrupting
continue;
}
uiReactionTime = uiReactionTime * 10; // x10 ... we will divide by 10 after all adjustments done
// adjust based on Agility
+13 -8
View File
@@ -1048,6 +1048,11 @@ void StartInterrupt( void )
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Interrupt ( could be hidden )" );
#endif
}
// SANDRO - show correct top message
if (pTempSoldier->bTeam == MILITIA_TEAM )
AddTopMessage( MILITIA_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
else
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, Message[STR_INTERRUPT] );
if (pTempSoldier != NULL)
StartNPCAI( pTempSoldier );
@@ -1357,14 +1362,14 @@ void EndInterrupt( BOOLEAN fMarkInterruptOccurred )
}
// Signal UI done enemy's turn
if(is_client)
{
guiPendingOverrideEvent = LA_BEGINUIOURTURNLOCK;
}
else
{
guiPendingOverrideEvent = LU_BEGINUILOCK;
}
if(is_client)
{
guiPendingOverrideEvent = LA_BEGINUIOURTURNLOCK;
}
else
{
guiPendingOverrideEvent = LU_BEGINUILOCK;
}
ClearIntList();
}
+46 -24
View File
@@ -4903,6 +4903,15 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
if ( HAS_SKILL_TRAIT( pSoldier, RANGER_NT ) )
iTraitModifier += gSkillTraitValues.ubRABonusCtHShotguns * NUM_SKILL_TRAITS( pSoldier, RANGER_NT ); // +10% per trait
}
// SANDRO - added Throwing trait support on knives
if ( Item[ usInHand ].usItemClass == IC_THROWING_KNIFE )
{
iTraitModifier += gSkillTraitValues.bCtHModifierThrowingKnives; // -15% for untrained mercs
if ( HAS_SKILL_TRAIT( pSoldier, THROWING_NT ) )
iTraitModifier += gSkillTraitValues.ubTHBladesCtHBonus * 2;
//+ (gSkillTraitValues.ubTHBladesCtHBonusPerClick * pSoldier->aiData.bAimTime) );
}
// Added small CtH penalty for robot if controller hasn't the Technician trait
if( AM_A_ROBOT( pSoldier ) )
@@ -5050,7 +5059,7 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
FLOAT umoda = (iGunBaseDifficulty * GetPercentHandlingModifier( pInHand, stance )) / 100;
FLOAT umodb = (iGunBaseDifficulty * GetPercentHandlingModifier( pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight )) / 100;
iGunBaseDifficulty += ((gGameExternalOptions.ubProneModifierPercentage * umoda + (100 - gGameExternalOptions.ubProneModifierPercentage) * umodb)/100);
// End gun difficulty modifiers
/////////////////////////////////////////////
@@ -5287,14 +5296,14 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
//Give 60% the normal stomp modifier as the aim mod
iAimModifier += (FLOAT)iTraitModifier * 0.6f;
}
else
{
//else
//{
// Flugente: drugs can temporarily cause a merc to go psycho
if ( pSoldier->ubProfile != NO_PROFILE && (gMercProfiles[ pSoldier->ubProfile ].bDisability == PSYCHO || MercUnderTheInfluence(pSoldier, DRUG_TYPE_PSYCHO) ) )
{
iAimModifier += gGameCTHConstants.AIM_PSYCHO;
}
}
//}
//////////////////////////////////////////
// Gun Handling modifiers
@@ -5538,38 +5547,51 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
// Add bonuses from Sniper Skill. Applies only when using a scope at or above its "best" range.
INT16 sDifference = 99 - uiCap;
INT16 sSniperSkillBonus = 0;
if (uiCap < gGameExternalOptions.ubMaximumCTH && iScopeMagFactor > 1.0 && iRange >= (INT32)uiBestScopeRange )
// SANDRO - added support to Throwing trait
if ( uiCap < gGameExternalOptions.ubMaximumCTH && Item[ usInHand ].usItemClass == IC_THROWING_KNIFE )
{
INT8 loop;
if(gGameOptions.fNewTraitSystem)
if( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, THROWING_NT ) )
{
if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL || Weapon[usInHand].ubWeaponType == GUN_M_PISTOL )
loop = NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT );
else
loop = NUM_SKILL_TRAITS( pSoldier, SNIPER_NT );
uiCap += (INT16)((sDifference * gSkillTraitValues.ubTHBladesCtHBonusPerClick*3) / gGameExternalOptions.ubMaximumCTH);
//uiCap += gSkillTraitValues.ubTHBladesCtHBonusPerClick*2;
}
else
loop = NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT );
for (INT8 cnt = 0; cnt < loop; cnt++)
}
else
{
if (uiCap < gGameExternalOptions.ubMaximumCTH && iScopeMagFactor > 1.0 && iRange >= (INT32)uiBestScopeRange )
{
// For each level of the sniper skill, cover a defined distance between the current CTH Cap and 99 (the
// maximum...
INT8 loop;
if(gGameOptions.fNewTraitSystem)
{
if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL || Weapon[usInHand].ubWeaponType == GUN_M_PISTOL )
sSniperSkillBonus += (INT16)((sDifference * gSkillTraitValues.ubGSAimingBonusPerClick) / gGameExternalOptions.ubMaximumCTH);
loop = NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT );
else
sSniperSkillBonus += (INT16)((sDifference * gSkillTraitValues.ubSNAimingBonusPerClick) / gGameExternalOptions.ubMaximumCTH);
loop = NUM_SKILL_TRAITS( pSoldier, SNIPER_NT );
}
else
sSniperSkillBonus += (INT16)((sDifference * gGameCTHConstants.AIM_SNIPER_SKILL) / gGameExternalOptions.ubMaximumCTH);
sDifference -= sSniperSkillBonus;
loop = NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT );
for (INT8 cnt = 0; cnt < loop; cnt++)
{
// For each level of the sniper skill, cover a defined distance between the current CTH Cap and 99 (the
// maximum...
// SANDRO - this results in giving us total bonus on uiCap of lame 3-8%... compared to 10-20% bonus *PER AIM CLICK* in OCTH
// ..as an artificial solution multiply the bonus by at least 2 to achieve some meaningful results
if(gGameOptions.fNewTraitSystem)
{
if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL || Weapon[usInHand].ubWeaponType == GUN_M_PISTOL )
sSniperSkillBonus += (INT16)((sDifference * gSkillTraitValues.ubGSAimingBonusPerClick * 2) / gGameExternalOptions.ubMaximumCTH);
else
sSniperSkillBonus += (INT16)((sDifference * gSkillTraitValues.ubSNAimingBonusPerClick * 2) / gGameExternalOptions.ubMaximumCTH);
}
else
sSniperSkillBonus += (INT16)((sDifference * gGameCTHConstants.AIM_SNIPER_SKILL * 2) / gGameExternalOptions.ubMaximumCTH);
sDifference -= sSniperSkillBonus;
}
uiCap += sSniperSkillBonus;
}
uiCap += sSniperSkillBonus;
}
// Make sure cap is within limits
uiCap = __max(uiCap, __max(0,(UINT32)iChance));
uiCap = __min(uiCap, gGameExternalOptions.ubMaximumCTH);
+2 -2
View File
@@ -4330,8 +4330,8 @@ STR16 BobbyRFilter[] =
L"Taschen",
L"Kopfausr.",
L"Trageausr.",
L"Optik", // Madd: new BR filters // TODO.Translate
L"Grip/LAM",
L"Optik", // Madd: new BR filters
L"Gri/Las",
L"Mündung",
L"Schaft",
L"Mag/Abz.",