/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program 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. * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* * This file is based on WME Lite. * http://dead-code.org/redir.php?target=wmelite * Copyright (c) 2011 Jan Nedoma */ #include "dcgf.h" #include "engines/wintermute/AdGame.h" #include "engines/wintermute/AdItem.h" #include "engines/wintermute/AdObject.h" #include "engines/wintermute/AdInventory.h" #include "engines/wintermute/AdLayer.h" #include "engines/wintermute/AdScene.h" #include "engines/wintermute/AdSceneNode.h" #include "engines/wintermute/AdSentence.h" #include "engines/wintermute/AdWaypointGroup.h" #include "engines/wintermute/BGame.h" #include "engines/wintermute/BFrame.h" #include "engines/wintermute/BSound.h" #include "engines/wintermute/BSurfaceStorage.h" #include "engines/wintermute/BSubFrame.h" #include "engines/wintermute/BFont.h" #include "engines/wintermute/BFontStorage.h" #include "engines/wintermute/BSprite.h" #include "engines/wintermute/BStringTable.h" #include "engines/wintermute/scriptables/ScEngine.h" #include "engines/wintermute/scriptables/ScScript.h" #include "engines/wintermute/scriptables/ScStack.h" #include "engines/wintermute/scriptables/ScValue.h" #include "common/str.h" #include "common/util.h" namespace WinterMute { IMPLEMENT_PERSISTENT(CAdObject, false) ////////////////////////////////////////////////////////////////////////// CAdObject::CAdObject(CBGame *inGame): CBObject(inGame) { m_Type = OBJECT_NONE; m_State = m_NextState = STATE_NONE; m_Active = true; m_Drawn = false; m_CurrentSprite = NULL; m_AnimSprite = NULL; m_TempSprite2 = NULL; m_Font = NULL; m_Sentence = NULL; m_ForcedTalkAnimName = NULL; m_ForcedTalkAnimUsed = false; m_BlockRegion = NULL; m_WptGroup = NULL; m_CurrentBlockRegion = NULL; m_CurrentWptGroup = NULL; m_IgnoreItems = false; m_SceneIndependent = false; m_StickRegion = NULL; m_SubtitlesModRelative = true; m_SubtitlesModX = 0; m_SubtitlesModY = 0; m_SubtitlesWidth = 0; m_SubtitlesModXCenter = true; m_Inventory = NULL; for (int i = 0; i < MAX_NUM_REGIONS; i++) m_CurrentRegions[i] = NULL; m_PartEmitter = NULL; m_PartFollowParent = false; m_PartOffsetX = m_PartOffsetY = 0; m_RegisterAlias = this; } ////////////////////////////////////////////////////////////////////////// CAdObject::~CAdObject() { m_CurrentSprite = NULL; // reference only, don't delete SAFE_DELETE(m_AnimSprite); SAFE_DELETE(m_Sentence); SAFE_DELETE_ARRAY(m_ForcedTalkAnimName); SAFE_DELETE(m_BlockRegion); SAFE_DELETE(m_WptGroup); SAFE_DELETE(m_CurrentBlockRegion); SAFE_DELETE(m_CurrentWptGroup); m_TempSprite2 = NULL; // reference only m_StickRegion = NULL; if (m_Font) Game->m_FontStorage->RemoveFont(m_Font); if (m_Inventory) { ((CAdGame *)Game)->UnregisterInventory(m_Inventory); m_Inventory = NULL; } if (m_PartEmitter) Game->UnregisterObject(m_PartEmitter); for (int i = 0; i < m_AttachmentsPre.GetSize(); i++) { Game->UnregisterObject(m_AttachmentsPre[i]); } m_AttachmentsPre.RemoveAll(); for (int i = 0; i < m_AttachmentsPost.GetSize(); i++) { Game->UnregisterObject(m_AttachmentsPost[i]); } m_AttachmentsPost.RemoveAll(); } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::PlayAnim(char *Filename) { SAFE_DELETE(m_AnimSprite); m_AnimSprite = new CBSprite(Game, this); if (!m_AnimSprite) { Game->LOG(0, "CAdObject::PlayAnim: error creating temp sprite (object:\"%s\" sprite:\"%s\")", m_Name, Filename); return E_FAIL; } HRESULT res = m_AnimSprite->LoadFile(Filename); if (FAILED(res)) { Game->LOG(res, "CAdObject::PlayAnim: error loading temp sprite (object:\"%s\" sprite:\"%s\")", m_Name, Filename); SAFE_DELETE(m_AnimSprite); return res; } m_State = STATE_PLAYING_ANIM; return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::Display() { return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::Update() { return S_OK; } ////////////////////////////////////////////////////////////////////////// // high level scripting interface ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name) { ////////////////////////////////////////////////////////////////////////// // PlayAnim / PlayAnimAsync ////////////////////////////////////////////////////////////////////////// if (strcmp(Name, "PlayAnim") == 0 || strcmp(Name, "PlayAnimAsync") == 0) { Stack->CorrectParams(1); if (FAILED(PlayAnim(Stack->Pop()->GetString()))) Stack->PushBool(false); else { if (strcmp(Name, "PlayAnimAsync") != 0) Script->WaitFor(this); Stack->PushBool(true); } return S_OK; } ////////////////////////////////////////////////////////////////////////// // Reset ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "Reset") == 0) { Stack->CorrectParams(0); Reset(); Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // IsTalking ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "IsTalking") == 0) { Stack->CorrectParams(0); Stack->PushBool(m_State == STATE_TALKING); return S_OK; } ////////////////////////////////////////////////////////////////////////// // StopTalk / StopTalking ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "StopTalk") == 0 || strcmp(Name, "StopTalking") == 0) { Stack->CorrectParams(0); if (m_Sentence) m_Sentence->Finish(); if (m_State == STATE_TALKING) { m_State = m_NextState; m_NextState = STATE_READY; Stack->PushBool(true); } else Stack->PushBool(false); return S_OK; } ////////////////////////////////////////////////////////////////////////// // ForceTalkAnim ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "ForceTalkAnim") == 0) { Stack->CorrectParams(1); char *AnimName = Stack->Pop()->GetString(); SAFE_DELETE_ARRAY(m_ForcedTalkAnimName); m_ForcedTalkAnimName = new char[strlen(AnimName) + 1]; strcpy(m_ForcedTalkAnimName, AnimName); m_ForcedTalkAnimUsed = false; Stack->PushBool(true); return S_OK; } ////////////////////////////////////////////////////////////////////////// // Talk / TalkAsync ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "Talk") == 0 || strcmp(Name, "TalkAsync") == 0) { Stack->CorrectParams(5); char *Text = Stack->Pop()->GetString(); CScValue *SoundVal = Stack->Pop(); int Duration = Stack->Pop()->GetInt(); CScValue *ValStances = Stack->Pop(); char *Stances = ValStances->IsNULL() ? NULL : ValStances->GetString(); int Align; CScValue *val = Stack->Pop(); if (val->IsNULL()) Align = TAL_CENTER; else Align = val->GetInt(); Align = MIN(MAX(0, Align), NUM_TEXT_ALIGN - 1); char *Sound = SoundVal->IsNULL() ? NULL : SoundVal->GetString(); Talk(Text, Sound, Duration, Stances, (TTextAlign)Align); if (strcmp(Name, "TalkAsync") != 0) Script->WaitForExclusive(this); Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // StickToRegion ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "StickToRegion") == 0) { Stack->CorrectParams(1); CAdLayer *Main = ((CAdGame *)Game)->m_Scene->m_MainLayer; bool RegFound = false; int i; CScValue *Val = Stack->Pop(); if (Val->IsNULL() || !Main) { m_StickRegion = NULL; RegFound = true; } else if (Val->IsString()) { char *RegionName = Val->GetString(); for (i = 0; i < Main->m_Nodes.GetSize(); i++) { if (Main->m_Nodes[i]->m_Type == OBJECT_REGION && Main->m_Nodes[i]->m_Region->m_Name && scumm_stricmp(Main->m_Nodes[i]->m_Region->m_Name, RegionName) == 0) { m_StickRegion = Main->m_Nodes[i]->m_Region; RegFound = true; break; } } } else if (Val->IsNative()) { CBScriptable *Obj = Val->GetNative(); for (i = 0; i < Main->m_Nodes.GetSize(); i++) { if (Main->m_Nodes[i]->m_Type == OBJECT_REGION && Main->m_Nodes[i]->m_Region == Obj) { m_StickRegion = Main->m_Nodes[i]->m_Region; RegFound = true; break; } } } if (!RegFound) m_StickRegion = NULL; Stack->PushBool(RegFound); return S_OK; } ////////////////////////////////////////////////////////////////////////// // SetFont ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SetFont") == 0) { Stack->CorrectParams(1); CScValue *Val = Stack->Pop(); if (Val->IsNULL()) SetFont(NULL); else SetFont(Val->GetString()); Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // GetFont ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "GetFont") == 0) { Stack->CorrectParams(0); if (m_Font && m_Font->m_Filename) Stack->PushString(m_Font->m_Filename); else Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // TakeItem ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "TakeItem") == 0) { Stack->CorrectParams(2); if (!m_Inventory) { m_Inventory = new CAdInventory(Game); ((CAdGame *)Game)->RegisterInventory(m_Inventory); } CScValue *val = Stack->Pop(); if (!val->IsNULL()) { char *ItemName = val->GetString(); val = Stack->Pop(); char *InsertAfter = val->IsNULL() ? NULL : val->GetString(); if (FAILED(m_Inventory->InsertItem(ItemName, InsertAfter))) Script->RuntimeError("Cannot add item '%s' to inventory", ItemName); else { // hide associated entities ((CAdGame *)Game)->m_Scene->HandleItemAssociations(ItemName, false); } } else Script->RuntimeError("TakeItem: item name expected"); Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // DropItem ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "DropItem") == 0) { Stack->CorrectParams(1); if (!m_Inventory) { m_Inventory = new CAdInventory(Game); ((CAdGame *)Game)->RegisterInventory(m_Inventory); } CScValue *val = Stack->Pop(); if (!val->IsNULL()) { if (FAILED(m_Inventory->RemoveItem(val->GetString()))) Script->RuntimeError("Cannot remove item '%s' from inventory", val->GetString()); else { // show associated entities ((CAdGame *)Game)->m_Scene->HandleItemAssociations(val->GetString(), true); } } else Script->RuntimeError("DropItem: item name expected"); Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // GetItem ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "GetItem") == 0) { Stack->CorrectParams(1); if (!m_Inventory) { m_Inventory = new CAdInventory(Game); ((CAdGame *)Game)->RegisterInventory(m_Inventory); } CScValue *val = Stack->Pop(); if (val->m_Type == VAL_STRING) { CAdItem *item = ((CAdGame *)Game)->GetItemByName(val->GetString()); if (item) Stack->PushNative(item, true); else Stack->PushNULL(); } else if (val->IsNULL() || val->GetInt() < 0 || val->GetInt() >= m_Inventory->m_TakenItems.GetSize()) Stack->PushNULL(); else Stack->PushNative(m_Inventory->m_TakenItems[val->GetInt()], true); return S_OK; } ////////////////////////////////////////////////////////////////////////// // HasItem ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "HasItem") == 0) { Stack->CorrectParams(1); if (!m_Inventory) { m_Inventory = new CAdInventory(Game); ((CAdGame *)Game)->RegisterInventory(m_Inventory); } CScValue *val = Stack->Pop(); if (!val->IsNULL()) { for (int i = 0; i < m_Inventory->m_TakenItems.GetSize(); i++) { if (val->GetNative() == m_Inventory->m_TakenItems[i]) { Stack->PushBool(true); return S_OK; } else if (scumm_stricmp(val->GetString(), m_Inventory->m_TakenItems[i]->m_Name) == 0) { Stack->PushBool(true); return S_OK; } } } else Script->RuntimeError("HasItem: item name expected"); Stack->PushBool(false); return S_OK; } ////////////////////////////////////////////////////////////////////////// // CreateParticleEmitter ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "CreateParticleEmitter") == 0) { Stack->CorrectParams(3); bool FollowParent = Stack->Pop()->GetBool(); int OffsetX = Stack->Pop()->GetInt(); int OffsetY = Stack->Pop()->GetInt(); CPartEmitter *Emitter = CreateParticleEmitter(FollowParent, OffsetX, OffsetY); if (Emitter) Stack->PushNative(m_PartEmitter, true); else Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // DeleteParticleEmitter ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "DeleteParticleEmitter") == 0) { Stack->CorrectParams(0); if (m_PartEmitter) { Game->UnregisterObject(m_PartEmitter); m_PartEmitter = NULL; } Stack->PushNULL(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // AddAttachment ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "AddAttachment") == 0) { Stack->CorrectParams(4); char *Filename = Stack->Pop()->GetString(); bool PreDisplay = Stack->Pop()->GetBool(true); int OffsetX = Stack->Pop()->GetInt(); int OffsetY = Stack->Pop()->GetInt(); HRESULT res; CAdEntity *Ent = new CAdEntity(Game); if (FAILED(res = Ent->LoadFile(Filename))) { SAFE_DELETE(Ent); Script->RuntimeError("AddAttachment() failed loading entity '%s'", Filename); Stack->PushBool(false); } else { Game->RegisterObject(Ent); Ent->m_PosX = OffsetX; Ent->m_PosY = OffsetY; Ent->m_Active = true; if (PreDisplay) m_AttachmentsPre.Add(Ent); else m_AttachmentsPost.Add(Ent); Stack->PushBool(true); } return S_OK; } ////////////////////////////////////////////////////////////////////////// // RemoveAttachment ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "RemoveAttachment") == 0) { Stack->CorrectParams(1); CScValue *Val = Stack->Pop(); bool Found = false; if (Val->IsNative()) { CBScriptable *Obj = Val->GetNative(); for (int i = 0; i < m_AttachmentsPre.GetSize(); i++) { if (m_AttachmentsPre[i] == Obj) { Found = true; Game->UnregisterObject(m_AttachmentsPre[i]); m_AttachmentsPre.RemoveAt(i); i--; } } for (int i = 0; i < m_AttachmentsPost.GetSize(); i++) { if (m_AttachmentsPost[i] == Obj) { Found = true; Game->UnregisterObject(m_AttachmentsPost[i]); m_AttachmentsPost.RemoveAt(i); i--; } } } else { char *Name = Val->GetString(); for (int i = 0; i < m_AttachmentsPre.GetSize(); i++) { if (m_AttachmentsPre[i]->m_Name && scumm_stricmp(m_AttachmentsPre[i]->m_Name, Name) == 0) { Found = true; Game->UnregisterObject(m_AttachmentsPre[i]); m_AttachmentsPre.RemoveAt(i); i--; } } for (int i = 0; i < m_AttachmentsPost.GetSize(); i++) { if (m_AttachmentsPost[i]->m_Name && scumm_stricmp(m_AttachmentsPost[i]->m_Name, Name) == 0) { Found = true; Game->UnregisterObject(m_AttachmentsPost[i]); m_AttachmentsPost.RemoveAt(i); i--; } } } Stack->PushBool(Found); return S_OK; } ////////////////////////////////////////////////////////////////////////// // GetAttachment ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "GetAttachment") == 0) { Stack->CorrectParams(1); CScValue *Val = Stack->Pop(); CAdObject *Ret = NULL; if (Val->IsInt()) { int Index = Val->GetInt(); int CurrIndex = 0; for (int i = 0; i < m_AttachmentsPre.GetSize(); i++) { if (CurrIndex == Index) Ret = m_AttachmentsPre[i]; CurrIndex++; } for (int i = 0; i < m_AttachmentsPost.GetSize(); i++) { if (CurrIndex == Index) Ret = m_AttachmentsPost[i]; CurrIndex++; } } else { char *Name = Val->GetString(); for (int i = 0; i < m_AttachmentsPre.GetSize(); i++) { if (m_AttachmentsPre[i]->m_Name && scumm_stricmp(m_AttachmentsPre[i]->m_Name, Name) == 0) { Ret = m_AttachmentsPre[i]; break; } } if (!Ret) { for (int i = 0; i < m_AttachmentsPost.GetSize(); i++) { if (m_AttachmentsPost[i]->m_Name && scumm_stricmp(m_AttachmentsPost[i]->m_Name, Name) == 0) { Ret = m_AttachmentsPre[i]; break; } } } } if (Ret != NULL) Stack->PushNative(Ret, true); else Stack->PushNULL(); return S_OK; } else return CBObject::ScCallMethod(Script, Stack, ThisStack, Name); } ////////////////////////////////////////////////////////////////////////// CScValue *CAdObject::ScGetProperty(char *Name) { m_ScValue->SetNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// if (strcmp(Name, "Type") == 0) { m_ScValue->SetString("object"); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // Active ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "Active") == 0) { m_ScValue->SetBool(m_Active); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // IgnoreItems ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "IgnoreItems") == 0) { m_ScValue->SetBool(m_IgnoreItems); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // SceneIndependent ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SceneIndependent") == 0) { m_ScValue->SetBool(m_SceneIndependent); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // SubtitlesWidth ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesWidth") == 0) { m_ScValue->SetInt(m_SubtitlesWidth); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosRelative ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosRelative") == 0) { m_ScValue->SetBool(m_SubtitlesModRelative); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosX ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosX") == 0) { m_ScValue->SetInt(m_SubtitlesModX); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosY ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosY") == 0) { m_ScValue->SetInt(m_SubtitlesModY); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosXCenter ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosXCenter") == 0) { m_ScValue->SetBool(m_SubtitlesModXCenter); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // NumItems (RO) ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "NumItems") == 0) { m_ScValue->SetInt(GetInventory()->m_TakenItems.GetSize()); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // ParticleEmitter (RO) ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "ParticleEmitter") == 0) { if (m_PartEmitter) m_ScValue->SetNative(m_PartEmitter, true); else m_ScValue->SetNULL(); return m_ScValue; } ////////////////////////////////////////////////////////////////////////// // NumAttachments (RO) ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "NumAttachments") == 0) { m_ScValue->SetInt(m_AttachmentsPre.GetSize() + m_AttachmentsPost.GetSize()); return m_ScValue; } else return CBObject::ScGetProperty(Name); } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::ScSetProperty(char *Name, CScValue *Value) { ////////////////////////////////////////////////////////////////////////// // Active ////////////////////////////////////////////////////////////////////////// if (strcmp(Name, "Active") == 0) { m_Active = Value->GetBool(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // IgnoreItems ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "IgnoreItems") == 0) { m_IgnoreItems = Value->GetBool(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // SceneIndependent ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SceneIndependent") == 0) { m_SceneIndependent = Value->GetBool(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // SubtitlesWidth ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesWidth") == 0) { m_SubtitlesWidth = Value->GetInt(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosRelative ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosRelative") == 0) { m_SubtitlesModRelative = Value->GetBool(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosX ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosX") == 0) { m_SubtitlesModX = Value->GetInt(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosY ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosY") == 0) { m_SubtitlesModY = Value->GetInt(); return S_OK; } ////////////////////////////////////////////////////////////////////////// // SubtitlesPosXCenter ////////////////////////////////////////////////////////////////////////// else if (strcmp(Name, "SubtitlesPosXCenter") == 0) { m_SubtitlesModXCenter = Value->GetBool(); return S_OK; } else return CBObject::ScSetProperty(Name, Value); } ////////////////////////////////////////////////////////////////////////// char *CAdObject::ScToString() { return "[ad object]"; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::SetFont(char *Filename) { if (m_Font) Game->m_FontStorage->RemoveFont(m_Font); if (Filename) { m_Font = Game->m_FontStorage->AddFont(Filename); return m_Font == NULL ? E_FAIL : S_OK; } else { m_Font = NULL; return S_OK; } } ////////////////////////////////////////////////////////////////////////// int CAdObject::GetHeight() { if (!m_CurrentSprite) return 0; else { CBFrame *frame = m_CurrentSprite->m_Frames[m_CurrentSprite->m_CurrentFrame]; int ret = 0; for (int i = 0; i < frame->m_Subframes.GetSize(); i++) { ret = MAX(ret, frame->m_Subframes[i]->m_HotspotY); } if (m_Zoomable) { float zoom = ((CAdGame *)Game)->m_Scene->GetZoomAt(m_PosX, m_PosY); ret = ret * zoom / 100; } return ret; } } ////////////////////////////////////////////////////////////////////////// void CAdObject::Talk(char *Text, char *Sound, uint32 Duration, char *Stances, TTextAlign Align) { if (!m_Sentence) m_Sentence = new CAdSentence(Game); if (!m_Sentence) return; if (m_ForcedTalkAnimName && m_ForcedTalkAnimUsed) { SAFE_DELETE_ARRAY(m_ForcedTalkAnimName); m_ForcedTalkAnimUsed = false; } SAFE_DELETE(m_Sentence->m_Sound); m_Sentence->SetText(Text); Game->m_StringTable->Expand(&m_Sentence->m_Text); m_Sentence->SetStances(Stances); m_Sentence->m_Duration = Duration; m_Sentence->m_Align = Align; m_Sentence->m_StartTime = Game->m_Timer; m_Sentence->m_CurrentStance = -1; m_Sentence->m_Font = m_Font == NULL ? Game->m_SystemFont : m_Font; m_Sentence->m_Freezable = m_Freezable; // try to locate speech file automatically bool DeleteSound = false; if (!Sound) { char *Key = Game->m_StringTable->GetKey(Text); if (Key) { Sound = ((CAdGame *)Game)->FindSpeechFile(Key); delete [] Key; if (Sound) DeleteSound = true; } } // load sound and set duration appropriately if (Sound) { CBSound *snd = new CBSound(Game); if (snd && SUCCEEDED(snd->SetSound(Sound, SOUND_SPEECH, true))) { m_Sentence->SetSound(snd); if (m_Sentence->m_Duration <= 0) { uint32 Length = snd->GetLength(); if (Length != 0) m_Sentence->m_Duration = Length; } } else delete snd; } // set duration by text length if (m_Sentence->m_Duration <= 0) {// TODO: Avoid longs. m_Sentence->m_Duration = MAX((unsigned long)1000, Game->m_SubtitlesSpeed * strlen(m_Sentence->m_Text)); } int x, y, width, height; x = m_PosX; y = m_PosY; if (!m_SceneIndependent && m_SubtitlesModRelative) { x -= ((CAdGame *)Game)->m_Scene->GetOffsetLeft(); y -= ((CAdGame *)Game)->m_Scene->GetOffsetTop(); } if (m_SubtitlesWidth > 0) width = m_SubtitlesWidth; else { if ((x < Game->m_Renderer->m_Width / 4 || x > Game->m_Renderer->m_Width * 0.75) && !Game->m_TouchInterface) { width = MAX(Game->m_Renderer->m_Width / 4, MIN(x * 2, (Game->m_Renderer->m_Width - x) * 2)); } else width = Game->m_Renderer->m_Width / 2; } height = m_Sentence->m_Font->GetTextHeight((byte *)m_Sentence->m_Text, width); y = y - height - GetHeight() - 5; if (m_SubtitlesModRelative) { x += m_SubtitlesModX; y += m_SubtitlesModY; } else { x = m_SubtitlesModX; y = m_SubtitlesModY; } if (m_SubtitlesModXCenter) x = x - width / 2; x = MIN(MAX(0, x), Game->m_Renderer->m_Width - width); y = MIN(MAX(0, y), Game->m_Renderer->m_Height - height); m_Sentence->m_Width = width; m_Sentence->m_Pos.x = x; m_Sentence->m_Pos.y = y; if (m_SubtitlesModRelative) { m_Sentence->m_Pos.x += ((CAdGame *)Game)->m_Scene->GetOffsetLeft(); m_Sentence->m_Pos.y += ((CAdGame *)Game)->m_Scene->GetOffsetTop(); } m_Sentence->m_FixedPos = !m_SubtitlesModRelative; m_Sentence->SetupTalkFile(Sound); m_State = STATE_TALKING; if (DeleteSound) delete [] Sound; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::Reset() { if (m_State == STATE_PLAYING_ANIM && m_AnimSprite != NULL) { SAFE_DELETE(m_AnimSprite); } else if (m_State == STATE_TALKING && m_Sentence) { m_Sentence->Finish(); } m_State = m_NextState = STATE_READY; Game->m_ScEngine->ResetObject(this); return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::Persist(CBPersistMgr *PersistMgr) { CBObject::Persist(PersistMgr); PersistMgr->Transfer(TMEMBER(m_Active)); PersistMgr->Transfer(TMEMBER(m_BlockRegion)); PersistMgr->Transfer(TMEMBER(m_CurrentBlockRegion)); PersistMgr->Transfer(TMEMBER(m_CurrentWptGroup)); PersistMgr->Transfer(TMEMBER(m_CurrentSprite)); PersistMgr->Transfer(TMEMBER(m_Drawn)); PersistMgr->Transfer(TMEMBER(m_Font)); PersistMgr->Transfer(TMEMBER(m_IgnoreItems)); PersistMgr->Transfer(TMEMBER_INT(m_NextState)); PersistMgr->Transfer(TMEMBER(m_Sentence)); PersistMgr->Transfer(TMEMBER_INT(m_State)); PersistMgr->Transfer(TMEMBER(m_AnimSprite)); PersistMgr->Transfer(TMEMBER(m_SceneIndependent)); PersistMgr->Transfer(TMEMBER(m_ForcedTalkAnimName)); PersistMgr->Transfer(TMEMBER(m_ForcedTalkAnimUsed)); PersistMgr->Transfer(TMEMBER(m_TempSprite2)); PersistMgr->Transfer(TMEMBER_INT(m_Type)); PersistMgr->Transfer(TMEMBER(m_WptGroup)); PersistMgr->Transfer(TMEMBER(m_StickRegion)); PersistMgr->Transfer(TMEMBER(m_SubtitlesModRelative)); PersistMgr->Transfer(TMEMBER(m_SubtitlesModX)); PersistMgr->Transfer(TMEMBER(m_SubtitlesModY)); PersistMgr->Transfer(TMEMBER(m_SubtitlesModXCenter)); PersistMgr->Transfer(TMEMBER(m_SubtitlesWidth)); PersistMgr->Transfer(TMEMBER(m_Inventory)); PersistMgr->Transfer(TMEMBER(m_PartEmitter)); for (int i = 0; i < MAX_NUM_REGIONS; i++) PersistMgr->Transfer(TMEMBER(m_CurrentRegions[i])); m_AttachmentsPre.Persist(PersistMgr); m_AttachmentsPost.Persist(PersistMgr); PersistMgr->Transfer(TMEMBER(m_RegisterAlias)); PersistMgr->Transfer(TMEMBER(m_PartFollowParent)); PersistMgr->Transfer(TMEMBER(m_PartOffsetX)); PersistMgr->Transfer(TMEMBER(m_PartOffsetY)); return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::UpdateSounds() { if (m_Sentence && m_Sentence->m_Sound) UpdateOneSound(m_Sentence->m_Sound); return CBObject::UpdateSounds(); } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::ResetSoundPan() { if (m_Sentence && m_Sentence->m_Sound) { m_Sentence->m_Sound->SetPan(0.0f); } return CBObject::ResetSoundPan(); } ////////////////////////////////////////////////////////////////////////// bool CAdObject::GetExtendedFlag(char *FlagName) { if (!FlagName) return false; else if (strcmp(FlagName, "usable") == 0) return true; else return CBObject::GetExtendedFlag(FlagName); } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::SaveAsText(CBDynBuffer *Buffer, int Indent) { if (m_BlockRegion) m_BlockRegion->SaveAsText(Buffer, Indent + 2, "BLOCKED_REGION"); if (m_WptGroup) m_WptGroup->SaveAsText(Buffer, Indent + 2); CBBase::SaveAsText(Buffer, Indent + 2); return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::UpdateBlockRegion() { CAdGame *AdGame = (CAdGame *)Game; if (AdGame->m_Scene) { if (m_BlockRegion && m_CurrentBlockRegion) m_CurrentBlockRegion->Mimic(m_BlockRegion, m_Zoomable ? AdGame->m_Scene->GetScaleAt(m_PosY) : 100.0f, m_PosX, m_PosY); if (m_WptGroup && m_CurrentWptGroup) m_CurrentWptGroup->Mimic(m_WptGroup, m_Zoomable ? AdGame->m_Scene->GetScaleAt(m_PosY) : 100.0f, m_PosX, m_PosY); } return S_OK; } ////////////////////////////////////////////////////////////////////////// CAdInventory *CAdObject::GetInventory() { if (!m_Inventory) { m_Inventory = new CAdInventory(Game); ((CAdGame *)Game)->RegisterInventory(m_Inventory); } return m_Inventory; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::AfterMove() { CAdRegion *NewRegions[MAX_NUM_REGIONS]; ((CAdGame *)Game)->m_Scene->GetRegionsAt(m_PosX, m_PosY, NewRegions, MAX_NUM_REGIONS); for (int i = 0; i < MAX_NUM_REGIONS; i++) { if (!NewRegions[i]) break; bool RegFound = false; for (int j = 0; j < MAX_NUM_REGIONS; j++) { if (m_CurrentRegions[j] == NewRegions[i]) { m_CurrentRegions[j] = NULL; RegFound = true; break; } } if (!RegFound) NewRegions[i]->ApplyEvent("ActorEntry"); } for (int i = 0; i < MAX_NUM_REGIONS; i++) { if (m_CurrentRegions[i] && Game->ValidObject(m_CurrentRegions[i])) { m_CurrentRegions[i]->ApplyEvent("ActorLeave"); } m_CurrentRegions[i] = NewRegions[i]; } return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::InvalidateCurrRegions() { for (int i = 0; i < MAX_NUM_REGIONS; i++) m_CurrentRegions[i] = NULL; return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::GetScale(float *ScaleX, float *ScaleY) { if (m_Zoomable) { if (m_ScaleX >= 0 || m_ScaleY >= 0) { *ScaleX = m_ScaleX < 0 ? 100 : m_ScaleX; *ScaleY = m_ScaleY < 0 ? 100 : m_ScaleY; } else if (m_Scale >= 0) *ScaleX = *ScaleY = m_Scale; else *ScaleX = *ScaleY = ((CAdGame *)Game)->m_Scene->GetZoomAt(m_PosX, m_PosY) + m_RelativeScale; } else { *ScaleX = *ScaleY = 100; } return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::UpdateSpriteAttachments() { for (int i = 0; i < m_AttachmentsPre.GetSize(); i++) { m_AttachmentsPre[i]->Update(); } for (int i = 0; i < m_AttachmentsPost.GetSize(); i++) { m_AttachmentsPost[i]->Update(); } return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::DisplaySpriteAttachments(bool PreDisplay) { if (PreDisplay) { for (int i = 0; i < m_AttachmentsPre.GetSize(); i++) { DisplaySpriteAttachment(m_AttachmentsPre[i]); } } else { for (int i = 0; i < m_AttachmentsPost.GetSize(); i++) { DisplaySpriteAttachment(m_AttachmentsPost[i]); } } return S_OK; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::DisplaySpriteAttachment(CAdObject *Attachment) { if (!Attachment->m_Active) return S_OK; float ScaleX, ScaleY; GetScale(&ScaleX, &ScaleY); int OrigX = Attachment->m_PosX; int OrigY = Attachment->m_PosY; // inherit position from owner Attachment->m_PosX = this->m_PosX + Attachment->m_PosX * ScaleX / 100.0f; Attachment->m_PosY = this->m_PosY + Attachment->m_PosY * ScaleY / 100.0f; // inherit other props Attachment->m_AlphaColor = this->m_AlphaColor; Attachment->m_BlendMode = this->m_BlendMode; Attachment->m_Scale = this->m_Scale; Attachment->m_RelativeScale = this->m_RelativeScale; Attachment->m_ScaleX = this->m_ScaleX; Attachment->m_ScaleY = this->m_ScaleY; Attachment->m_Rotate = this->m_Rotate; Attachment->m_RelativeRotate = this->m_RelativeRotate; Attachment->m_RotateValid = this->m_RotateValid; Attachment->m_RegisterAlias = this; Attachment->m_Registrable = this->m_Registrable; HRESULT ret = Attachment->Display(); Attachment->m_PosX = OrigX; Attachment->m_PosY = OrigY; return ret; } ////////////////////////////////////////////////////////////////////////// CPartEmitter *CAdObject::CreateParticleEmitter(bool FollowParent, int OffsetX, int OffsetY) { m_PartFollowParent = FollowParent; m_PartOffsetX = OffsetX; m_PartOffsetY = OffsetY; if (!m_PartEmitter) { m_PartEmitter = new CPartEmitter(Game, this); if (m_PartEmitter) { Game->RegisterObject(m_PartEmitter); } } UpdatePartEmitter(); return m_PartEmitter; } ////////////////////////////////////////////////////////////////////////// HRESULT CAdObject::UpdatePartEmitter() { if (!m_PartEmitter) return E_FAIL; if (m_PartFollowParent) { float ScaleX, ScaleY; GetScale(&ScaleX, &ScaleY); m_PartEmitter->m_PosX = m_PosX + (ScaleX / 100.0f) * m_PartOffsetX; m_PartEmitter->m_PosY = m_PosY + (ScaleY / 100.0f) * m_PartOffsetY; } return m_PartEmitter->Update(); } } // end of namespace WinterMute