/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of Penumbra Overture.
*
* Penumbra Overture 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 3 of the License, or
* (at your option) any later version.
*
* Penumbra Overture 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 Penumbra Overture. If not, see .
*/
#include "StdAfx.h"
#include "GameLamp.h"
#include "Init.h"
#include "MapHandler.h"
#include "Player.h"
#include "EffectHandler.h"
#include "Inventory.h"
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// LOADER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cEntityLoader_GameLamp::cEntityLoader_GameLamp(const tString &asName, cInit *apInit)
: cEntityLoader_Object(asName)
{
mpInit = apInit;
}
cEntityLoader_GameLamp::~cEntityLoader_GameLamp()
{
}
//-----------------------------------------------------------------------
void cEntityLoader_GameLamp::BeforeLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
cWorld3D *apWorld)
{
}
//-----------------------------------------------------------------------
void cEntityLoader_GameLamp::AfterLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
cWorld3D *apWorld)
{
cGameLamp *pObject = hplNew( cGameLamp, (mpInit,mpEntity->GetName()) );
pObject->msFileName = msFileName;
pObject->m_mtxOnLoadTransform = a_mtxTransform;
// Set the engine objects to the object
pObject->SetBodies(mvBodies);
pObject->SetMeshEntity(mpEntity);
pObject->SetLights(mvLights);
pObject->SetParticleSystems(mvParticleSystems);
pObject->SetBillboards(mvBillboards);
pObject->SetSoundEntities(mvSoundEntities);
pObject->SetHapticShapes(mvHapticShapes);
///////////////////////////////////
// Load game properties
TiXmlElement *pGameElem = apRootElem->FirstChildElement("GAME");
if(pGameElem)
{
pObject->mfTurnOnTime = cString::ToFloat(pGameElem->Attribute("TurnOnTime"),0);
pObject->mfTurnOffTime = cString::ToFloat(pGameElem->Attribute("TurnOffTime"),0);
if(mpInit->mbHasHaptics==false)
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("InteractDist"),1.8f);
pObject->mbInteractOff = cString::ToBool(pGameElem->Attribute("InteractOff"),true);
pObject->mbInteractOn = cString::ToBool(pGameElem->Attribute("InteractOn"),true);
pObject->msTurnOnSound = cString::ToString(pGameElem->Attribute("TurnOnSound"),"");
pObject->msTurnOffSound = cString::ToString(pGameElem->Attribute("TurnOffSound"),"");
pObject->msOnItem = cString::ToString(pGameElem->Attribute("OnItem"),"");
pObject->msOffItem = cString::ToString(pGameElem->Attribute("OffItem"),"");
pObject->msOffMaterialName = cString::ToString(pGameElem->Attribute("OffMaterial"),"");
pObject->msOffSubMesh = cString::ToString(pGameElem->Attribute("OffSubMesh"),"");
////////////////////////////////////
//Flickering
pObject->mbFlickering = cString::ToBool(pGameElem->Attribute("Flickering"),false);
pObject->msFlickerOffSound = cString::ToString(pGameElem->Attribute("FlickerOffSound"),"");
pObject->msFlickerOnSound = cString::ToString(pGameElem->Attribute("FlickerOnSound"),"");
pObject->msFlickerOffPS = cString::ToString(pGameElem->Attribute("FlickerOffPS"),"");
pObject->msFlickerOnPS = cString::ToString(pGameElem->Attribute("FlickerOnPS"),"");
pObject->mfFlickerOnMinLength = cString::ToFloat(pGameElem->Attribute("FlickerOnMinLength"),0);
pObject->mfFlickerOffMinLength = cString::ToFloat(pGameElem->Attribute("FlickerOffMinLength"),0);
pObject->mfFlickerOnMaxLength = cString::ToFloat(pGameElem->Attribute("FlickerOnMaxLength"),0);
pObject->mfFlickerOffMaxLength = cString::ToFloat(pGameElem->Attribute("FlickerOffMaxLength"),0);
pObject->mFlickerOffColor = cString::ToColor(pGameElem->Attribute("FlickerOffColor"),cColor(0,0));
pObject->mfFlickerOffRadius = cString::ToFloat(pGameElem->Attribute("FlickerOffRadius"),0);
pObject->mbFlickerFade = cString::ToBool(pGameElem->Attribute("FlickerFade"),0);
pObject->mfFlickerOnFadeLength = cString::ToFloat(pGameElem->Attribute("FlickerOnFadeLength"),0);
pObject->mfFlickerOffFadeLength = cString::ToFloat(pGameElem->Attribute("FlickerOffFadeLength"),0);
}
else
{
Error("Couldn't find game element for entity '%s'\n",mpEntity->GetName().c_str());
}
///////////////////////////////////
// Add a the object as user data to the body, to get the obejct later on.
for(size_t i=0; iSetUserData((void*)pObject);
}
}
pObject->Init();
/////////////////////////////////
// Add to map handler
mpInit->mpMapHandler->AddGameEntity(pObject);
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cGameLamp::cGameLamp(cInit *apInit,const tString& asName) : iGameEntity(apInit,asName)
{
mType = eGameEntityType_Lamp;
mbHasInteraction = true;
mbLit = true;
mfMaxInteractDist = 2.1f;
mfAlpha = 1;
mfTurnOnTime = 2;
mfTurnOffTime = 1;
mbInteractOff = true;
mbInteractOn = true;
msTurnOnSound = "";
msTurnOffSound = "";
msOnItem = "";
msOffItem = "";
mpOffMaterial = NULL;
mpOnMaterial = NULL;
mpSubMesh = NULL;
mbSaveLights = false;
}
//-----------------------------------------------------------------------
cGameLamp::~cGameLamp(void)
{
mpSubMesh->SetCustomMaterial(NULL,false);
if(mpOffMaterial) mpInit->mpGame->GetResources()->GetMaterialManager()->Destroy(mpOffMaterial);
if(mpOnMaterial) mpInit->mpGame->GetResources()->GetMaterialManager()->Destroy(mpOnMaterial);
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cGameLamp::OnPlayerPick()
{
float fPickedDist = mpInit->mpPlayer->GetPickedDist();
if( fPickedDist < mfMaxInteractDist)
{
if((mbLit && mbInteractOff && msOffItem=="") || (!mbLit && mbInteractOn && msOnItem==""))
{
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_Active);
}
else if(msDescription == _W(""))
{
if(mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_Inactive);
else
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_None);
}
}
else if(fPickedDist > mfMaxExamineDist || msDescription == _W(""))
{
if(mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_Inactive);
else
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_None);
}
}
//-----------------------------------------------------------------------
void cGameLamp::OnPlayerInteract()
{
if(mpInit->mpPlayer->GetPickedDist() < mfMaxInteractDist)
{
bool bInteracted=false;
if(mbLit && mbInteractOff && msOffItem==""){
SetLit(false,true);
bInteracted = true;
}
else if(!mbLit && mbInteractOn && msOnItem==""){
SetLit(true,true);
bInteracted = true;
}
//If no interaction, use grab mode
if( mpInit->mpPlayer->GetPickedBody()->GetMass() != 0 &&
bInteracted==false && ((mbLit && mbInteractOff) || (!mbLit && mbInteractOn)))
{
if(mpInit->mbHasHaptics && mpInit->mpPlayer->mbProxyTouching==false) return;
mpInit->mpPlayer->mbPickAtPoint = false;
mpInit->mpPlayer->mbRotateWithPlayer = true;
mpInit->mpPlayer->mbUseNormalMass = false;
mpInit->mpPlayer->mfGrabMassMul = (float)mvBodies.size();
mpInit->mpPlayer->mfCurrentMaxInteractDist = mfMaxInteractDist;
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
mpInit->mpPlayer->ChangeState(ePlayerState_Grab);
}
}
}
//-----------------------------------------------------------------------
bool cGameLamp::OnUseItem(cInventoryItem *apItem)
{
if(mbLit && mbInteractOff && msOffItem==apItem->GetName())
{
SetLit(false,true);
return true;
}
else if(!mbLit && mbInteractOn && msOnItem==apItem->GetName())
{
SetLit(true,true);
return true;
}
return false;
}
//-----------------------------------------------------------------------
void cGameLamp::Update(float afTimeStep)
{
////////////////////////////////
//Update alpha
if(mbFlickering && mbLit)
{
for(size_t i=0; iGetFlickerActive()==false &&
mvLights[i]->IsFading()==false)
{
mvLights[i]->SetFlickerActive(true);
SetUpFlicker((int)i);
}
}
}
////////////////////////////////
//Check if material should be off because of flickering
if(mbLit && mbFlickering && mpSubMesh && mpOffMaterial)
{
bool bHasLight = true;
for(size_t i=0; iGetDiffuseColor() == cColor(0,0,0,0) ||
pLight->GetFarAttenuation() <=0)
{
bHasLight = false;
break;
}
}
if(bHasLight)
{
if(mpOnMaterial)
mpSubMesh->SetCustomMaterial(mpOnMaterial,false);
else
mpSubMesh->SetCustomMaterial(NULL,false);
}
else
{
mpSubMesh->SetCustomMaterial(mpOffMaterial,false);
}
}
////////////////////////////////
//Update alpha
bool bChanged = false;
if(mbLit && mfAlpha < 1)
{
mfAlpha += (1/mfTurnOnTime)*afTimeStep;
if(mfAlpha > 1.0f) mfAlpha = 1.0f;
bChanged = true;
}
else if(mbLit==false && mfAlpha>0)
{
mfAlpha -= (1/mfTurnOffTime)*afTimeStep;
if(mfAlpha < 0) mfAlpha = 0;
bChanged = true;
}
////////////////////////////////
//Update billboards
if(bChanged)
{
for(size_t i=0; iSetColor(mvBBColors[i] * mfAlpha);
if(mfAlpha ==0)
mvBillboards[i]->SetVisible(false);
else if(mvBillboards[i]->IsVisible()==false)
mvBillboards[i]->SetVisible(true);
}
}
}
//-----------------------------------------------------------------------
void cGameLamp::SetLit(bool abX, bool abFade)
{
if(mbLit == abX) return;
mbLit = abX;
cWorld3D *pWorld= mpInit->mpGame->GetScene()->GetWorld3D();
//////////////////////////////////////////////
//Turn On
if(mbLit)
{
for(size_t i=0; iSetVisible(true);
if(abFade)
{
mvLights[i]->SetFlickerActive(false);
mvLights[i]->FadeTo(mvLightColors[i],mvLights[i]->GetFarAttenuation(),mfTurnOnTime);
}
else
{
mvLights[i]->SetDiffuseColor(mvLightColors[i]);
mvLights[i]->SetFlickerActive(mbFlickering);
}
}
for(size_t i=0; iCreateParticleSystem(
mvParticleSystemNames[i].msName,
mvParticleSystemNames[i].msDataName,
1,mvParticleSystemNames[i].m_mtxTransform);
mpMeshEntity->AddChild(mvParticleSystems[i]);
/*Log("Creating ps %s at pos (%s) meshpos: (%s)\n",
mvParticleSystems[i]->GetName().c_str(),
mvParticleSystems[i]->GetWorldPosition().ToString().c_str(),
mpMeshEntity->GetWorldPosition().ToString().c_str());*/
mvParticleSystems[i]->SetTransformUpdated(true);
if(!abFade){
for(int j=0; j<3*60;++j)
mvParticleSystems[i]->UpdateLogic(1.0f/60.0f);
}
}
for(size_t i=0; iSetVisible(true);
}
for(size_t i=0; iPlay(true);
else
mvSoundEntities[i]->Play(false);
}
if(mpSubMesh && mpOffMaterial)
{
if(mpOnMaterial)
mpSubMesh->SetCustomMaterial(mpOnMaterial,false);
else
mpSubMesh->SetCustomMaterial(NULL,false);
}
if(!abFade) mfAlpha = 1;
if(msTurnOnSound!="" && abFade)
{
cSoundEntity *pSound = pWorld->CreateSoundEntity("LampOn",msTurnOnSound,true);
if(pSound) pSound->SetPosition(mpMeshEntity->GetBoundingVolume()->GetWorldCenter());
}
}
//////////////////////////////////////////////
//Turn Off
else
{
for(size_t i=0; iSetFlickerActive(false);
if(abFade)
mvLights[i]->FadeTo(cColor(0,0),mvLights[i]->GetFarAttenuation(),mfTurnOffTime);
else
{
mvLights[i]->SetVisible(false);
mvLights[i]->SetDiffuseColor(cColor(0,0));
}
}
for(size_t i=0; iKill();
else
mvParticleSystems[i]->KillInstantly();
mvParticleSystems[i] = NULL;
}
for(size_t i=0; iSetVisible(false);
}
for(size_t i=0; iStop(true);
else
mvSoundEntities[i]->Stop(false);
}
if(mpSubMesh && mpOffMaterial) mpSubMesh->SetCustomMaterial(mpOffMaterial,false);
if(!abFade)mfAlpha = 0;
if(msTurnOffSound!="" && abFade)
{
cSoundEntity *pSound = pWorld->CreateSoundEntity("LampOff",msTurnOffSound,true);
if(pSound) pSound->SetPosition(mpMeshEntity->GetBoundingVolume()->GetWorldCenter());
}
}
if(msLitChangeCallback != "")
{
tString sBool = mbLit ? "true" : "false";
tString sCommand = msLitChangeCallback + "("+sBool+")";
mpInit->RunScriptCommand(sCommand);
}
}
//-----------------------------------------------------------------------
void cGameLamp::SetFlicker(bool abX)
{
mbFlickering = abX;
for(size_t i=0; iSetFlickerActive(mbFlickering);
SetUpFlicker((int)i);
}
}
//-----------------------------------------------------------------------
void cGameLamp::Init()
{
//////////////////////
//Set up lights
for(size_t i=0; iGetDiffuseColor());
//Log("Setting lamp %s color to: %s\n",msName.c_str(),mvLights[i]->GetDiffuseColor().ToString().c_str());
mvLights[i]->SetFlickerActive(mbFlickering);
if(mbFlickering)
{
SetUpFlicker((int)i);
}
}
//////////////////////
//Billboards
for(size_t i=0; iGetColor());
}
//////////////////////
//Set up Particle Systems
mvParticleSystemNames.resize(mvParticleSystems.size());
for(size_t i=0; iGetName();
mvParticleSystemNames[i].msDataName = mvParticleSystems[i]->GetDataName();
mvParticleSystemNames[i].m_mtxTransform = mvParticleSystems[i]->GetLocalMatrix();
}
//////////////////////
//Set up Materials
if(msOffSubMesh == "" || mpMeshEntity->GetSubMeshEntityNum()==1)
{
mpSubMesh = mpMeshEntity->GetSubMeshEntity(0);
}
else
{
mpSubMesh = mpMeshEntity->GetSubMeshEntityName(msOffSubMesh);
}
if(mpSubMesh)
{
mpOffMaterial = mpInit->mpGame->GetResources()->GetMaterialManager()->CreateMaterial(msOffMaterialName);
if(mpOffMaterial==NULL){
Warning("Could not load material '%s'\n",msOffMaterialName.c_str());
return;
}
mpOnMaterial = mpSubMesh->GetCustomMaterial();
}
else
{
Warning("Couldn't find sub mesh '%s' for lamp\n",msOffSubMesh.c_str());
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cGameLamp::SetUpFlicker(int alIdx)
{
mvLights[alIdx]->SetFlicker( mFlickerOffColor, mfFlickerOffRadius,
mfFlickerOnMinLength, mfFlickerOnMaxLength,
msFlickerOnSound,msFlickerOnPS,
mfFlickerOffMinLength, mfFlickerOffMaxLength,
msFlickerOffSound,msFlickerOffPS,
mbFlickerFade, mfFlickerOnFadeLength, mfFlickerOffFadeLength);
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SAVE OBJECT STUFF
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
kBeginSerialize(cGameLamp_SaveData,iGameEntity_SaveData)
kSerializeVar(mbLit, eSerializeType_Bool)
kSerializeVar(mbFlickering, eSerializeType_Bool)
kSerializeVar(msLitChangeCallback, eSerializeType_String)
kEndSerialize()
//-----------------------------------------------------------------------
iGameEntity* cGameLamp_SaveData::CreateEntity()
{
return NULL;
}
//-----------------------------------------------------------------------
iGameEntity_SaveData* cGameLamp::CreateSaveData()
{
return hplNew( cGameLamp_SaveData, () );
}
//-----------------------------------------------------------------------
void cGameLamp::SaveToSaveData(iGameEntity_SaveData *apSaveData)
{
__super::SaveToSaveData(apSaveData);
cGameLamp_SaveData *pData = static_cast(apSaveData);
kCopyToVar(pData,mbLit);
kCopyToVar(pData,msLitChangeCallback);
kCopyToVar(pData,mbFlickering);
}
//-----------------------------------------------------------------------
void cGameLamp::LoadFromSaveData(iGameEntity_SaveData *apSaveData)
{
__super::LoadFromSaveData(apSaveData);
cGameLamp_SaveData *pData = static_cast(apSaveData);
kCopyFromVar(pData,msLitChangeCallback);
SetLit(pData->mbLit,false);
SetFlicker(pData->mbFlickering);
}
//-----------------------------------------------------------------------