/*
===========================================================================
ARX FATALIS GPL Source Code
Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
Arx Fatalis Source Code 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.
Arx Fatalis Source Code 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 Arx Fatalis Source Code. If not, see
.
In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
//////////////////////////////////////////////////////////////////////////////////////
// @@ @@@ @@@ @@ @@@@@ //
// @@@ @@@@@@ @@@ @@ @@@@ @@@ @@@ //
// @@@ @@@@@@@ @@@ @@@@ @@@@ @@ @@@@ //
// @@@ @@ @@@@ @@@ @@@@@ @@@@@@ @@@ @@@ //
// @@@@@ @@ @@@@ @@@ @@@@@ @@@@@@@ @@@ @ @@@ //
// @@@@@ @@ @@@@ @@@@@@@@ @@@@ @@@ @@@@@ @@ @@@@@@@ //
// @@ @@@ @@ @@@@ @@@@@@@ @@@ @@@ @@@@@@ @@ @@@@ //
// @@@ @@@ @@@ @@@@ @@@@@ @@@@@@@@@ @@@@@@@ @@@ @@@@ //
// @@@ @@@@ @@@@@@@ @@@@@@ @@@ @@@@ @@@ @@@ @@@ @@@@ //
// @@@@@@@@ @@@@@ @@@@@@@@@@ @@@ @@@ @@@ @@@ @@@ @@@@@ //
// @@@ @@@@ @@@@ @@@ @@@@@@@ @@@ @@@ @@@@ @@@ @@@@ @@@@@ //
//@@@ @@@@ @@@@@ @@@ @@@@@@ @@ @@@ @@@@ @@@@@@@ @@@@@ @@@@@ //
//@@@ @@@@@ @@@@@ @@@@ @@@ @@ @@ @@@@ @@@@@@@ @@@@@@@@@ //
//@@@ @@@@ @@@@@@@ @@@@ @@ @@ @@@@ @@@@@ @@@@@ //
//@@@ @@@@ @@@@@@@ @@@@ @@ @@ @@@@ @@@@@ @@ //
//@@@ @@@ @@@ @@@@@ @@ @@@ //
// @@@ @@@ @@ @@ STUDIOS //
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// ARX_Speech
//////////////////////////////////////////////////////////////////////////////////////
//
// Description:
// ARX Speech & Conversation Management
//
// Updates: (date) (person) (update)
//
// Code: Cyril Meynier
//
// Copyright (c) 1999-2000 ARKANE Studios SA. All rights reserved
//////////////////////////////////////////////////////////////////////////////////////
#include
#include "danae.h"
#include "ARX_Interface.h"
#include "ARX_Speech.h"
#include "ARX_Text.h"
#include "ARX_Script.h"
#include "ARX_Sound.h"
#include "ARX_Input.h"
#include "ARX_Text.h"
#include "ARX_Loc.h"
#include "ARX_Time.h"
#include "EERIEDRAW.h"
#include "hermesmain.h"
#define _CRTDBG_MAP_ALLOC
#include
//-----------------------------------------------------------------------------
extern TextureContainer * arx_logo_tc;
extern long ARX_CONVERSATION;
extern long EXTERNALVIEW;
extern long REQUEST_SPEECH_SKIP;
//-----------------------------------------------------------------------------
ARX_SPEECH aspeech[MAX_ASPEECH];
long HIDESPEECH = 0;
STRUCT_SPEECH speech[MAX_SPEECH];
//-----------------------------------------------------------------------------
void ARX_SPEECH_Init()
{
memset(speech, 0, sizeof(STRUCT_SPEECH)*MAX_SPEECH);
}
//-----------------------------------------------------------------------------
void ARX_SPEECH_MoveUp()
{
if (speech[0].timecreation != 0)
{
if (speech[0].lpszUText != NULL)
{
free(speech[0].lpszUText);
speech[0].lpszUText = NULL;
}
}
for (long j = 0; j < MAX_SPEECH - 1; j++)
{
memcpy(&speech[j], &speech[j+1], sizeof(STRUCT_SPEECH));
}
memset(&speech[MAX_SPEECH-1], 0, sizeof(STRUCT_SPEECH));
}
//-----------------------------------------------------------------------------
void ARX_SPEECH_ClearAll()
{
for (long i = 0; i < MAX_SPEECH; i++)
{
if (speech[i].timecreation != 0)
{
if (speech[i].lpszUText != NULL)
{
free(speech[i].lpszUText);
speech[i].lpszUText = NULL;
}
speech[i].timecreation = 0;
}
}
}
//-----------------------------------------------------------------------------
long ARX_SPEECH_Add(INTERACTIVE_OBJ * io, _TCHAR * _lpszUText, long duration)
{
if (_lpszUText == NULL) return -1;
if (_lpszUText[0] == 0) return -1;
unsigned long tim = ARXTimeUL();
if (tim == 0) tim = 1;
if (speech[MAX_SPEECH-1].timecreation != 0)
ARX_SPEECH_MoveUp();
for (long i = 0; i < MAX_SPEECH; i++)
{
if (speech[i].timecreation == 0)
{
long length = _tcslen(_lpszUText);
// We allocate memory for new speech
speech[i].lpszUText = (_TCHAR *) malloc(__min(length + 1, 4096) * sizeof(_TCHAR));
// Sets creation time
speech[i].timecreation = tim;
// Sets/computes speech duration
if (duration == -1) speech[i].duration = 2000 + length * 60;
else speech[i].duration = duration;
if (length > 4095)
{
memcpy(&speech[i].lpszUText, &_lpszUText, 4095 * sizeof(_TCHAR));
speech[i].lpszUText[4095] = 0;
}
else _tcscpy(speech[i].lpszUText, _lpszUText);
// Sets speech color
if (io == NULL)
{
speech[i].io = NULL;
strcpy(speech[i].name, " ");
}
else
{
speech[i].io = io;
strcpy(speech[i].name, GetName(io->filename));
}
speech[i].color = D3DRGB(1.f, 1.f, 1.f);
// Successfull allocation
return speech[i].duration;
}
}
return -1;
}
//-----------------------------------------------------------------------------
bool CheckLastSpeech(int _iI)
{
for (long i = _iI + 1; i < MAX_SPEECH; i++)
{
if ((speech[i].timecreation != 0) &&
(speech[i].lpszUText != NULL))
{
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
void ARX_SPEECH_Render(LPDIRECT3DDEVICE7 pd3dDevice)
{
_TCHAR temp[4096];
long igrec = 14;
HDC hDC;
SIZE sSize;
if (SUCCEEDED(danaeApp.m_pddsRenderTarget->GetDC(&hDC)))
{
SelectObject(hDC, InBookFont);
GetTextExtentPoint32W(hDC,
_T("p"),
1,
&sSize);
danaeApp.m_pddsRenderTarget->ReleaseDC(hDC);
sSize.cy *= 3;
}
else
{
sSize.cy = DANAESIZY >> 1;
}
GDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE);
GDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE);
SETALPHABLEND(pd3dDevice, TRUE);
int iEnd = igrec + sSize.cy;
for (long i = 0; i < MAX_SPEECH; i++)
{
if (speech[i].timecreation != 0)
{
if (speech[i].lpszUText != NULL)
{
if ((speech[i].name) && (speech[i].name[0] != ' '))
_stprintf(temp, _T("%S > %s"), speech[i].name, speech[i].lpszUText);
else
_stprintf(temp, _T(" %s"), speech[i].lpszUText);//>
EERIEDrawBitmap(GDevice,
120 * Xratio - 16 * Xratio, ARX_CLEAN_WARN_CAST_FLOAT(igrec),
16 * Xratio, 16 * Xratio,
0.00001f,
arx_logo_tc,
D3DCOLORWHITE);
igrec += ARX_TEXT_DrawRect(pd3dDevice, InBookFont,
120.f * Xratio, (float)igrec,
-3, 0,
500 * Xratio, 200 * Yratio, temp, speech[i].color, NULL, 0x00FF00FF, 1);
if ((igrec > iEnd) &&
!CheckLastSpeech(i))
{
ARX_SPEECH_MoveUp();
break;
}
}
}
}
SETALPHABLEND(pd3dDevice, FALSE);
}
void ARX_SPEECH_Check(LPDIRECT3DDEVICE7 pd3dDevice)
{
bool bClear = false;
long exist = 0;
for (long i = 0; i < MAX_SPEECH; i++)
{
if (speech[i].timecreation != 0)
{
if (ARXTime > speech[i].timecreation + speech[i].duration)
{
ARX_SPEECH_MoveUp();
i--;
}
else exist++;
bClear = true;
}
}
if (bClear)
{
if (pTextManage)
{
pTextManage->Clear();
}
}
if (exist) ARX_SPEECH_Render(pd3dDevice);
}
//-----------------------------------------------------------------------------
void ARX_SPEECH_Launch_No_Unicode_Seek(char * string, INTERACTIVE_OBJ * io_source, long mood)
{
mood = ANIM_TALK_NEUTRAL;
long speechnum = ARX_SPEECH_AddSpeech(io_source, string, PARAM_LOCALISED, mood, 4);
if (speechnum >= 0)
{
aspeech[speechnum].scrpos = -1;
aspeech[speechnum].es = NULL;
aspeech[speechnum].ioscript = io_source;
aspeech[speechnum].flags = 0;
ARX_CINEMATIC_SPEECH acs;
acs.type = ARX_CINE_SPEECH_NONE;
memcpy(&aspeech[speechnum].cine, &acs, sizeof(ARX_CINEMATIC_SPEECH));
}
}
ARX_CONVERSATION_STRUCT main_conversation;
void ARX_CONVERSATION_FirstInit()
{
main_conversation.actors_nb = 0;
main_conversation.current = -1;
}
void ARX_CONVERSATION_Reset()
{
main_conversation.actors_nb = 0;
main_conversation.current = -1;
}
void ARX_CONVERSATION_CheckAcceleratedSpeech()
{
if (REQUEST_SPEECH_SKIP)
{
for (long i = 0; i < MAX_ASPEECH; i++)
{
if ((aspeech[i].exist) && !(aspeech[i].flags & ARX_SPEECH_FLAG_UNBREAKABLE))
{
aspeech[i].duration = 0;
}
}
REQUEST_SPEECH_SKIP = 0;
}
}
void ARX_SPEECH_FirstInit()
{
memset(aspeech, 0, sizeof(ARX_SPEECH)*MAX_ASPEECH);
}
long ARX_SPEECH_GetFree()
{
for (long i = 0; i < MAX_ASPEECH; i++)
{
if (!aspeech[i].exist)
{
aspeech[i].cine.type = 0;
return i;
}
}
return -1;
}
long ARX_SPEECH_GetIOSpeech(INTERACTIVE_OBJ * io)
{
for (long i = 0; i < MAX_ASPEECH; i++)
{
if ((aspeech[i].exist)
&& (aspeech[i].io == io))
return i;
}
return -1;
}
void ARX_SPEECH_Release(long i)
{
if (aspeech[i].exist)
{
ARX_SOUND_Stop(aspeech[i].sample);
if (aspeech[i].text != NULL)
free(aspeech[i].text);
if ((ValidIOAddress(aspeech[i].io))
&& (aspeech[i].io->animlayer[2].cur_anim))
{
AcquireLastAnim(aspeech[i].io);
aspeech[i].io->animlayer[2].cur_anim = NULL;
}
memset(&aspeech[i], 0, sizeof(ARX_SPEECH));
}
}
void ARX_SPEECH_ReleaseIOSpeech(INTERACTIVE_OBJ * io)
{
for (long i = 0; i < MAX_ASPEECH; i++)
{
if ((aspeech[i].exist)
&& (aspeech->io == io))
{
ARX_SPEECH_Release(i);
}
}
}
void ARX_SPEECH_Reset()
{
for (long i = 0; i < MAX_ASPEECH; i++)
{
ARX_SPEECH_Release(i);
}
}
void ARX_SPEECH_ClearIOSpeech(INTERACTIVE_OBJ * io)
{
if (!io) return;
for (long i = 0; i < MAX_ASPEECH; i++)
{
if ((aspeech[i].exist)
&& (aspeech[i].io == io))
{
EERIE_SCRIPT * es = aspeech[i].es;
INTERACTIVE_OBJ * io = aspeech[i].ioscript;
long scrpos = aspeech[i].scrpos;
ARX_SPEECH_Release(i);
if ((es)
&& (ValidIOAddress(io)))
{
SendScriptEvent(es, SM_EXECUTELINE, "", io, NULL, scrpos);
}
}
}
}
long ARX_SPEECH_AddSpeech(INTERACTIVE_OBJ * io, char * data, long param, long mood, long flags)
{
if (!data || !data[0]) return -1;
long num = ARX_SPEECH_GetFree();
if (num < 0) return -1;
long ioo = ARX_SPEECH_GetIOSpeech(io);
if (ioo != -1)
{
for (long i = 0; i < MAX_ASPEECH; i++)
if (aspeech[i].exist && aspeech[i].io == io)
{
EERIE_SCRIPT * es = aspeech[i].es;
INTERACTIVE_OBJ * io = aspeech[i].ioscript;
long scrpos = aspeech[i].scrpos;
ARX_SPEECH_Release(i);
if ((es)
&& (ValidIOAddress(io)))
SendScriptEvent(es, SM_EXECUTELINE, "", io, NULL, scrpos);
}
}
aspeech[num].exist = 1;
aspeech[num].time_creation = ARX_TIME_GetUL();
aspeech[num].io = io; // can be NULL
aspeech[num].duration = 2000; // Minimum value
aspeech[num].flags = flags;
aspeech[num].sample = -1;
aspeech[num].fDeltaY = 0.f;
aspeech[num].iTimeScroll = 0;
aspeech[num].fPixelScroll = 0.f;
aspeech[num].color = 0xFFFFFFFF;
aspeech[num].mood = mood;
long flg = 0;
_TCHAR lpszUSection[512];
MultiByteToWideChar(CP_ACP, 0, data, -1, lpszUSection, 512);
if (!(flags & ARX_SPEECH_FLAG_NOTEXT))
{
_TCHAR _output[4096];
ZeroMemory(_output, 4096 * sizeof(_TCHAR));
flg = HERMES_UNICODE_GetProfileString(lpszUSection,
_T("string"),
_T(""),
_output,
4096,
NULL,
io->lastspeechflag
);
io->lastspeechflag = (short)flg;
aspeech[num].text = (_TCHAR *) malloc((_tcslen(_output) + 1) * sizeof(_TCHAR));
_tcscpy(aspeech[num].text, _output);
aspeech[num].duration = __max(aspeech[num].duration, (_tcslen(_output) + 1) * 100);
}
char speech_label[256];
char speech_sample[256];
strcpy(speech_label, data + 1);
speech_label[strlen(speech_label) - 1] = 0;
if (flags & ARX_SPEECH_FLAG_NOTEXT)
{
long count = 0;
count = HERMES_UNICODE_GetProfileSectionKeyCount(lpszUSection);
F2L((float)(rnd() *(float)count), &flg);
while ((io->lastspeechflag == flg) && (count > 1))
{
F2L((float)(rnd() *(float)count), &flg);
}
if (flg > count) flg = count;
else if (flg <= 0) flg = 1;
io->lastspeechflag = (short)flg;
}
if (flg > 1)
sprintf(speech_sample, "%s%d", speech_label, flg);
else
strcpy(speech_sample, speech_label);
if (aspeech[num].flags & ARX_SPEECH_FLAG_OFFVOICE)
aspeech[num].sample = ARX_SOUND_PlaySpeech(speech_sample);
else
aspeech[num].sample = ARX_SOUND_PlaySpeech(speech_sample, io);
//Next lines must be removed (use callback instead)
aspeech[num].duration = (unsigned long)ARX_SOUND_GetDuration(aspeech[num].sample);
//This div is not good in the intro because speakpitch value is bad
//But correct it inside arx_script ligne 7913 create a crash
float fDiv = aspeech[num].duration / io->_npcdata->speakpitch;
//ARX_CHECK_ULONG(fDiv);
if ((io->ioflags & IO_NPC) && !(aspeech[num].flags & ARX_SPEECH_FLAG_OFFVOICE))
aspeech[num].duration = ARX_CLEAN_WARN_CAST_ULONG(fDiv);
if (aspeech[num].duration < 500) aspeech[num].duration = 2000;
if (ARX_CONVERSATION && io)
for (long j = 0; j < main_conversation.actors_nb; j++)
if (main_conversation.actors[j] >= 0 && io == inter.iobj[main_conversation.actors[j]])
main_conversation.current = num;
return num;
}
//*************************************************************************************
//*************************************************************************************
void ARX_SPEECH_Update(LPDIRECT3DDEVICE7 pd3dDevice)
{
unsigned long tim = ARXTimeUL();
if (CINEMASCOPE || BLOCK_PLAYER_CONTROLS) ARX_CONVERSATION_CheckAcceleratedSpeech();
for (long i = 0 ; i < MAX_ASPEECH ; i++)
{
if (aspeech[i].exist)
{
INTERACTIVE_OBJ * io = aspeech[i].io;
// updates animations
if (io)
{
if (aspeech[i].flags & ARX_SPEECH_FLAG_OFFVOICE)
ARX_SOUND_RefreshSpeechPosition(aspeech[i].sample);
else
ARX_SOUND_RefreshSpeechPosition(aspeech[i].sample, io);
if (((io != inter.iobj[0]) || ((io == inter.iobj[0]) && (EXTERNALVIEW)))
&& ValidIOAddress(io))
{
if (io->anims[aspeech[i].mood] == NULL) aspeech[i].mood = ANIM_TALK_NEUTRAL;
if (io->anims[aspeech[i].mood] != NULL)
{
if ((io->animlayer[2].cur_anim != io->anims[aspeech[i].mood])
|| (io->animlayer[2].flags & EA_ANIMEND))
{
AcquireLastAnim(io);
ANIM_Set(&io->animlayer[2], io->anims[aspeech[i].mood]);
}
}
}
}
// checks finished speech
if (tim >= aspeech[i].time_creation + aspeech[i].duration)
{
EERIE_SCRIPT * es = aspeech[i].es;
INTERACTIVE_OBJ * io = aspeech[i].ioscript;
long scrpos = aspeech[i].scrpos;
ARX_SPEECH_Release(i);
if ((es)
&& (ValidIOAddress(io)))
SendScriptEvent(es, SM_EXECUTELINE, "", io, NULL, scrpos);
}
}
}
for (int i = 0 ; i < MAX_ASPEECH ; i++)
{
ARX_SPEECH * speech = &aspeech[i];
if (speech->exist)
{
if (speech->text != NULL)
{
if ((ARX_CONVERSATION) && (speech->io))
{
long ok = 0;
for (long j = 0 ; j < main_conversation.actors_nb ; j++)
{
if (main_conversation.actors[j] >= 0)
if (speech->io == inter.iobj[main_conversation.actors[j]])
{
ok = 1;
}
}
if (!ok) goto next;
}
if (CINEMASCOPE && !HIDESPEECH)
{
if (CINEMA_DECAL >= 100.f)
{
HRGN hRgn;
HDC hDC;
SIZE sSize;
sSize.cx = sSize.cy = 0;
if (SUCCEEDED(danaeApp.m_pddsRenderTarget->GetDC(&hDC)))
{
SelectObject(hDC, InBookFont);
GetTextExtentPoint32W(hDC,
speech->text,
_tcslen(speech->text),
&sSize);
danaeApp.m_pddsRenderTarget->ReleaseDC(hDC);
}
else
{
ARX_CHECK_NO_ENTRY();
}
float fZoneClippHeight = ARX_CLEAN_WARN_CAST_FLOAT(sSize.cy * 3);
float fStartYY = 100 * Yratio;
float fStartY = ARX_CLEAN_WARN_CAST_FLOAT(((int)fStartYY - (int)fZoneClippHeight) >> 1);
float fDepY = ((float)DANAESIZY) - fStartYY + fStartY - speech->fDeltaY + sSize.cy;
float fZoneClippY = fDepY + speech->fDeltaY;
float fAdd = fZoneClippY + fZoneClippHeight ;
ARX_CHECK_INT(fZoneClippY);
ARX_CHECK_INT(fAdd);
hRgn = CreateRectRgn(0,
ARX_CLEAN_WARN_CAST_INT(fZoneClippY),
DANAESIZX,
ARX_CLEAN_WARN_CAST_INT(fAdd));
danaeApp.DANAEEndRender();
float iTaille = (float)ARX_TEXT_DrawRect(
pd3dDevice,
InBookFont,
10.f,
fDepY + fZoneClippHeight,
-3,
0,
-10.f + (float)DANAESIZX,
0, //taille recalculée
speech->text,
RGB(255, 255, 255),
hRgn);
if (hRgn)
{
DeleteObject(hRgn);
}
danaeApp.DANAEStartRender();
SETTC(GDevice, NULL);
GDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ZERO);
GDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCCOLOR);
GDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
GDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE);
EERIEDrawFill2DRectDegrad(GDevice,
0.f,
fZoneClippY - 1.f,
ARX_CLEAN_WARN_CAST_FLOAT(DANAESIZX),
fZoneClippY + (sSize.cy * 3 / 4),
0.f,
RGBA_MAKE(255, 255, 255, 255),
RGBA_MAKE(0, 0, 0, 255));
EERIEDrawFill2DRectDegrad(GDevice,
0.f,
fZoneClippY + fZoneClippHeight - (sSize.cy * 3 / 4),
ARX_CLEAN_WARN_CAST_FLOAT(DANAESIZX),
fZoneClippY + fZoneClippHeight,
0.f,
RGBA_MAKE(0, 0, 0, 255),
RGBA_MAKE(255, 255, 255, 255));
GDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE);
GDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ZERO);
danaeApp.EnableZBuffer();
GDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, FALSE);
iTaille += (int)fZoneClippHeight;
if (((int)speech->fDeltaY) <= iTaille)
{
//vitesse du scroll
float fDTime;
if (speech->sample)
{
fDTime = ((float)iTaille * (float)FrameDiff) / (float)ARX_SOUND_GetDuration(speech->sample); //speech->duration;
float fTimeOneLine = ((float)sSize.cy) * fDTime;
if (((float)speech->iTimeScroll) >= fTimeOneLine)
{
float fResteLine = (float)sSize.cy - speech->fPixelScroll;
float fTimePlus = ((float)fResteLine * (float)FrameDiff) / (float)ARX_SOUND_GetDuration(speech->sample);
fDTime -= fTimePlus;
speech->fPixelScroll = 0.f;
speech->iTimeScroll = 0;
}
ARX_CHECK_INT(speech->iTimeScroll + FrameDiff);
speech->iTimeScroll += ARX_CLEAN_WARN_CAST_INT(FrameDiff);
}
else
{
fDTime = ((float)iTaille * (float)FrameDiff) / 4000.f;
}
speech->fDeltaY += fDTime;
speech->fPixelScroll += fDTime;
}
}
}
}
next:
;
}
}
}
//-----------------------------------------------------------------------------
BOOL ApplySpeechPos(EERIE_CAMERA * conversationcamera, long is)
{
if (is < 0) return FALSE;
if (aspeech[is].io == NULL) return FALSE;
conversationcamera->d_pos.x = aspeech[is].io->pos.x;
conversationcamera->d_pos.y = aspeech[is].io->pos.y + PLAYER_BASE_HEIGHT;
conversationcamera->d_pos.z = aspeech[is].io->pos.z;
float t = (aspeech[is].io->angle.b);
conversationcamera->pos.x = conversationcamera->d_pos.x + (float)EEsin(t) * 100.f;
conversationcamera->pos.y = conversationcamera->d_pos.y;
conversationcamera->pos.z = conversationcamera->d_pos.z - (float)EEcos(t) * 100.f;
return TRUE;
}