Initial commit

This commit is contained in:
Eagle517
2025-02-17 23:17:30 -06:00
commit 7cad314c94
4726 changed files with 1145203 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,186 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIAVIBITMAPCTRL_H_
#define _GUIAVIBITMAPCTRL_H_
#if !ENABLE_AVI_GUI || !ENABLE_MPG_GUI
class GuiAviBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
bool mDone;
public:
DECLARE_CONOBJECT(GuiAviBitmapCtrl);
GuiAviBitmapCtrl();
~GuiAviBitmapCtrl();
static void initPersistFields();
};
#endif /* No movie control */
#if ENABLE_AVI_GUI
class GuiAviBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
StringTableEntry mAviFilename;
StringTableEntry mWavFilename;
U32 mNumTextures;
TextureHandle *mTextureHandles;
U32 mWidthCount;
U32 mHeightCount;
U32 mBitmapWidth;
U32 mBitmapAlignedWidth;
U32 mBitmapHeight;
PAVIFILE mPFile;
PAVISTREAM mPAviVideo; // video stream to play
AUDIOHANDLE mWavHandle; // music to play along with it
bool mBPlaying;
bool mDone;
bool mLetterBox;
F32 mFrate;
F32 mSpeed;
S32 mTimePlayStart;
S32 mTimePlayStartPos;
S16 mPlayFPrev;
S16 mPlayFSkipped;
S32 mVidsCurrent; // attempted frame to draw
S32 mVidsPrevious; // last successfully decoded frame
S32 mVidsPrevKey, mVidsNextKey;
S32 mVidsFirst, mVidsLast;
S32 mCBVBuf;
U8 *mPVBuf;
HIC mHic;
FOURCC mFccHandler;
BITMAPINFOHEADER *mPBiSrc;
BITMAPINFOHEADER *mPBiDst;
S32 mCBuf;
U8 *mPBuf;
bool mSwapRB;
ALint mAudioLatency;
S32 fileOpen();
S32 fileClose();
S32 movieOpen();
S32 movieClose();
S32 vidsVideoOpen();
S32 vidsVideoClose();
S32 vidsVideoStart();
S32 vidsVideoStop();
S32 vidsVideoDraw();
S32 vidsTimeToSample(S32 lTime);
bool vidsIsKey(S32 frame = -1);
void vidsResetDraw() { mVidsPrevious = -1; }
bool vidsSync();
void vidsCatchup();
S32 vcmOpen(FOURCC fccHandler, BITMAPINFOHEADER *pbiSrc);
S32 vcmClose();
S32 vcmBegin();
S32 vcmEnd();
S32 vcmDrawStart();
S32 vcmDrawStop();
S32 vcmDraw(U64 dwICflags = 0);
S32 vcmDrawIn(U64 dwICflags = 0);
bool sndOpen();
void sndStart();
void sndStop();
S32 getMilliseconds();
public:
DECLARE_CONOBJECT(GuiAviBitmapCtrl);
GuiAviBitmapCtrl();
~GuiAviBitmapCtrl();
static void initPersistFields();
void setFilename(const char *filename);
S32 movieStart();
S32 movieStop();
bool onWake();
void onSleep();
void onMouseDown(const GuiEvent&);
bool onKeyDown(const GuiEvent&);
void onRender(Point2I offset, const RectI &updateRect);
};
#endif /* ENABLE_AVI_GUI */
#if ENABLE_MPG_GUI
class GuiAviBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
StringTableEntry mAviFilename;
StringTableEntry mWavFilename;
U32 mNumTextures;
TextureHandle *mTextureHandles;
U32 mWidthCount;
U32 mHeightCount;
U32 mBitmapWidth;
U32 mBitmapAlignedWidth;
U32 mBitmapHeight;
SDL_Surface *mSurface;
U8 *mPBuf;
SDL_mutex *mDecodeLock;
ALint mAudioLatency;
SMPEG *mMPEG; // video stream to play
AUDIOHANDLE mWavHandle; // music to play along with it
bool mBPlaying;
bool mDone;
bool mLetterBox;
SMPEG_Info mInfo;
S32 fileOpen();
S32 fileClose();
S32 movieOpen();
S32 movieClose();
bool sndOpen();
void sndStart();
void sndStop();
public:
DECLARE_CONOBJECT(GuiAviBitmapCtrl);
GuiAviBitmapCtrl();
~GuiAviBitmapCtrl();
static void initPersistFields();
void setFilename(const char *filename);
S32 movieStart();
S32 movieStop();
bool onWake();
void onSleep();
void onMouseDown(const GuiEvent&);
bool onKeyDown(const GuiEvent&);
void onRender(Point2I offset, const RectI &updateRect);
};
#endif /* ENABLE_MPG_GUI */
#endif /* _GUIAVIBITMAPCTRL_H_ */

View File

@ -0,0 +1,148 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "dgl/gBitmap.h"
#include "gui/core/guiControl.h"
#include "dgl/gTexManager.h"
#include "dgl/gChunkedTexManager.h"
class GuiChunkedBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
void renderRegion(const Point2I &offset, const Point2I &extent);
protected:
StringTableEntry mBitmapName;
ChunkedTextureHandle mTexHandle;
bool mUseVariable;
bool mTile;
public:
//creation methods
DECLARE_CONOBJECT(GuiChunkedBitmapCtrl);
GuiChunkedBitmapCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onSleep();
void setBitmap(const char *name);
void onRender(Point2I offset, const RectI &updateRect);
};
IMPLEMENT_CONOBJECT(GuiChunkedBitmapCtrl);
void GuiChunkedBitmapCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Misc");
addField( "bitmap", TypeFilename, Offset( mBitmapName, GuiChunkedBitmapCtrl ) );
addField( "useVariable", TypeBool, Offset( mUseVariable, GuiChunkedBitmapCtrl ) );
addField( "tile", TypeBool, Offset( mTile, GuiChunkedBitmapCtrl ) );
endGroup("Misc");
}
ConsoleMethod( GuiChunkedBitmapCtrl, setBitmap, void, 3, 3, "(string filename)"
"Set the bitmap contained in this control.")
{
object->setBitmap( argv[2] );
}
GuiChunkedBitmapCtrl::GuiChunkedBitmapCtrl()
{
mBitmapName = StringTable->insert("");
mUseVariable = false;
mTile = false;
}
void GuiChunkedBitmapCtrl::setBitmap(const char *name)
{
bool awake = mAwake;
if(awake)
onSleep();
mBitmapName = StringTable->insert(name);
if(awake)
onWake();
setUpdate();
}
bool GuiChunkedBitmapCtrl::onWake()
{
if(!Parent::onWake())
return false;
if ( mUseVariable )
mTexHandle = ChunkedTextureHandle( getVariable() );
else
mTexHandle = ChunkedTextureHandle( mBitmapName );
return true;
}
void GuiChunkedBitmapCtrl::onSleep()
{
mTexHandle = NULL;
Parent::onSleep();
}
void GuiChunkedBitmapCtrl::renderRegion(const Point2I &offset, const Point2I &extent)
{
U32 widthCount = mTexHandle.getTextureCountWidth();
U32 heightCount = mTexHandle.getTextureCountHeight();
if(!widthCount || !heightCount)
return;
F32 widthScale = F32(extent.x) / F32(mTexHandle.getWidth());
F32 heightScale = F32(extent.y) / F32(mTexHandle.getHeight());
dglSetBitmapModulation(ColorF(1,1,1));
for(U32 i = 0; i < widthCount; i++)
{
for(U32 j = 0; j < heightCount; j++)
{
TextureHandle t = mTexHandle.getSubTexture(i, j);
RectI stretchRegion;
stretchRegion.point.x = (S32)(i * 256 * widthScale + offset.x);
stretchRegion.point.y = (S32)(j * 256 * heightScale + offset.y);
if(i == widthCount - 1)
stretchRegion.extent.x = extent.x + offset.x - stretchRegion.point.x;
else
stretchRegion.extent.x = (S32)((i * 256 + t.getWidth() ) * widthScale + offset.x - stretchRegion.point.x);
if(j == heightCount - 1)
stretchRegion.extent.y = extent.y + offset.y - stretchRegion.point.y;
else
stretchRegion.extent.y = (S32)((j * 256 + t.getHeight()) * heightScale + offset.y - stretchRegion.point.y);
dglDrawBitmapStretch(t, stretchRegion);
}
}
}
void GuiChunkedBitmapCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if(mTexHandle)
{
if (mTile)
{
int stepy = 0;
for(int y = 0; offset.y + stepy < mBounds.extent.y; stepy += mTexHandle.getHeight())
{
int stepx = 0;
for(int x = 0; offset.x + stepx < mBounds.extent.x; stepx += mTexHandle.getWidth())
renderRegion(Point2I(offset.x+stepx, offset.y+stepy), Point2I(mTexHandle.getWidth(), mTexHandle.getHeight()) );
}
}
else
renderRegion(offset, mBounds.extent);
renderChildControls(offset, updateRect);
}
else
Parent::onRender(offset, updateRect);
}

View File

@ -0,0 +1,119 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "gui/controls/guiBitmapCtrl.h"
class GuiFadeinBitmapCtrl : public GuiBitmapCtrl
{
typedef GuiBitmapCtrl Parent;
public:
U32 wakeTime;
bool done;
U32 fadeinTime;
U32 waitTime;
U32 fadeoutTime;
GuiFadeinBitmapCtrl()
{
wakeTime = 0;
fadeinTime = 1000;
waitTime = 2000;
fadeoutTime = 1000;
done = false;
}
void onPreRender()
{
Parent::onPreRender();
setUpdate();
}
void onMouseDown(const GuiEvent &)
{
Con::executef(this, 1, "click");
}
bool onKeyDown(const GuiEvent &)
{
Con::executef(this, 1, "click");
return true;
}
DECLARE_CONOBJECT(GuiFadeinBitmapCtrl);
bool onWake()
{
if(!Parent::onWake())
return false;
wakeTime = Platform::getRealMilliseconds();
return true;
}
void onRender(Point2I offset, const RectI &updateRect)
{
Parent::onRender(offset, updateRect);
U32 elapsed = Platform::getRealMilliseconds() - wakeTime;
U32 alpha;
if (elapsed < fadeinTime)
{
// fade-in
alpha = 255 - (255 * (F32(elapsed) / F32(fadeinTime)));
}
else if (elapsed < (fadeinTime+waitTime))
{
// wait
alpha = 0;
}
else if (elapsed < (fadeinTime+waitTime+fadeoutTime))
{
// fade out
elapsed -= (fadeinTime+waitTime);
alpha = 255 * F32(elapsed) / F32(fadeoutTime);
}
else
{
// done state
alpha = fadeoutTime ? 255 : 0;
done = true;
}
ColorI color(0,0,0,alpha);
dglDrawRectFill(offset, mBounds.extent + offset, color);
}
static void initPersistFields()
{
Parent::initPersistFields();
addField("fadeinTime", TypeS32, Offset(fadeinTime, GuiFadeinBitmapCtrl));
addField("waitTime", TypeS32, Offset(waitTime, GuiFadeinBitmapCtrl));
addField("fadeoutTime", TypeS32, Offset(fadeoutTime, GuiFadeinBitmapCtrl));
addField("done", TypeBool, Offset(done, GuiFadeinBitmapCtrl));
}
};
IMPLEMENT_CONOBJECT(GuiFadeinBitmapCtrl);

View File

@ -0,0 +1,817 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/game/guiMessageVectorCtrl.h"
#include "gui/utility/messageVector.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "gui/containers/guiScrollCtrl.h"
IMPLEMENT_CONOBJECT(GuiMessageVectorCtrl);
//-------------------------------------- Console functions
ConsoleMethod(GuiMessageVectorCtrl, attach, bool, 3, 3, "(MessageVector item)"
"Make this gui control display messages from the specified MessageVector")
{
MessageVector* pMV = NULL;
Sim::findObject(argv[2], pMV);
if (pMV == NULL) {
Con::errorf(ConsoleLogEntry::General, "Could not find MessageVector: %s", argv[2]);
return false;
}
return object->attach(pMV);
}
ConsoleMethod(GuiMessageVectorCtrl, detach, void, 2, 2, "()"
"Stop listing messages from the MessageVector previously attached to, if any.")
{
if (object->isAttached() == false) {
Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl: double detach");
return;
}
object->detach();
}
struct TempLineBreak
{
S32 start;
S32 end;
};
//--------------------------------------------------------------------------
// Callback for messageVector
void sMVCtrlCallback(void * spectatorKey,
const MessageVector::MessageCode code,
const U32 argument)
{
GuiMessageVectorCtrl* pMVC = reinterpret_cast<GuiMessageVectorCtrl*>(spectatorKey);
pMVC->callbackRouter(code, argument);
}
//--------------------------------------------------------------------------
GuiMessageVectorCtrl::GuiMessageVectorCtrl()
{
VECTOR_SET_ASSOCIATION(mLineWrappings);
VECTOR_SET_ASSOCIATION(mSpecialMarkers);
VECTOR_SET_ASSOCIATION(mLineElements);
mMessageVector = NULL;
mLineSpacingPixels = 0;
mLineContinuationIndent = 10;
mMouseDown = false;
mMouseSpecialLine = -1;
mMouseSpecialRef = -1;
for (U32 i = 0; i < 16; i++)
mAllowedMatches[i] = "";
mSpecialColor.set(0, 0, 255);
mMaxColorIndex = 9;
}
//--------------------------------------------------------------------------
GuiMessageVectorCtrl::~GuiMessageVectorCtrl()
{
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
AssertFatal(mSpecialMarkers.size() == 0, "Error, special markers not properly cleared!");
AssertFatal(mLineElements.size() == 0, "Error, line elements not properly cleared!");
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("lineSpacing", TypeS32, Offset(mLineSpacingPixels, GuiMessageVectorCtrl));
addField("lineContinuedIndex", TypeS32, Offset(mLineContinuationIndent, GuiMessageVectorCtrl));
addField("allowedMatches", TypeString, Offset(mAllowedMatches, GuiMessageVectorCtrl), 16);
addField("matchColor", TypeColorI, Offset(mSpecialColor, GuiMessageVectorCtrl));
addField("maxColorIndex", TypeS32, Offset(mMaxColorIndex, GuiMessageVectorCtrl));
}
bool GuiMessageVectorCtrl::onAdd()
{
return Parent::onAdd();
}
void GuiMessageVectorCtrl::onRemove()
{
Parent::onRemove();
}
//--------------------------------------------------------------------------
bool GuiMessageVectorCtrl::isAttached() const
{
return (mMessageVector != NULL);
}
//--------------------------------------------------------------------------
bool GuiMessageVectorCtrl::attach(MessageVector* newAttachment)
{
AssertFatal(newAttachment, "No attachment!");
if (newAttachment == NULL || !isAwake())
return false;
if (isAttached()) {
Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::attach: overriding attachment");
detach();
}
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
mMessageVector = newAttachment;
mMessageVector->registerSpectator(sMVCtrlCallback, this);
return true;
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::detach()
{
if (isAttached() == false) {
Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::detach: not attached!");
return;
}
mMessageVector->unregisterSpectator(this);
mMessageVector = NULL;
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::lineInserted(const U32 arg)
{
AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
GuiScrollCtrl* pScroll = dynamic_cast<GuiScrollCtrl*>(getParent());
bool fullyScrolled = pScroll->isScrolledToBottom();
mSpecialMarkers.insert(arg);
createSpecialMarkers(mSpecialMarkers[arg], mMessageVector->getLine(arg).message);
mLineWrappings.insert(arg);
createLineWrapping(mLineWrappings[arg], mMessageVector->getLine(arg).message);
mLineElements.insert(arg);
createLineElement(mLineElements[arg], mLineWrappings[arg], mSpecialMarkers[arg]);
U32 numLines = 0;
for (U32 i = 0; i < mLineWrappings.size(); i++) {
// We need to rebuild the physicalLineStart markers at the same time as
// we find out how many of them are left...
mLineElements[i].physicalLineStart = numLines;
numLines += mLineWrappings[i].numLines;
}
U32 newHeight = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1));
resize(mBounds.point, Point2I(mBounds.extent.x, newHeight));
if(fullyScrolled)
pScroll->scrollTo(0, 0x7FFFFFFF);
}
void GuiMessageVectorCtrl::lineDeleted(const U32 arg)
{
AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
AssertFatal(arg < mLineWrappings.size(), "Error, out of bounds line deleted!");
// It's a somewhat involved process to delete the lineelements...
LineElement& rElement = mLineElements[arg];
TextElement* walk = rElement.headLineElements;
while (walk != NULL) {
TextElement* lineWalk = walk->nextInLine;
while (lineWalk != NULL) {
TextElement* temp = lineWalk;
lineWalk = lineWalk->nextPhysicalLine;
delete temp;
}
TextElement* temp = walk;
walk = walk->nextPhysicalLine;
delete temp;
}
rElement.headLineElements = NULL;
mLineElements.erase(arg);
delete [] mLineWrappings[arg].startEndPairs;
mLineWrappings.erase(arg);
delete [] mSpecialMarkers[arg].specials;
mSpecialMarkers.erase(arg);
U32 numLines = 0;
for (U32 i = 0; i < mLineWrappings.size(); i++) {
// We need to rebuild the physicalLineStart markers at the same time as
// we find out how many of them are left...
mLineElements[i].physicalLineStart = numLines;
numLines += mLineWrappings[i].numLines;
}
U32 newHeight = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1));
resize(mBounds.point, Point2I(mBounds.extent.x, newHeight));
}
void GuiMessageVectorCtrl::vectorDeleted()
{
AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared out!");
mMessageVector = NULL;
U32 newHeight = mProfile->mFont->getHeight() + mLineSpacingPixels;
resize(mBounds.point, Point2I(mBounds.extent.x, newHeight));
}
void GuiMessageVectorCtrl::callbackRouter(const MessageVector::MessageCode code,
const U32 arg)
{
switch (code) {
case MessageVector::LineInserted:
lineInserted(arg);
break;
case MessageVector::LineDeleted:
lineDeleted(arg);
break;
case MessageVector::VectorDeletion:
vectorDeleted();
break;
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::createSpecialMarkers(SpecialMarkers& rSpecial, const char* string)
{
// The first thing we need to do is create a version of the string with no uppercase
// chars for matching...
char* pLCCopy = new char[dStrlen(string) + 1];
for (U32 i = 0; string[i] != '\0'; i++)
pLCCopy[i] = dTolower(string[i]);
pLCCopy[dStrlen(string)] = '\0';
Vector<TempLineBreak> tempSpecials(__FILE__, __LINE__);
Vector<S32> tempTypes(__FILE__, __LINE__);
const char* pCurr = pLCCopy;
while (pCurr[0] != '\0') {
const char* pMinMatch = &pLCCopy[dStrlen(string)];
U32 minMatchType = 0xFFFFFFFF;
AssertFatal(pMinMatch[0] == '\0', "Error, bad positioning of sentry...");
for (U32 i = 0; i < 16; i++) {
if (mAllowedMatches[i][0] == '\0')
continue;
// Not the most efficient...
char matchBuffer[512];
dStrncpy(matchBuffer, mAllowedMatches[i], 500);
matchBuffer[499] = '\0';
dStrcat(matchBuffer, "://");
const char* pMatch = dStrstr(pCurr, (const char*)matchBuffer);
if (pMatch != NULL && pMatch < pMinMatch) {
pMinMatch = pMatch;
minMatchType = i;
}
}
if (pMinMatch[0] != '\0') {
AssertFatal(minMatchType != 0xFFFFFFFF, "Hm, that's bad");
// Found a match...
U32 start = pMinMatch - pLCCopy;
U32 j;
for (j = 0; pLCCopy[start + j] != '\0'; j++) {
if (pLCCopy[start + j] == '\n' ||
pLCCopy[start + j] == ' ' ||
pLCCopy[start + j] == '\t')
break;
}
AssertFatal(j > 0, "Error, j must be > 0 at this point!");
U32 end = start + j - 1;
tempSpecials.increment();
tempSpecials.last().start = start;
tempSpecials.last().end = end;
tempTypes.push_back(minMatchType);
pCurr = &pLCCopy[end + 1];
} else {
// No match. This will cause the while loop to terminate...
pCurr = pMinMatch;
}
}
if ((rSpecial.numSpecials = tempSpecials.size()) != 0) {
rSpecial.specials = new SpecialMarkers::Special[tempSpecials.size()];
for (U32 i = 0; i < tempSpecials.size(); i++) {
rSpecial.specials[i].start = tempSpecials[i].start;
rSpecial.specials[i].end = tempSpecials[i].end;
rSpecial.specials[i].specialType = tempTypes[i];
}
} else {
rSpecial.specials = NULL;
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::createLineWrapping(LineWrapping& rWrapping, const char* string)
{
Vector<TempLineBreak> tempBreaks(__FILE__, __LINE__);
U32 i;
U32 currStart = 0;
if (dStrlen(string) != 0) {
for (i = 0; i < dStrlen(string); i++) {
if (string[i] == '\n') {
tempBreaks.increment();
tempBreaks.last().start = currStart;
tempBreaks.last().end = i-1;
currStart = i+1;
} else if (i == dStrlen(string) - 1) {
tempBreaks.increment();
tempBreaks.last().start = currStart;
tempBreaks.last().end = i;
currStart = i+1;
}
}
} else {
tempBreaks.increment();
tempBreaks.last().start = 0;
tempBreaks.last().end = -1;
}
bool used = false;
U32 splitWidth = mBounds.extent.x;
U32 currLine = 0;
while (currLine < tempBreaks.size()) {
TempLineBreak& rLine = tempBreaks[currLine];
if (rLine.start >= rLine.end) {
if (currLine == 0)
splitWidth -= mLineContinuationIndent;
currLine++;
continue;
}
// Ok, there's some actual text in this line. How long is it?
U32 baseLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], rLine.end-rLine.start+1);
if (baseLength > splitWidth) {
// DMMNOTE: Replace with bin search eventually
U32 currPos;
U32 breakPos;
for (currPos = 0; currPos < rLine.end-rLine.start+1; currPos++) {
U32 currLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], currPos+1);
if (currLength > splitWidth) {
// Make sure that the currPos has advanced, then set the breakPoint.
breakPos = currPos != 0 ? currPos - 1 : 0;
break;
}
}
if (currPos == rLine.end-rLine.start+1) {
AssertFatal(false, "Error, if the line must be broken, the position must be before this point!");
currLine++;
continue;
}
// Ok, the character at breakPos is the last valid char we can render. We
// want to scan back to the first whitespace character (which, in the bounds
// of the line, is guaranteed to be a space or a tab).
U32 originalBreak = breakPos;
while (true) {
if (string[rLine.start + breakPos] == ' ' || string[rLine.start + breakPos] == '\t') {
break;
} else {
AssertFatal(string[rLine.start + breakPos] != '\n',
"Bad characters in line range...");
if (breakPos == 0) {
breakPos = originalBreak;
break;
}
breakPos--;
}
}
// Ok, everything up to and including breakPos is in the currentLine. Insert
// a new line at this point, and put everything after in that line.
S32 oldStart = rLine.start;
S32 oldEnd = rLine.end;
rLine.end = rLine.start + breakPos;
// Note that rLine is NOTNOTNOTNOT valid after this point!
tempBreaks.insert(currLine+1);
tempBreaks[currLine+1].start = oldStart + breakPos + 1;
tempBreaks[currLine+1].end = oldEnd;
}
if (currLine == 0)
splitWidth -= mLineContinuationIndent;
currLine++;
}
rWrapping.numLines = tempBreaks.size();
rWrapping.startEndPairs = new LineWrapping::SEPair[tempBreaks.size()];
for (i = 0; i < tempBreaks.size(); i++) {
rWrapping.startEndPairs[i].start = tempBreaks[i].start;
rWrapping.startEndPairs[i].end = tempBreaks[i].end;
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::createLineElement(LineElement& rElement,
LineWrapping& rWrapping,
SpecialMarkers& rSpecial)
{
// First, do a straighforward translation of the wrapping...
TextElement** ppWalk = &rElement.headLineElements;
for (U32 i = 0; i < rWrapping.numLines; i++) {
*ppWalk = new TextElement;
(*ppWalk)->nextInLine = NULL;
(*ppWalk)->nextPhysicalLine = NULL;
(*ppWalk)->specialReference = -1;
(*ppWalk)->start = rWrapping.startEndPairs[i].start;
(*ppWalk)->end = rWrapping.startEndPairs[i].end;
ppWalk = &((*ppWalk)->nextPhysicalLine);
}
if (rSpecial.numSpecials != 0) {
// Ok. Now, walk down the lines, and split the elements into their contiuent parts...
//
TextElement* walk = rElement.headLineElements;
while (walk) {
TextElement* walkAcross = walk;
while (walkAcross) {
S32 specialMatch = -1;
for (U32 i = 0; i < rSpecial.numSpecials; i++) {
if (walkAcross->start <= rSpecial.specials[i].end &&
walkAcross->end >= rSpecial.specials[i].start) {
specialMatch = i;
break;
}
}
if (specialMatch != -1) {
// We have a match here. Break it into the appropriate number of segments.
// this will vary depending on how the overlap is occuring...
if (walkAcross->start >= rSpecial.specials[specialMatch].start) {
if (walkAcross->end <= rSpecial.specials[specialMatch].end) {
// The whole thing is part of the special
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->specialReference = specialMatch;
} else {
// The first part is in the special, the tail is out
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->specialReference = -1;
walkAcross->specialReference = specialMatch;
walkAcross->nextInLine->end = walkAcross->end;
walkAcross->end = rSpecial.specials[specialMatch].end;
walkAcross->nextInLine->start = rSpecial.specials[specialMatch].end + 1;
AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!");
}
walkAcross = walkAcross->nextInLine;
} else {
if (walkAcross->end <= rSpecial.specials[specialMatch].end) {
// The first part is out of the special, the second part in.
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->specialReference = specialMatch;
walkAcross->specialReference = -1;
walkAcross->nextInLine->end = walkAcross->end;
walkAcross->end = rSpecial.specials[specialMatch].start - 1;
walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start;
AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!");
walkAcross = walkAcross->nextInLine;
} else {
// First out, middle in, last out. Oy.
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->specialReference = specialMatch;
walkAcross->nextInLine->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->nextInLine->specialReference = -1;
walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start;
walkAcross->nextInLine->end = rSpecial.specials[specialMatch].end;
walkAcross->nextInLine->nextInLine->start = rSpecial.specials[specialMatch].end+1;
walkAcross->nextInLine->nextInLine->end = walkAcross->end;
walkAcross->end = walkAcross->nextInLine->start - 1;
AssertFatal((walkAcross->end >= walkAcross->start &&
walkAcross->nextInLine->end >= walkAcross->nextInLine->start &&
walkAcross->nextInLine->nextInLine->end >= walkAcross->nextInLine->nextInLine->start),
"Bad textelements generated!");
walkAcross = walkAcross->nextInLine->nextInLine;
}
}
} else {
walkAcross = walkAcross->nextInLine;
}
}
walk = walk->nextPhysicalLine;
}
}
}
//--------------------------------------------------------------------------
bool GuiMessageVectorCtrl::onWake()
{
if (Parent::onWake() == false)
return false;
if (bool(mProfile->mFont) == false)
return false;
mMinSensibleWidth = 1;
for (U32 i = 0; i < 256; i++) {
if (mProfile->mFont->isValidChar(U8(i))) {
if (mProfile->mFont->getCharWidth(U8(i)) > mMinSensibleWidth)
mMinSensibleWidth = mProfile->mFont->getCharWidth(U8(i));
}
}
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
return true;
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::onSleep()
{
if (isAttached())
detach();
Parent::onSleep();
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::onRender(Point2I offset,
const RectI& updateRect)
{
Parent::onRender(offset, updateRect);
if (isAttached()) {
U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels;
U32 currLine = 0;
for (U32 i = 0; i < mMessageVector->getNumLines(); i++) {
TextElement* pElement = mLineElements[i].headLineElements;
ColorI lastColor = mProfile->mFontColor;
while (pElement != NULL) {
Point2I localStart(pElement == mLineElements[i].headLineElements ? 0 : mLineContinuationIndent, currLine * linePixels);
Point2I globalCheck = localToGlobalCoord(localStart);
U32 globalRangeStart = globalCheck.y;
U32 globalRangeEnd = globalCheck.y + mProfile->mFont->getHeight();
if (globalRangeStart > updateRect.point.y + updateRect.extent.y ||
globalRangeEnd < updateRect.point.y) {
currLine++;
pElement = pElement->nextPhysicalLine;
continue;
}
TextElement* walkAcross = pElement;
while (walkAcross) {
if (walkAcross->start > walkAcross->end)
break;
Point2I globalStart = localToGlobalCoord(localStart);
U32 strWidth;
if (walkAcross->specialReference == -1) {
dglSetBitmapModulation(lastColor);
dglSetTextAnchorColor(mProfile->mFontColor);
strWidth = dglDrawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start],
walkAcross->end - walkAcross->start + 1, mProfile->mFontColors, mMaxColorIndex);
dglGetBitmapModulation(&lastColor);
} else {
dglGetBitmapModulation(&lastColor);
dglSetBitmapModulation(mSpecialColor);
dglSetTextAnchorColor(mProfile->mFontColor);
strWidth = dglDrawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start],
walkAcross->end - walkAcross->start + 1);
// in case we have 2 in a row...
dglSetBitmapModulation(lastColor);
}
if (walkAcross->specialReference != -1) {
Point2I lineStart = localStart;
Point2I lineEnd = localStart;
lineStart.y += mProfile->mFont->getBaseline() + 1;
lineEnd.x += strWidth;
lineEnd.y += mProfile->mFont->getBaseline() + 1;
dglDrawLine(localToGlobalCoord(lineStart),
localToGlobalCoord(lineEnd),
mSpecialColor);
}
localStart.x += strWidth;
walkAcross = walkAcross->nextInLine;
}
currLine++;
pElement = pElement->nextPhysicalLine;
}
}
dglClearBitmapModulation();
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::inspectPostApply()
{
Parent::inspectPostApply();
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::parentResized(const Point2I& oldSize,
const Point2I& newSize)
{
Parent::parentResized(oldSize, newSize);
// If we have a MesssageVector, detach/reattach so we can reflow the text.
if (mMessageVector)
{
MessageVector *reflowme = mMessageVector;
detach();
attach(reflowme);
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::findSpecialFromCoord(const Point2I& point, S32* specialLine, S32* specialRef)
{
if (mLineElements.size() == 0) {
*specialLine = -1;
*specialRef = -1;
return;
}
U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels;
if ((point.x < 0 || point.x >= mBounds.extent.x) ||
(point.y < 0 || point.y >= mBounds.extent.y)) {
*specialLine = -1;
*specialRef = -1;
return;
}
// Ok, we have real work to do here. Let's determine the physical line that it's on...
U32 physLine = point.y / linePixels;
AssertFatal(physLine >= 0, "Bad physical line!");
// And now we find the LineElement that contains that physicalLine...
U32 elemIndex;
for (elemIndex = 0; elemIndex < mLineElements.size(); elemIndex++) {
if (mLineElements[elemIndex].physicalLineStart > physLine) {
// We've passed it.
AssertFatal(elemIndex != 0, "Error, bad elemIndex, check assumptions.");
elemIndex--;
break;
}
}
if (elemIndex == mLineElements.size()) {
// On the last line...
elemIndex = mLineElements.size() - 1;
}
TextElement* line = mLineElements[elemIndex].headLineElements;
for (U32 i = 0; i < physLine - mLineElements[elemIndex].physicalLineStart; i++)
{
if (line->nextPhysicalLine == NULL)
{
*specialLine = -1;
*specialRef = -1;
return;
}
line = line->nextPhysicalLine;
AssertFatal(line != NULL, "Error, moved too far!");
}
// Ok, line represents the current line. We now need to find out which textelement
// the points x coord falls in.
U32 currX = 0;
if ((physLine - mLineElements[elemIndex].physicalLineStart) != 0) {
currX = mLineContinuationIndent;
// First, if this isn't the first line in this wrapping, we have to make sure
// that the coord isn't in the margin...
if (point.x < mLineContinuationIndent) {
*specialLine = -1;
*specialRef = -1;
return;
}
}
if (line->start > line->end) {
// Empty line special case...
*specialLine = -1;
*specialRef = -1;
return;
}
U32 currElem = 0;
while (line) {
U32 newX = currX + mProfile->mFont->getStrNWidth((const UTF8 *)mMessageVector->getLine(elemIndex).message,
line->end - line->start + 1);
if (point.x < newX) {
// That's the one!
*specialLine = elemIndex;
*specialRef = line->specialReference;
return;
}
currX = newX;
line = line->nextInLine;
}
// Off to the right. Aw...
*specialLine = -1;
*specialRef = -1;
}
void GuiMessageVectorCtrl::onMouseDown(const GuiEvent& event)
{
Parent::onMouseDown(event);
mouseUnlock();
mMouseDown = true;
// Find the special we are in, if any...
findSpecialFromCoord(globalToLocalCoord(event.mousePoint),
&mMouseSpecialLine, &mMouseSpecialRef);
}
void GuiMessageVectorCtrl::onMouseUp(const GuiEvent& event)
{
Parent::onMouseUp(event);
mouseUnlock();
// Is this an up from a dragged click?
if (mMouseDown == false)
return;
// Find the special we are in, if any...
S32 currSpecialLine;
S32 currSpecialRef;
findSpecialFromCoord(globalToLocalCoord(event.mousePoint), &currSpecialLine, &currSpecialRef);
if (currSpecialRef != -1 &&
(currSpecialLine == mMouseSpecialLine &&
currSpecialRef == mMouseSpecialRef)) {
// Execute the callback
SpecialMarkers& rSpecial = mSpecialMarkers[currSpecialLine];
S32 specialStart = rSpecial.specials[currSpecialRef].start;
S32 specialEnd = rSpecial.specials[currSpecialRef].end;
char* copyURL = new char[specialEnd - specialStart + 2];
dStrncpy(copyURL, &mMessageVector->getLine(currSpecialLine).message[specialStart], specialEnd - specialStart + 1);
copyURL[specialEnd - specialStart + 1] = '\0';
Con::executef(this, 2, "urlClickCallback", copyURL);
delete [] copyURL;
}
mMouseDown = false;
mMouseSpecialLine = -1;
mMouseSpecialRef = -1;
}

View File

@ -0,0 +1,135 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIMESSAGEVECTORCTRL_H_
#define _GUIMESSAGEVECTORCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _MESSAGEVECTOR_H_
#include "gui/utility/messageVector.h"
#endif
/// Render a MessageVector (chat HUD)
///
/// This renders messages from a MessageVector; the important thing
/// here is that the MessageVector holds all the messages we care
/// about, while we can destroy and create these GUI controls as
/// needed. (For instance, Tribes 2 used seperate GuiMessageVectorCtrl
/// controls in the different HUD modes.)
class GuiMessageVectorCtrl : public GuiControl
{
typedef GuiControl Parent;
//-------------------------------------- Public interfaces...
public:
struct SpecialMarkers {
struct Special {
S32 specialType;
S32 start;
S32 end;
};
U32 numSpecials;
Special* specials;
};
GuiMessageVectorCtrl();
~GuiMessageVectorCtrl();
bool isAttached() const;
bool attach(MessageVector*);
void detach();
// Gui control overrides
protected:
bool onAdd();
void onRemove();
bool onWake();
void onSleep();
void onRender(Point2I offset, const RectI &updateRect);
void inspectPostApply();
void parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent);
void onMouseUp(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
// void onMouseMove(const GuiEvent &event);
// Overrideables
protected:
virtual void lineInserted(const U32);
virtual void lineDeleted(const U32);
virtual void vectorDeleted();
MessageVector* mMessageVector;
// Font resource
protected:
U32 mMinSensibleWidth;
U32 mLineSpacingPixels;
U32 mLineContinuationIndent;
StringTableEntry mAllowedMatches[16];
ColorI mSpecialColor;
U32 mMaxColorIndex;
bool mMouseDown;
S32 mMouseSpecialLine;
S32 mMouseSpecialRef;
/// Derived classes must keep this set of variables consistent if they
/// use this classes onRender. Note that this has the fairly large
/// disadvantage of requiring lots of ugly, tiny mem allocs. A rewrite
/// to a more memory friendly version might be a good idea...
struct LineWrapping {
struct SEPair {
S32 start;
S32 end;
};
U32 numLines;
SEPair* startEndPairs; /// start[linex] = startEndPairs[linex*2+0]
/// end[linex] = startEndPairs[linex*2+1]
/// if end < start, line is empty...
};
struct TextElement {
TextElement* nextInLine;
TextElement* nextPhysicalLine;
S32 specialReference; /// if (!= -1), indicates a special reference
S32 start;
S32 end;
};
struct LineElement {
U32 physicalLineStart;
TextElement* headLineElements;
};
Vector<LineWrapping> mLineWrappings;
Vector<SpecialMarkers> mSpecialMarkers;
Vector<LineElement> mLineElements;
void createLineWrapping(LineWrapping&, const char*);
void createSpecialMarkers(SpecialMarkers&, const char*);
void createLineElement(LineElement&, LineWrapping&, SpecialMarkers&);
void findSpecialFromCoord(const Point2I&, S32*, S32*);
public:
void callbackRouter(const MessageVector::MessageCode, const U32);
public:
DECLARE_CONOBJECT(GuiMessageVectorCtrl);
static void initPersistFields();
};
#endif // _H_GUIMESSAGEVECTORCTRL_

View File

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "gui/game/guiProgressCtrl.h"
IMPLEMENT_CONOBJECT(GuiProgressCtrl);
GuiProgressCtrl::GuiProgressCtrl()
{
mProgress = 0.0f;
}
const char* GuiProgressCtrl::getScriptValue()
{
char * ret = Con::getReturnBuffer(64);
dSprintf(ret, 64, "%g", mProgress);
return ret;
}
void GuiProgressCtrl::setScriptValue(const char *value)
{
//set the value
if (! value)
mProgress = 0.0f;
else
mProgress = dAtof(value);
//validate the value
mProgress = mClampF(mProgress, 0.f, 1.f);
setUpdate();
}
void GuiProgressCtrl::onPreRender()
{
const char * var = getVariable();
if(var)
{
F32 value = mClampF(dAtof(var), 0.f, 1.f);
if(value != mProgress)
{
mProgress = value;
setUpdate();
}
}
}
void GuiProgressCtrl::onRender(Point2I offset, const RectI &updateRect)
{
RectI ctrlRect(offset, mBounds.extent);
//draw the progress
S32 width = (S32)((F32)mBounds.extent.x * mProgress);
if (width > 0)
{
RectI progressRect = ctrlRect;
progressRect.extent.x = width;
dglDrawRectFill(progressRect, mProfile->mFillColor);
}
//now draw the border
if (mProfile->mBorder)
{
dglDrawRect(ctrlRect, mProfile->mBorderColor);
RectI copyRect = ctrlRect;
copyRect.point.x += 1;
copyRect.point.y += 1;
copyRect.extent.x -= 2;
copyRect.extent.y -= 2;
ColorI color = mProfile->mBorderColor*0.75;
color.alpha = 255;
dglDrawRect(copyRect, color);
}
//render the children
renderChildControls(offset, updateRect);
}

View File

@ -0,0 +1,33 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIPROGRESSCTRL_H_
#define _GUIPROGRESSCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiProgressCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
F32 mProgress;
public:
//creation methods
DECLARE_CONOBJECT(GuiProgressCtrl);
GuiProgressCtrl();
//console related methods
virtual const char *getScriptValue();
virtual void setScriptValue(const char *value);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
};
#endif