Serious-Engine/Sources/GameMP/Computer.cpp

1329 lines
37 KiB
C++
Raw Normal View History

2016-03-12 01:20:51 +01:00
/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
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. */
2016-03-11 14:57:17 +01:00
#include "StdAfx.h"
#include "LCDDrawing.h"
#include "CompMessage.h"
extern CGame *_pGame;
static const FLOAT tmComputerFade = 1.0f; // how many seconds it takes computer to fade in/out
static FLOAT fComputerFadeValue = 0.0f; // faded value of computer (0..1)
static CTimerValue tvComputerLast;
static CTimerValue _tvMessageAppear;
static CPlayer *_ppenPlayer = NULL;
extern FLOAT _fMsgAppearFade = 0.0f;
extern FLOAT _fMsgAppearDelta = 0.0f;
// player statistics are set here
extern CTString _strStatsDetails = "";
// mouse cursor position
static PIX2D _vpixMouse;
static PIX2D _vpixExternMouse;
static PIX _pixSliderDragJ = -1;
static PIX _iSliderDragLine = -1;
static PIX _bSliderDragText = FALSE;
// font metrics
static PIX _pixCharSizeI = 1;
static PIX _pixCharSizeJ = 1;
static PIX _pixCharSize2I = 1;
static PIX _pixCharSize2J = 1;
static PIX _pixMarginI = 1;
static PIX _pixMarginJ = 1;
// general geometry data
static FLOAT _fScaling = 1;
static FLOAT _fScaling2 = 1;
static PIX _pixSizeI=0;
static PIX _pixSizeJ=0;
static PIXaabbox2D _boxTitle;
static PIXaabbox2D _boxExit;
static PIXaabbox2D _boxMsgList;
static PIXaabbox2D _boxMsgText;
static PIXaabbox2D _boxMsgImage;
static PIXaabbox2D _boxButton[CMT_COUNT];
static INDEX _ctMessagesOnScreen = 5;
static INDEX _ctTextLinesOnScreen = 20;
static INDEX _ctTextCharsPerRow = 20;
// position of the message list
static INDEX _iFirstMessageOnScreen = -1;
static INDEX _iWantedFirstMessageOnScreen = 0;
static INDEX _iLastActiveMessage = -1;
static INDEX _iActiveMessage = 0;
// message type selected in the buttons list
static enum CompMsgType _cmtCurrentType = (enum CompMsgType)-1;
static enum CompMsgType _cmtWantedType = CMT_INFORMATION;
// current scroll position of message text
static INDEX _iTextLineOnScreen = 0;
// message list cache for messages of current type
static CStaticStackArray<CCompMessage> _acmMessages;
// message image data
static CTextureObject _toPicture;
// text/graphics colors
static COLOR _colLight;
static COLOR _colMedium;
static COLOR _colDark;
static COLOR _colBoxes;
static void SetFont1(CDrawPort *pdp)
{
pdp->SetFont(_pfdConsoleFont);
pdp->SetTextScaling(_fScaling);
pdp->SetTextAspect(1.0f);
}
static void SetFont2(CDrawPort *pdp)
{
pdp->SetFont(_pfdDisplayFont);
pdp->SetTextScaling(_fScaling2);
pdp->SetTextAspect(1.0f);
}
static COLOR MouseOverColor(const PIXaabbox2D &box, COLOR colNone,
COLOR colOff, COLOR colOn)
{
if (box>=_vpixMouse) {
return LCDBlinkingColor(colOff, colOn);
} else {
return colNone;
}
}
static PIXaabbox2D GetMsgListBox(INDEX i)
{
PIX pixI0 = _boxMsgList.Min()(1)+_pixMarginI;
PIX pixI1 = _boxMsgList.Max()(1)-_pixMarginI*3;
PIX pixJ0 = _boxMsgList.Min()(2)+_pixMarginJ;
PIX pixDJ = _pixCharSizeJ;
return PIXaabbox2D(
PIX2D(pixI0, pixJ0+pixDJ*i),
PIX2D(pixI1, pixJ0+pixDJ*(i+1)-1));
}
static PIXaabbox2D GetSliderBox(INDEX iFirst, INDEX iVisible, INDEX iTotal,
PIXaabbox2D boxFull)
{
FLOAT fSize = ClampUp(FLOAT(iVisible)/iTotal, 1.0f);
PIX pixFull = boxFull.Size()(2);
PIX pixSize = PIX(pixFull*fSize);
pixSize = ClampDn(pixSize, boxFull.Size()(1));
PIX pixTop = pixFull*(FLOAT(iFirst)/iTotal)+boxFull.Min()(2);
PIX pixI0 = boxFull.Min()(1);
PIX pixI1 = boxFull.Max()(1);
return PIXaabbox2D(PIX2D(pixI0, pixTop), PIX2D(pixI1, pixTop+pixSize));
}
static INDEX SliderPixToIndex(PIX pixOffset, INDEX iVisible, INDEX iTotal, PIXaabbox2D boxFull)
{
FLOAT fSize = ClampUp(FLOAT(iVisible)/iTotal, 1.0f);
PIX pixFull = boxFull.Size()(2);
PIX pixSize = PIX(pixFull*fSize);
if (pixSize>=boxFull.Size()(2)) {
return 0;
}
return (iTotal*pixOffset)/pixFull;
}
static PIXaabbox2D GetTextSliderSpace(void)
{
PIX pixSizeI = _boxMsgText.Size()(1);
PIX pixSizeJ = _boxMsgText.Size()(2);
PIX pixSliderSizeI = _pixMarginI*2;
if (pixSliderSizeI<5) {
pixSliderSizeI=5;
}
return PIXaabbox2D(
PIX2D(pixSizeI-pixSliderSizeI, _pixMarginJ*4),
PIX2D(pixSizeI, pixSizeJ));
}
static PIXaabbox2D GetMsgSliderSpace(void)
{
PIX pixSizeI = _boxMsgList.Size()(1);
PIX pixSizeJ = _boxMsgList.Size()(2);
PIX pixSliderSizeI = _pixMarginI*2;
if (pixSliderSizeI<5) {
pixSliderSizeI=5;
}
return PIXaabbox2D(
PIX2D(pixSizeI-pixSliderSizeI, 0),
PIX2D(pixSizeI, pixSizeJ));
}
static PIXaabbox2D GetTextSliderBox(void)
{
if (_iActiveMessage>=_acmMessages.Count()) {
return PIXaabbox2D();
}
INDEX ctTextLines = _acmMessages[_iActiveMessage].cm_ctFormattedLines;
PIX pixSizeI = _boxMsgText.Size()(1);
PIX pixSizeJ = _boxMsgText.Size()(2);
return GetSliderBox(
_iTextLineOnScreen, _ctTextLinesOnScreen, ctTextLines, GetTextSliderSpace());
}
static PIXaabbox2D GetMsgSliderBox(void)
{
INDEX ctLines = _acmMessages.Count();
PIX pixSizeI = _boxMsgList.Size()(1);
PIX pixSizeJ = _boxMsgList.Size()(2);
return GetSliderBox(
_iFirstMessageOnScreen, _ctMessagesOnScreen, ctLines, GetMsgSliderSpace());
}
// syncronize message list scrolling to show active message
void SyncScrollWithActive(void)
{
if (_iActiveMessage<_iFirstMessageOnScreen) {
_iWantedFirstMessageOnScreen = _iActiveMessage;
}
if (_iActiveMessage>_iFirstMessageOnScreen+_ctMessagesOnScreen-1) {
_iWantedFirstMessageOnScreen = _iActiveMessage-_ctMessagesOnScreen+1;
}
}
// select next unread message
static void NextUnreadMessage(void)
{
INDEX i=_iActiveMessage;
FOREVER {
i++;
if (i>=_acmMessages.Count()) {
i = 0;
}
if (i==_iActiveMessage) {
return;
}
if (!_acmMessages[i].cm_bRead) {
_iActiveMessage = i;
SyncScrollWithActive();
return;
}
}
}
// select last unread message, or last message if all read
void LastUnreadMessage(void)
{
BOOL bFound = FALSE;
for(_iActiveMessage=_acmMessages.Count()-1; _iActiveMessage>=0; _iActiveMessage--) {
if (!_acmMessages[_iActiveMessage].cm_bRead) {
bFound = TRUE;
break;
}
}
if (!bFound) {
_iActiveMessage = ClampDn<INDEX>(_acmMessages.Count()-1, 0);
}
SyncScrollWithActive();
}
// go to next/previous message
void PrevMessage(void)
{
if (_iActiveMessage<_acmMessages.Count()==0) {
return;
}
_iActiveMessage--;
if (_iActiveMessage<0) {
_iActiveMessage = 0;
}
SyncScrollWithActive();
}
void NextMessage(void)
{
if (_iActiveMessage<_acmMessages.Count()==0) {
return;
}
_iActiveMessage++;
if (_iActiveMessage>=_acmMessages.Count()) {
_iActiveMessage = _acmMessages.Count()-1;
}
SyncScrollWithActive();
}
void MessagesUpDn(INDEX ctLines)
{
INDEX ctMessages = _acmMessages.Count();
_iWantedFirstMessageOnScreen += ctLines;
INDEX iMaxFirst = ClampDn(0L, ctMessages-_ctMessagesOnScreen);
_iWantedFirstMessageOnScreen = Clamp(_iWantedFirstMessageOnScreen, 0L, iMaxFirst);
_iActiveMessage = Clamp(_iActiveMessage,
_iWantedFirstMessageOnScreen,
_iWantedFirstMessageOnScreen+_ctMessagesOnScreen-1L);
}
void SelectMessage(INDEX i)
{
if (_acmMessages.Count()==0) {
return;
}
_iActiveMessage = i;
if (_iActiveMessage<0) {
_iActiveMessage = 0;
}
if (_iActiveMessage>=_acmMessages.Count()) {
_iActiveMessage = _acmMessages.Count()-1;
}
SyncScrollWithActive();
}
// scroll message text
void MessageTextUp(INDEX ctLines)
{
_iTextLineOnScreen-=ctLines;
if (_iTextLineOnScreen<0) {
_iTextLineOnScreen = 0;
}
}
void MessageTextDn(INDEX ctLines)
{
// if no message do nothing
if (_iActiveMessage<_acmMessages.Count()==0) {
return;
}
// find text lines count
_acmMessages[_iActiveMessage].PrepareMessage(_ctTextCharsPerRow);
INDEX ctTextLines = _acmMessages[_iActiveMessage].cm_ctFormattedLines;
// calculate maximum value for first visible line
INDEX iFirstLine = ctTextLines-_ctTextLinesOnScreen;
if (iFirstLine<0) {
iFirstLine = 0;
}
// increment
_iTextLineOnScreen+=ctLines;
if (_iTextLineOnScreen>iFirstLine) {
_iTextLineOnScreen = iFirstLine;
}
}
void MessageTextUpDn(INDEX ctLines)
{
if (ctLines>0) {
MessageTextDn(ctLines);
} else if (ctLines<0) {
MessageTextUp(-ctLines);
}
}
// mark current message as read
void MarkCurrentRead(void)
{
if (_iActiveMessage>=_acmMessages.Count()) {
return;
}
// if running in background
if (_pGame->gm_csComputerState == CS_ONINBACKGROUND) {
// do nothing
return;
}
ASSERT(_ppenPlayer!=NULL);
if (_ppenPlayer==NULL) {
return;
}
// if already read
if (_acmMessages[_iActiveMessage].cm_bRead) {
// do nothing
return;
}
// mark as read
_ppenPlayer->m_ctUnreadMessages--;
_acmMessages[_iActiveMessage].MarkRead();
}
// update scroll position for message list
static void UpdateFirstOnScreen(void)
{
if (_iFirstMessageOnScreen==_iWantedFirstMessageOnScreen) {
return;
}
_iFirstMessageOnScreen=_iWantedFirstMessageOnScreen;
ASSERT(
_iFirstMessageOnScreen>=0&&
_iFirstMessageOnScreen<=_acmMessages.Count());
_iFirstMessageOnScreen = Clamp(_iFirstMessageOnScreen, INDEX(0), _acmMessages.Count());
// for each message
for(INDEX i=0; i<_acmMessages.Count(); i++) {
CCompMessage &cm = _acmMessages[i];
// if on screen
if (i>=_iWantedFirstMessageOnScreen
&&i<_iWantedFirstMessageOnScreen+_ctMessagesOnScreen) {
// load
cm.PrepareMessage(_ctTextCharsPerRow);
// if not on screen
} else {
// unload
cm.UnprepareMessage();
}
}
}
// update current active message category
static void UpdateType(BOOL bForce=FALSE)
{
if (_cmtCurrentType==_cmtWantedType && !bForce) {
return;
}
// cleare message cache
_acmMessages.Clear();
// for each player's message
CDynamicStackArray<CCompMessageID> &acmiMsgs = _ppenPlayer->m_acmiMessages;
for(INDEX i=0; i<acmiMsgs.Count(); i++) {
CCompMessageID &cmi = acmiMsgs[i];
// if it is of given type
if (cmi.cmi_cmtType == _cmtWantedType) {
// add it to cache
CCompMessage &cm = _acmMessages.Push();
cm.SetMessage(&cmi);
}
}
if (!bForce) {
_cmtCurrentType=_cmtWantedType;
_iFirstMessageOnScreen = -1;
_iWantedFirstMessageOnScreen = 0;
_iActiveMessage = 0;
_iLastActiveMessage = -2;
_iTextLineOnScreen = 0;
LastUnreadMessage();
UpdateFirstOnScreen();
}
}
static void UpdateMessageAppearing(void)
{
if (_iLastActiveMessage!=_iActiveMessage) {
_pShell->Execute("FreeUnusedStock();"); // make sure user doesn't overflow memory
_iTextLineOnScreen = 0;
_iLastActiveMessage=_iActiveMessage;
_tvMessageAppear = _pTimer->GetHighPrecisionTimer();
}
CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
_fMsgAppearDelta = (tvNow-_tvMessageAppear).GetSeconds();
if (fComputerFadeValue<0.99f) {
_tvMessageAppear = _pTimer->GetHighPrecisionTimer();
_fMsgAppearDelta = 0.0f;
}
_fMsgAppearFade = Clamp(_fMsgAppearDelta/0.5f, 0.0f,1.0f);
}
// update screen geometry
static void UpdateSize(CDrawPort *pdp)
{
// get screen size
PIX pixSizeI = pdp->GetWidth();
PIX pixSizeJ = pdp->GetHeight();
// remember new size
_pixSizeI = pixSizeI;
_pixSizeJ = pixSizeJ;
// determine scaling
_fScaling = 1.0f;
_fScaling2 = 1.0f;
if (pixSizeJ<384) {
_fScaling = 1.0f;
_fScaling2 = pixSizeJ/480.0f;
}
// remember font size
CFontData *pfd = _pfdConsoleFont;
_pixCharSizeI = pfd->fd_pixCharWidth + pfd->fd_pixCharSpacing;
_pixCharSizeJ = pfd->fd_pixCharHeight + pfd->fd_pixLineSpacing;
_pixCharSize2I = _pixCharSizeI*_fScaling2;
_pixCharSize2J = _pixCharSizeJ*_fScaling2;
_pixCharSizeI = _pixCharSizeI*_fScaling;
_pixCharSizeJ = _pixCharSizeJ*_fScaling;
_pixMarginI = 5*_fScaling2;
_pixMarginJ = 5*_fScaling2;
PIX pixBoxMarginI = 10*_fScaling2;
PIX pixBoxMarginJ = 10*_fScaling2;
PIX pixJ0Dn = pixBoxMarginJ;
PIX pixJ1Up = pixJ0Dn+_pixCharSize2J+_pixMarginI*2;
PIX pixJ1Dn = pixJ1Up+pixBoxMarginJ;
PIX pixJ2Up = pixJ1Dn+_pixCharSize2J*6*2+pixBoxMarginJ;
PIX pixJ2Dn = pixJ2Up+pixBoxMarginJ;
PIX pixJ3Up = _pixSizeJ-pixBoxMarginJ;
PIX pixI0Rt = pixBoxMarginI;
PIX pixI1Lt = pixI0Rt+_pixCharSize2I*20+pixBoxMarginI;
PIX pixI1Rt = pixI1Lt+pixBoxMarginI;
PIX pixI2Lt = _pixSizeI/2-pixBoxMarginI/2;
PIX pixI2Rt = _pixSizeI/2+pixBoxMarginI/2;
PIX pixI4Lt = _pixSizeI-pixBoxMarginI;
PIX pixI3Rt = pixI4Lt-pixBoxMarginI*2-_pixCharSize2I*10;
PIX pixI3Lt = pixI3Rt-pixBoxMarginI;
// calculate box sizes
_boxTitle = PIXaabbox2D( PIX2D(0, pixJ0Dn-1), PIX2D(pixI3Lt, pixJ1Up));
_boxExit = PIXaabbox2D( PIX2D( pixI3Rt, pixJ0Dn-1), PIX2D(_pixSizeI, pixJ1Up));
PIX pixD = 5;
PIX pixH = (pixJ2Up-pixJ1Dn-pixD*(CMT_COUNT-1))/CMT_COUNT;
INDEX i;
for( i=0; i<CMT_COUNT; i++) {
_boxButton[i] = PIXaabbox2D(
PIX2D(0, pixJ1Dn+(pixH+pixD)*i),
PIX2D(pixI1Lt, pixJ1Dn+(pixH+pixD)*i+pixH));
}
_boxMsgList = PIXaabbox2D( PIX2D(pixI1Rt, pixJ1Dn), PIX2D(pixI4Lt, pixJ2Up));
if (GetSP()->sp_bCooperative) {
_boxMsgText = PIXaabbox2D( PIX2D(pixI2Rt, pixJ2Dn), PIX2D(pixI4Lt, pixJ3Up));
_boxMsgImage= PIXaabbox2D( PIX2D(pixI0Rt, pixJ2Dn), PIX2D(pixI2Lt, pixJ3Up));
} else {
_boxMsgText = PIXaabbox2D( PIX2D(pixI0Rt, pixJ2Dn), PIX2D(pixI4Lt, pixJ3Up));
_boxMsgImage= PIXaabbox2D();
}
FLOAT fSlideSpeed = Max(_pixSizeI, _pixSizeJ*2L);
FLOAT fGroup0 = ClampDn((1-fComputerFadeValue)*fSlideSpeed-_pixSizeJ, 0.0f);
FLOAT fGroup1 = (1-fComputerFadeValue)*fSlideSpeed;
// animate box positions
_boxTitle -= PIX2D( fGroup1, 0);
_boxExit += PIX2D( fGroup1, 0);
for( i=0; i<CMT_COUNT; i++) {
FLOAT fOffs = ClampDn(fGroup1-(CMT_COUNT-i)*_pixMarginJ*10, 0.0f);
_boxButton[i] -= PIX2D(fOffs, 0);
}
_boxMsgList -= PIX2D(0, fGroup0);
_boxMsgText += PIX2D(fGroup0, 0);
_boxMsgImage+= PIX2D(0, fGroup0);
_ctMessagesOnScreen = (_boxMsgList.Size()(2) - _pixMarginJ*2) / _pixCharSizeJ;
_ctTextCharsPerRow = (_boxMsgText.Size()(1) - _pixMarginI*4) / _pixCharSizeI;
_ctTextLinesOnScreen = (_boxMsgText.Size()(2) - _pixMarginJ*2 - _pixMarginJ*4) / _pixCharSizeJ;
}
static char *_astrButtonTexts[CMT_COUNT];
// print message type buttons
void PrintButton(CDrawPort *pdp, INDEX iButton)
{
CDrawPort dpButton(pdp, _boxButton[iButton]);
if (!dpButton.Lock()) {
return;
}
LCDSetDrawport(&dpButton);
_pGame->LCDRenderCompGrid();
LCDRenderClouds2();
LCDScreenBoxOpenLeft(_colBoxes);
SetFont2(&dpButton);
// count messages
INDEX ctTotal=0;
INDEX ctRead=0;
CDynamicStackArray<CCompMessageID> &acmiMsgs = _ppenPlayer->m_acmiMessages;
{for(INDEX i=0; i<acmiMsgs.Count(); i++) {
CCompMessageID &cmi = acmiMsgs[i];
if (cmi.cmi_cmtType==iButton) {
ctTotal++;
if (cmi.cmi_bRead) {
ctRead++;
}
}
}}
INDEX ctUnread = ctTotal-ctRead;
// prepare color
COLOR col = _colMedium;
if (iButton==_cmtCurrentType) {
col = _colLight;
}
col = MouseOverColor(_boxButton[iButton], col, _colDark, _colLight);
// prepare string
CTString str;
if (ctUnread==0) {
str = _astrButtonTexts[iButton];
} else {
str.PrintF("%s (%d)", _astrButtonTexts[iButton], ctUnread);
}
// print it
dpButton.PutTextR( str, _boxButton[iButton].Size()(1)-_pixMarginI, _pixCharSize2J/2+1, col);
dpButton.Unlock();
}
// print title
void PrintTitle(CDrawPort *pdp)
{
SetFont2(pdp);
CTString strTitle;
strTitle.PrintF(TRANS("NETRICSA v2.01 - personal version for: %s"),
_ppenPlayer->GetPlayerName());
pdp->PutText( strTitle, _pixMarginI*3, _pixMarginJ-2*_fScaling2+1, _colMedium);
}
// print exit button
void PrintExit(CDrawPort *pdp)
{
SetFont2(pdp);
pdp->PutTextR( TRANS("Exit"), _boxExit.Size()(1)-_pixMarginI*3, _pixMarginJ-2*_fScaling2+1,
MouseOverColor(_boxExit, _colMedium, _colDark, _colLight));
}
// print list of messages
void PrintMessageList(CDrawPort *pdp)
{
PIX pixTextX = _pixMarginI;
PIX pixYLine = _pixMarginJ;
SetFont1(pdp);
INDEX iFirst = _iFirstMessageOnScreen;
INDEX iLast = Min(INDEX(_iFirstMessageOnScreen+_ctMessagesOnScreen), _acmMessages.Count())-1;
if (iFirst>iLast) {
pdp->PutText( TRANS("no messages"), pixTextX, pixYLine, _colDark);
}
for(INDEX i=iFirst; i<=iLast; i++) {
COLOR col = _colMedium;
if (_acmMessages[i].cm_bRead) {
col = _colDark;
}
if (i==_iActiveMessage) {
col = _colLight;
}
if (GetMsgListBox(i-_iFirstMessageOnScreen)>=_vpixMouse) {
col = LCDBlinkingColor(_colLight, _colMedium);
}
pdp->PutText( _acmMessages[i].cm_strSubject, pixTextX, pixYLine, col);
pixYLine+=_pixCharSizeJ;
}
PIXaabbox2D boxSliderSpace = GetMsgSliderSpace();
LCDDrawBox(0,0,boxSliderSpace, _colBoxes);
PIXaabbox2D boxSlider = GetMsgSliderBox();
COLOR col = _colBoxes;
PIXaabbox2D boxSliderTrans = boxSlider;
boxSliderTrans+=_boxMsgList.Min();
if (boxSliderTrans>=_vpixMouse) {
col = LCDBlinkingColor(_colLight, _colDark);
}
pdp->Fill( boxSlider.Min()(1)+2, boxSlider.Min()(2)+2,
boxSlider.Size()(1)-4, boxSlider.Size()(2)-4, col);
}
// print text of current message
void PrintMessageText(CDrawPort *pdp)
{
if (_acmMessages.Count()==0 ||
_iActiveMessage>=_acmMessages.Count()||
fComputerFadeValue<0.99f) {
return;
}
SetFont2(pdp);
// print subject
CTString strSubject0;
CTString strSubject1;
CTString strSubject2;
//strSubject.PrintF("%g", _fMsgAppearFade);
const char *strSubject = _acmMessages[_iActiveMessage].cm_strSubject;
INDEX ctSubjectLen = strlen(strSubject);
INDEX ctToPrint = int(_fMsgAppearDelta*20.0f);
for (INDEX iChar=0; iChar<ctSubjectLen; iChar++) {
char strChar[2];
strChar[0] = strSubject[iChar];
strChar[1] = 0;
if (iChar>ctToPrint) {
NOTHING;
} else if (iChar==ctToPrint) {
strSubject2+=strChar;
} else if (iChar==ctToPrint-1) {
strSubject1+=strChar;
} else {
strSubject0+=strChar;
}
}
PIX pixWidth0 = pdp->GetTextWidth(strSubject0);
PIX pixWidth1 = pdp->GetTextWidth(strSubject1);
pdp->PutText(strSubject0, _pixMarginI, _pixMarginJ-1, _colMedium);
pdp->PutText(strSubject1, _pixMarginI+pixWidth0, _pixMarginJ-1, LerpColor( _colLight, _colMedium, 0.5f));
pdp->PutText(strSubject2, _pixMarginI+pixWidth0+pixWidth1, _pixMarginJ-1, _colLight);
pdp->DrawLine(0, PIX(_pixMarginJ*4), _boxMsgText.Size()(1), PIX(_pixMarginJ*4), _colBoxes);
// fill in fresh player statistics
if (strncmp(_acmMessages[_iActiveMessage].cm_strText, "$STAT", 5)==0) {
_ppenPlayer->GetStats(_strStatsDetails, CST_DETAIL, _ctTextCharsPerRow);
_acmMessages[_iActiveMessage].cm_ctFormattedWidth = 0;
}
// format text
_acmMessages[_iActiveMessage].PrepareMessage(_ctTextCharsPerRow);
SetFont1(pdp);
INDEX ctLineToPrint = int(_fMsgAppearDelta*20.0f);
// print it
PIX pixJ = _pixMarginJ*4;
for (INDEX iLine = _iTextLineOnScreen;
iLine<_iTextLineOnScreen+_ctTextLinesOnScreen;
iLine++) {
INDEX iPrintLine = iLine-_iTextLineOnScreen;
if (iPrintLine>ctLineToPrint) {
continue;
}
COLOR col = LerpColor( _colLight, _colMedium, Clamp( FLOAT(ctLineToPrint-iPrintLine)/3, 0.0f, 1.0f));
pdp->PutText(_acmMessages[_iActiveMessage].GetLine(iLine),
_pixMarginI, pixJ, col);
pixJ+=_pixCharSizeJ;
}
PIXaabbox2D boxSliderSpace = GetTextSliderSpace();
LCDDrawBox(0,0,boxSliderSpace, _colBoxes);
PIXaabbox2D boxSlider = GetTextSliderBox();
COLOR col = _colBoxes;
PIXaabbox2D boxSliderTrans = boxSlider;
boxSliderTrans+=_boxMsgText.Min();
if (boxSliderTrans>=_vpixMouse) {
col = LCDBlinkingColor(_colLight, _colDark);
}
pdp->Fill( boxSlider.Min()(1)+2, boxSlider.Min()(2)+2,
boxSlider.Size()(1)-4, boxSlider.Size()(2)-4, col);
}
void RenderMessagePicture(CDrawPort *pdp)
{
CCompMessage &cm = _acmMessages[_iActiveMessage];
// try to
try {
// load image
_toPicture.SetData_t(cm.cm_fnmPicture);
((CTextureData*)_toPicture.GetData())->Force(TEX_CONSTANT);
// if failed
} catch(char *strError) {
// report error
CPrintF("Cannot load '%s':\n%s\n", (CTString&)cm.cm_fnmPicture, strError);
// do nothing
return;
}
// get image and box sizes
PIX pixImgSizeI = _toPicture.GetWidth();
PIX pixImgSizeJ = _toPicture.GetHeight();
PIXaabbox2D boxPic(PIX2D(_pixMarginI, _pixMarginJ),
PIX2D(_boxMsgImage.Size()(1)-_pixMarginI, _boxMsgImage.Size()(2)-_pixMarginJ));
PIX pixBoxSizeI = boxPic.Size()(1);
PIX pixBoxSizeJ = boxPic.Size()(2);
PIX pixCenterI = _boxMsgImage.Size()(1)/2;
PIX pixCenterJ = _boxMsgImage.Size()(2)/2;
// find image stretch to fit in box
FLOAT fStretch = Min(FLOAT(pixBoxSizeI)/pixImgSizeI, FLOAT(pixBoxSizeJ)/pixImgSizeJ);
// draw the image
pdp->PutTexture(&_toPicture,
PIXaabbox2D(
PIX2D(pixCenterI-pixImgSizeI*fStretch/2, pixCenterJ-pixImgSizeJ*fStretch/2),
PIX2D(pixCenterI+pixImgSizeI*fStretch/2, pixCenterJ+pixImgSizeJ*fStretch/2)));
}
void RenderMessageStats(CDrawPort *pdp)
{
CSessionProperties *psp = (CSessionProperties *)_pNetwork->GetSessionProperties();
ULONG ulLevelMask = psp->sp_ulLevelsMask;
INDEX iLevel = -1;
if (psp->sp_bCooperative) {
extern void RenderMap( CDrawPort *pdp, ULONG ulLevelMask, CProgressHookInfo *pphi);
if (pdp->Lock()) {
// get sizes
PIX pixSizeI = pdp->GetWidth();
PIX pixSizeJ = pdp->GetHeight();
// clear bcg
pdp->Fill( 1, 1, pixSizeI-2, pixSizeJ-2, C_BLACK|CT_OPAQUE);
// render the map if not fading
COLOR colFade = LCDFadedColor(C_WHITE|255);
if( (colFade&255) == 255) {
RenderMap( pdp, ulLevelMask, NULL);
}
pdp->Unlock();
}
}
}
extern void RenderMessageModel(CDrawPort *pdp, const CTString &strModel);
// draw image of current message
void RenderMessageImage(CDrawPort *pdp)
{
if (!GetSP()->sp_bCooperative) {
return;
}
// if no message
if (_acmMessages.Count()==0 || fComputerFadeValue<0.99f) {
// render empty
LCDRenderClouds2();
LCDScreenBox(_colBoxes);
return;
}
CCompMessage &cm = _acmMessages[_iActiveMessage];
if (cm.cm_itImage == CCompMessage::IT_STATISTICS) {
_pGame->LCDRenderCompGrid();
}
LCDRenderClouds2();
LCDScreenBox(_colBoxes);
// if no image
if (cm.cm_itImage == CCompMessage::IT_NONE) {
// do nothing
return;
} else if (cm.cm_itImage == CCompMessage::IT_PICTURE) {
RenderMessagePicture(pdp);
} else if (cm.cm_itImage == CCompMessage::IT_STATISTICS) {
RenderMessageStats(pdp);
} else if (cm.cm_itImage == CCompMessage::IT_MODEL) {
RenderMessageModel(pdp, cm.cm_strModel);
} else {
ASSERT(FALSE);
}
}
// find first group with some unread message
static BOOL FindGroupWithUnread(void)
{
CDynamicStackArray<CCompMessageID> &acmiMsgs = _ppenPlayer->m_acmiMessages;
for(INDEX i=acmiMsgs.Count()-1; i>=0; i--) {
CCompMessageID &cmi = acmiMsgs[i];
// if it unread
if (!cmi.cmi_bRead) {
_cmtWantedType = cmi.cmi_cmtType;
return TRUE;
}
}
// if none found, select statistics
_cmtWantedType = CMT_STATISTICS;
return FALSE;
}
static void ComputerOn(void)
{
// init button names
_astrButtonTexts[CMT_INFORMATION ] = TRANS("tactical data");
_astrButtonTexts[CMT_BACKGROUND ] = TRANS("strategic data");
_astrButtonTexts[CMT_WEAPONS ] = TRANS("weapons");
_astrButtonTexts[CMT_ENEMIES ] = TRANS("enemies");
_astrButtonTexts[CMT_STATISTICS ] = TRANS("statistics");
_iFirstMessageOnScreen = -1;
_iWantedFirstMessageOnScreen = 0;
_iActiveMessage = 0;
_cmtCurrentType = (enum CompMsgType)-1;
_cmtWantedType = CMT_INFORMATION;
_acmMessages.Clear();
ASSERT(_ppenPlayer!=NULL);
if (_ppenPlayer==NULL) {
return;
}
// fill in player statistics
_ppenPlayer->GetStats(_strStatsDetails, CST_DETAIL, _ctTextCharsPerRow);
// if end of level
if (_ppenPlayer!=NULL && _ppenPlayer->m_bEndOfLevel || _pNetwork->IsGameFinished()) {
// select statistics
_cmtWantedType = CMT_STATISTICS;
// if not end of level
} else {
// find group with some unread messages
FindGroupWithUnread();
}
}
static void ComputerOff(void)
{
_acmMessages.Clear();
_pShell->Execute("FreeUnusedStock();");
}
static void ExitRequested(void)
{
// if end of game
if (_ppenPlayer!=NULL && _ppenPlayer->m_bEndOfGame || _pNetwork->IsGameFinished()) {
// if in single player
if (GetSP()->sp_bSinglePlayer) {
// request app to show high score
_pShell->Execute("sam_bMenuHiScore=1;");
}
// hard turn off
_pGame->gm_csComputerState = CS_OFF;
fComputerFadeValue = 0.0f;
ComputerOff();
cmp_ppenPlayer = NULL;
// stop current game
_pGame->StopGame();
// if not end of level
} else {
// if can be rendered on second display
if (cmp_ppenDHPlayer!=NULL) {
// clear pressed keys
_pInput->ClearInput();
// just switch to background fast
_pGame->gm_csComputerState = CS_ONINBACKGROUND;
cmp_ppenPlayer = NULL;
// if no second display
} else {
// start turning off
_pGame->gm_csComputerState = CS_TURNINGOFF;
}
}
// turn off end of level for player
if (_ppenPlayer!=NULL) {
_ppenPlayer->m_bEndOfLevel = FALSE;
}
}
void CGame::ComputerMouseMove(PIX pixX, PIX pixY)
{
_vpixMouse(1) += pixX-_vpixExternMouse(1);
_vpixMouse(2) += pixY-_vpixExternMouse(2);
_vpixExternMouse(1) = pixX;
_vpixExternMouse(2) = pixY;
// if dragging
if (_pixSliderDragJ>=0) {
PIX pixDelta = _vpixMouse(2)-_pixSliderDragJ;
if (_bSliderDragText) {
if (_iActiveMessage<_acmMessages.Count()) {
INDEX ctTextLines = _acmMessages[_iActiveMessage].cm_ctFormattedLines;
INDEX iWantedLine = _iSliderDragLine+
SliderPixToIndex(pixDelta, _ctTextLinesOnScreen, ctTextLines, GetTextSliderSpace());
MessageTextUpDn(iWantedLine-_iTextLineOnScreen);
}
} else {
INDEX ctLines = _acmMessages.Count();
INDEX iWantedLine = _iSliderDragLine+
SliderPixToIndex(pixDelta, _ctMessagesOnScreen, ctLines, GetMsgSliderSpace());
MessagesUpDn(iWantedLine-_iFirstMessageOnScreen);
}
}
}
void CGame::ComputerKeyDown(MSG msg)
{
static BOOL bRDown = FALSE;
// if computer is not active
if (_pGame->gm_csComputerState!=CS_ON && _pGame->gm_csComputerState!=CS_TURNINGON) {
// do nothing
return;
}
// if escape pressed
if (msg.message==WM_KEYDOWN && msg.wParam==VK_ESCAPE) {
ExitRequested();
}
// if right mouse pressed
if (msg.message==WM_RBUTTONDOWN || msg.message==WM_RBUTTONDBLCLK) {
bRDown = TRUE;
}
// if right mouse released
if (bRDown && msg.message==WM_RBUTTONUP) {
bRDown = FALSE;
// mark current message as read
MarkCurrentRead();
// find a group with first unread message
BOOL bHasUnread = FindGroupWithUnread();
// if some
if (bHasUnread) {
// select first unread message in it
NextUnreadMessage();
} else {
ExitRequested();
}
}
if (msg.message==WM_KEYDOWN) {
switch (msg.wParam) {
// change message types on number keys
case '1': _cmtWantedType = CMT_INFORMATION ; return;
case '2': _cmtWantedType = CMT_WEAPONS ; return;
case '3': _cmtWantedType = CMT_ENEMIES ; return;
case '4': _cmtWantedType = CMT_BACKGROUND ; return;
case '5': _cmtWantedType = CMT_STATISTICS ; return;
// go to next unread
case 'U':
case VK_SPACE:
NextUnreadMessage(); return;
// scroll message list
case 219: PrevMessage(); return;
case 221: NextMessage(); return;
// mark current message as read and go to next
case VK_RETURN: MarkCurrentRead(); NextUnreadMessage(); return;
// scroll message text
case VK_UP: MessageTextUp(1); return;
case VK_DOWN: MessageTextDn(1); return;
case VK_PRIOR:MessageTextUp(_ctTextLinesOnScreen-1); return;
case VK_NEXT: MessageTextDn(_ctTextLinesOnScreen-1); return;
};
}
// if left mouse pressed
if (msg.message==WM_LBUTTONDOWN || msg.message==WM_LBUTTONDBLCLK) {
BOOL bOverMsgSlider = FALSE;
// if over slider
{PIXaabbox2D boxSlider = GetTextSliderBox();
PIXaabbox2D boxSliderTrans = boxSlider;
boxSliderTrans+=_boxMsgText.Min();
if (boxSliderTrans>=_vpixMouse) {
bOverMsgSlider = TRUE;
// start dragging
_bSliderDragText = TRUE;
_pixSliderDragJ=_vpixMouse(2);
_iSliderDragLine = _iTextLineOnScreen;
}}
// if over slider
{PIXaabbox2D boxSlider = GetMsgSliderBox();
PIXaabbox2D boxSliderTrans = boxSlider;
boxSliderTrans+=_boxMsgList.Min();
if (boxSliderTrans>=_vpixMouse) {
// start dragging
_bSliderDragText = FALSE;
_pixSliderDragJ=_vpixMouse(2);
_iSliderDragLine = _iFirstMessageOnScreen;
}}
// if over some button
{for(INDEX i=0; i<CMT_COUNT; i++) {
if (_boxButton[i]>=_vpixMouse) {
// switch to that message type
_cmtWantedType = (CompMsgType)i;
}
}}
// if over some message
{for(INDEX i=0; i<_ctMessagesOnScreen; i++) {
if (GetMsgListBox(i)>=_vpixMouse && !bOverMsgSlider) {
// switch to that message
SelectMessage(_iFirstMessageOnScreen+i);
}
}}
}
// if left mouse released
if (msg.message==WM_LBUTTONUP) {
// stop dragging
_pixSliderDragJ=-1;
// if over exit
if (_boxExit>=_vpixMouse) {
// exit
ExitRequested();
}
}
}
void CGame::ComputerRender(CDrawPort *pdp)
{
// if playing a demo
if (_pNetwork->IsPlayingDemo()) {
// never call computer
cmp_ppenPlayer = NULL;
}
// disable netricsa for non-local players
if (cmp_ppenPlayer!=NULL && !_pNetwork->IsPlayerLocal(cmp_ppenPlayer)) {
cmp_ppenPlayer = NULL;
}
if (cmp_ppenDHPlayer!=NULL && !_pNetwork->IsPlayerLocal(cmp_ppenDHPlayer)) {
cmp_ppenDHPlayer = NULL;
}
if (cmp_ppenDHPlayer!=NULL && !pdp->IsDualHead()) {
cmp_ppenDHPlayer = NULL;
}
// initially - no player
_ppenPlayer=NULL;
// if player calls computer
if (cmp_ppenPlayer!=NULL) {
// use that player
_ppenPlayer = cmp_ppenPlayer;
// if computer is on in background
if (_pGame->gm_csComputerState==CS_ONINBACKGROUND) {
// just toggle to on
_pGame->gm_csComputerState=CS_ON;
// find group with some unread messages
FindGroupWithUnread();
// force reinit
_cmtCurrentType = (enum CompMsgType)-1;
}
// if using dualhead to render computer on second display
} else if (cmp_ppenDHPlayer!=NULL) {
// use that player
_ppenPlayer = cmp_ppenDHPlayer;
// clear dualhead request - it has to be reinitialized every frame
cmp_ppenDHPlayer = NULL;
// if viewing statistics
if (_cmtWantedType == CMT_STATISTICS) {
// fill in fresh player statistics
_ppenPlayer->GetStats(_strStatsDetails, CST_DETAIL, _ctTextCharsPerRow);
// force updating
UpdateType(TRUE);
}
// if computer is not on or on in background
if (_pGame->gm_csComputerState!=CS_ON && _pGame->gm_csComputerState!=CS_ONINBACKGROUND) {
// switch on fast
ComputerOn();
fComputerFadeValue = 1.0f;
_pGame->gm_csComputerState = CS_ONINBACKGROUND;
cmp_bInitialStart = FALSE; // end of eventual initial start
}
// if should update to new message
if (cmp_bUpdateInBackground) {
cmp_bUpdateInBackground = FALSE;
FindGroupWithUnread();
// force reinit
_cmtCurrentType = (enum CompMsgType)-1;
}
}
// if no player
if (_ppenPlayer==NULL) {
// make sure computer is off
_pGame->gm_csComputerState=CS_OFF;
// do nothing more
return;
}
// if computer is not active
if (_pGame->gm_csComputerState==CS_OFF) {
// just remember time
tvComputerLast = _pTimer->GetHighPrecisionTimer();
// if a player wants computer
if (_ppenPlayer!=NULL) {
// start turning on
_pGame->gm_csComputerState=CS_TURNINGON;
ComputerOn();
} else {
return;
}
}
// calculate up-down speed to be independent of refresh speed
CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
CTimerValue tvDelta = tvNow - tvComputerLast;
tvComputerLast = tvNow;
FLOAT fFadeSpeed = (FLOAT)(tvDelta.GetSeconds() / tmComputerFade);
// if computer is dropping down
if( _pGame->gm_csComputerState==CS_TURNINGON) {
// move it down
fComputerFadeValue += fFadeSpeed;
// if finished moving
if( fComputerFadeValue>1.0f) {
// stop
fComputerFadeValue = 1.0f;
_pGame->gm_csComputerState = CS_ON;
cmp_bInitialStart = FALSE; // end of eventual initial start
}
}
// if computer is pulling up
if( _pGame->gm_csComputerState==CS_TURNINGOFF) {
// move it up
fComputerFadeValue -= fFadeSpeed;
// if finished moving
if( fComputerFadeValue<0.0f) {
// stop
fComputerFadeValue = 0.0f;
_pGame->gm_csComputerState = CS_OFF;
ComputerOff();
cmp_ppenPlayer = NULL;
// exit computer
return;
}
}
// safety check -> do not proceed if no player
if (_ppenPlayer==NULL) {
return;
}
// lock drawport
CDrawPort dpComp(pdp, FALSE);
if(!dpComp.Lock()) {
// do nothing
return;
}
// if in fullscreen
CDisplayMode dmCurrent;
_pGfx->GetCurrentDisplayMode(dmCurrent);
if (dmCurrent.IsFullScreen() && dmCurrent.IsDualHead()) {
// clamp mouse pointer
_vpixMouse(1) = Clamp(_vpixMouse(1), 0L, dpComp.GetWidth());
_vpixMouse(2) = Clamp(_vpixMouse(2), 0L, dpComp.GetHeight());
// if in window
} else {
// use same mouse pointer as windows
_vpixMouse = _vpixExternMouse;
// if dualhead
if (dpComp.dp_MinI>0) {
// offset by half screen
_vpixMouse(1) -= dpComp.GetWidth();
}
// if widescreen
if (dpComp.dp_MinJ>0) {
// offset by screen top
_vpixMouse(2) -= dpComp.dp_MinJ;
}
}
TIME tmOld = _pTimer->CurrentTick();
FLOAT fLerpOld = _pTimer->GetLerpFactor();
FLOAT fSec = tvNow.GetSeconds();
TIME tmTick = floor(fSec/_pTimer->TickQuantum)*_pTimer->TickQuantum;
FLOAT fLerp = (fSec-tmTick)/_pTimer->TickQuantum;
_pTimer->SetCurrentTick(tmTick);
_pTimer->SetLerp(fLerp);
LCDPrepare(1.0f);//ClampUp(fComputerFadeValue*10,1.0f));
LCDSetDrawport(&dpComp);
// if initial start
if (cmp_bInitialStart) {
// do not allow game to show through
dpComp.Fill(C_BLACK|255);
// if normal start
} else {
// fade over game view
dpComp.Fill(LCDFadedColor(C_BLACK|255));
}
dpComp.FillZBuffer(1.0f);
// update screen geometry
UpdateSize(&dpComp);
// update scroll positions
UpdateType();
UpdateFirstOnScreen();
// check for message change
UpdateMessageAppearing();
// mark current message as read
MarkCurrentRead();
// get current time and alpha value
FLOAT tmNow = (FLOAT)tvNow.GetSeconds();
ULONG ulA = NormFloatToByte(fComputerFadeValue);
_colLight = LCDFadedColor(C_WHITE|255);
_colMedium = LCDFadedColor(SE_COL_BLUE_LIGHT|255);
_colDark = LCDFadedColor(LerpColor(SE_COL_BLUE_DARK, SE_COL_BLUE_LIGHT, 0.5f)|255);
_colBoxes = LCDFadedColor(LerpColor(SE_COL_BLUE_DARK, SE_COL_BLUE_LIGHT, 0.5f)|255);
// background
LCDRenderCloudsForComp();
// dpComp.DrawLine( 0, pixSizeJ-1, pixSizeI, pixSizeJ-1, C_GREEN|ulA);
// all done
dpComp.Unlock();
// print title
CDrawPort dpTitle(&dpComp, _boxTitle);
if (dpTitle.Lock()) {
LCDSetDrawport(&dpTitle);
LCDRenderCompGrid();
LCDRenderClouds2();
LCDScreenBoxOpenLeft(_colBoxes);
PrintTitle(&dpTitle);
dpTitle.Unlock();
}
// print exit button
CDrawPort dpExit(&dpComp, _boxExit);
if (dpExit.Lock()) {
LCDSetDrawport(&dpExit);
LCDRenderCompGrid();
LCDRenderClouds2();
LCDScreenBoxOpenRight(_colBoxes);
PrintExit(&dpExit);
dpExit.Unlock();
}
// print buttons
for (INDEX i=0; i<CMT_COUNT; i++) {
PrintButton(&dpComp, i);
}
// print list of messages
CDrawPort dpMsgList(&dpComp, _boxMsgList);
if (dpMsgList.Lock()) {
LCDSetDrawport(&dpMsgList);
LCDRenderCompGrid();
LCDRenderClouds2();
LCDScreenBox(_colBoxes);
PrintMessageList(&dpMsgList);
dpMsgList.Unlock();
}
// print text of current message
CDrawPort dpMsgText(&dpComp, _boxMsgText);
if (dpMsgText.Lock()) {
LCDSetDrawport(&dpMsgText);
LCDRenderCompGrid();
LCDRenderClouds2();
LCDScreenBox(_colBoxes);
PrintMessageText(&dpMsgText);
dpMsgText.Unlock();
}
// draw image of current message
CDrawPort dpMsgImage(&dpComp, _boxMsgImage);
if (dpMsgImage.Lock()) {
LCDSetDrawport(&dpMsgImage);
RenderMessageImage(&dpMsgImage);
dpMsgImage.Unlock();
}
// render mouse pointer on top of everything else
if (_pGame->gm_csComputerState != CS_ONINBACKGROUND) {
if (dpComp.Lock()) {
LCDSetDrawport(&dpComp);
LCDDrawPointer(_vpixMouse(1), _vpixMouse(2));
dpComp.Unlock();
}
}
_pTimer->SetCurrentTick(tmOld);
_pTimer->SetLerp(fLerpOld);
}
void CGame::ComputerForceOff()
{
cmp_ppenPlayer=NULL;
cmp_ppenDHPlayer=NULL;
_pGame->gm_csComputerState = CS_OFF;
fComputerFadeValue = 0.0f;
_ppenPlayer = NULL;
}