/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see .
===========================================================================
*/
// Script Command Sequences
//
// -- jweier
// this include must remain at the top of every Icarus CPP file
#include "icarus.h"
#include
#include "../code/qcommon/ojk_saved_game_helper.h"
CSequence::CSequence( void )
{
m_numCommands = 0;
m_numChildren = 0;
m_flags = 0;
m_iterations = 1;
m_parent = NULL;
m_return = NULL;
}
CSequence::~CSequence( void )
{
Delete();
}
/*
-------------------------
Create
-------------------------
*/
CSequence *CSequence::Create( void )
{
CSequence *seq = new CSequence;
//TODO: Emit warning
assert(seq);
if ( seq == NULL )
return NULL;
seq->SetFlag( SQ_COMMON );
return seq;
}
/*
-------------------------
Delete
-------------------------
*/
void CSequence::Delete( void )
{
block_l::iterator bi;
sequence_l::iterator si;
//Notify the parent of the deletion
if ( m_parent )
{
m_parent->RemoveChild( this );
}
//Clear all children
if ( m_numChildren > 0 )
{
for ( si = m_children.begin(); si != m_children.end(); ++si )
{
(*si)->SetParent( NULL );
}
}
//Clear all held commands
for ( bi = m_commands.begin(); bi != m_commands.end(); ++bi )
{
delete (*bi); //Free() handled internally
}
m_commands.clear();
m_children.clear();
}
/*
-------------------------
AddChild
-------------------------
*/
void CSequence::AddChild( CSequence *child )
{
assert( child );
if ( child == NULL )
return;
m_children.insert( m_children.end(), child );
m_childrenMap[ m_numChildren ] = child;
m_numChildren++;
}
/*
-------------------------
RemoveChild
-------------------------
*/
void CSequence::RemoveChild( CSequence *child )
{
assert( child );
if ( child == NULL )
return;
//Remove the child
m_children.remove( child );
m_numChildren--;
}
/*
-------------------------
HasChild
-------------------------
*/
bool CSequence::HasChild( CSequence *sequence )
{
sequence_l::iterator ci;
for ( ci = m_children.begin(); ci != m_children.end(); ++ci )
{
if ( (*ci) == sequence )
return true;
if ( (*ci)->HasChild( sequence ) )
return true;
}
return false;
}
/*
-------------------------
SetParent
-------------------------
*/
void CSequence::SetParent( CSequence *parent )
{
m_parent = parent;
if ( parent == NULL )
return;
//Inherit the parent's properties (this avoids messy tree walks later on)
if ( parent->m_flags & SQ_RETAIN )
m_flags |= SQ_RETAIN;
if ( parent->m_flags & SQ_PENDING )
m_flags |= SQ_PENDING;
}
/*
-------------------------
PopCommand
-------------------------
*/
CBlock *CSequence::PopCommand( int type )
{
CBlock *command = NULL;
//Make sure everything is ok
assert( (type == POP_FRONT) || (type == POP_BACK) );
if ( m_commands.empty() )
return NULL;
switch ( type )
{
case POP_FRONT:
command = m_commands.front();
m_commands.pop_front();
m_numCommands--;
return command;
break;
case POP_BACK:
command = m_commands.back();
m_commands.pop_back();
m_numCommands--;
return command;
break;
}
//Invalid flag
return NULL;
}
/*
-------------------------
PushCommand
-------------------------
*/
int CSequence::PushCommand( CBlock *block, int type )
{
//Make sure everything is ok
assert( (type == PUSH_FRONT) || (type == PUSH_BACK) );
assert( block );
switch ( type )
{
case PUSH_FRONT:
m_commands.push_front( block );
m_numCommands++;
return true;
break;
case PUSH_BACK:
m_commands.push_back( block );
m_numCommands++;
return true;
break;
}
//Invalid flag
return false;
}
/*
-------------------------
SetFlag
-------------------------
*/
void CSequence::SetFlag( int flag )
{
m_flags |= flag;
}
/*
-------------------------
RemoveFlag
-------------------------
*/
void CSequence::RemoveFlag( int flag, bool children )
{
m_flags &= ~flag;
if ( children )
{
sequence_l::iterator si;
for ( si = m_children.begin(); si != m_children.end(); ++si )
{
(*si)->RemoveFlag( flag, true );
}
}
}
/*
-------------------------
HasFlag
-------------------------
*/
int CSequence::HasFlag( int flag )
{
return (m_flags & flag);
}
/*
-------------------------
SetReturn
-------------------------
*/
void CSequence::SetReturn ( CSequence *sequence )
{
assert( sequence != this );
m_return = sequence;
}
/*
-------------------------
GetChild
-------------------------
*/
CSequence *CSequence::GetChild( int id )
{
if ( id < 0 )
return NULL;
//NOTENOTE: Done for safety reasons, I don't know what this template will return on underflow ( sigh... )
sequenceID_m::iterator mi = m_childrenMap.find( id );
if ( mi == m_childrenMap.end() )
return NULL;
return (*mi).second;
}
/*
-------------------------
SaveCommand
-------------------------
*/
int CSequence::SaveCommand( CBlock *block )
{
unsigned char flags;
int numMembers, bID, size;
CBlockMember *bm;
ojk::SavedGameHelper saved_game(
m_owner->GetInterface()->saved_game);
//Save out the block ID
bID = block->GetBlockID();
saved_game.write_chunk(
INT_ID('B', 'L', 'I', 'D'),
bID);
//Save out the block's flags
flags = block->GetFlags();
saved_game.write_chunk(
INT_ID('B', 'F', 'L', 'G'),
flags);
//Save out the number of members to read
numMembers = block->GetNumMembers();
saved_game.write_chunk(
INT_ID('B', 'N', 'U', 'M'),
numMembers);
for ( int i = 0; i < numMembers; i++ )
{
bm = block->GetMember( i );
//Save the block id
bID = bm->GetID();
saved_game.write_chunk(
INT_ID('B', 'M', 'I', 'D'),
bID);
//Save out the data size
size = bm->GetSize();
saved_game.write_chunk(
INT_ID('B', 'S', 'I', 'Z'),
size);
//Save out the raw data
const uint8_t* raw_data = static_cast(bm->GetData());
saved_game.write_chunk(
INT_ID('B', 'M', 'E', 'M'),
raw_data,
size);
}
return true;
}
/*
-------------------------
Save
-------------------------
*/
int CSequence::Save( void )
{
sequence_l::iterator ci;
block_l::iterator bi;
int id;
ojk::SavedGameHelper saved_game(
m_owner->GetInterface()->saved_game);
//Save the parent (by GUID)
id = ( m_parent != NULL ) ? m_parent->GetID() : -1;
saved_game.write_chunk(
INT_ID('S', 'P', 'I', 'D'),
id);
//Save the return (by GUID)
id = ( m_return != NULL ) ? m_return->GetID() : -1;
saved_game.write_chunk(
INT_ID('S', 'R', 'I', 'D'),
id);
//Save the number of children
saved_game.write_chunk(
INT_ID('S', 'N', 'C', 'H'),
m_numChildren);
//Save out the children (only by GUID)
STL_ITERATE( ci, m_children )
{
id = (*ci)->GetID();
saved_game.write_chunk(
INT_ID('S', 'C', 'H', 'D'),
id);
}
//Save flags
saved_game.write_chunk(
INT_ID('S', 'F', 'L', 'G'),
m_flags);
//Save iterations
saved_game.write_chunk(
INT_ID('S', 'I', 'T', 'R'),
m_iterations);
//Save the number of commands
saved_game.write_chunk(
INT_ID('S', 'N', 'M', 'C'),
m_numCommands);
//Save the commands
STL_ITERATE( bi, m_commands )
{
SaveCommand( (*bi) );
}
return true;
}
/*
-------------------------
Load
-------------------------
*/
int CSequence::Load( void )
{
unsigned char flags = 0;
CSequence *sequence;
CBlock *block;
int id = 0, numMembers = 0;
int i;
int bID, bSize;
void *bData;
ojk::SavedGameHelper saved_game(
m_owner->GetInterface()->saved_game);
//Get the parent sequence
saved_game.read_chunk(
INT_ID('S', 'P', 'I', 'D'),
id);
m_parent = ( id != -1 ) ? m_owner->GetSequence( id ) : NULL;
//Get the return sequence
saved_game.read_chunk(
INT_ID('S', 'R', 'I', 'D'),
id);
m_return = ( id != -1 ) ? m_owner->GetSequence( id ) : NULL;
//Get the number of children
saved_game.read_chunk(
INT_ID('S', 'N', 'C', 'H'),
m_numChildren);
//Reload all children
for ( i = 0; i < m_numChildren; i++ )
{
//Get the child sequence ID
saved_game.read_chunk(
INT_ID('S', 'C', 'H', 'D'),
id);
//Get the desired sequence
if ( ( sequence = m_owner->GetSequence( id ) ) == NULL )
return false;
//Insert this into the list
STL_INSERT( m_children, sequence );
//Restore the connection in the child / ID map
m_childrenMap[ i ] = sequence;
}
//Get the sequence flags
saved_game.read_chunk(
INT_ID('S', 'F', 'L', 'G'),
m_flags);
//Get the number of iterations
saved_game.read_chunk(
INT_ID('S', 'I', 'T', 'R'),
m_iterations);
int numCommands = 0;
//Get the number of commands
saved_game.read_chunk(
INT_ID('S', 'N', 'M', 'C'),
numCommands);
//Get all the commands
for ( i = 0; i < numCommands; i++ )
{
//Get the block ID and create a new container
saved_game.read_chunk(
INT_ID('B', 'L', 'I', 'D'),
id);
block = new CBlock;
block->Create( id );
//Read the block's flags
saved_game.read_chunk(
INT_ID('B', 'F', 'L', 'G'),
flags);
block->SetFlags( flags );
numMembers = 0;
//Get the number of block members
saved_game.read_chunk(
INT_ID('B', 'N', 'U', 'M'),
numMembers);
for ( int j = 0; j < numMembers; j++ )
{
bID = 0;
//Get the member ID
saved_game.read_chunk(
INT_ID('B', 'M', 'I', 'D'),
bID);
bSize = 0;
//Get the member size
saved_game.read_chunk(
INT_ID('B', 'S', 'I', 'Z'),
bSize);
//Get the member's data
if ( ( bData = ICARUS_Malloc( bSize ) ) == NULL )
return false;
//Get the actual raw data
saved_game.read_chunk(
INT_ID('B', 'M', 'E', 'M'),
static_cast(bData),
bSize);
//Write out the correct type
switch ( bID )
{
case TK_INT:
{
assert(0);
int data = *(int *) bData;
block->Write( TK_FLOAT, (float) data );
}
break;
case TK_FLOAT:
block->Write( TK_FLOAT, *(float *) bData );
break;
case TK_STRING:
case TK_IDENTIFIER:
case TK_CHAR:
block->Write( TK_STRING, (char *) bData );
break;
case TK_VECTOR:
case TK_VECTOR_START:
block->Write( TK_VECTOR, *(vec3_t *) bData );
break;
case ID_TAG:
block->Write( ID_TAG, (float) ID_TAG );
break;
case ID_GET:
block->Write( ID_GET, (float) ID_GET );
break;
case ID_RANDOM:
block->Write( ID_RANDOM, *(float *) bData );//(float) ID_RANDOM );
break;
case TK_EQUALS:
case TK_GREATER_THAN:
case TK_LESS_THAN:
case TK_NOT:
block->Write( bID, 0 );
break;
default:
assert(0);
return false;
break;
}
//Get rid of the temp memory
ICARUS_Free( bData );
}
//Save the block
//STL_INSERT( m_commands, block );
PushCommand( block, PUSH_BACK );
}
return true;
}