2021-09-02 21:46:08 +00:00
|
|
|
//=============================================================================
|
|
|
|
// KFWeap_Rifle_ParasiteImplanter
|
|
|
|
//=============================================================================
|
|
|
|
// Weapon code for parasite implanter.
|
|
|
|
//=============================================================================
|
|
|
|
// Killing Floor 2
|
|
|
|
// Copyright (C) 2021 Tripwire Interactive LLC
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
class KFWeap_Rifle_ParasiteImplanter extends KFWeap_ScopedBase;
|
|
|
|
|
|
|
|
`define PARASITE_MIC_LED_INDEX 0
|
|
|
|
|
|
|
|
var float LastFireInterval;
|
|
|
|
|
|
|
|
/** How many seed ammo to recharge per second */
|
|
|
|
var float SeedFullRechargeSeconds;
|
|
|
|
/** How many ammo to recharge per second. */
|
|
|
|
var transient float SeedRechargePerSecond;
|
|
|
|
|
|
|
|
var transient float SeedIncrement;
|
|
|
|
var repnotify byte SeedAmmo;
|
|
|
|
|
|
|
|
const SecondaryFireAnim = 'Shoot_Secondary';
|
|
|
|
const SecondaryFireIronAnim = 'Shoot_Secondary_Iron';
|
|
|
|
|
|
|
|
/** Material colors applied to different fire modes */
|
|
|
|
var LinearColor NoAmmoMaterialColor;
|
|
|
|
var LinearColor AmmoReadyMaterialColor;
|
|
|
|
|
|
|
|
/** How much recoil the altfire should do */
|
|
|
|
var protected const float AltFireRecoilScale;
|
|
|
|
|
|
|
|
replication
|
|
|
|
{
|
|
|
|
if (bNetDirty && Role == ROLE_Authority)
|
|
|
|
SeedAmmo;
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated event ReplicatedEvent(name VarName)
|
|
|
|
{
|
|
|
|
if (VarName == nameof(SeedAmmo))
|
|
|
|
{
|
|
|
|
if (AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE] && SeedAmmo >= AmmoCost[ALTFIRE_FIREMODE])
|
|
|
|
{
|
|
|
|
UpdateMaterial(true);
|
|
|
|
}
|
|
|
|
else if (AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE] && SeedAmmo < AmmoCost[ALTFIRE_FIREMODE])
|
|
|
|
{
|
|
|
|
UpdateMaterial(false);
|
|
|
|
}
|
|
|
|
AmmoCount[ALTFIRE_FIREMODE] = SeedAmmo;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Super.ReplicatedEvent(VarName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* @name Trader
|
|
|
|
*********************************************************************************************/
|
|
|
|
|
|
|
|
/** Returns trader filter index based on weapon type */
|
|
|
|
static simulated event EFilterTypeUI GetTraderFilter()
|
|
|
|
{
|
|
|
|
if( default.FiringStatesArray[DEFAULT_FIREMODE] == 'WeaponFiring' || default.FiringStatesArray[DEFAULT_FIREMODE] == 'WeaponBurstFiring' )
|
|
|
|
{
|
|
|
|
return FT_Assault;
|
|
|
|
}
|
|
|
|
else // if( FiringStatesArray[DEFAULT_FIREMODE] == 'WeaponSingleFiring')
|
|
|
|
{
|
|
|
|
return FT_Rifle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated event PreBeginPlay()
|
|
|
|
{
|
|
|
|
super.PreBeginPlay();
|
|
|
|
StartSeedRecharge();
|
|
|
|
}
|
|
|
|
|
|
|
|
function StartSeedRecharge()
|
|
|
|
{
|
|
|
|
// local KFPerk InstigatorPerk;
|
|
|
|
local float UsedSeedRechargeTime;
|
|
|
|
|
|
|
|
// begin ammo recharge on server
|
|
|
|
if( Role == ROLE_Authority )
|
|
|
|
{
|
|
|
|
UsedSeedRechargeTime = SeedFullRechargeSeconds;
|
|
|
|
SeedRechargePerSecond = MagazineCapacity[ALTFIRE_FIREMODE] / UsedSeedRechargeTime;
|
|
|
|
SeedIncrement = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function RechargeSeed(float DeltaTime)
|
|
|
|
{
|
|
|
|
if ( Role == ROLE_Authority )
|
|
|
|
{
|
|
|
|
SeedIncrement += SeedRechargePerSecond * DeltaTime;
|
|
|
|
|
|
|
|
if( SeedIncrement >= 1.0 && AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE] )
|
|
|
|
{
|
|
|
|
AmmoCount[ALTFIRE_FIREMODE]++;
|
|
|
|
SeedIncrement -= 1.0;
|
|
|
|
SeedAmmo = AmmoCount[ALTFIRE_FIREMODE];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Overridden to call StartHealRecharge on server */
|
|
|
|
function GivenTo( Pawn thisPawn, optional bool bDoNotActivate )
|
|
|
|
{
|
|
|
|
super.GivenTo( thisPawn, bDoNotActivate );
|
|
|
|
|
|
|
|
if( Role == ROLE_Authority && !thisPawn.IsLocallyControlled() )
|
|
|
|
{
|
|
|
|
StartSeedRecharge();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
|
|
@name Actor
|
|
|
|
********************************************************************************************* */
|
|
|
|
simulated event Tick( FLOAT DeltaTime )
|
|
|
|
{
|
|
|
|
local bool bWasLowAmmo;
|
|
|
|
bWasLowAmmo = AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE];
|
|
|
|
|
|
|
|
if( AmmoCount[ALTFIRE_FIREMODE] < MagazineCapacity[ALTFIRE_FIREMODE] )
|
|
|
|
{
|
|
|
|
RechargeSeed(DeltaTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
|
|
|
{
|
|
|
|
if (bWasLowAmmo && AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE])
|
|
|
|
{
|
|
|
|
UpdateMaterial(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (bWasLowAmmo && AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE])
|
|
|
|
{
|
|
|
|
NotifyAltAmmoReady(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Super.Tick(DeltaTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Trader
|
|
|
|
********************************************************************************************/
|
|
|
|
|
|
|
|
/** Allows weapon to set its own trader stats (can set number of stats, names and values of stats) */
|
|
|
|
static simulated event SetTraderWeaponStats( out array<STraderItemWeaponStats> WeaponStats )
|
|
|
|
{
|
|
|
|
super.SetTraderWeaponStats( WeaponStats );
|
|
|
|
|
|
|
|
WeaponStats.Length = WeaponStats.Length + 1;
|
|
|
|
WeaponStats[WeaponStats.Length-1].StatType = TWS_RechargeTime;
|
|
|
|
WeaponStats[WeaponStats.Length-1].StatValue = default.SeedFullRechargeSeconds;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Seeds doesn't count as ammo for purposes of inventory management (e.g. switching) */
|
|
|
|
simulated function bool HasAnyAmmo()
|
|
|
|
{
|
|
|
|
return HasSpareAmmo() || HasAmmo(DEFAULT_FIREMODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function ConsumeAmmo( byte FireModeNum )
|
|
|
|
{
|
|
|
|
local bool bWasHighAmmo;
|
|
|
|
|
|
|
|
if( FireModeNum != ALTFIRE_FIREMODE )
|
|
|
|
{
|
|
|
|
Super.ConsumeAmmo(FireModeNum);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
`if(`notdefined(ShippingPC))
|
|
|
|
if( bInfiniteAmmo )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
`endif
|
|
|
|
|
|
|
|
bWasHighAmmo = AmmoCount[ALTFIRE_FIREMODE] >= AmmoCost[ALTFIRE_FIREMODE];
|
|
|
|
|
|
|
|
// If AmmoCount is being replicated, don't allow the client to modify it here
|
|
|
|
if (Role == ROLE_Authority)
|
|
|
|
{
|
|
|
|
// Don't consume ammo if magazine size is 0 (infinite ammo with no reload)
|
|
|
|
if (MagazineCapacity[1] > 0 && AmmoCount[1] > 0)
|
|
|
|
{
|
|
|
|
// Reduce ammo amount by heal ammo cost
|
|
|
|
AmmoCount[1] = Max(AmmoCount[1] - AmmoCost[1], 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
|
|
|
{
|
|
|
|
if (bWasHighAmmo && AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE])
|
|
|
|
{
|
|
|
|
UpdateMaterial(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (bWasHighAmmo && AmmoCount[ALTFIRE_FIREMODE] < AmmoCost[ALTFIRE_FIREMODE])
|
|
|
|
{
|
|
|
|
NotifyAltAmmoReady(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Instead of switch fire mode use as immediate alt fire */
|
|
|
|
simulated function AltFireMode()
|
|
|
|
{
|
|
|
|
if ( !Instigator.IsLocallyControlled() )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
StartFire(ALTFIRE_FIREMODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function float GetFireInterval(byte FireModeNum)
|
|
|
|
{
|
|
|
|
if (FireModeNum == DEFAULT_FIREMODE && AmmoCount[FireModeNum] == 0)
|
|
|
|
{
|
|
|
|
return LastFireInterval;
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.GetFireInterval(FireModeNum);
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function name GetWeaponFireAnim(byte FireModeNum)
|
|
|
|
{
|
|
|
|
if (FireModeNum == ALTFIRE_FIREMODE)
|
|
|
|
{
|
|
|
|
return bUsingSights ? SecondaryFireIronAnim : SecondaryFireAnim;
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.GetWeaponFireAnim(FireModeNum);
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function UpdateMaterial(bool HasEnoughAmmo)
|
|
|
|
{
|
|
|
|
local LinearColor MatColor;
|
|
|
|
|
|
|
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
|
|
|
{
|
|
|
|
MatColor = HasEnoughAmmo ? AmmoReadyMaterialColor : NoAmmoMaterialColor;
|
|
|
|
|
|
|
|
if( WeaponMICs.Length >= `PARASITE_MIC_LED_INDEX )
|
|
|
|
{
|
|
|
|
WeaponMICs[`PARASITE_MIC_LED_INDEX].SetVectorParameterValue('Vector_GlowColor', MatColor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Should replicate to 3P to show the alt fire ready
|
|
|
|
*/
|
|
|
|
simulated function NotifyAltAmmoReady(bool bActive)
|
|
|
|
{
|
|
|
|
local KFPawn KFP;
|
|
|
|
|
|
|
|
if (WorldInfo.NetMode != NM_Client)
|
|
|
|
{
|
|
|
|
KFP = KFPawn(Instigator);
|
|
|
|
KFP.OnWeaponSpecialAction(bActive ? 2 : 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function ModifyRecoil( out float CurrentRecoilModifier )
|
|
|
|
{
|
|
|
|
if( CurrentFireMode == ALTFIRE_FIREMODE )
|
|
|
|
{
|
|
|
|
CurrentRecoilModifier *= AltFireRecoilScale;
|
|
|
|
}
|
|
|
|
|
|
|
|
super.ModifyRecoil( CurrentRecoilModifier );
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function bool ShouldAutoReload(byte FireModeNum)
|
|
|
|
{
|
|
|
|
if (FireModeNum == ALTFIRE_FIREMODE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return super.ShouldAutoReload(FireModeNum);
|
|
|
|
}
|
|
|
|
|
2022-05-11 15:13:25 +00:00
|
|
|
/** Called during reload state */
|
|
|
|
simulated function bool CanOverrideMagReload(byte FireModeNum)
|
|
|
|
{
|
|
|
|
return super.CanOverrideMagReload(FireModeNum) || FireModeNum == ALTFIRE_FIREMODE;
|
|
|
|
}
|
|
|
|
|
2021-09-02 21:46:08 +00:00
|
|
|
defaultproperties
|
|
|
|
{
|
|
|
|
SeedFullRechargeSeconds=14 //10
|
|
|
|
|
|
|
|
// Inventory / Grouping
|
|
|
|
InventorySize=7
|
|
|
|
GroupPriority=100
|
|
|
|
WeaponSelectTexture=Texture2D'wep_ui_parasiteimplanter_tex.UI_WeaponSelect_ParasiteImplanter'
|
|
|
|
AssociatedPerkClasses(0)=class'KFPerk_FieldMedic'
|
|
|
|
AssociatedPerkClasses(1)=class'KFPerk_SharpShooter'
|
|
|
|
|
|
|
|
// FOV
|
|
|
|
MeshFOV=70 //65
|
|
|
|
MeshIronSightFOV=27 //25 //45
|
|
|
|
PlayerIronSightFOV=70 //65
|
|
|
|
|
|
|
|
// Depth of field
|
|
|
|
DOF_BlendInSpeed=3.0
|
|
|
|
DOF_FG_FocalRadius=0 //50
|
|
|
|
DOF_FG_MaxNearBlurSize=3.5
|
|
|
|
|
|
|
|
// Content
|
|
|
|
PackageKey="ParasiteImplanter"
|
|
|
|
FirstPersonMeshName="WEP_1P_ParasiteImplanter_MESH.Wep_1stP_ParasiteImplanter_Rig"
|
|
|
|
FirstPersonAnimSetNames(0)="WEP_1P_ParasiteImplanter_ANIM.Wep_1stP_ParasiteImplanter_Anim"
|
|
|
|
PickupMeshName="WEP_3P_ParasiteImplanter_MESH.Wep_3rdP_ParasiteImplanter_Pickup"
|
|
|
|
AttachmentArchetypeName="WEP_ParasiteImplanter_ARCH.Wep_ParasiteImplanter_3P"
|
|
|
|
MuzzleFlashTemplateName="WEP_ParasiteImplanter_ARCH.Wep_ParasiteImplanter_MuzzleFlash"
|
|
|
|
|
|
|
|
// Ammo
|
|
|
|
MagazineCapacity[0]=6
|
|
|
|
SpareAmmoCapacity[0]=78
|
|
|
|
InitialSpareMags[0]=3
|
|
|
|
bCanBeReloaded=true
|
|
|
|
bReloadFromMagazine=true
|
|
|
|
|
|
|
|
MagazineCapacity[1]=100
|
|
|
|
SeedAmmo=100
|
|
|
|
bCanRefillSecondaryAmmo=false
|
|
|
|
|
|
|
|
// Zooming/Position
|
|
|
|
PlayerViewOffset=(X=3.0,Y=8,Z=-1.8)
|
|
|
|
//IronSightPosition=(X=0,Y=-0.07,Z=1.03)
|
2021-12-09 16:33:06 +00:00
|
|
|
IronSightPosition=(X=0,Y=-0.09,Z=1.042)
|
2021-09-02 21:46:08 +00:00
|
|
|
|
|
|
|
// AI warning system
|
|
|
|
bWarnAIWhenAiming=true
|
|
|
|
AimWarningDelay=(X=0.4f, Y=0.8f)
|
|
|
|
AimWarningCooldown=0.0f
|
|
|
|
|
|
|
|
// Recoil
|
|
|
|
maxRecoilPitch=575 //700
|
|
|
|
minRecoilPitch=425 //600
|
|
|
|
maxRecoilYaw=135
|
|
|
|
minRecoilYaw=-135
|
|
|
|
RecoilRate=0.08
|
|
|
|
RecoilMaxYawLimit=500
|
|
|
|
RecoilMinYawLimit=65035
|
|
|
|
RecoilMaxPitchLimit=1250
|
|
|
|
RecoilMinPitchLimit=64785
|
|
|
|
RecoilISMaxYawLimit=50
|
|
|
|
RecoilISMinYawLimit=65485
|
|
|
|
RecoilISMaxPitchLimit=500
|
|
|
|
RecoilISMinPitchLimit=65485
|
|
|
|
RecoilViewRotationScale=0.6
|
|
|
|
IronSightMeshFOVCompensationScale=1.5
|
|
|
|
AltFireRecoilScale=0.2f
|
|
|
|
|
|
|
|
// Scope Render
|
|
|
|
// 2D scene capture
|
|
|
|
Begin Object Name=SceneCapture2DComponent0
|
|
|
|
//TextureTarget=TextureRenderTarget2D'Wep_Mat_Lib.WEP_ScopeLense_Target'
|
|
|
|
FieldOfView=12.5 //23.0 // "1.5X" = 35.0(our real world FOV determinant)/1.5
|
|
|
|
End Object
|
|
|
|
|
|
|
|
ScopedSensitivityMod=8.0 //16.0
|
|
|
|
ScopeLenseMICTemplate=MaterialInstanceConstant'WEP_1P_ParasiteImplanter_MAT.Wep_1stP_Parasite_Lens_MIC'
|
|
|
|
ScopeMICIndex = 2
|
|
|
|
|
|
|
|
// DEFAULT_FIREMODE
|
|
|
|
FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletSingle'
|
|
|
|
FiringStatesArray(DEFAULT_FIREMODE)=WeaponSingleFiring
|
|
|
|
WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit
|
|
|
|
WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_ParasiteImplanter'
|
|
|
|
InstantHitDamage(DEFAULT_FIREMODE)=275 //250
|
|
|
|
InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_ParasiteImplanter'
|
|
|
|
FireInterval(DEFAULT_FIREMODE)=+1.0 //60 RPM
|
|
|
|
Spread(DEFAULT_FIREMODE)=0.005
|
|
|
|
PenetrationPower(DEFAULT_FIREMODE)=3
|
|
|
|
FireOffset=(X=25,Y=3.0,Z=-2.5)
|
|
|
|
LastFireInterval=0.3
|
|
|
|
|
|
|
|
// ALT_FIREMODE
|
|
|
|
FiringStatesArray(ALTFIRE_FIREMODE)=WeaponSingleFiring
|
|
|
|
WeaponFireTypes(ALTFIRE_FIREMODE)=EWFT_Projectile
|
|
|
|
FireInterval(ALTFIRE_FIREMODE)=+0.5 //+1.0 //+0.175
|
|
|
|
InstantHitDamage(ALTFIRE_FIREMODE)=1.0
|
|
|
|
Spread(ALTFIRE_FIREMODE)=0.005
|
|
|
|
AmmoCost(ALTFIRE_FIREMODE)=50
|
|
|
|
WeaponProjectiles(ALTFIRE_FIREMODE)=class'KFProj_Bullet_ParasiteImplanterAlt'
|
|
|
|
InstantHitDamageTypes(ALTFIRE_FIREMODE)=class'KFDT_Ballistic_ParasiteImplanterAlt'
|
|
|
|
SecondaryAmmoTexture=Texture2D'ui_firemodes_tex.UI_FireModeSelect_Electricity'
|
|
|
|
|
|
|
|
// BASH_FIREMODE
|
|
|
|
InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_ParasiteImplanter'
|
|
|
|
InstantHitDamage(BASH_FIREMODE)=26
|
|
|
|
|
|
|
|
// Custom animations
|
|
|
|
FireSightedAnims=(Shoot_Iron)
|
|
|
|
BonesToLockOnEmpty=(RW_Hammer)
|
|
|
|
bHasFireLastAnims=true
|
|
|
|
|
|
|
|
// Fire Effects
|
|
|
|
WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_3P_Rifle', FirstPersonCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_1P_Rifle')
|
|
|
|
WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_Dry_Fire_Rifle'
|
|
|
|
WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_3P_Altfire', FirstPersonCue=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_1P_Altfire')
|
|
|
|
WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_ParasiteImplanter.Play_WEP_ParasiteImplanter_Dry_Fire_Altfire'
|
|
|
|
EjectedShellForegroundDuration=1.5f
|
|
|
|
|
|
|
|
// Attachments
|
|
|
|
bHasIronSights=true
|
|
|
|
bHasFlashlight=false
|
|
|
|
|
|
|
|
WeaponFireWaveForm=ForceFeedbackWaveform'FX_ForceFeedback_ARCH.Gunfire.Medium_Recoil'
|
|
|
|
|
|
|
|
WeaponUpgrades[1]=(Stats=((Stat=EWUS_Damage0, Scale=1.15f), (Stat=EWUS_Weight, Add=1)))
|
|
|
|
|
|
|
|
// From original KFWeap_RifleBase base class
|
|
|
|
AimCorrectionSize=40.f
|
|
|
|
|
|
|
|
NoAmmoMaterialColor=(R=0.0f,G=0.0f,B=0.0f)
|
|
|
|
AmmoReadyMaterialColor=(R=0.08f,G=1.0f,B=0.08f)
|
|
|
|
|
|
|
|
NumBloodMapMaterials=2
|
|
|
|
}
|