/*virtual*/ void WBPELookup::Evaluate( const WBParamEvaluator::SPEContext& Context, WBParamEvaluator::SEvaluatedParam& EvaluatedParam ) const { ASSERT( Context.m_Entity ); WBCompPEMap* const PEMap = GET_WBCOMP( Context.m_Entity, PEMap ); if( !PEMap ) { WARNDESC( "No PEMap for PE lookup." ); return; } WBPE* const PE = PEMap->GetPE( m_Key ); if( !PE ) { #if BUILD_DEV if( m_WarnFailure ) { WARNDESC( "PE lookup found nothing in PEMap." ); } #endif return; } PE->Evaluate( Context, EvaluatedParam ); }
Surface::Surface( const IDataStream& Stream, ESurfaceFileType FileType ) : m_Width( 0 ) , m_Height( 0 ) , m_Stride( 0 ) , m_Padding( 0 ) #if BUILD_WINDOWS_NO_SDL , m_hDC( NULL ) , m_BMP( NULL ) , m_OldBMP( NULL ) #endif , m_pPixels( NULL ) , m_BitmapInfo() , m_ViewportLeft( 0 ) , m_ViewportTop( 0 ) , m_ViewportRight( 0 ) , m_ViewportBottom( 0 ) { // Load depending on extension if( FileType == ESFT_BMP ) { LoadBMP( Stream ); } else if( FileType == ESFT_TGA ) { LoadTGA( Stream ); } else { WARNDESC( "Unknown file type constructing Surface." ); } }
/*static*/ D3DFORMAT D3D9RenderTarget::GetD3DFormat( const ERenderTargetFormat Format ) { switch( Format ) { case ERTF_Unknown: return D3DFMT_UNKNOWN; case ERTF_X8R8G8B8: return D3DFMT_X8R8G8B8; case ERTF_A8R8G8B8: return D3DFMT_A8R8G8B8; case ERTF_A16B16G16R16: return D3DFMT_A16B16G16R16; case ERTF_A16B16G16R16F: return D3DFMT_A16B16G16R16F; case ERTF_A32B32G32R32F: return D3DFMT_A32B32G32R32F; case ERTF_R32F: return D3DFMT_R32F; case ERTF_D24S8: return D3DFMT_D24S8; default: WARNDESC( "D3D texture format not matched" ); return D3DFMT_UNKNOWN; } }
void WBCompRodinBehaviorTree::Start(RodinBTNode* pNode, RodinBTNode* pParentNode /*= NULL*/) { ASSERT(pNode); ASSERT(!pNode->m_IsSleeping); #if BUILD_DEBUG // TODO: Make sure this node isn't already in the scheduled array. // While it is valid for two nodes to have a shared child node, it is never // valid for them // both to be running that child simultaneously (because the node only has one // state). // 12 Apr 2013: Actually, I don't think that *is* a valid use case; two child // nodes can // share the same config definition, but they'll be uniquely constructed for // each parent.) uint NodeIndex = 0; if (FindScheduledNode(pNode, NodeIndex)) { WARNDESC("WBCompRodinBehaviorTree::Start: Node pair is already scheduled!"); } #endif SScheduledNode ScheduledNode; ScheduledNode.m_Node = pNode; ScheduledNode.m_ParentNode = pParentNode; m_ScheduledNodes.PushBack(ScheduledNode); pNode->OnStart(); }
void ReverseHash::RegisterHash( const HashedString& Hash, const SimpleString& String ) { if( gReverseHashEnabled ) { #if BUILD_DEV const Map<HashedString, SimpleString>::Iterator HashIter = gReverseHashMap.Search( Hash ); if( HashIter.IsValid() ) { const SimpleString ExistingString = HashIter.GetValue(); if( ExistingString != String ) { PRINTF( "ReverseHash: Hash collision detected between \"%s\" and \"%s\"!\n", ExistingString.CStr(), String.CStr() ); } } #endif gReverseHashMap[ Hash ] = String; } else { // Even though it's safe, this shouldn't be called if the reverse hash isn't enabled, // because it could be wasting time doing the SimpleString construction. WARNDESC( "ReverseHash: Not enabled." ); } }
/*static*/ WBComponent* WBComponent::Create( const HashedString& TypeName, const SimpleString& DefinitionName, WBEntity* const pEntity ) { // NOTE: This supports nulling out a component in a mod by using an empty string. if( DefinitionName == "" ) { return NULL; } FactoryMap::Iterator FactoryIter = m_WBCompFactoryMap.Search( TypeName ); if( FactoryIter.IsNull() ) { PRINTF( "Invalid type requested for WBComponent %s.\n", DefinitionName.CStr() ); WARNDESC( "Invalid WBComponent type requested." ); return NULL; } WBCompFactoryFunc pWBCompFactory = ( *FactoryIter ); ASSERT( pWBCompFactory ); WBComponent* pNewComponent = pWBCompFactory(); ASSERT( pNewComponent ); pNewComponent->SetEntity( pEntity ); pNewComponent->Initialize(); pNewComponent->InitializeFromDefinition( DefinitionName ); return pNewComponent; }
RodinBTNode* RodinBTNodeFactory::Create( const SimpleString& DefinitionName, WBCompRodinBehaviorTree* const pBehaviorTree) { STATICHASH(NodeType); MAKEHASH(DefinitionName); HashedString NodeType = ConfigManager::GetHash(sNodeType, "", sDefinitionName); Map<HashedString, RodinBTNodeFactoryFunc>::Iterator FactoryIter = sFactoryFuncMap.Search(NodeType); if (FactoryIter.IsNull()) { PRINTF("Invalid type requested for RodinBTNode %s.\n", DefinitionName.CStr()); WARNDESC("Invalid RodinBTNode type requested."); return nullptr; } RodinBTNodeFactoryFunc pFactory = (*FactoryIter); ASSERT(pFactory); RodinBTNode* pNewNode = pFactory(); ASSERT(pNewNode); pNewNode->m_DefinitionName = DefinitionName; pNewNode->m_BehaviorTree = pBehaviorTree; pNewNode->InitializeFromDefinition(DefinitionName); return pNewNode; }
// If a localized month is needed, pass the return // value to ConfigManager::GetLocalizedString SimpleString TimeDate::GetMonthName(int Month) { switch (Month) { case 0: return "January"; case 1: return "February"; case 2: return "March"; case 3: return "April"; case 4: return "May"; case 5: return "June"; case 6: return "July"; case 7: return "August"; case 8: return "September"; case 9: return "October"; case 10: return "November"; case 11: return "December"; default: WARNDESC("Invalid month."); return ""; } }
void WBCompRodinBehaviorTree::Stop(RodinBTNode* pNode) { ASSERT(pNode); uint NodeIndex = 0; if (FindScheduledNode(pNode, NodeIndex)) { Finish(NodeIndex, RodinBTNode::ETS_Fail); } else { WARNDESC("Given task was not found in the scheduler."); } }
void ConfigManager::Save( const IDataStream& Stream ) { Stream.WriteUInt32( 'FCCD' ); // Magic ID: DCCF Stream.WriteUInt32( GetInstance()->m_Vars.Size() ); // Number of contexts const Map< HashedString, VarMap >& Vars = GetInstance()->m_Vars; FOR_EACH_MAP( ContextIterator, Vars, HashedString, VarMap ) { const HashedString& ContextKey = ContextIterator.GetKey(); const VarMap& VarMap = ContextIterator.GetValue(); Stream.WriteUInt32( ContextKey.GetHash() ); // Hashed context name Stream.WriteUInt32( VarMap.Size() ); // Number of vars in context FOR_EACH_MAP( VarIterator, VarMap, HashedString, ConfigVar ) { const HashedString& Key = VarIterator.GetKey(); const ConfigVar& Value = VarIterator.GetValue(); Stream.WriteUInt32( Key.GetHash() ); // Hashed var name Stream.WriteUInt8( ( uint8 )Value.m_Type ); switch( Value.m_Type ) { case ConfigVar::EVT_Bool: Stream.WriteUInt8( Value.m_Bool ); break; case ConfigVar::EVT_Int: Stream.WriteInt32( Value.m_Int ); break; case ConfigVar::EVT_Float: Stream.WriteFloat( Value.m_Float ); break; case ConfigVar::EVT_String: { // Doesn't write the terminating null uint Length = ( uint )strlen( Value.m_String ); Stream.WriteUInt32( Length ); Stream.Write( Length, Value.m_String ); Stream.WriteUInt32( Value.m_Hash.GetHash() ); } break; default: WARNDESC( "Bad config var type" ); break; } } } }
Clock::MultiplierRequest* Clock::AddMultiplierRequest( float Duration, float Multiplier ) { if( Multiplier < 0.0f ) { Multiplier = 0.0f; WARNDESC( "Clock multiplier should not be less than zero!" ); } MultiplierRequest* pMultiplierRequest = new MultiplierRequest; pMultiplierRequest->m_Duration = Duration; pMultiplierRequest->m_Multiplier = Multiplier; m_MultiplierRequests.PushBack( pMultiplierRequest ); return pMultiplierRequest; }
D3DMULTISAMPLE_TYPE GetD3DMultiSampleType(EMultiSampleType MultiSampleType) { switch (MultiSampleType) { case EMST_None: return D3DMULTISAMPLE_NONE; case EMST_2X: return D3DMULTISAMPLE_2_SAMPLES; case EMST_4X: return D3DMULTISAMPLE_4_SAMPLES; case EMST_8X: return D3DMULTISAMPLE_8_SAMPLES; default: WARNDESC("D3D multisample type not matched"); return D3DMULTISAMPLE_NONE; } }
/*virtual*/ void GL2ShaderProgram::Initialize( IVertexShader* const pVertexShader, IPixelShader* const pPixelShader, IVertexDeclaration* const pVertexDeclaration ) { XTRACE_FUNCTION; m_VertexShader = pVertexShader; m_PixelShader = pPixelShader; ASSERT( m_VertexShader ); ASSERT( m_PixelShader ); m_ShaderProgram = glCreateProgram(); ASSERT( m_ShaderProgram != 0 ); GLuint VertexShader = *static_cast<GLuint*>( m_VertexShader->GetHandle() ); ASSERT( VertexShader != 0 ); glAttachShader( m_ShaderProgram, VertexShader ); GLuint PixelShader = *static_cast<GLuint*>( m_PixelShader->GetHandle() ); ASSERT( PixelShader != 0 ); glAttachShader( m_ShaderProgram, PixelShader ); BindAttributes( pVertexDeclaration ); glLinkProgram( m_ShaderProgram ); GLERRORCHECK; GLint LinkStatus; glGetProgramiv( m_ShaderProgram, GL_LINK_STATUS, &LinkStatus ); if( LinkStatus != GL_TRUE ) { GLint LogLength; glGetProgramiv( m_ShaderProgram, GL_INFO_LOG_LENGTH, &LogLength ); Array<GLchar> Log; Log.Resize( LogLength ); glGetProgramInfoLog( m_ShaderProgram, LogLength, NULL, Log.GetData() ); if( LogLength > 0 ) { PRINTF( "GLSL shader program link failed:\n" ); PRINTF( Log.GetData() ); } WARNDESC( "GLSL shader program link failed" ); } BuildUniformTable(); SetSamplerUniforms(); }
/*static*/ void EldritchFramework::OnSetRes(void* pUIElement, void* pVoid) { Unused(pVoid); EldritchFramework* const pFramework = EldritchFramework::GetInstance(); Display* const pDisplay = pFramework->GetDisplay(); UIWidget* const pWidget = static_cast<UIWidget*>(pUIElement); UIScreenEldSetRes* const pSetRes = pFramework->GetUIManager()->GetScreen<UIScreenEldSetRes>("SetResScreen"); const SDisplayMode ChosenRes = pSetRes->GetRes(pWidget->m_Name); if (!pDisplay->m_Fullscreen && (ChosenRes.Width > pDisplay->m_ScreenWidth || ChosenRes.Height > pDisplay->m_ScreenHeight)) { WARNDESC("Mode too large for screen."); } else { pFramework->SetResolution(ChosenRes.Width, ChosenRes.Height); } }
void GL2PixelShader::Initialize( const IDataStream& Stream ) { XTRACE_FUNCTION; const int Length = Stream.Size(); byte* pBuffer = new byte[ Length ]; Stream.Read( Length, pBuffer ); m_PixelShader = glCreateShader( GL_FRAGMENT_SHADER ); ASSERT( m_PixelShader != 0 ); // Copy the GLSL source const GLsizei NumStrings = 1; const GLchar* Strings[] = { reinterpret_cast<GLchar*>( pBuffer ) }; const GLint StringLengths[] = { Length }; // I don't trust this file to be null-terminated, so explicitly declare the length. glShaderSource( m_PixelShader, NumStrings, Strings, StringLengths ); // Compile the shader glCompileShader( m_PixelShader ); GLERRORCHECK; GLint CompileStatus; glGetShaderiv( m_PixelShader, GL_COMPILE_STATUS, &CompileStatus ); if( CompileStatus != GL_TRUE ) { GLint LogLength; glGetShaderiv( m_PixelShader, GL_INFO_LOG_LENGTH, &LogLength ); Array<GLchar> Log; Log.Resize( LogLength ); glGetShaderInfoLog( m_PixelShader, LogLength, NULL, Log.GetData() ); if( LogLength > 0 ) { PRINTF( "GLSL fragment shader compile failed:\n" ); PRINTF( Log.GetData() ); } WARNDESC( "GLSL fragment shader compile failed" ); } SafeDeleteArray( pBuffer ); }
SimpleString ReverseHash::ReversedHash( const HashedString& Hash ) { if( gReverseHashEnabled ) { const Map<HashedString, SimpleString>::Iterator HashIter = gReverseHashMap.Search( Hash ); if( HashIter.IsValid() ) { return HashIter.GetValue(); } else { WARN; return SimpleString( "(Hash)" ); } } else { WARNDESC( "ReverseHash: Not enabled." ); return SimpleString( "" ); } }
GLenum GetGLFormat(const ERenderTargetFormat Format) { switch (Format) { case ERTF_Unknown: return 0; case ERTF_X8R8G8B8: return GL_RGBA8; // Meh? case ERTF_A8R8G8B8: return GL_RGBA8; #ifndef HAVE_GLES case ERTF_A16B16G16R16: return GL_RGBA16; case ERTF_A16B16G16R16F: ASSERT(GLEW_ARB_texture_float); return GL_RGBA16F_ARB; case ERTF_A32B32G32R32F: ASSERT(GLEW_ARB_texture_float); return GL_RGBA32F_ARB; case ERTF_R32F: ASSERT(GLEW_ARB_texture_float); return GL_INTENSITY32F_ARB; #endif case ERTF_D24S8: return GL_DEPTH24_STENCIL8; case ERTF_UseDefault: // HACKHACK: GL doesn't support binding to the default depth buffer, // so promote ERTF_UseDefault to a reasonable default format. // NOTE: This won't work if the pass(es) associated with this target // needed to use data already in the buffer. It works for Eldritch // because the buffer was always cleared with each target, and reusing // the default was merely an optimization. return GL_DEPTH24_STENCIL8; default: WARNDESC("GL texture format not matched"); return 0; } }
// InnerParse is poorly named; it really just accepts any character not handled in the calling function // and determines the token type for unknown right-hand tokens. void InnerParse( const char c, char& StrMark, Array< char >& TokenString, const int LineCount, SToken::ETokenType& InOutTokenType, bool* OutClosedString = NULL ) { switch( InOutTokenType ) { case SToken::ET_None: // Determine the type of the RHS from the first character // (Can change from int to float later, on finding a . or f) if( IsNum( c ) || c == '-' ) { InOutTokenType = SToken::ET_Int; TokenString.PushBack( c ); } else if( IsBool( c ) ) { InOutTokenType = SToken::ET_Bool; TokenString.PushBack( c ); } else if( c == '\"' || c == '\'' ) { InOutTokenType = SToken::ET_String; StrMark = c; } else if( c == '.' ) { InOutTokenType = SToken::ET_Float; TokenString.PushBack( c ); } else { PRINTF( "Unexpected character %c in undetermined token at line %d\n", c, LineCount ); WARNDESC( "Unexpected character in undetermined token" ); } break; case SToken::ET_Name: case SToken::ET_Context: case SToken::ET_Macro: TokenString.PushBack( c ); break; case SToken::ET_Bool: TokenString.PushBack( c ); break; case SToken::ET_Int: if( c == '.' ) { InOutTokenType = SToken::ET_Float; } else { ASSERT( IsNum( c ) ); } TokenString.PushBack( c ); break; case SToken::ET_Float: ASSERT( IsNum( c ) || c == 'f' ); TokenString.PushBack( c ); break; case SToken::ET_String: if( c == StrMark ) { TokenString.PushBack( '\0' ); if( OutClosedString ) { *OutClosedString = true; } } else { TokenString.PushBack( c ); } break; default: WARNDESC( "Unexpected token" ); break; } }
bool Cylinder::Intersects( const Segment& s, CollisionInfo* const pInfo /*= NULL*/ ) const { Vector d = m_Point2 - m_Point1; // Cylinder axis Vector m = s.m_Point1 - m_Point1; // Vector from cylinder base to segment base? Vector n = s.m_Point2 - s.m_Point1; // Segment vector float md = m.Dot( d ); float nd = n.Dot( d ); float dd = d.Dot( d ); if( md < 0.0f && md + nd < 0.0f ) { return false; } if( md > dd && md + nd > dd ) { return false; } float nn = n.Dot( n ); float mn = m.Dot( n ); float a = dd * nn - nd * nd; float k = m.Dot( m ) - m_Radius * m_Radius; float c = dd * k - md * md; float t = 0.0f; if( Abs( a ) < SMALLER_EPSILON ) { // Segment (n) runs parallel to cylinder axis (d) if( c > 0.0f ) { return false; } if( md < 0.0f ) { t = -mn / nn; } else if( md > dd ) { t = ( nd - mn ) / nn; } else { // TODO: This seems to be problematic (or getting here is indicative of an earlier problem) WARNDESC( "THAT CYLINDER COLLISION BUG" ); t = 0.0f; } if( pInfo ) { Vector Intersection = s.m_Point1 + t * n; Vector PointOnLine = Line( m_Point1, m_Point2 - m_Point1 ).NearestPointTo( pInfo->m_Intersection ); Vector Normal = ( Intersection - PointOnLine ).GetNormalized(); pInfo->m_Collision = true; pInfo->m_Intersection = Intersection; pInfo->m_HitT = t; pInfo->m_Plane = Plane( Normal, PointOnLine ); } return true; } float b = dd * mn - nd * md; float Discr = b * b - a * c; if( Discr < 0.0f ) { return false; } t = ( -b - SqRt( Discr ) ) / a; if( t < 0.0f || t > 1.0f ) { return false; } // Test endcaps--if we collide with them, count it as not colliding with cylinder if( md + t * nd < 0.0f ) { return false; //// Segment outside cylinder on first side //if( nd <= 0.0f ) //{ // // Segment pointing away from endcap // return false; //} //float t2 = -md / nd; //if( k + t2 * ( 2.0f * mn + t2 * nn ) > 0.0f ) //{ // return false; //} } else if( md + t * nd > dd ) { return false; //// Segment outside cylinder on second side //if( nd >= 0.0f ) //{ // // Segment pointing away from endcap // return false; //} //float t2 = ( dd - md ) / nd; //if( k + dd - 2.0f * md + t2 * ( 2.0f * ( mn - nd ) + t2 * nn ) > 0.0f ) //{ // return false; //} } if( pInfo ) { Vector Intersection = s.m_Point1 + t * n; Vector PointOnLine = Line( m_Point1, m_Point2 - m_Point1 ).NearestPointTo( Intersection ); Vector Normal = ( Intersection - PointOnLine ).GetNormalized(); pInfo->m_Collision = true; pInfo->m_Intersection = Intersection; pInfo->m_HitT = t; pInfo->m_Plane = Plane( Normal, PointOnLine ); } return true; }
void ConfigParser::Parse( const IDataStream& Stream ) { Array< SToken > Tokens; SToken Token; Token.m_TokenType = SToken::ET_Name; char StrMark = 0; // Stores the character (either ' or ") that opened a string SToken MacroToken; List<int> ArrayCounters; Array<char> CounterCharArray; int LineCount = 1; for(;;) { char c = Stream.ReadInt8(); // Skip the UTF-8 byte order mark if present. if( UTF8_BOM_0 == static_cast<byte>( c ) ) { CHECK( UTF8_BOM_1 == static_cast<byte>( Stream.ReadInt8() ) ); CHECK( UTF8_BOM_2 == static_cast<byte>( Stream.ReadInt8() ) ); c = Stream.ReadInt8(); } if( Stream.EOS() ) { if( Token.m_TokenString.Empty() ) { Token.m_TokenType = SToken::ET_None; Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); } else { Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); } break; } if( c == '&' ) { if( Token.m_TokenType == SToken::ET_Name ) { // Increment the current counter and add it to the current name. ASSERT( ArrayCounters.Size() > 0 ); List<int>::Iterator CounterIter = ArrayCounters.Back(); ( *CounterIter )++; SimpleString CounterString = SimpleString::PrintF( "%d", *CounterIter ); CounterCharArray.Clear(); CounterString.FillArray( CounterCharArray ); Token.m_TokenString.Append( CounterCharArray ); } else if( Token.m_TokenType == SToken::ET_None ) { // Add a new counter // Push a counter token that will be replaced with the count int later. ArrayCounters.PushBack( -1 ); Token.m_TokenType = SToken::ET_Counter; } else if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { WARNDESC( "Unexpected character '&' in token." ); } } else if( c == '^' ) { if( Token.m_TokenType == SToken::ET_Name ) { // Add the current counter to the current name. ASSERT( ArrayCounters.Size() > 0 ); List<int>::Iterator CounterIter = ArrayCounters.Back(); ASSERT( ( *CounterIter ) >= 0 ); SimpleString CounterString = SimpleString::PrintF( "%d", *CounterIter ); CounterCharArray.Clear(); CounterString.FillArray( CounterCharArray ); Token.m_TokenString.Append( CounterCharArray ); } else if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { WARNDESC( "Unexpected character '^' in token." ); } } else if( c == ' ' || c == '\t' ) { switch( Token.m_TokenType ) { case SToken::ET_None: case SToken::ET_Equals: // Ignore whitespace break; case SToken::ET_Name: case SToken::ET_Context: case SToken::ET_Macro: if( Token.m_TokenString.Empty() ) { // If the name is empty, ignore whitespace (before the name) } else { // Close current token, push it, and expect an equals // If we're closing a macro, save it as such if( Token.m_TokenType == SToken::ET_Macro ) { MacroToken = Token; } Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); if( Token.m_TokenType == SToken::ET_Name ) { Token.m_TokenType = SToken::ET_Equals; } else if( Token.m_TokenType == SToken::ET_Context || Token.m_TokenType == SToken::ET_Macro ) { Token.m_TokenType = SToken::ET_Name; } } break; case SToken::ET_Bool: case SToken::ET_Int: case SToken::ET_Float: // Close current token, push it, and expect nothing Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_None; break; case SToken::ET_String: Token.m_TokenString.PushBack( c ); break; default: WARNDESC( "Unexpected token" ); break; } } else if( c == '=' ) { switch( Token.m_TokenType ) { case SToken::ET_Name: // Close current token, push it and an equals Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_Equals; DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); Tokens.PushBack( Token ); Token.m_TokenType = SToken::ET_None; break; case SToken::ET_Equals: // Already expecting =, just push it Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); Token.m_TokenType = SToken::ET_None; break; case SToken::ET_String: Token.m_TokenString.PushBack( c ); break; default: WARNDESC( "Unexpected token" ); break; } } else if( c == '#' ) { // # starts a comment // Allow # inside a string if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { c = Stream.ReadInt8(); if( c == '!' ) // #! and !# indicate the start and end of block comments { while( !Stream.EOS() ) { if( Stream.ReadInt8() == '!' && Stream.ReadInt8() == '#' ) { break; } } } else { // Read to end of line while( c != '\n' && c!= '\v' && !Stream.EOS() ) // Vertical tab stupidity again { c = Stream.ReadInt8(); } // Change the context because we're on a new line Token.m_TokenType = SToken::ET_Name; ++LineCount; } } } else if( c == '\\' && Token.m_TokenType == SToken::ET_String ) { // Escape sequence, intended to insert linebreaks (and maybe other things in the future) // Config string escape sequences are not the same as C++ escape sequences. They can be // \\, \n, \?, \", or \xx (where x are hex digits, to specify any character by hex). char next = Stream.ReadInt8(); if( next == 'n' ) { Token.m_TokenString.PushBack( '\n' ); } else if( next == '\"' ) { Token.m_TokenString.PushBack( '\"' ); } else if( next == '\\' ) { Token.m_TokenString.PushBack( '\\' ); } else if( next == 'x' ) { char Hex = 0; for( uint HexIndex = 0; HexIndex < 2; ++HexIndex ) { next = Stream.ReadInt8(); ASSERT( IsHex( next ) ); Hex = ( Hex << 4 ) | GetHex( next ); } Token.m_TokenString.PushBack( Hex ); } else if( next == 'u' ) { // First, extract a unicode code point (e.g. \u00d7 for U+00D7) // NOTE: This only support the first Unicode plane, and is strict about // using four characters, so \ud7 is not a valid substitute for \u00d7. unicode_t CodePoint = 0; for( uint UnicodeIndex = 0; UnicodeIndex < 4; ++UnicodeIndex ) { next = Stream.ReadInt8(); ASSERT( IsHex( next ) ); CodePoint = ( CodePoint << 4 ) | GetHex( next ); } // Then convert the two-byte code point to UTF-8. Array<unicode_t> CodePointArray; CodePointArray.PushBack( CodePoint ); const SimpleString UTF8String = SimpleString::SetUTF8( CodePointArray ); for( uint CharIndex = 0; CharIndex < UTF8String.Length(); ++CharIndex ) { const char NextChar = UTF8String.GetChar( CharIndex ); Token.m_TokenString.PushBack( NextChar ); } } else { PRINTF( "Unrecognized escape sequence \\%c at line %d\n", next, LineCount ); WARNDESC( "Unrecognized escape sequence" ); } } else if( c == 0x0d ) { // DOS linebreak is 0D 0A, so ignore and expect \n to follow } else if( c == '\0' ) { // Don't know how these are getting in either, but ignore them } else if( c == '\n' || c == '\v' ) { if( Token.m_TokenType == SToken::ET_Macro ) { MacroToken = Token; } // Dunno how vertical tabs are getting in, but treat them as linebreaks if( Token.m_TokenString.Empty() ) { if( Token.m_TokenType != SToken::ET_Counter ) { Token.m_TokenType = SToken::ET_None; } Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); Token.m_TokenType = SToken::ET_Name; } else { Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_Name; } ++LineCount; } else if( c == '[' ) { if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { // We should only ever open a context when we're expecting a name ASSERT( Token.m_TokenType == SToken::ET_Name ); Token.m_TokenType = SToken::ET_Context; // Opening a new context, clear the macro token. MacroToken = SToken(); } } else if( c == ']' ) { // If we've already closed the context, ignore; else, push token if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { ASSERT( Token.m_TokenType == SToken::ET_Context ); Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); } } else if( c == '@' ) { if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { // We should only ever declare or insert a macro when we're expecting a name ASSERT( Token.m_TokenType == SToken::ET_Name ); c = Stream.ReadInt8(); if( c == '@' ) { // @@... means we're inserting the current macro into a name // Make sure there is a current macro. If this fails, a macro probably // wasn't opened in the current context. ASSERT( MacroToken.m_TokenString.Size() > 0 ); const uint MacroLength = MacroToken.m_TokenString.Size(); for( uint MacroIndex = 0; MacroIndex < MacroLength; ++MacroIndex ) { Token.m_TokenString.PushBack( MacroToken.m_TokenString[ MacroIndex ] ); } } else { // @... means we're declaring a new macro Token.m_TokenType = SToken::ET_Macro; if( c == ' ' || c == '\t' ) { // Ignore whitespace at the front of macro } else { Token.m_TokenString.PushBack( c ); } } } } else { bool ClosedString = false; InnerParse( c, StrMark, Token.m_TokenString, LineCount, Token.m_TokenType, &ClosedString ); if( ClosedString ) { Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_None; } } } SimpleString Context = ""; // Tokens are made, now create config vars for( uint i = 0; i < Tokens.Size(); ++i ) { SToken& NameToken = Tokens[i]; SimpleString Name = ""; const char* ValueString = NULL; if( NameToken.m_TokenType == SToken::ET_Name ) { Name = NameToken.m_TokenString.GetData(); ASSERT( Tokens[ i + 1 ].m_TokenType == SToken::ET_Equals ); SToken& ValueToken = Tokens[ i + 2 ]; ValueString = ValueToken.m_TokenString.GetData(); if( Context != "" ) { CATPRINTF( "Core", 2, "%s:", Context.CStr() ); } CATPRINTF( "Core", 2, "%s: %s: %s\n", Name.CStr(), SToken::m_TokenNames[ ValueToken.m_TokenType ], ValueString ); switch( ValueToken.m_TokenType ) { case SToken::ET_Bool: { // Just use the first character to determine truth bool Value = false; char first = ValueString[0]; if( first == 't' || first == 'T' ) { Value = true; } ConfigManager::SetBool( Name, Value, Context ); } break; case SToken::ET_Int: { int Value = atoi( ValueString ); ConfigManager::SetInt( Name, Value, Context ); } break; case SToken::ET_Counter: { List<int>::Iterator NextCounterIter = ArrayCounters.Front(); ( *NextCounterIter )++; // Add one to the last value we incremented, and that's the total for this array ConfigManager::SetInt( Name, *NextCounterIter, Context ); ArrayCounters.PopFront(); } break; case SToken::ET_Float: { float Value = (float)atof( ValueString ); ConfigManager::SetFloat( Name, Value, Context ); } break; case SToken::ET_String: { // Make a permanent copy of the string uint Length = (uint)strlen( ValueString ); char* pString = new char[ Length + 1]; memcpy_s( pString, Length + 1, ValueString, Length ); pString[ Length ] = '\0'; StringManager::AddString( StringManager::ESL_Permanent, pString ); ConfigManager::SetString( Name, pString, Context ); } break; default: WARNDESC( "Unexpected token" ); break; } i += 2; } else if( NameToken.m_TokenType == SToken::ET_Context ) { Context = NameToken.m_TokenString.GetData(); //CATPRINTF( "Core", 2, "Pushed context %s\n", Context.CStr() ); } else { DEBUGCATPRINTF( "Core", 2, "Skipped unexpected token %s (expected ET_Name)\n", SToken::m_TokenNames[ NameToken.m_TokenType ] ); } } // Clean up for( uint i = 0; i < Tokens.Size(); ++i ) { Tokens[i].m_TokenString.Clear(); } Tokens.Clear(); }
// NOTE: This is a lot of duplicated code from the value parsing parts // of Parse(). Could do to clean this up and use common functions. void ConfigParser::ParseTiny( const IDataStream& Stream ) { Array< char > FirstPart; // Could be name or context Array< char > SecondPart; // Name if first part is context Array< char > ValuePart; SToken::ETokenType Type = SToken::ET_None; char QuoteType = '\"'; bool StringClosed = false; enum EParseState { EPS_FirstPart, EPS_SecondPart, EPS_ValuePart, } ParseState = EPS_FirstPart; while( !Stream.EOS() ) { char c = Stream.ReadInt8(); if( ParseState == EPS_FirstPart ) { if( c == ' ' || c == '\t' || c == '\n' || c == '\0' ) { // Ignore whitespace continue; } else if( c == ':' ) { FirstPart.PushBack( '\0' ); ParseState = EPS_SecondPart; continue; } else if( c == '=' ) { FirstPart.PushBack( '\0' ); ParseState = EPS_ValuePart; continue; } else { FirstPart.PushBack( c ); } } else if( ParseState == EPS_SecondPart ) { if( c == ' ' || c == '\t' || c == '\n' || c == '\0' ) { // Ignore whitespace continue; } else if( c == '=' ) { SecondPart.PushBack( '\0' ); ParseState = EPS_ValuePart; continue; } else { SecondPart.PushBack( c ); } } else if( ParseState == EPS_ValuePart ) { if( ( ( Type != SToken::ET_String || StringClosed ) && ( c == ' ' || c == '\t' ) ) || c == '\n' || c == '\0' ) { // Parse the value and set the config var ValuePart.PushBack( '\0' ); SimpleString Name = ( SecondPart.Size() > 1 ) ? SecondPart.GetData() : FirstPart.GetData(); SimpleString Context = ( SecondPart.Size() > 1 ) ? FirstPart.GetData() : ""; switch( Type ) { case SToken::ET_Bool: { // Just use the first character to determine truth bool Value = false; char first = ValuePart[0]; if( first == 't' || first == 'T' ) { Value = true; } ConfigManager::SetBool( Name, Value, Context ); } break; case SToken::ET_Int: { int Value = atoi( ValuePart.GetData() ); ConfigManager::SetInt( Name, Value, Context ); } break; case SToken::ET_Float: { float Value = (float)atof( ValuePart.GetData() ); ConfigManager::SetFloat( Name, Value, Context ); } break; case SToken::ET_String: { // Make a permanent copy of the string uint Length = (uint)strlen( ValuePart.GetData() ); char* pString = new char[ Length + 1]; memcpy_s( pString, Length + 1, ValuePart.GetData(), Length ); pString[ Length ] = '\0'; StringManager::AddString( StringManager::ESL_Permanent, pString ); ConfigManager::SetString( Name, pString, Context ); } break; default: WARNDESC( "Unexpected token" ); break; } // Prepare for next variable FirstPart.Clear(); SecondPart.Clear(); ValuePart.Clear(); Type = SToken::ET_None; ParseState = EPS_FirstPart; StringClosed = false; continue; } else { InnerParse( c, QuoteType, ValuePart, 0, Type, &StringClosed ); } } } }
void ConfigManager::Load( const IDataStream& Stream ) { uint Pos = Stream.GetPos(); if( 'FCCD' == Stream.ReadUInt32() ) { uint NumContexts = Stream.ReadUInt32(); for( uint ContextIndex = 0; ContextIndex < NumContexts; ++ContextIndex ) { HashedString ContextKey( Stream.ReadUInt32() ); uint NumVars = Stream.ReadUInt32(); VarMap& VarMap = GetInstance()->m_Vars[ ContextKey ]; for( uint VarIndex = 0; VarIndex < NumVars; ++VarIndex ) { ConfigVar ConfigVar; HashedString Key( Stream.ReadUInt32() ); ConfigVar.m_Type = ( ConfigVar::EVarType )Stream.ReadUInt8(); switch( ConfigVar.m_Type ) { case ConfigVar::EVT_Bool: ConfigVar.m_Bool = ( Stream.ReadUInt8() != 0 ); break; case ConfigVar::EVT_Int: ConfigVar.m_Int = Stream.ReadInt32(); break; case ConfigVar::EVT_Float: ConfigVar.m_Float = Stream.ReadFloat(); break; case ConfigVar::EVT_String: { uint Length = Stream.ReadUInt32(); char* pString = AllocateString( Length + 1 ); Stream.Read( Length, pString ); pString[ Length ] = '\0'; StringManager::AddString( StringManager::ESL_Permanent, pString ); ConfigVar.m_String = pString; ConfigVar.m_Hash = Stream.ReadUInt32(); if( ReverseHash::IsEnabled() ) { ReverseHash::RegisterHash( ConfigVar.m_Hash, ConfigVar.m_String ); } } break; default: WARNDESC( "Bad config var type" ); break; } VarMap.Insert( Key, ConfigVar ); } } } else { Stream.SetPos( Pos ); ConfigParser::Parse( Stream ); } }
void WBCompEldRope::DropRope() { WBEntity* const pEntity = GetEntity(); DEVASSERT(pEntity); WBCompEldTransform* const pTransform = pEntity->GetTransformComponent<WBCompEldTransform>(); DEVASSERT(pTransform); // HookVector is directed away from the surface (same as hit normal) const Vector HookVector = Quantize(pTransform->GetOrientation().ToVector()); const Vector DropVector = Vector(0.0f, 0.0f, -1.0f); ASSERT(HookVector.LengthSquared() == 1.0f); ASSERT((HookVector.y == 0.0f && HookVector.z == 0.0f) || (HookVector.x == 0.0f && HookVector.z == 0.0f) || (HookVector.x == 0.0f && HookVector.y == 0.0f)); WBCompEldCollision* const pCollision = GET_WBCOMP(pEntity, EldCollision); DEVASSERT(pCollision); WBCompEldMesh* const pMesh = GET_WBCOMP(pEntity, EldMesh); DEVASSERT(pMesh); EldritchWorld* const pWorld = GetWorld(); const Vector HitLocation = pTransform->GetLocation(); const Vector StartLocation = HitLocation + (HookVector * m_HookLength); const Ray TraceRay = Ray(StartLocation, DropVector); CollisionInfo Info; Info.m_CollideWorld = true; Info.m_CollideEntities = true; Info.m_CollidingEntity = pEntity; Info.m_UserFlags = EECF_CollideAsEntity | EECF_CollideStaticEntities; if (pWorld->Trace(TraceRay, Info)) { m_Dropped = true; // Reset orientation now that it is being "baked" into extents. pTransform->SetOrientation(Angles()); // StartLocation is where the rope attaches to the hook // EndLocation is where the rope dangles above the ground ASSERT(Info.m_Plane.m_Normal.Equals(Vector::Up, EPSILON)); const Vector EndLocation = Info.m_Intersection + Info.m_Plane.m_Normal * m_DangleHeight; pTransform->SetLocation(0.5f * (StartLocation + EndLocation)); const Vector HalfExtents = (0.5f * (EndLocation - StartLocation)).GetAbs(); const Vector CollisionFatten = Vector(m_CollisionFatten, m_CollisionFatten, m_CollisionFatten); pCollision->SetExtents(HalfExtents + CollisionFatten); const Vector MeshFatten = Vector(m_MeshFatten, m_MeshFatten, m_MeshFatten); pMesh->SetMeshScale(HalfExtents + MeshFatten); m_Anchor = HitLocation - (HookVector * m_AnchorDepth); // Spawn rope hook entity and set up its transform and anchor { WBEntity* const pHookEntity = WBWorld::GetInstance()->CreateEntity(m_HookEntity); ASSERT(pHookEntity); WBCompEldTransform* const pHookTransform = pHookEntity->GetTransformComponent<WBCompEldTransform>(); ASSERT(pHookTransform); WBCompEldAnchor* const pHookAnchor = GET_WBCOMP(pHookEntity, EldAnchor); ASSERT(pHookAnchor); pHookTransform->SetLocation(StartLocation); pHookTransform->SetOrientation((-HookVector).ToAngles()); } } else { WARNDESC("Rope could not be launched."); pEntity->Destroy(); } }