/* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 0 A.D. If not, see . */ #include "precompiled.h" #include "Technology.h" #include "TechnologyCollection.h" #include "ps/CStr.h" #include "ps/CLogger.h" #include "scripting/ScriptingHost.h" #include "ps/XML/Xeromyces.h" #include "ps/XML/XeroXMB.h" #include "Entity.h" #include "EntityTemplate.h" #include "EntityTemplateCollection.h" #include "ps/Player.h" #include "scripting/ScriptableComplex.inl" #define LOG_CATEGORY L"Techs" STL_HASH_SET CTechnology::m_scriptsLoaded; CTechnology::CTechnology( const CStrW& name, CPlayer* player ) : m_Name(name), m_player(player) { m_researched = false; m_excluded = false; m_inProgress = false; } bool CTechnology::LoadXml( const VfsPath& pathname ) { CXeromyces XeroFile; if (XeroFile.Load(pathname) != PSRETURN_OK) return false; #define EL(x) int el_##x = XeroFile.GetElementID(#x) EL(tech); EL(id); EL(req); EL(effect); #undef EL XMBElement Root = XeroFile.GetRoot(); if ( Root.GetNodeName() != el_tech ) { LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology: XML root was not \"Tech\" in file %ls. Load failed.", pathname.string().c_str() ); return false; } XMBElementList RootChildren = Root.GetChildNodes(); bool ret; for ( int i=0; im_inProgress ) return false; } return ( HasReqEntities() && HasReqTechs() ); } bool CTechnology::HasReqEntities() { // Check whether we have ALL the required entities. std::vector entities; // m_player->GetControlledEntities(entities); for ( std::vector::iterator it=m_ReqEntities.begin(); it != m_ReqEntities.end(); it++ ) { // For each required class, check that we have it bool got = false; for( CEntityList::iterator it2=entities.begin(); it2 != entities.end(); it2++ ) { if ( (*it2)->m_classes.IsMember(*it) ) { got = true; break; } } if( !got ) return false; } return true; } bool CTechnology::HasReqTechs() { // Check whether we have ANY of the required techs (this is slightly confusing but required for // the way the tech system is currently planned; ideally we'd have an or in the XML). if ( m_ReqTechs.size() == 0 ) return true; for ( std::vector::iterator it=m_ReqTechs.begin(); it != m_ReqTechs.end(); it++ ) { if ( g_TechnologyCollection.GetTechnology( (CStrW)*it, m_player )->IsResearched() ) { return true; } } return false; } void CTechnology::Apply( CEntity* entity ) { // Find out if the unit has one of our target classes bool ok = false; for ( std::vector::iterator it = m_Targets.begin(); it != m_Targets.end(); it++ ) { if ( entity->m_classes.IsMember( *it ) ) { ok = true; break; } } if( !ok ) return; // Apply modifiers for ( std::vector::iterator mod=m_Modifiers.begin(); mod!=m_Modifiers.end(); mod++ ) { jsval oldVal; if( entity->GetProperty( g_ScriptingHost.getContext(), mod->attribute, &oldVal ) ) { float modValue; if( mod->isPercent ) { jsval baseVal; entity->m_base->m_unmodified->GetProperty( g_ScriptingHost.getContext(), mod->attribute, &baseVal ); modValue = ToPrimitive(baseVal) * mod->value / 100.0f; } else { modValue = mod->value; } jsval newVal = ToJSVal( ToPrimitive(oldVal) + modValue ); entity->SetProperty( g_ScriptingHost.GetContext(), mod->attribute, &newVal ); } } // Apply sets for ( std::vector::iterator mod=m_Sets.begin(); mod!=m_Sets.end(); mod++ ) { jsval newVal = ToJSVal( mod->value ); entity->SetProperty( g_ScriptingHost.GetContext(), mod->attribute, &newVal ); } } //JS stuff void CTechnology::ScriptingInit() { AddClassProperty(L"name", &CTechnology::m_Name, true); AddClassProperty(L"player", &CTechnology::m_player, true); AddClassProperty(L"generic", &CTechnology::m_Generic, true); AddClassProperty(L"specific", &CTechnology::m_Specific, true); AddClassProperty(L"icon", &CTechnology::m_Icon); //GUI might want to change this...? AddClassProperty(L"icon_cell", &CTechnology::m_IconCell); AddClassProperty(L"classes", &CTechnology::m_Classes, true); AddClassProperty(L"history", &CTechnology::m_History, true); AddClassProperty(L"time", &CTechnology::m_ReqTime); //Techs may upgrade research time and cost of other techs AddClassProperty(L"in_progress", &CTechnology::m_inProgress); AddMethod( "applyEffects", 2 ); AddMethod( "isExcluded", 0 ); AddMethod( "isValid", 0 ); AddMethod( "isResearched", 0 ); AddMethod( "getPlayerID", 0 ); CJSComplex::ScriptingInit("Technology"); } bool CTechnology::ApplyEffects( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { // Unmark ourselves as in progress m_inProgress = false; if ( !IsTechValid() ) { return false; } // Disable any paired techs for ( std::vector::iterator it=m_Pairs.begin(); it != m_Pairs.end(); it++ ) g_TechnologyCollection.GetTechnology(*it, m_player)->SetExclusion(true); // Disable ourselves so we can't be researched twice m_excluded = true; // Mark ourselves as researched m_researched = true; // Apply effects to all entities std::vector entities; // m_player->GetControlledEntities(entities); for ( size_t i=0; iGetScript(), &rval, 1, &arg ); } // Add ourselves to player's researched techs m_player->AddActiveTech( this ); return true; } bool CTechnology::IsValid( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { return IsTechValid(); } bool CTechnology::IsExcluded( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { return m_excluded; } bool CTechnology::IsResearched( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { return IsResearched(); } size_t CTechnology::GetPlayerID( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { return m_player->GetPlayerID(); }