#include "gamestate.h" #include "game.h" #include "astleaf.h" #include "astleafliteral.h" #include "smlnode.h" #include "interface.h" #include #include #include using namespace Bu; GameState::GameState( Game *pGame, Interface *pIface ) : pGame( pGame ), pIface( pIface ), bRunning( true ), bReturnOnly( false ) { } GameState::~GameState() { } Gats::Dictionary *GameState::toGats() const { Gats::Dictionary *pRoot = new Gats::Dictionary; Gats::Dictionary *pSit = pRoot->insertDict("situations"); for( ScopeHash::const_iterator i = hsSituation.begin(); i; i++ ) { pSit->insert( i.getKey(), scopeToGats( *i ) ); } pRoot->insert("global", scopeToGats( &sGlobal ) ); pRoot->insert("player", scopeToGats( &sPlayer ) ); pRoot->insert("curSituation", sCurSituation ); pRoot->insert("curPrompt", sPrompt ); return pRoot; } void GameState::fromGats( Gats::Dictionary *pRoot ) { sPrompt = pRoot->getStr("curPrompt"); sCurSituation = pRoot->getStr("curSituation"); gatsToScope( pRoot->getDict("global"), &sGlobal ); gatsToScope( pRoot->getDict("player"), &sPlayer ); for( Gats::Dictionary::iterator i = pRoot->getDict("situations")->begin(); i; i++ ) { Scope *pSc = new Scope(); gatsToScope( dynamic_cast(*i), pSc ); hsSituation.insert( i.getKey(), pSc ); } } Gats::Object *GameState::scopeToGats( const Scope *pSrc ) const { Gats::Dictionary *pDst = new Gats::Dictionary(); for( Scope::const_iterator i = pSrc->begin(); i; i++ ) { pDst->insert( i.getKey(), variableToGats( *i ) ); } return pDst; } Gats::Object *GameState::variableToGats( const Variable &rVar ) const { Gats::List *pLst = new Gats::List(); pLst->append( rVar.getType() ); switch( rVar.getType() ) { case Variable::tNull: break; case Variable::tBool: pLst->append( new Gats::Boolean( rVar.getBool() ) ); break; case Variable::tInt: pLst->append( rVar.getInt() ); break; case Variable::tFloat: pLst->append( rVar.getFloat() ); break; case Variable::tSituation: pLst->append( rVar.getString() ); break; case Variable::tVariable: pLst->append( rVar.getVariableRef().sid ); pLst->append( rVar.getVariableRef().sName ); break; case Variable::tVarPtr: sio << "No!!!" << sio.nl; break; case Variable::tList: { Gats::List *pSub = pLst->appendList(); for( Variable::VariableArray::const_iterator i = rVar.getList().begin(); i; i++ ) { pSub->append( variableToGats( *i ) ); } } break; case Variable::tDictionary: { Gats::List *pSub = pLst->appendList(); for( Variable::VariableHash::const_iterator i = rVar.getHash().begin(); i; i++ ) { pSub->append( variableToGats( i.getKey() ) ); pSub->append( variableToGats( *i ) ); } } break; case Variable::tString: pLst->append( rVar.getString() ); break; } return pLst; } void GameState::gatsToScope( Gats::Dictionary *pRoot, Scope *pSrc ) const { for( Gats::Dictionary::iterator i = pRoot->begin(); i; i++ ) { pSrc->insert( i.getKey(), gatsToVariable( dynamic_cast(*i) ) ); } } Variable GameState::gatsToVariable( Gats::List *pLst ) const { Gats::List::iterator i = pLst->begin(); switch( dynamic_cast(*i)->getValue() ) { case Variable::tNull: return Variable( Variable::tNull ); break; case Variable::tBool: return Variable( dynamic_cast(*(i+1))->getValue() ); break; case Variable::tInt: return Variable( dynamic_cast(*(i+1))->getValue() ); break; case Variable::tFloat: return Variable( dynamic_cast(*(i+1))->getValue() ); break; case Variable::tSituation: return Variable::newSituationName( *dynamic_cast(*(i+1)) ); break; case Variable::tVariable: return Variable::newVariableName( *dynamic_cast(*(i+2)), (ScopeId)dynamic_cast(*(i+1))->getValue() ); break; case Variable::tVarPtr: sio << "No!!!" << sio.nl; break; case Variable::tList: { Variable vRet( Variable::tList ); for( Gats::List::iterator k = dynamic_cast(*(i+1))->begin(); k; k++ ) { vRet += gatsToVariable( dynamic_cast(*k) ); } return vRet; } break; case Variable::tDictionary: { Variable vRet( Variable::tDictionary ); for( Gats::List::iterator k = dynamic_cast(*(i+1))->begin(); k; k++ ) { Variable vKey = gatsToVariable( dynamic_cast(*k) ); k++; if( !k ) { throw Bu::ExceptionBase("Premature end of dict."); } Variable vValue = gatsToVariable( dynamic_cast(*k) ); vRet.insert( vKey, vValue ); } return vRet; } break; case Variable::tString: return Variable( *dynamic_cast(*(i+1)) ); break; } throw Bu::ExceptionBase("Type unknown: %d", dynamic_cast(*i)->getValue() ); } class Situation *GameState::getCurSituation() { return pGame->getSituation( sCurSituation ); } /* void GameState::run( class AstBranch *pAst ) { // pushScope( pAst ); run(); // if( lsLocal.getSize() == iDepth ) // delete lsLocal.peekPop(); } void GameState::pushScope( class AstBranch *pAst ) { sio << "--> pushScope: " << pAst->getType() << sio.nl; if( pAst->getType() != AstNode::tScope ) throw Bu::ExceptionBase("Nope, nothing doing, you can't run a non-scope AstBranch."); lsLocal.push( new Scope() ); // int iDepth = lsLocal.getSize(); sProg.push( pAst->getNodeList().begin() ); } */ void GameState::init() { Variable vStart = pGame->getParam("start"); if( vStart.getType() != Variable::tSituation ) throw Bu::ExceptionBase("game.start is not set to a situation name."); gotoSituation( vStart.getString() ); } void GameState::gotoSituation( const Bu::String &sName ) { lStack.clear(); for( ScopeList::iterator i = lsLocal.begin(); i; i++ ) delete *i; lsLocal.clear(); Situation *pSit = pGame->getSituation( sName ); sCurSituation = sName; sGoto.clear(); if( !hsSituation.has( sName ) ) { hsSituation.insert( sName, new Scope() ); pSit->exec( *this, Situation::modeSetup ); if( !sGoto.isSet() ) { pSit->exec( *this, Situation::modeEnter ); } } else { pSit->exec( *this, Situation::modeEnter ); } if( sGoto.isSet() ) { gotoSituation( sGoto.clone() ); } } void GameState::callFunction( const Bu::String &sName ) { pGame->getFunction( sName )->call( *this ); } void GameState::execCommand( const Bu::String &sCmd ) { Bu::StringList lCmd = tokenize( sCmd ); if( !pGame->getSituation( sCurSituation )->execCommand( *this, lCmd ) ) { if( !pGame->execCommand( *this, lCmd ) ) { pIface->display( SmlNode::parse( "I don't understand that command." ) ); return; } } if( sGoto.isSet() ) gotoSituation( sGoto.clone() ); } void GameState::execOption( int idx ) { pGame->getSituation( sCurSituation )->execOption( *this, idx ); if( sGoto.isSet() ) gotoSituation( sGoto.clone() ); } bool GameState::hasVariable( const Bu::String &sName, ScopeId id ) { switch( id ) { case sidLocal: return lsLocal.peek()->has( sName ); case sidGlobal: return sGlobal.has( sName ); case sidPlayer: return sPlayer.has( sName ); case sidSituation: return hsSituation.get( sCurSituation )->has( sName ); } throw Bu::ExceptionBase("Really bad scopeid passed into getVariable"); } void GameState::delVariable( const Bu::String &sName, ScopeId id ) { switch( id ) { case sidLocal: return lsLocal.peek()->erase( sName ); case sidGlobal: return sGlobal.erase( sName ); case sidPlayer: return sPlayer.erase( sName ); case sidSituation: return hsSituation.get( sCurSituation )->erase( sName ); } throw Bu::ExceptionBase("Really bad scopeid passed into getVariable"); } Variable &GameState::getVariable( const Bu::String &sName, ScopeId id ) { try { switch( id ) { case sidLocal: return lsLocal.peek()->get( sName ); case sidGlobal: return sGlobal.get( sName ); case sidPlayer: return sPlayer.get( sName ); case sidSituation: return hsSituation.get( sCurSituation )->get( sName ); } } catch( Bu::HashException &e ) { throw Bu::ExceptionBase("No %d variable named %s found.", id, sName.getStr() ); } throw Bu::ExceptionBase("Really bad scopeid passed into getVariable"); } void GameState::setVariable( const Bu::String &sName, const Variable &v, ScopeId id ) { switch( id ) { case sidLocal: lsLocal.peek()->insert( sName, v ); return; case sidGlobal: sGlobal.insert( sName, v ); return; case sidPlayer: sPlayer.insert( sName, v ); return; case sidSituation: hsSituation.get( sCurSituation )->insert( sName, v ); return; } throw Bu::ExceptionBase("Really bad scopeid passed into setVariable"); } Variable &GameState::deref( Variable &src, bool bCreate ) { if( src.getType() == Variable::tVariable ) { VariableRef r = src.getVariableRef(); try { switch( r.sid ) { case sidLocal: return lsLocal.peek()->get( r.sName ); case sidGlobal: return sGlobal.get( r.sName ); case sidPlayer: return sPlayer.get( r.sName ); case sidSituation: return hsSituation.get( sCurSituation )->get( r.sName ); } } catch( Bu::HashException &e ) { if( bCreate ) { switch( r.sid ) { case sidLocal: lsLocal.peek()->insert( r.sName, Variable() ); return lsLocal.peek()->get( r.sName ); case sidGlobal: sGlobal.insert( r.sName, Variable() ); return sGlobal.get( r.sName ); case sidPlayer: sPlayer.insert( r.sName, Variable() ); return sPlayer.get( r.sName ); case sidSituation: if( hsSituation.has( sCurSituation ) ) { hsSituation.get( sCurSituation )->insert( r.sName, Variable() ); return hsSituation.get( sCurSituation )->get( r.sName ); } else { Bu::println("No such situation: %1").arg( sCurSituation ); Bu::println("Available:"); for( ScopeHash::iterator i = hsSituation.begin(); i; i++ ) { Bu::println(" <<%1>>").arg( i.getKey() ); } ::exit( 0 ); } } } else { throw Bu::ExceptionBase("No %d variable named %s found.", r.sid, r.sName.getStr() ); } } } else if( src.getType() == Variable::tVarPtr ) { return *src.getVariablePtr(); } return src; } Bu::StringList GameState::tokenize( const Bu::String &sSrc ) { Bu::StringList lRet; Bu::String sToken; bool bWs = true; Bu::String::const_iterator i = sSrc.begin(); while( i ) { for(; i; i++ ) { if( *i != ' ' && *i != '\t' && *i != '\n' && *i != '\r' ) break; } for(; i; i++ ) { if( i == ' ' || i == '\t' || *i == '\n' || *i == '\r' ) { break; } sToken.append( *i ); } if( sToken.getSize() > 0 ) { lRet.append( sToken ); sToken.clear(); } } return lRet; } void GameState::exit() { bRunning = false; bEscape = true; } Variable GameState::popDeref() { Variable v = lStack.peekPop(); if( v.getType() == Variable::tVariable ) { VariableRef r = v.getVariableRef(); return getVariable( r.sName, r.sid ); } else if( v.getType() == Variable::tVarPtr ) { return *v.getVariablePtr(); } return v; } void GameState::run( const class AstBranch *pAst, bool bNewScope ) { if( pAst->getType() != AstNode::tScope ) throw Bu::ExceptionBase("Nope, nothing doing, you can't run a non-scope AstBranch."); run( pAst->getNodeList(), bNewScope ); } void GameState::run( const AstBranch::NodeList &lNode, bool bNewScope ) { ProgramStack sProg; if( bNewScope ) lsLocal.push( new Scope() ); sProg.push( lNode.begin() ); while( !sProg.isEmpty() ) { ProgramCounter &i = sProg.peek(); if( i ) { // sio << "Stack: " << lStack << sio.nl; // sio << "exec: " << (*i)->getType() << sio.nl; switch( (*i)->getType() ) { // tLeaf case AstNode::tNot: { Variable x = popDeref(); if( x.getType() != Variable::tBool ) throw Bu::ExceptionBase("Non-bool used with logical not operator."); push( Variable( !x.getBool() ) ); } break; case AstNode::tComp: { push( popDeref() == popDeref() ); } break; case AstNode::tCompGt: { Variable y = popDeref(); Variable x = popDeref(); push( x > y ); } break; case AstNode::tCompLt: { Variable y = popDeref(); Variable x = popDeref(); push( x < y ); } break; case AstNode::tCompGtEq: { Variable y = popDeref(); Variable x = popDeref(); push( x >= y ); } break; case AstNode::tCompLtEq: { Variable y = popDeref(); Variable x = popDeref(); push( x <= y ); } break; case AstNode::tStore: { Variable y = popDeref(); deref( lStack.peek(), true ) = y; /* Variable dst = pop(); VariableRef r = dst.getVariableRef(); setVariable( r.sName, y, r.sid ); */ } break; case AstNode::tStoreRev: { Variable dst = pop(); Variable y = popDeref(); VariableRef r = dst.getVariableRef(); setVariable( r.sName, y, r.sid ); } break; case AstNode::tAnd: { Variable y = popDeref(); Variable x = popDeref(); if( x.getType() != Variable::tBool || y.getType() != Variable::tBool ) { throw Bu::ExceptionBase("Non-bool used in logical AND operator."); } lStack.push( Variable( x.getBool() && y.getBool() ) ); } break; case AstNode::tOr: { Variable y = popDeref(); Variable x = popDeref(); if( x.getType() != Variable::tBool || y.getType() != Variable::tBool ) { throw Bu::ExceptionBase("Non-bool used in logical OR operator."); } lStack.push( Variable( x.getBool() || y.getBool() ) ); } break; case AstNode::tPlus: { Variable y = popDeref(); Variable x = popDeref(); lStack.push( x + y ); } break; case AstNode::tMinus: { Variable y = popDeref(); Variable x = popDeref(); lStack.push( x - y ); } break; case AstNode::tDivide: { Variable y = popDeref(); Variable x = popDeref(); lStack.push( x / y ); } break; case AstNode::tMultiply: { Variable y = popDeref(); Variable x = popDeref(); lStack.push( x * y ); } break; case AstNode::tPlusStore: { Variable y = popDeref(); deref( lStack.peek(), true ) += y; } break; case AstNode::tMinusStore: { Variable y = popDeref(); deref( lStack.peek(), true ) -= y; } break; case AstNode::tDivideStore: { Variable y = popDeref(); deref( lStack.peek(), true ) /= y; } break; case AstNode::tMultiplyStore: { Variable y = popDeref(); deref( lStack.peek(), true ) *= y; } break; case AstNode::tNegate: push( -popDeref() ); break; case AstNode::tIn: { Variable v = popDeref(); Variable x = popDeref(); if( v.getType() == Variable::tDictionary || v.getType() == Variable::tList ) { push( Variable( v.has( x ) ) ); } else { throw Bu::ExceptionBase("Invalid type for operator in"); } } break; case AstNode::tGoto: { Variable x = popDeref(); if( x.getType() != Variable::tSituation ) throw Bu::ExceptionBase("You cannot goto anything but a situation."); sGoto = x.getString(); return; } break; case AstNode::tSwap: { Variable y = pop(); Variable x = pop(); push( y ); push( x ); } break; case AstNode::tReturn: if( bNewScope ) lsLocal.pop(); return; case AstNode::tAppend: { Variable v = popDeref(); deref( lStack.peek() ) += v; } break; case AstNode::tInsert: { Variable v = popDeref(); Variable k = popDeref(); deref( lStack.peek() ).insert( k, v ); } break; case AstNode::tIndex: { Variable v = popDeref(); Variable cx = pop(); Variable &c = deref( cx ); push( Variable( &c.get( v ) ) ); } break; case AstNode::tPop: pop(); break; case AstNode::tDeref: push( popDeref() ); break; case AstNode::tExists: { Variable v = pop(); VariableRef r = v.getVariableRef(); push( Variable( hasVariable( r.sName, r.sid ) ) ); } break; case AstNode::tDelete: { Variable v = pop(); VariableRef r = v.getVariableRef(); delVariable( r.sName, r.sid ); push( Variable( Variable::tNull ) ); } break; // tLeafLiteral case AstNode::tVarName: case AstNode::tLiteral: lStack.push( dynamic_cast(*i)->getValue() ); break; case AstNode::tFuncCall: callFunction( dynamic_cast(*i) ->getValue().getString() ); break; // tBranch case AstNode::tScope: throw Bu::ExceptionBase("Scope? that shouldn't be here..."); break; case AstNode::tIf: { AstBranch::NodeList lIf = dynamic_cast(*i)->getNodeList(); Variable v = popDeref(); if( v.getType() != Variable::tBool ) throw Bu::ExceptionBase("conditional did not evaluate to boolean."); AstBranch::NodeList::const_iterator iIf = lIf.begin(); if( v.getBool() ) { i++; sProg.push( dynamic_cast(*iIf)-> getNodeList().begin() ); continue; } else { iIf++; if( iIf ) { i++; sProg.push( dynamic_cast(*iIf)-> getNodeList().begin() ); continue; } } } break; case AstNode::tForEach: { AstBranch::NodeList lFe = dynamic_cast(*i)->getNodeList(); AstBranch::NodeList::const_iterator iEach = lFe.begin(); AstBranch::NodeList::const_iterator iIn = iEach+1; AstBranch::NodeList::const_iterator iDo = iIn+1; const AstBranch::NodeList &lEachVrs = dynamic_cast(*iEach)->getNodeList(); AstBranch::NodeList::const_iterator iEachVrs = lEachVrs.begin(); bool bUseKey = false; VariableRef vrKey, vrValue; if( lEachVrs.getSize() == 2 ) { vrKey = dynamic_cast(*iEachVrs)-> getValue().getVariableRef(); iEachVrs++; vrValue = dynamic_cast(*iEachVrs)-> getValue().getVariableRef(); bUseKey = true; } else { vrValue = dynamic_cast(*iEachVrs)-> getValue().getVariableRef(); } run( dynamic_cast(*iIn), false ); Variable vIn = popDeref(); const AstBranch::NodeList &rDo = dynamic_cast(*iDo)->getNodeList(); if( vIn.getType() == Variable::tDictionary ) { const Variable::VariableHash &rHash = vIn.getHash(); for( Variable::VariableHash::const_iterator i = rHash.begin(); i; i++ ) { if( bUseKey ) setVariable( vrKey.sName, i.getKey(), vrKey.sid ); setVariable( vrValue.sName, i.getValue(), vrValue.sid ); run( rDo, false ); } } else if( vIn.getType() == Variable::tList ) { if( bUseKey ) { throw Bu::ExceptionBase("You cannot use key:value pairs as iterators in a for each loop iterating over a list."); } const Variable::VariableArray &rList = vIn.getList(); for( Variable::VariableArray::const_iterator i = rList.begin(); i; i++ ) { setVariable( vrValue.sName, *i, vrValue.sid ); run( rDo, false ); } } } break; case AstNode::tWhile: { AstBranch::NodeList lWhile = dynamic_cast(*i)->getNodeList(); AstBranch::NodeList::const_iterator iTest = lWhile.begin(); AstBranch::NodeList::const_iterator iDo = iTest+1; for(;;) { run( dynamic_cast(*iTest)-> getNodeList(), false ); Variable v = popDeref(); if( v.getType() != Variable::tBool ) throw Bu::ExceptionBase("conditional did not evaluate to boolean."); if( !v.getBool() ) break; run( dynamic_cast(*iDo)-> getNodeList(), false ); } } break; } if( sGoto.isSet() ) return; ++sProg.peek(); } while( !sProg.isEmpty() && !sProg.peek() ) { sProg.pop(); } } if( bNewScope ) lsLocal.pop(); }