STEP 1:
Download Frank Bignones zip file from perso.wanadoo.fr/hysteria/docs/guiCtrls.htm
STEP 2:
Add the additional files to your engine code:
- guiObjectView.cc & guiObjectView.h : place them in your game
directory (i.e, /engine/game)
- guiTerView.cc & guiTerView.h : place them in your game directory
(i.e, /engine/game) and add them to your Visual Studio Project (right-click on the folders
containing the new files, "Add files to folder" - you have to change the file filter to *.* to see the .cc files!)
STEP 3:
There is one little extra utility function we need in the guiObjectView class:
add this to guiObjectView.cc:
void GuiObjectView::setEmpty() {
if ( mModel ) {
delete mModel;
mModel = NULL;
}
}
ConsoleMethod( GuiObjectView, setEmpty, void, 2, 2, "objectView.setEmpty( )" ) {
argc;
GuiObjectView* view = static_cast<GuiObjectView*>( object );
view->setEmpty();
}
And, of course, the declaration in the header file, guiObjectView.h:
void setEmpty();Then, add the following function to core/resManager.cc:
ResourceObject *
ResManager::createResource (const char * fileName)
{
StringTableEntry path, file;
getPaths (fileName, path, file);
ResourceObject *newRO = dictionary.find (path, file);
if (newRO)
return newRO;
newRO = new ResourceObject;
newRO->path = path;
newRO->name = file;
newRO->lockCount = 0;
newRO->mInstance = NULL;
newRO->flags = ResourceObject::Added;
newRO->next = newRO->prev = NULL;
newRO->nextResource = resourceList.nextResource;
resourceList.nextResource = newRO;
newRO->prevResource = &resourceList;
if (newRO->nextResource)
newRO->nextResource->prevResource = newRO;
dictionary.insert (newRO, path, file);
newRO->fileSize = newRO->fileOffset = newRO->compressedFileSize = 0;
newRO->zipPath = NULL;
newRO->zipName = NULL;
newRO->crc = InvalidCRC;
return newRO;
}
And the function declaration to the resManager.h file, of course (as the first entry in the "public" section e.g.):
ResourceObject* createResource(const char *);
bool TerrainBlock::addToClientGraph()
{
setPosition(Point3F(-squareSize * (BlockSize >> 1), -squareSize * (BlockSize >> 1), 0));
MaterialPropertyMap* pMatMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
StringTableEntry fn = mMaterialFileName[0];
if(!dStrncmp(fn, "terrain.", 8))
fn += 8;
char nameBuff[512];
dStrcpy(nameBuff, mTerrFileName);
char *p = dStrrchr(nameBuff, '/');
if (p) p++;
else p = nameBuff;
dStrcat(p,fn);
mMPMIndex[0] = pMatMap->getIndexFromName(nameBuff);
mObjBox.min.set(-1e8, -1e8, -1e8);
mObjBox.max.set( 1e8, 1e8, 1e8);
resetWorldBox();
setRenderTransform(mObjToWorld);
if(mDetailTextureName && mDetailTextureName[0])
mDetailTextureHandle = TextureHandle(mDetailTextureName, DetailTexture);
lightMap = new GBitmap(LightmapSize, LightmapSize, false, GBitmap::RGB5551);
if (!buildMaterialMap())
return false;
mTextureCallbackKey = TextureManager::registerEventCallback(terrainTextureEventCB, U32(this));
mDynLightTexture = TextureHandle("special/lightFalloffMono", BitmapTexture, true);
if (dglDoesSupportVertexBuffer())
mVertexBuffer = glAllocateVertexBufferEXT(VertexBufferSize,GL_V12MTVFMT_EXT,true);
else
mVertexBuffer = -1;
if(!unpackEmptySquares())
return(false);
return true;
}
I've added it just before void TerrainBlock::onRemove().
STEP 5:
Add this piece in terrData.h
public:
bool addToClientGraph();
e.g. right after
void unpackUpdate(NetConnection *, BitStream *stream);
STEP 6:
compile your engine (CLEAN build would be a good thing :-) !!
STEP 7:
Now for the scripting, I've put all the script for the GUI in one file
named "terrainPreviewGui.gui", and added
exec("./ui/terrainPreviewGui.gui");
to fps/client/init.cs.
Then I call it from mainMenuGui.cs instead of the normal
startMissionGui:
//command = "Canvas.setContent(startMissionGui);";
command = "Canvas.setContent(terrainPreviewGui);";
STEP 8:
In order to switch players/cars in a mod-specific way, I set a global
variable in this new GUI file named
"$playerCSFile" and in the "game.cs" file of the actual mod I
call:
//exec("./player.cs");
exec($playerCSFile);
or, for the car mod
//exec("./car.cs");
exec($playerCSFile);
respectively.
Furthermore, a variable $currentUserMod is set in
"example/main.cs", which simply saves the recent mod.
I use it in terrainPreviewGui to set the paths to search for terrain
and dts files:
(starting around line 62)
case "-mod":
$argUsed[$i]++;
if ($hasNextArg)
{
// save mod for global use - beffy 03/24/02
$currentUserMod = $nextArg;
So, now here is the complete "terrainPreviewGui.gui" code (should be
almost self explanatory - you can also download it here):
UPDATE 25/09/02: Note that the function "localConnect($pref::Player::Name);" has been replaced
with
%conn = new GameConnection(ServerConnection); RootGroup.add(ServerConnection); %conn.setConnectArgs($pref::Player::Name); %conn.setJoinPassword($Client::Password); %conn.connectLocal();to work with the current HEAD.
if($currentUserMod $= "") $currentUserMod = $userMods;Finally, the "newMission.ter" file is excluded from the terrain file loading to prevent problems:
// 25/09/02: ignore "newMission.ter" also
if (strStr(%file, "CVS/") == -1 && strStr(%file, "/newMission") == -1)
//--- OBJECT WRITE BEGIN ---
new GuiChunkedBitmapCtrl(TerrainPreviewGui) {
profile = "GuiDefaultProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "0 0";
extent = "780 580";
minExtent = "8 8";
visible = "1";
helpTag = "0";
new GuiControl() {
profile = "GuiWindowProfile";
horizSizing = "center";
vertSizing = "center";
position = "60 74";
extent = "710 400";
minExtent = "8 8";
visible = "1";
helpTag = "0";
new GuiButtonCtrl(PrevModelButton) {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "top";
position = "495 310";
extent = "80 23";
minExtent = "8 8";
visible = "0";
command = "TerrainPreviewGui::prevModel();";
helpTag = "1";
text = "<< Back";
};
new GuiButtonCtrl(NextModelButton) {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "top";
position = "590 310";
extent = "80 23";
minExtent = "8 8";
visible = "1";
command = "TerrainPreviewGui::nextModel();";
helpTag = "0";
text = "Next >>";
};
new GuiTextCtrl(PlayerSelectTextCtrl) {
profile = "GuiTextProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "485 25";
extent = "88 20";
minExtent = "8 8";
visible = "1";
text = "Select Model:";
helpTag = "0";
maxLength = "255";
};
// place to render the selected item
new GuiObjectView(ToRKDisplayWindow) {
profile = "GuiDefaultProfile";
horizSizing = "relative";
vertSizing = "relative";
position = "485 64";
extent = "230 220";
minExtent = "8 8";
visible = "1";
helpTag = "0";
};
new GuiTerView(MM_ShowTerrain) {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "210 93";
extent = "240 240";
minExtent = "8 8";
visible = "1";
modal = "0";
helpTag = "0";
};
new GuiTextCtrl(MissionTextCtrl) {
profile = "GuiTextProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "210 4";
extent = "88 20";
minExtent = "8 8";
visible = "1";
helpTag = "0";
maxLength = "255";
};
new GuiTextCtrl(TerrainTextCtrl) {
profile = "GuiTextProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "210 25";
extent = "88 20";
minExtent = "8 8";
visible = "1";
helpTag = "0";
maxLength = "255";
};
new GuiButtonCtrl() {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "top";
position = "10 184";
extent = "127 23";
minExtent = "8 8";
visible = "1";
command = "TerrainPreviewGui::StartMission();";
helpTag = "0";
text = "Go!";
};
new GuiButtonCtrl() {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "top";
position = "10 212";
extent = "127 23";
minExtent = "8 8";
visible = "1";
//command = "TerrainPreviewGui::selectNextTerrain();";
command = "TerrainPreviewGui::previewTerrain();";
helpTag = "0";
text = "Preview";
};
new GuiButtonCtrl() {
profile = "GuiButtonProfile";
horizSizing = "right";
vertSizing = "top";
position = "10 240";
extent = "127 23";
minExtent = "8 8";
visible = "1";
command = "Canvas.getContent().exit();";
helpTag = "0";
text = "<< Back";
};
new GuiCheckBoxCtrl(ML_isMultiplayer) {
profile = "GuiCheckBoxProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "12 303";
extent = "147 23";
minExtent = "8 8";
visible = "1";
variable = "pref::HostMultiPlayer";
helpTag = "0";
text = "Multiplayer Mission";
maxLength = "255";
};
new GuiTextEditCtrl() {
profile = "GuiTextEditProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "12 30";
extent = "164 16";
minExtent = "8 8";
visible = "1";
variable = "pref::Player::Name";
helpTag = "0";
maxLength = "255";
historySize = "0";
password = "0";
tabComplete = "0";
};
new GuiTextCtrl() {
profile = "GuiTextProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "12 11";
extent = "79 20";
minExtent = "8 8";
visible = "1";
helpTag = "0";
text = "Player Name:";
maxLength = "255";
};
new GuiScrollCtrl() {
profile = "GuiScrollProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "12 55";
extent = "164 100";
minExtent = "8 8";
visible = "1";
helpTag = "0";
willFirstRespond = "1";
hScrollBar = "dynamic";
vScrollBar = "alwaysOn";
constantThumbHeight = "0";
defaultLineHeight = "15";
childMargin = "0 0";
new GuiTextListCtrl(ToRK_missionList) {
profile = "GuiTextArrayProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "0 0";
extent = "150 40";
minExtent = "8 8";
visible = "1";
helpTag = "0";
enumerate = "0";
resizeCell = "1";
columns = "0";
fitParentWidth = "1";
clipColumnText = "0";
noDuplicates = "false";
};
};
};
};
//--- OBJECT WRITE END ---
//----------------------------------------
function TerrainPreviewGui::onWake()
{
//reset vars
$numDTS = 0;
$currentDTS = 0;
$playerCSFile = "";
$currTerrainId = 0;
$terrCounter = 0;
// 25/09/02: set default mod
if($currentUserMod $= "")
$currentUserMod = $userMods;
// clear Gui elements
ToRK_missionList.clear();
ToRKDisplayWindow.setEmpty();
MM_ShowTerrain.detachTerrain();
// search *.ter files and init the mission list
TerrainPreviewGui::loadTerrains();
TerrainPreviewGui::loadPlayerShapes($currentUserMod);
// display first model in array
TerrainPreviewGui::displayDTSItem();
}
//----------------------------------------
function TerrainPreviewGui::cancel(%this)
{
}
//----------------------------------------
function TerrainPreviewGui::exit(%this)
{
// cleanup
MM_ShowTerrain.detachTerrain();
ToRKDisplayWindow.setEmpty();
Canvas.setContent(mainMenuGui);
}
//--------------------------------------------------------------------
// Search *.ter files, fill arrays and display all missions in list
//--------------------------------------------------------------------
function TerrainPreviewGui::loadTerrains()
{
//$currentUserMod is set in "example/main.cs"...
%filespec = $currentUserMod @ "/data" @ "/*.ter";
%missionPath = $currentUserMod @ "/data/missions";
// a little hack to get the mod directory
//%delpos = strpos($Client::MissionFileSpec, "/*");
//%modDir = getSubStr($Client::MissionFileSpec, 0, %delpos);
for(%file = findFirstFile(%filespec); %file !$= ""; %file = findNextFile(%filespec))
{
// ignore CVS folder...
// 25/09/02: ignore "newMission.ter" also
if (strStr(%file, "CVS/") == -1 && strStr(%file, "/newMission") == -1)
{
//echo("count: " @ getFileCount("*.ter"));
$terrFiles[$terrCounter] = filePath(%file) @ "/" @ fileName(%file);
$missionFiles[$terrCounter] = %missionPath @ "/" @ fileBase(%file) @ ".mis";
echo("File " @ $terrCounter @ ": " @ $terrFiles[$terrCounter]);
ToRK_missionList.addRow($terrCounter, getMissionDisplayName($missionFiles[$terrCounter]) @ "\t" @ %file );
$terrCounter++;
}
}
ToRK_missionList.sort(0);
ToRK_missionList.setSelectedRow(0);
ToRK_missionList.scrollVisible(0);
}
//--------------------------------------------------------------------
// Preview selected mission terrain
//--------------------------------------------------------------------
function TerrainPreviewGui::previewTerrain()
{
%id = ToRK_missionList.getSelectedId();
TerrainTextCtrl.setText("Terrain file: " @ $terrFiles[%id]);
MissionTextCtrl.setText("Mission: " @ getMissionDisplayName($missionFiles[%id]));
MM_ShowTerrain.detachTerrain();
MM_ShowTerrain.attachTerrain($terrFiles[%id]);
}
//--------------------------------------------------------------------
// Return the next terrain in cycle
//--------------------------------------------------------------------
function TerrainPreviewGui::cycleTerrains()
{
if($currTerrainId++ == $terrCounter)
{
$currTerrainId = 0;
}
TerrainTextCtrl.setText("Terrain file: " @ $terrFiles[$currTerrainId]);
MissionTextCtrl.setText("Mission: " @ getMissionDisplayName($missionFiles[$currTerrainId]));
return $terrFiles[$currTerrainId];
}
//--------------------------------------------------------------------
// Select the next terrain in cycle
//--------------------------------------------------------------------
function TerrainPreviewGui::selectNextTerrain()
{
MM_ShowTerrain.detachTerrain();
MM_ShowTerrain.attachTerrain(TerrainPreviewGui::cycleTerrains());
}
//--------------------------------------------------------------------
// Start the selected mission
//--------------------------------------------------------------------
function TerrainPreviewGui::StartMission()
{
MM_ShowTerrain.detachTerrain();
%mission = $missionFiles[ToRK_missionList.getSelectedId()];
// used in game.cs to load the appropriate player script
$playerCSFile = $playerFiles[$currentDTS];
if ($pref::HostMultiPlayer)
%serverType = "MultiPlayer";
else
%serverType = "SinglePlayer";
createServer(%serverType, %mission);
// 25/09/02: changed for latest HEAD
%conn = new GameConnection(ServerConnection);
RootGroup.add(ServerConnection);
%conn.setConnectArgs($pref::Player::Name);
%conn.setJoinPassword($Client::Password);
%conn.connectLocal();
}
//--------------------------------------------------------------------
// Parse mission files for mission name
//--------------------------------------------------------------------
function getMissionDisplayName( %missionFile )
{
%file = new FileObject();
%MissionInfoObject = "";
if ( %file.openForRead( %missionFile ) ) {
%inInfoBlock = false;
while ( !%file.isEOF() ) {
%line = %file.readLine();
%line = trim( %line );
if( %line $= "new ScriptObject(MissionInfo) {" )
%inInfoBlock = true;
else if( %inInfoBlock && %line $= "};" ) {
%inInfoBlock = false;
%MissionInfoObject = %MissionInfoObject @ %line;
break;
}
if( %inInfoBlock )
%MissionInfoObject = %MissionInfoObject @ %line @ " ";
}
%file.close();
}
%MissionInfoObject = "%MissionInfoObject = " @ %MissionInfoObject;
eval( %MissionInfoObject );
%file.delete();
if( %MissionInfoObject.name !$= "" )
return %MissionInfoObject.name;
else
return fileBase(%missionFile);
}
//--------------------------------------------------------------------
// initialize arrays with shape- and scriptpaths
//--------------------------------------------------------------------
function TerrainPreviewGui::loadPlayerShapes(%mod)
{
if(%mod $= "fps")
{
$playerShapes[0] = %mod @ "/data/shapes/player/player.dts";
$playerFiles[0] = %mod @ "/server/scripts/player.cs";
$numDTS++;
}
else if(%mod $= "racing")
{
$playerShapes[2] = %mod @ "/data/shapes/car/car.dts";
$playerFiles[2] = %mod @ "/server/scripts/car.cs";
$numDTS++;
}
else
{
error("Unknown mod name!");
}
}
//--------------------------------------------------------------------
// display current *.dts
//--------------------------------------------------------------------
function TerrainPreviewGui::displayDTSItem()
{
PrevModelButton.visible = "1";
NextModelButton.visible = "1";
ToRKDisplayWindow.setEmpty();
%file = $playerShapes[$currentDTS];
echo("Loading " @ %file);
ToRKDisplayWindow.setModel(%file , "");
}
//--------------------------------------------------------------------
// go back one dts
//--------------------------------------------------------------------
function TerrainPreviewGui::prevModel()
{
if($currentDTS-- < 0)
{
$currentDTS = $numDTS-1;
}
echo($currentDTS);
TerrainPreviewGui::displayDTSItem();
}
//--------------------------------------------------------------------
// show next dts
//--------------------------------------------------------------------
function TerrainPreviewGui::nextModel()
{
if($currentDTS++ >= $numDTS)
{
$currentDTS = 0;
}
echo($currentDTS);
TerrainPreviewGui::displayDTSItem();
}
Here is a screenshot of it.
STEP 9: Preview, select and enjoy! :-)