2009-05-08 13:28:41 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 1998 - 2000 , Matthes Bender
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright ( c ) 2009 - 2016 , The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2009-05-08 13:28:41 +00:00
*/
/* Material definitions used by the landscape */
2016-04-03 18:07:56 +00:00
# include "C4Include.h"
# include "landscape/C4Material.h"
2009-05-08 13:28:41 +00:00
2017-04-30 08:49:09 +00:00
# include "c4group/C4Components.h"
2016-04-03 18:07:56 +00:00
# include "c4group/C4Group.h"
# include "editor/C4ToolsDlg.h" // For C4TLS_MatSky...
2017-04-30 08:49:09 +00:00
# include "game/C4Physics.h" // For GravAccel
# include "landscape/C4PXS.h"
2016-04-03 18:07:56 +00:00
# include "landscape/C4Texture.h"
# include "landscape/C4Landscape.h"
2017-04-30 08:49:09 +00:00
# include "lib/C4Random.h"
2016-04-03 18:07:56 +00:00
# include "platform/C4SoundSystem.h"
2017-04-30 08:49:09 +00:00
# include "script/C4Aul.h"
2016-04-03 18:07:56 +00:00
# include "script/C4Effect.h"
2009-05-08 13:28:41 +00:00
2015-08-30 13:19:36 +00:00
int32_t MVehic = MNone , MHalfVehic = MNone , MTunnel = MNone , MWater = MNone , MEarth = MNone ;
2009-06-12 18:52:21 +00:00
BYTE MCVehic = 0 ;
2015-08-30 13:19:36 +00:00
BYTE MCHalfVehic = 0 ;
2009-05-08 13:28:41 +00:00
// -------------------------------------- C4MaterialReaction
struct ReactionFuncMapEntry { const char * szRFName ; C4MaterialReactionFunc pFunc ; } ;
2010-03-28 18:58:01 +00:00
const ReactionFuncMapEntry ReactionFuncMap [ ] =
{
2009-05-08 13:28:41 +00:00
{ " Script " , & C4MaterialMap : : mrfScript } ,
{ " Convert " , & C4MaterialMap : : mrfConvert } ,
{ " Poof " , & C4MaterialMap : : mrfPoof } ,
{ " Corrode " , & C4MaterialMap : : mrfCorrode } ,
{ " Insert " , & C4MaterialMap : : mrfInsert } ,
2016-11-02 23:58:02 +00:00
{ nullptr , & C4MaterialReaction : : NoReaction }
2010-03-28 18:58:01 +00:00
} ;
2009-05-08 13:28:41 +00:00
void C4MaterialReaction : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2017-03-11 14:05:41 +00:00
if ( pComp - > isDeserializer ( ) ) pScriptFunc = nullptr ;
2009-05-08 13:28:41 +00:00
// compile reaction func ptr
StdStrBuf sReactionFuncName ;
int32_t i = 0 ; while ( ReactionFuncMap [ i ] . szRFName & & ( ReactionFuncMap [ i ] . pFunc ! = pFunc ) ) + + i ;
sReactionFuncName = ReactionFuncMap [ i ] . szRFName ;
2012-12-16 17:52:32 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sReactionFuncName , StdCompiler : : RCT_IdtfAllowEmpty ) , " Type " , StdCopyStrBuf ( ) ) ) ;
2009-05-08 13:28:41 +00:00
i = 0 ; while ( ReactionFuncMap [ i ] . szRFName & & ! SEqual ( ReactionFuncMap [ i ] . szRFName , sReactionFuncName . getData ( ) ) ) + + i ;
pFunc = ReactionFuncMap [ i ] . pFunc ;
// compile the rest
2012-12-16 17:52:32 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( TargetSpec , StdCompiler : : RCT_All ) , " TargetSpec " , StdCopyStrBuf ( ) ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( ScriptFunc , StdCompiler : : RCT_IdtfAllowEmpty ) , " ScriptFunc " , StdCopyStrBuf ( ) ) ) ;
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkNamingAdapt ( iExecMask , " ExecMask " , ~ 0u ) ) ;
pComp - > Value ( mkNamingAdapt ( fReverse , " Reverse " , false ) ) ;
pComp - > Value ( mkNamingAdapt ( fInverseSpec , " InverseSpec " , false ) ) ;
pComp - > Value ( mkNamingAdapt ( fInsertionCheck , " CheckSlide " , true ) ) ;
pComp - > Value ( mkNamingAdapt ( iDepth , " Depth " , 0 ) ) ;
2012-12-16 17:52:32 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sConvertMat , StdCompiler : : RCT_IdtfAllowEmpty ) , " ConvertMat " , StdCopyStrBuf ( ) ) ) ;
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkNamingAdapt ( iCorrosionRate , " CorrosionRate " , 100 ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4MaterialReaction : : ResolveScriptFuncs ( const char * szMatName )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get script func for script-defined behaviour
if ( pFunc = = & C4MaterialMap : : mrfScript )
2011-10-15 00:11:57 +00:00
{
pScriptFunc = : : ScriptEngine . GetPropList ( ) - > GetFunc ( this - > ScriptFunc . getData ( ) ) ;
if ( ! pScriptFunc )
2017-05-03 18:28:00 +00:00
DebugLogF ( R " (Error getting function " % s " for Material reaction of " % s " ) " , this - > ScriptFunc . getData ( ) , szMatName ) ;
2011-10-15 00:11:57 +00:00
}
2009-05-08 13:28:41 +00:00
else
2016-11-02 23:58:02 +00:00
pScriptFunc = nullptr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// -------------------------------------- C4MaterialCore
C4MaterialCore : : C4MaterialCore ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4MaterialCore : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
CustomReactionList . clear ( ) ;
sTextureOverlay . Clear ( ) ;
sPXSGfx . Clear ( ) ;
sBlastShiftTo . Clear ( ) ;
sInMatConvert . Clear ( ) ;
sInMatConvertTo . Clear ( ) ;
sBelowTempConvertTo . Clear ( ) ;
sAboveTempConvertTo . Clear ( ) ;
* Name = ' \0 ' ;
2011-09-07 12:25:32 +00:00
MapChunkType = C4M_Flat ;
2009-05-08 13:28:41 +00:00
Density = 0 ;
Friction = 0 ;
DigFree = 0 ;
BlastFree = 0 ;
2010-01-25 03:14:52 +00:00
Dig2Object = C4ID : : None ;
2009-05-08 13:28:41 +00:00
Dig2ObjectRatio = 0 ;
2011-08-20 14:43:58 +00:00
Dig2ObjectCollect = 0 ;
2010-01-25 03:14:52 +00:00
Blast2Object = C4ID : : None ;
2009-05-08 13:28:41 +00:00
Blast2ObjectRatio = 0 ;
Blast2PXSRatio = 0 ;
Instable = 0 ;
MaxAirSpeed = 0 ;
MaxSlide = 0 ;
WindDrift = 0 ;
Inflammable = 0 ;
2016-02-09 22:45:02 +00:00
Incendiary = 0 ;
2009-05-08 13:28:41 +00:00
Extinguisher = 0 ;
Corrosive = 0 ;
Corrode = 0 ;
Soil = 0 ;
Placement = 0 ;
2015-01-28 21:35:14 +00:00
Light = 0 ;
2009-05-08 13:28:41 +00:00
OverlayType = 0 ;
PXSGfxRt . Default ( ) ;
PXSGfxSize = 0 ;
InMatConvertDepth = 0 ;
BelowTempConvert = 0 ;
BelowTempConvertDir = 0 ;
AboveTempConvert = 0 ;
AboveTempConvertDir = 0 ;
TempConvStrength = 0 ;
MinHeightCount = 0 ;
2010-03-27 16:05:02 +00:00
SplashRate = 10 ;
2014-08-05 15:01:37 +00:00
KeepSinglePixels = false ;
2015-09-20 13:50:22 +00:00
AnimationSpeed = 20 ;
LightAngle = 255 ;
for ( int i = 0 ; i < 3 ; i + + ) {
LightEmit [ i ] = 0 ;
LightSpot [ i ] = 16 ;
}
2015-10-09 04:11:53 +00:00
MinShapeOverlap = 25 ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4MaterialCore : : Default ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4MaterialCore : : Load ( C4Group & hGroup ,
2010-03-28 18:58:01 +00:00
const char * szEntryName )
{
2009-05-08 13:28:41 +00:00
StdStrBuf Source ;
2011-03-05 01:44:26 +00:00
if ( ! hGroup . LoadEntryString ( szEntryName , & Source ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
StdStrBuf Name = hGroup . GetFullName ( ) + DirSep + szEntryName ;
2010-03-28 18:58:01 +00:00
if ( ! CompileFromBuf_LogWarn < StdCompilerINIRead > ( * this , Source , Name . getData ( ) ) )
2009-08-15 18:50:32 +00:00
return false ;
2009-05-08 13:28:41 +00:00
// adjust placement, if not specified
if ( ! Placement )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( DensitySolid ( Density ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Placement = 30 ;
if ( ! DigFree ) Placement + = 20 ;
if ( ! BlastFree ) Placement + = 10 ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( DensityLiquid ( Density ) )
Placement = 10 ;
else Placement = 5 ;
}
2010-03-28 18:58:01 +00:00
return true ;
}
2009-05-08 13:28:41 +00:00
void C4MaterialCore : : CompileFunc ( StdCompiler * pComp )
2010-03-28 18:58:01 +00:00
{
2016-02-09 22:45:02 +00:00
assert ( pComp - > hasNaming ( ) ) ;
2017-03-11 14:05:41 +00:00
if ( pComp - > isDeserializer ( ) ) Clear ( ) ;
2009-05-08 13:28:41 +00:00
pComp - > Name ( " Material " ) ;
2011-09-07 12:25:32 +00:00
pComp - > Value ( mkNamingAdapt ( toC4CStr ( Name ) , " Name " , " " ) ) ;
const StdEnumEntry < C4MaterialCoreShape > Shapes [ ] =
{
{ " Flat " , C4M_Flat } ,
{ " TopFlat " , C4M_TopFlat } ,
{ " Smooth " , C4M_Smooth } ,
{ " Rough " , C4M_Rough } ,
{ " Octagon " , C4M_Octagon } ,
{ " Smoother " , C4M_Smoother } ,
2016-11-02 23:58:02 +00:00
{ nullptr , C4M_Flat }
2011-09-07 12:25:32 +00:00
} ;
pComp - > Value ( mkNamingAdapt ( mkEnumAdaptT < uint8_t > ( MapChunkType , Shapes ) ,
" Shape " , C4M_Flat ) ) ;
pComp - > Value ( mkNamingAdapt ( Density , " Density " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Friction , " Friction " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( DigFree , " DigFree " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( BlastFree , " BlastFree " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Blast2Object , " Blast2Object " , C4ID : : None ) ) ;
pComp - > Value ( mkNamingAdapt ( Dig2Object , " Dig2Object " , C4ID : : None ) ) ;
pComp - > Value ( mkNamingAdapt ( Dig2ObjectRatio , " Dig2ObjectRatio " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Dig2ObjectCollect , " Dig2ObjectCollect " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Blast2ObjectRatio , " Blast2ObjectRatio " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Blast2PXSRatio , " Blast2PXSRatio " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Instable , " Instable " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( MaxAirSpeed , " MaxAirSpeed " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( MaxSlide , " MaxSlide " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( WindDrift , " WindDrift " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Inflammable , " Inflammable " , 0 ) ) ;
2017-03-11 14:05:41 +00:00
if ( pComp - > isDeserializer ( ) )
2016-02-09 22:45:02 +00:00
{
// The value used to have a wrong spelling ("Incindiary"). If there's
// no "Incendiary" value, use the wrong spelling instead
try
{
pComp - > Value ( mkNamingAdapt ( Incendiary , " Incendiary " ) ) ;
}
catch ( StdCompiler : : NotFoundException * ex )
{
delete ex ;
pComp - > Value ( mkNamingAdapt ( Incendiary , " Incindiary " , 0 ) ) ;
}
}
else
{
// When serializing, write both spellings because some script might be
// calling GetMaterialVal with the wrong one
pComp - > Value ( mkNamingAdapt ( Incendiary , " Incendiary " ) ) ;
pComp - > Value ( mkNamingAdapt ( Incendiary , " Incindiary " ) ) ;
}
2011-09-07 12:25:32 +00:00
pComp - > Value ( mkNamingAdapt ( Corrode , " Corrode " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Corrosive , " Corrosive " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Extinguisher , " Extinguisher " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Soil , " Soil " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( Placement , " Placement " , 0 ) ) ;
2015-01-28 21:35:14 +00:00
pComp - > Value ( mkNamingAdapt ( Light , " Light " , 0 ) ) ;
2011-09-07 12:25:32 +00:00
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sTextureOverlay , StdCompiler : : RCT_IdtfAllowEmpty ) ,
" TextureOverlay " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( OverlayType , " OverlayType " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sPXSGfx , StdCompiler : : RCT_IdtfAllowEmpty ) ,
" PXSGfx " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( PXSGfxRt , " PXSGfxRt " , TargetRect0 ) ) ;
pComp - > Value ( mkNamingAdapt ( PXSGfxSize , " PXSGfxSize " , PXSGfxRt . Wdt ) ) ;
pComp - > Value ( mkNamingAdapt ( TempConvStrength , " TempConvStrength " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sBlastShiftTo , StdCompiler : : RCT_IdtfAllowEmpty ) ,
" BlastShiftTo " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sInMatConvert , StdCompiler : : RCT_IdtfAllowEmpty ) ,
" InMatConvert " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sInMatConvertTo , StdCompiler : : RCT_IdtfAllowEmpty ) ,
" InMatConvertTo " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( InMatConvertDepth , " InMatConvertDepth " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( AboveTempConvert , " AboveTempConvert " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( AboveTempConvertDir , " AboveTempConvertDir " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sAboveTempConvertTo , StdCompiler : : RCT_IdtfAllowEmpty ) ,
" AboveTempConvertTo " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( BelowTempConvert , " BelowTempConvert " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( BelowTempConvertDir , " BelowTempConvertDir " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( sBelowTempConvertTo , StdCompiler : : RCT_IdtfAllowEmpty ) ,
" BelowTempConvertTo " , " " ) ) ;
pComp - > Value ( mkNamingAdapt ( MinHeightCount , " MinHeightCount " , 0 ) ) ;
pComp - > Value ( mkNamingAdapt ( SplashRate , " SplashRate " , 10 ) ) ;
2014-08-05 15:01:37 +00:00
pComp - > Value ( mkNamingAdapt ( KeepSinglePixels , " KeepSinglePixels " , false ) ) ;
2015-09-20 13:50:22 +00:00
pComp - > Value ( mkNamingAdapt ( AnimationSpeed , " AnimationSpeed " , 100 ) ) ;
pComp - > Value ( mkNamingAdapt ( LightAngle , " LightAngle " , 255 ) ) ;
pComp - > Value ( mkNamingAdapt ( mkArrayAdaptDM ( LightEmit , 0 ) , " LightEmit " ) ) ;
pComp - > Value ( mkNamingAdapt ( mkArrayAdaptDM ( LightSpot , 16 ) , " LightSpot " ) ) ;
2015-10-09 04:11:53 +00:00
pComp - > Value ( mkNamingAdapt ( MinShapeOverlap , " MinShapeOverlap " , 25 ) ) ;
2009-05-08 13:28:41 +00:00
pComp - > NameEnd ( ) ;
// material reactions
2011-09-07 12:25:32 +00:00
pComp - > Value ( mkNamingAdapt ( mkSTLContainerAdapt ( CustomReactionList ) ,
" Reaction " , std : : vector < C4MaterialReaction > ( ) ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// -------------------------------------- C4Material
C4Material : : C4Material ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
BlastShiftTo = 0 ;
InMatConvertTo = MNone ;
BelowTempConvertTo = 0 ;
AboveTempConvertTo = 0 ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Material : : UpdateScriptPointers ( )
2010-03-28 18:58:01 +00:00
{
2017-05-03 18:28:00 +00:00
for ( auto & i : CustomReactionList )
i . ResolveScriptFuncs ( Name ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// -------------------------------------- C4MaterialMap
C4MaterialMap : : C4MaterialMap ( ) : DefReactConvert ( & mrfConvert ) , DefReactPoof ( & mrfPoof ) , DefReactCorrode ( & mrfCorrode ) , DefReactIncinerate ( & mrfIncinerate ) , DefReactInsert ( & mrfInsert )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Default ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4MaterialMap : : ~ C4MaterialMap ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
Clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4MaterialMap : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2018-12-31 12:45:43 +00:00
delete [ ] Map ; Map = nullptr ; Num = 0 ;
2016-11-02 23:58:02 +00:00
delete [ ] ppReactionMap ; ppReactionMap = nullptr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-09-21 04:00:57 +00:00
int32_t C4MaterialMap : : Load ( C4Group & hGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
char entryname [ 256 + 1 ] ;
2010-03-27 16:05:02 +00:00
// Determine number of materials in files
int32_t mat_num = hGroup . EntryCount ( C4CFN_MaterialFiles ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Allocate new map
2009-05-08 13:28:41 +00:00
C4Material * pNewMap = new C4Material [ mat_num + Num ] ;
2010-03-28 18:58:01 +00:00
if ( ! pNewMap ) return 0 ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Load material cores to map
hGroup . ResetSearch ( ) ; int32_t cnt = 0 ;
2009-05-08 13:28:41 +00:00
while ( hGroup . FindNextEntry ( C4CFN_MaterialFiles , entryname ) )
2010-03-28 18:58:01 +00:00
{
2017-12-10 14:09:51 +00:00
if ( cnt > = mat_num ) {
Log ( " Internal Error: More materials loaded than expected. Make sure your material file names are unique (ignoring case). " ) ;
break ;
}
2009-05-08 13:28:41 +00:00
// Load mat
if ( ! pNewMap [ cnt ] . Load ( hGroup , entryname ) )
{ delete [ ] pNewMap ; return 0 ; }
// A new material?
2010-03-28 18:58:01 +00:00
if ( Get ( pNewMap [ cnt ] . Name ) = = MNone )
2009-05-08 13:28:41 +00:00
cnt + + ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Take over old materials.
2010-03-28 18:58:01 +00:00
for ( int32_t i = 0 ; i < Num ; i + + )
{
2009-05-08 13:28:41 +00:00
pNewMap [ cnt + i ] = Map [ i ] ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
delete [ ] Map ;
Map = pNewMap ;
// set material number
Num + = cnt ;
2010-03-27 16:05:02 +00:00
return cnt ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4MaterialMap : : HasMaterials ( C4Group & hGroup ) const
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return ! ! hGroup . EntryCount ( C4CFN_MaterialFiles ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4MaterialMap : : Get ( const char * szMaterial )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
int32_t cnt ;
for ( cnt = 0 ; cnt < Num ; cnt + + )
if ( SEqualNoCase ( szMaterial , Map [ cnt ] . Name ) )
return cnt ;
return MNone ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2013-01-08 22:41:20 +00:00
bool C4MaterialMap : : CrossMapMaterials ( const char * szEarthMaterial ) // Called after load
2010-03-28 18:58:01 +00:00
{
2009-06-12 18:52:21 +00:00
// Check material number
if ( : : MaterialMap . Num > C4MaxMaterial )
{ LogFatal ( LoadResStr ( " IDS_PRC_TOOMANYMATS " ) ) ; return false ; }
2009-05-08 13:28:41 +00:00
// build reaction function map
delete [ ] ppReactionMap ;
typedef C4MaterialReaction * C4MaterialReactionPtr ;
ppReactionMap = new C4MaterialReactionPtr [ ( Num + 1 ) * ( Num + 1 ) ] ;
for ( int32_t iMatPXS = - 1 ; iMatPXS < Num ; iMatPXS + + )
2010-03-28 18:58:01 +00:00
{
2016-11-02 23:58:02 +00:00
C4Material * pMatPXS = ( iMatPXS + 1 ) ? Map + iMatPXS : nullptr ;
2009-05-08 13:28:41 +00:00
for ( int32_t iMatLS = - 1 ; iMatLS < Num ; iMatLS + + )
2010-03-28 18:58:01 +00:00
{
2016-11-02 23:58:02 +00:00
C4MaterialReaction * pReaction = nullptr ;
C4Material * pMatLS = ( iMatLS + 1 ) ? Map + iMatLS : nullptr ;
2009-05-08 13:28:41 +00:00
// natural stuff: material conversion here?
if ( pMatPXS & & pMatPXS - > sInMatConvert . getLength ( ) & & SEqualNoCase ( pMatPXS - > sInMatConvert . getData ( ) , pMatLS ? pMatLS - > Name : C4TLS_MatSky ) )
pReaction = & DefReactConvert ;
2013-03-30 13:05:12 +00:00
// non-sky reactions
else if ( pMatPXS & & pMatLS )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// incindiary vs extinguisher
2016-02-09 22:45:02 +00:00
if ( ( pMatPXS - > Incendiary & & pMatLS - > Extinguisher ) | | ( pMatPXS - > Extinguisher & & pMatLS - > Incendiary ) )
2009-05-08 13:28:41 +00:00
pReaction = & DefReactPoof ;
// incindiary vs inflammable
2016-02-09 22:45:02 +00:00
else if ( ( pMatPXS - > Incendiary & & pMatLS - > Inflammable ) | | ( pMatPXS - > Inflammable & & pMatLS - > Incendiary ) )
2009-05-08 13:28:41 +00:00
pReaction = & DefReactIncinerate ;
// corrosive vs corrode
else if ( pMatPXS - > Corrosive & & pMatLS - > Corrode )
pReaction = & DefReactCorrode ;
2013-03-30 13:05:12 +00:00
// liquid hitting liquid or solid: Material insertion
else if ( DensityLiquid ( MatDensity ( iMatPXS ) ) & & DensitySemiSolid ( MatDensity ( iMatLS ) ) )
pReaction = & DefReactInsert ;
// solid hitting solid: Material insertion
else if ( DensitySolid ( MatDensity ( iMatPXS ) ) & & DensitySolid ( MatDensity ( iMatLS ) ) )
2009-05-08 13:28:41 +00:00
pReaction = & DefReactInsert ;
2010-03-28 18:58:01 +00:00
}
2016-11-02 23:58:02 +00:00
// assign the function; or nullptr for no reaction
2009-05-08 13:28:41 +00:00
SetMatReaction ( iMatPXS , iMatLS , pReaction ) ;
}
2010-03-28 18:58:01 +00:00
}
2011-07-16 19:50:49 +00:00
// reset max shape size
max_shape_width = max_shape_height = 0 ;
2009-05-08 13:28:41 +00:00
// material-specific initialization
int32_t cnt ;
for ( cnt = 0 ; cnt < Num ; cnt + + )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Material * pMat = Map + cnt ;
2016-11-02 23:58:02 +00:00
const char * szTextureOverlay = nullptr ;
2009-05-08 13:28:41 +00:00
// newgfx: init pattern
if ( Map [ cnt ] . sTextureOverlay . getLength ( ) )
2009-06-05 15:09:54 +00:00
if ( : : TextureMap . GetTexture ( Map [ cnt ] . sTextureOverlay . getLength ( ) ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
szTextureOverlay = Map [ cnt ] . sTextureOverlay . getData ( ) ;
// backwards compatibility: if a pattern was specified although the no-pattern flag was set, overwrite that flag
if ( Map [ cnt ] . OverlayType & C4MatOv_None )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
DebugLogF ( " Error in overlay of material %s: Flag C4MatOv_None ignored because a custom overlay (%s) was specified! " , Map [ cnt ] . Name , szTextureOverlay ) ;
Map [ cnt ] . OverlayType & = ~ C4MatOv_None ;
}
2010-03-28 18:58:01 +00:00
}
2011-08-27 16:10:31 +00:00
// default to first texture in texture map
int iTexMapIx ;
2016-11-02 23:58:02 +00:00
if ( ! szTextureOverlay & & ( iTexMapIx = : : TextureMap . GetIndex ( Map [ cnt ] . Name , nullptr , false ) ) )
2011-08-27 16:10:31 +00:00
szTextureOverlay = TextureMap . GetEntry ( iTexMapIx ) - > GetTextureName ( ) ;
2009-05-08 13:28:41 +00:00
// default to smooth
if ( ! szTextureOverlay )
2011-08-29 21:41:41 +00:00
szTextureOverlay = " none " ;
2009-05-08 13:28:41 +00:00
// search/create entry in texmap
2009-08-15 18:50:32 +00:00
Map [ cnt ] . DefaultMatTex = : : TextureMap . GetIndex ( Map [ cnt ] . Name , szTextureOverlay , true ,
2010-03-28 18:58:01 +00:00
FormatString ( " DefaultMatTex of mat %s " , Map [ cnt ] . Name ) . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
// init PXS facet
2011-10-03 14:07:07 +00:00
C4Surface * sfcTexture ;
2009-05-08 13:28:41 +00:00
C4Texture * Texture ;
if ( Map [ cnt ] . sPXSGfx . getLength ( ) )
2010-01-25 04:00:59 +00:00
if ( ( Texture = : : TextureMap . GetTexture ( Map [ cnt ] . sPXSGfx . getData ( ) ) ) )
if ( ( sfcTexture = Texture - > Surface32 ) )
2009-05-08 13:28:41 +00:00
Map [ cnt ] . PXSFace . Set ( sfcTexture , Map [ cnt ] . PXSGfxRt . x , Map [ cnt ] . PXSGfxRt . y , Map [ cnt ] . PXSGfxRt . Wdt , Map [ cnt ] . PXSGfxRt . Hgt ) ;
// evaluate reactions for that material
2017-05-03 18:28:00 +00:00
for ( auto & iRCnt : pMat - > CustomReactionList )
2010-03-28 18:58:01 +00:00
{
2017-05-03 18:28:00 +00:00
C4MaterialReaction * pReact = & iRCnt ;
2009-05-08 13:28:41 +00:00
if ( pReact - > sConvertMat . getLength ( ) ) pReact - > iConvertMat = Get ( pReact - > sConvertMat . getData ( ) ) ; else pReact - > iConvertMat = - 1 ;
// evaluate target spec
int32_t tmat ;
if ( MatValid ( tmat = Get ( pReact - > TargetSpec . getData ( ) ) ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// single material target
if ( pReact - > fInverseSpec )
2014-04-17 17:16:24 +00:00
for ( int32_t cnt2 = - 1 ; cnt2 < Num ; cnt2 + + ) {
if ( cnt2 ! = tmat )
SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
else
SetMatReaction ( cnt , tmat , pReact ) ;
2014-04-17 17:16:24 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " All " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all materials, including sky
if ( ! pReact - > fInverseSpec ) for ( int32_t cnt2 = - 1 ; cnt2 < Num ; cnt2 + + ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Solid " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all solid materials
if ( pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( DensitySolid ( Map [ cnt2 ] . Density ) ! = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " SemiSolid " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all semisolid materials
if ( pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( DensitySemiSolid ( Map [ cnt2 ] . Density ) ! = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Background " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all BG materials, including sky
if ( ! pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( ! Map [ cnt2 ] . Density ! = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Sky " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to sky
if ( ! pReact - > fInverseSpec )
SetMatReaction ( cnt , - 1 , pReact ) ;
else
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2016-02-09 22:45:02 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Incendiary " ) | | SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Incindiary " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all incendiary materials
if ( pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
2016-02-09 22:45:02 +00:00
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( ! Map [ cnt2 ] . Incendiary = = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Extinguisher " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all incendiary materials
if ( pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( ! Map [ cnt2 ] . Extinguisher = = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Inflammable " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all incendiary materials
if ( pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( ! Map [ cnt2 ] . Inflammable = = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Corrosive " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all incendiary materials
if ( pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( ! Map [ cnt2 ] . Corrosive = = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( SEqualNoCase ( pReact - > TargetSpec . getData ( ) , " Corrode " ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add to all incendiary materials
if ( pReact - > fInverseSpec ) SetMatReaction ( cnt , - 1 , pReact ) ;
for ( int32_t cnt2 = 0 ; cnt2 < Num ; cnt2 + + ) if ( ! Map [ cnt2 ] . Corrode = = pReact - > fInverseSpec ) SetMatReaction ( cnt , cnt2 , pReact ) ;
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// second loop (DefaultMatTex is needed by GetIndexMatTex)
2010-03-27 16:05:02 +00:00
for ( cnt = 0 ; cnt < Num ; cnt + + )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
if ( Map [ cnt ] . sBlastShiftTo . getLength ( ) )
2016-11-02 23:58:02 +00:00
Map [ cnt ] . BlastShiftTo = : : TextureMap . GetIndexMatTex ( Map [ cnt ] . sBlastShiftTo . getData ( ) , nullptr , true , FormatString ( " BlastShiftTo of mat %s " , Map [ cnt ] . Name ) . getData ( ) ) ;
2010-03-27 16:05:02 +00:00
if ( Map [ cnt ] . sInMatConvertTo . getLength ( ) )
Map [ cnt ] . InMatConvertTo = Get ( Map [ cnt ] . sInMatConvertTo . getData ( ) ) ;
if ( Map [ cnt ] . sBelowTempConvertTo . getLength ( ) )
2016-11-02 23:58:02 +00:00
Map [ cnt ] . BelowTempConvertTo = : : TextureMap . GetIndexMatTex ( Map [ cnt ] . sBelowTempConvertTo . getData ( ) , nullptr , true , FormatString ( " BelowTempConvertTo of mat %s " , Map [ cnt ] . Name ) . getData ( ) ) ;
2010-03-27 16:05:02 +00:00
if ( Map [ cnt ] . sAboveTempConvertTo . getLength ( ) )
2016-11-02 23:58:02 +00:00
Map [ cnt ] . AboveTempConvertTo = : : TextureMap . GetIndexMatTex ( Map [ cnt ] . sAboveTempConvertTo . getData ( ) , nullptr , true , FormatString ( " AboveTempConvertTo of mat %s " , Map [ cnt ] . Name ) . getData ( ) ) ;
2010-03-28 18:58:01 +00:00
}
2013-01-08 22:41:20 +00:00
2009-06-12 18:52:21 +00:00
// Get hardcoded system material indices
2013-01-08 22:41:20 +00:00
const C4TexMapEntry * earth_entry = : : TextureMap . GetEntry ( : : TextureMap . GetIndexMatTex ( szEarthMaterial ) ) ;
2012-02-19 18:33:35 +00:00
if ( ! earth_entry )
2017-05-03 18:28:00 +00:00
{ LogFatal ( FormatString ( R " (Earth material " % s " not found!) " , szEarthMaterial ) . getData ( ) ) ; return false ; }
2012-02-19 18:33:35 +00:00
2015-08-30 13:19:36 +00:00
MVehic = Get ( " Vehicle " ) ; MCVehic = Mat2PixColDefault ( MVehic ) ;
MHalfVehic = Get ( " HalfVehicle " ) ; MCHalfVehic = Mat2PixColDefault ( MHalfVehic ) ;
MTunnel = Get ( " Tunnel " ) ;
MWater = Get ( " Water " ) ;
MEarth = Get ( earth_entry - > GetMaterialName ( ) ) ;
2012-02-19 18:33:35 +00:00
2009-06-12 18:52:21 +00:00
if ( ( MVehic = = MNone ) | | ( MTunnel = = MNone ) )
{ LogFatal ( LoadResStr ( " IDS_PRC_NOSYSMATS " ) ) ; return false ; }
2013-01-08 22:41:20 +00:00
2009-06-12 18:52:21 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4MaterialMap : : SetMatReaction ( int32_t iPXSMat , int32_t iLSMat , C4MaterialReaction * pReact )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// evaluate reaction swap
2015-11-15 12:53:01 +00:00
if ( pReact & & pReact - > fReverse ) std : : swap ( iPXSMat , iLSMat ) ;
2009-05-08 13:28:41 +00:00
// set it
ppReactionMap [ ( iLSMat + 1 ) * ( Num + 1 ) + iPXSMat + 1 ] = pReact ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4MaterialMap : : SaveEnumeration ( C4Group & hGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
char * mapbuf = new char [ 1000 ] ;
mapbuf [ 0 ] = 0 ;
2019-02-19 14:28:12 +00:00
SAppend ( " [Enumeration] " , mapbuf ) ; SAppend ( " \n " , mapbuf ) ;
2010-03-27 16:05:02 +00:00
for ( int32_t cnt = 0 ; cnt < Num ; cnt + + )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
SAppend ( Map [ cnt ] . Name , mapbuf ) ;
2019-02-19 14:28:12 +00:00
SAppend ( " \n " , mapbuf ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return hGroup . Add ( C4CFN_MatMap , mapbuf , SLen ( mapbuf ) , false , true ) ;
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4MaterialMap : : LoadEnumeration ( C4Group & hGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Load enumeration map (from savegame), succeed if not present
StdStrBuf mapbuf ;
2011-03-05 01:44:26 +00:00
if ( ! hGroup . LoadEntryString ( C4CFN_MatMap , & mapbuf ) ) return true ;
2009-05-08 13:28:41 +00:00
// Sort material array by enumeration map, fail if some missing
const char * csearch ;
char cmatname [ C4M_MaxName + 1 ] ;
int32_t cmat = 0 ;
2009-08-15 18:50:32 +00:00
if ( ! ( csearch = SSearch ( mapbuf . getData ( ) , " [Enumeration] " ) ) ) { return false ; }
2009-05-08 13:28:41 +00:00
csearch = SAdvanceSpace ( csearch ) ;
while ( IsIdentifier ( * csearch ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
SCopyIdentifier ( csearch , cmatname , C4M_MaxName ) ;
if ( ! SortEnumeration ( cmat , cmatname ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Output error message!
2009-08-15 18:50:32 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
cmat + + ;
csearch + = SLen ( cmatname ) ;
csearch = SAdvanceSpace ( csearch ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-08-15 18:50:32 +00:00
bool C4MaterialMap : : SortEnumeration ( int32_t iMat , const char * szMatName )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Not enough materials loaded
2009-08-15 18:50:32 +00:00
if ( iMat > = Num ) return false ;
2009-05-08 13:28:41 +00:00
// Find requested mat
int32_t cmat ;
for ( cmat = iMat ; cmat < Num ; cmat + + )
if ( SEqual ( szMatName , Map [ cmat ] . Name ) )
break ;
// Not found
2009-08-15 18:50:32 +00:00
if ( cmat > = Num ) return false ;
2009-05-08 13:28:41 +00:00
// already the same?
2009-08-15 18:50:32 +00:00
if ( cmat = = iMat ) return true ;
2009-05-08 13:28:41 +00:00
// Move requested mat to indexed position
C4Material mswap ;
mswap = Map [ iMat ] ;
Map [ iMat ] = Map [ cmat ] ;
Map [ cmat ] = mswap ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4MaterialMap : : Default ( )
2010-03-28 18:58:01 +00:00
{
2010-03-27 16:05:02 +00:00
Num = 0 ;
2016-11-02 23:58:02 +00:00
Map = nullptr ;
ppReactionMap = nullptr ;
2011-07-16 19:50:49 +00:00
max_shape_width = max_shape_height = 0 ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4MaterialReaction * C4MaterialMap : : GetReaction ( int32_t iPXSMat , int32_t iLandscapeMat )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// safety
2016-11-02 23:58:02 +00:00
if ( ! ppReactionMap ) return nullptr ;
if ( ! Inside < int32_t > ( iPXSMat , - 1 , Num - 1 ) ) return nullptr ;
if ( ! Inside < int32_t > ( iLandscapeMat , - 1 , Num - 1 ) ) return nullptr ;
2009-05-08 13:28:41 +00:00
// values OK; get func!
return GetReactionUnsafe ( iPXSMat , iLandscapeMat ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2015-12-19 00:17:40 +00:00
static void Smoke ( int32_t tx , int32_t ty , int32_t level )
{
// Use scripted function (global func Smoke) to create smoke
// Caution: This makes engine internal smoking a synced call.
2014-05-30 23:03:23 +00:00
C4AulParSet pars ( tx , ty , level ) ;
2015-12-19 00:17:40 +00:00
: : ScriptEngine . GetPropList ( ) - > Call ( P_Smoke , & pars ) ;
}
2009-05-08 13:28:41 +00:00
2017-10-14 15:46:04 +00:00
bool mrfInsertCheck ( int32_t & iX , int32_t & iY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , bool * pfPosChanged , bool no_slide = false )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// always manipulating pos/speed here
if ( pfPosChanged ) * pfPosChanged = true ;
2015-10-18 02:26:03 +00:00
// Move up by up to 3px to account for moving SolidMasks, other material insertions, etc.
2015-11-15 12:53:01 +00:00
int32_t mdens = std : : min ( : : MaterialMap . Map [ iPxsMat ] . Density , C4M_Solid ) ;
2015-10-18 02:26:03 +00:00
int32_t max_upwards = 3 ;
bool was_pushed_upwards = false ;
while ( max_upwards - - & & ( : : Landscape . GetDensity ( iX , iY ) > = mdens ) )
{
- - iY ;
was_pushed_upwards = true ;
}
2009-05-08 13:28:41 +00:00
// Rough contact? May splash
if ( fYDir > itofix ( 1 ) )
2009-06-05 18:46:03 +00:00
if ( : : MaterialMap . Map [ iPxsMat ] . SplashRate & & ! Random ( : : MaterialMap . Map [ iPxsMat ] . SplashRate ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
fYDir = - fYDir / 8 ;
2010-05-19 03:19:49 +00:00
fXDir = fXDir / 8 + C4REAL100 ( Random ( 200 ) - 100 ) ;
2009-05-08 13:28:41 +00:00
if ( fYDir ) return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Contact: Stop
2015-10-18 02:26:03 +00:00
fYDir = - GravAccel ;
2009-05-08 13:28:41 +00:00
2016-02-09 22:45:02 +00:00
// Incendiary mats smoke on contact even before doing their slide
if ( : : MaterialMap . Map [ iPxsMat ] . Incendiary )
2012-11-17 15:30:12 +00:00
if ( ! Random ( 25 ) )
{
Smoke ( iX , iY , 4 + Random ( 3 ) ) ;
}
2009-05-08 13:28:41 +00:00
// Move by mat path/slide
int32_t iSlideX = iX , iSlideY = iY ;
2015-10-18 02:26:03 +00:00
2017-10-14 15:46:04 +00:00
if ( ! no_slide & & : : Landscape . FindMatSlide ( iSlideX , iSlideY , Sign ( GravAccel ) , mdens , : : MaterialMap . Map [ iPxsMat ] . MaxSlide ) )
2010-03-28 18:58:01 +00:00
{
2015-10-18 02:26:03 +00:00
// Sliding on equal material: Move directly to optimize insertion of rain onto lakes
// Also move directly when shifted upwards to ensure movement on permamently moving SolidMask
if ( iPxsMat = = iLsMat | | was_pushed_upwards )
2017-01-17 01:31:52 +00:00
{
iX = iSlideX ;
iY = iSlideY ;
fXDir = 0 ;
if ( was_pushed_upwards )
{
// When pushed upwards and slide was found into a target position, insert directly to allow additional PXS at same location to solidify in next position in same frame
2017-02-05 12:51:04 +00:00
if ( : : Landscape . GetDensity ( iX , iY + Sign ( GravAccel ) ) > = mdens )
2017-01-17 01:31:52 +00:00
{
return true ;
}
}
// Continue existing (and fall down next frame)
return false ;
}
2015-10-18 02:26:03 +00:00
// Otherwise, just move using xdir/ydir for nice visuals when rain is moving over landscape
2009-05-08 13:28:41 +00:00
// Accelerate into the direction
2010-05-19 03:19:49 +00:00
fXDir = ( fXDir * 10 + Sign ( iSlideX - iX ) ) / 11 + C4REAL10 ( Random ( 5 ) - 2 ) ;
2009-05-08 13:28:41 +00:00
// Slide target in range? Move there directly.
2010-03-28 18:58:01 +00:00
if ( Abs ( iX - iSlideX ) < = Abs ( fixtoi ( fXDir ) ) )
{
2009-05-08 13:28:41 +00:00
iX = iSlideX ;
iY = iSlideY ;
2010-03-28 18:58:01 +00:00
if ( fYDir < = 0 ) fXDir = 0 ;
}
2009-05-08 13:28:41 +00:00
// Continue existance
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// insertion OK
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-05-04 15:35:18 +00:00
bool mrfUserCheck ( C4MaterialReaction * pReaction , int32_t & iX , int32_t & iY , int32_t iLSPosX , int32_t iLSPosY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , MaterialInteractionEvent evEvent , bool * pfPosChanged )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// check execution mask
if ( ( 1 < < evEvent ) & ~ pReaction - > iExecMask ) return false ;
// do splash/slide check, if desired
if ( pReaction - > fInsertionCheck & & evEvent = = meePXSMove )
if ( ! mrfInsertCheck ( iX , iY , fXDir , fYDir , iPxsMat , iLsMat , pfPosChanged ) )
return false ;
// checks OK; reaction may be applied
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-05-04 15:35:18 +00:00
bool C4MaterialMap : : mrfConvert ( C4MaterialReaction * pReaction , int32_t & iX , int32_t & iY , int32_t iLSPosX , int32_t iLSPosY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , MaterialInteractionEvent evEvent , bool * pfPosChanged )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( pReaction - > fUserDefined ) if ( ! mrfUserCheck ( pReaction , iX , iY , iLSPosX , iLSPosY , fXDir , fYDir , iPxsMat , iLsMat , evEvent , pfPosChanged ) ) return false ;
switch ( evEvent )
2010-03-28 18:58:01 +00:00
{
case meePXSMove : // PXS movement
// for hardcoded stuff: only InMatConvert is Snow in Water, which does not have any collision proc
if ( ! pReaction - > fUserDefined ) break ;
// user-defined conversions may also convert upon hitting materials
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case meePXSPos : // PXS check before movement
{
// Check depth
int32_t iDepth = pReaction - > fUserDefined ? pReaction - > iDepth : : : MaterialMap . Map [ iPxsMat ] . InMatConvertDepth ;
if ( ! iDepth | | GBackMat ( iX , iY - iDepth ) = = iLsMat )
{
// Convert
iPxsMat = pReaction - > fUserDefined ? pReaction - > iConvertMat : : : MaterialMap . Map [ iPxsMat ] . InMatConvertTo ;
if ( ! MatValid ( iPxsMat ) )
// Convert failure (target mat not be loaded, or target may be C4TLS_MatSky): Kill Pix
return true ;
// stop movement after conversion
fXDir = fYDir = 0 ;
if ( pfPosChanged ) * pfPosChanged = true ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
break ;
case meeMassMove : // MassMover-movement
// Conversion-transfer to PXS
: : PXS . Create ( iPxsMat , itofix ( iX ) , itofix ( iY ) ) ;
return true ;
}
2009-05-08 13:28:41 +00:00
// not handled
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-05-04 15:35:18 +00:00
bool C4MaterialMap : : mrfPoof ( C4MaterialReaction * pReaction , int32_t & iX , int32_t & iY , int32_t iLSPosX , int32_t iLSPosY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , MaterialInteractionEvent evEvent , bool * pfPosChanged )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( pReaction - > fUserDefined ) if ( ! mrfUserCheck ( pReaction , iX , iY , iLSPosX , iLSPosY , fXDir , fYDir , iPxsMat , iLsMat , evEvent , pfPosChanged ) ) return false ;
switch ( evEvent )
2010-03-28 18:58:01 +00:00
{
case meeMassMove : // MassMover-movement
case meePXSPos : // PXS check before movement: Kill both landscape and PXS mat
2015-05-01 16:04:42 +00:00
: : Landscape . ExtractMaterial ( iLSPosX , iLSPosY , false ) ;
2011-01-02 03:05:21 +00:00
if ( ! Random ( 3 ) ) Smoke ( iX , iY , 3 ) ;
2015-12-13 21:14:55 +00:00
if ( ! Random ( 3 ) ) StartSoundEffectAt ( " Liquids::Pshshsh " , iX , iY ) ;
2010-03-28 18:58:01 +00:00
return true ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case meePXSMove : // PXS movement
// incindiary/extinguisher/corrosives are always same density proc; so do insertion check first
2017-10-14 15:46:04 +00:00
// Do not allow sliding though (e.g. water on lava).
2010-03-28 18:58:01 +00:00
if ( ! pReaction - > fUserDefined )
2017-10-14 15:46:04 +00:00
if ( ! mrfInsertCheck ( iX , iY , fXDir , fYDir , iPxsMat , iLsMat , pfPosChanged , true ) )
2010-03-28 18:58:01 +00:00
// either splash or slide prevented interaction
return false ;
// Always kill both landscape and PXS mat
2015-05-01 16:04:42 +00:00
: : Landscape . ExtractMaterial ( iLSPosX , iLSPosY , false ) ;
2011-01-02 03:05:21 +00:00
if ( ! Random ( 3 ) ) Smoke ( iX , iY , 3 ) ;
2015-12-13 21:14:55 +00:00
if ( ! Random ( 3 ) ) StartSoundEffectAt ( " Liquids::Pshshsh " , iX , iY ) ;
2010-03-28 18:58:01 +00:00
return true ;
}
2009-05-08 13:28:41 +00:00
// not handled
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-05-04 15:35:18 +00:00
bool C4MaterialMap : : mrfCorrode ( C4MaterialReaction * pReaction , int32_t & iX , int32_t & iY , int32_t iLSPosX , int32_t iLSPosY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , MaterialInteractionEvent evEvent , bool * pfPosChanged )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( pReaction - > fUserDefined ) if ( ! mrfUserCheck ( pReaction , iX , iY , iLSPosX , iLSPosY , fXDir , fYDir , iPxsMat , iLsMat , evEvent , pfPosChanged ) ) return false ;
switch ( evEvent )
2010-03-28 18:58:01 +00:00
{
case meePXSPos : // PXS check before movement
// No corrosion - it would make acid incredibly effective
break ;
case meeMassMove : // MassMover-movement
{
// evaluate corrosion percentage
2012-11-17 15:30:12 +00:00
bool fDoCorrode ; int d100 = Random ( 100 ) ;
2010-03-28 18:58:01 +00:00
if ( pReaction - > fUserDefined )
2012-11-17 15:30:12 +00:00
fDoCorrode = ( d100 < pReaction - > iCorrosionRate ) ;
2010-03-28 18:58:01 +00:00
else
2012-11-17 15:30:12 +00:00
fDoCorrode = ( d100 < : : MaterialMap . Map [ iPxsMat ] . Corrosive ) & & ( d100 < : : MaterialMap . Map [ iLsMat ] . Corrode ) ;
2010-03-28 18:58:01 +00:00
if ( fDoCorrode )
2009-05-08 13:28:41 +00:00
{
2016-04-02 15:50:49 +00:00
: : Landscape . ClearPix ( iLSPosX , iLSPosY ) ;
2010-03-28 18:58:01 +00:00
//::Landscape.CheckInstabilityRange(iLSPosX,iLSPosY); - more correct, but makes acid too effective as well
2012-11-17 15:30:12 +00:00
if ( ! Random ( 5 ) )
{
Smoke ( iX , iY , 3 + Random ( 3 ) ) ;
}
2015-12-13 21:14:55 +00:00
if ( ! Random ( 20 ) ) StartSoundEffectAt ( " Liquids::Corrode " , iX , iY ) ;
2009-05-08 13:28:41 +00:00
return true ;
}
}
2010-03-28 18:58:01 +00:00
break ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case meePXSMove : // PXS movement
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// corrodes to corrosives are always same density proc; so do insertion check first
if ( ! pReaction - > fUserDefined )
2009-05-08 13:28:41 +00:00
if ( ! mrfInsertCheck ( iX , iY , fXDir , fYDir , iPxsMat , iLsMat , pfPosChanged ) )
// either splash or slide prevented interaction
return false ;
2010-03-28 18:58:01 +00:00
// evaluate corrosion percentage
2012-11-17 15:30:12 +00:00
bool fDoCorrode ; int d100 = Random ( 100 ) ;
2010-03-28 18:58:01 +00:00
if ( pReaction - > fUserDefined )
2012-11-17 15:30:12 +00:00
fDoCorrode = ( d100 < pReaction - > iCorrosionRate ) ;
2010-03-28 18:58:01 +00:00
else
2012-11-17 15:30:12 +00:00
fDoCorrode = ( d100 < : : MaterialMap . Map [ iPxsMat ] . Corrosive ) & & ( d100 < : : MaterialMap . Map [ iLsMat ] . Corrode ) ;
2010-03-28 18:58:01 +00:00
if ( fDoCorrode )
{
2016-04-02 15:50:49 +00:00
: : Landscape . ClearPix ( iLSPosX , iLSPosY ) ;
2010-03-28 18:58:01 +00:00
: : Landscape . CheckInstabilityRange ( iLSPosX , iLSPosY ) ;
2012-11-17 15:30:12 +00:00
if ( ! Random ( 5 ) )
{
Smoke ( iX , iY , 3 + Random ( 3 ) ) ;
}
2015-12-13 21:14:55 +00:00
if ( ! Random ( 20 ) ) StartSoundEffectAt ( " Liquids::Corrode " , iX , iY ) ;
2009-05-08 13:28:41 +00:00
return true ;
}
2010-03-28 18:58:01 +00:00
// Else: dead. Insert material here
2013-02-17 23:48:25 +00:00
: : Landscape . InsertMaterial ( iPxsMat , & iX , & iY ) ;
2010-03-28 18:58:01 +00:00
return true ;
}
}
2009-05-08 13:28:41 +00:00
// not handled
return false ;
2010-03-28 18:58:01 +00:00
}
2010-05-04 15:35:18 +00:00
bool C4MaterialMap : : mrfIncinerate ( C4MaterialReaction * pReaction , int32_t & iX , int32_t & iY , int32_t iLSPosX , int32_t iLSPosY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , MaterialInteractionEvent evEvent , bool * pfPosChanged )
2010-03-28 18:58:01 +00:00
{
// not available as user reaction
assert ( ! pReaction - > fUserDefined ) ;
switch ( evEvent )
{
case meeMassMove : // MassMover-movement
case meePXSPos : // PXS check before movement
2015-10-09 20:24:16 +00:00
if ( : : Landscape . Incinerate ( iX , iY , NO_OWNER ) ) return true ;
2010-03-28 18:58:01 +00:00
break ;
case meePXSMove : // PXS movement
// incinerate to inflammables are always same density proc; so do insertion check first
if ( ! mrfInsertCheck ( iX , iY , fXDir , fYDir , iPxsMat , iLsMat , pfPosChanged ) )
// either splash or slide prevented interaction
return false ;
// evaluate inflammation (should always succeed)
2015-10-09 20:24:16 +00:00
if ( : : Landscape . Incinerate ( iX , iY , NO_OWNER ) ) return true ;
2010-03-28 18:58:01 +00:00
// Else: dead. Insert material here
2013-02-17 23:48:25 +00:00
: : Landscape . InsertMaterial ( iPxsMat , & iX , & iY ) ;
2010-03-28 18:58:01 +00:00
return true ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// not handled
return false ;
}
2009-05-08 13:28:41 +00:00
2010-05-04 15:35:18 +00:00
bool C4MaterialMap : : mrfInsert ( C4MaterialReaction * pReaction , int32_t & iX , int32_t & iY , int32_t iLSPosX , int32_t iLSPosY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , MaterialInteractionEvent evEvent , bool * pfPosChanged )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( pReaction - > fUserDefined ) if ( ! mrfUserCheck ( pReaction , iX , iY , iLSPosX , iLSPosY , fXDir , fYDir , iPxsMat , iLsMat , evEvent , pfPosChanged ) ) return false ;
switch ( evEvent )
2010-03-28 18:58:01 +00:00
{
case meePXSPos : // PXS check before movement
break ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case meePXSMove : // PXS movement
{
// check for bounce/slide
if ( ! pReaction - > fUserDefined )
if ( ! mrfInsertCheck ( iX , iY , fXDir , fYDir , iPxsMat , iLsMat , pfPosChanged ) )
// continue existing
return false ;
// Else: dead. Insert material here
2013-02-17 23:48:25 +00:00
: : Landscape . InsertMaterial ( iPxsMat , & iX , & iY ) ;
2010-03-28 18:58:01 +00:00
return true ;
}
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case meeMassMove : // MassMover-movement
break ;
}
2009-05-08 13:28:41 +00:00
// not handled
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-05-04 15:35:18 +00:00
bool C4MaterialMap : : mrfScript ( C4MaterialReaction * pReaction , int32_t & iX , int32_t & iY , int32_t iLSPosX , int32_t iLSPosY , C4Real & fXDir , C4Real & fYDir , int32_t & iPxsMat , int32_t iLsMat , MaterialInteractionEvent evEvent , bool * pfPosChanged )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// do generic checks for user-defined reactions
if ( ! mrfUserCheck ( pReaction , iX , iY , iLSPosX , iLSPosY , fXDir , fYDir , iPxsMat , iLsMat , evEvent , pfPosChanged ) )
return false ;
// check script func
if ( ! pReaction - > pScriptFunc ) return false ;
// OK - let's call it!
// 0 1 2 3 4 5 6 7 8
int32_t iXDir1 , iYDir1 , iXDir2 , iYDir2 ;
2014-05-30 23:03:23 +00:00
C4AulParSet pars ( iX , iY , iLSPosX , iLSPosY , iXDir1 = fixtoi ( fXDir , 100 ) , iYDir1 = fixtoi ( fYDir , 100 ) , iPxsMat , iLsMat , int ( evEvent ) ) ;
2016-11-02 23:58:02 +00:00
if ( ! ! pReaction - > pScriptFunc - > Exec ( nullptr , & pars , false ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// PXS shall be killed!
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// PXS shall exist further: write back parameters
iPxsMat = pars [ 6 ] . getInt ( ) ;
int32_t iX2 = pars [ 0 ] . getInt ( ) , iY2 = pars [ 1 ] . getInt ( ) ;
iXDir2 = pars [ 4 ] . getInt ( ) ; iYDir2 = pars [ 5 ] . getInt ( ) ;
if ( iX ! = iX2 | | iY ! = iY2 | | iXDir1 ! = iXDir2 | | iYDir1 ! = iYDir2 )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// changes to pos/speed detected
if ( pfPosChanged ) * pfPosChanged = true ;
iX = iX2 ; iY = iY2 ;
2010-05-19 03:19:49 +00:00
fXDir = C4REAL100 ( iXDir2 ) ;
fYDir = C4REAL100 ( iYDir2 ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// OK; done
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4MaterialMap : : UpdateScriptPointers ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// update in all materials
for ( int32_t i = 0 ; i < Num ; + + i ) Map [ i ] . UpdateScriptPointers ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2009-06-05 18:46:03 +00:00
C4MaterialMap MaterialMap ;