Tuesday, December 4, 2007

Creating chat rooms for TGE 1.5






Creating chat rooms for TGE 1.5







Basic Information

Author: JuanMa (Nov 21, 2007)








Synopsis:

This tutorial will explain how to create simple in-game chat rooms
using torque script only. It mods the way MessageVector and
GuiMessageVectorCtrl are used in the original version of the
starter.fps game to manage the global chat only. This will not allow
you to dynamically create chat rooms in game or private chat rooms
managed by one user, though the idea of this resource is to provide
some basic understanding on how the chat works and how it could be mod
hoping that the community will take this idea to work on top of it and
create better and cooler resources. If you have any idea how to make
this resource better I will appreciate any comments.







Keywords:

ChatRoom chat room onChatMessage clientCmdChatMessage MessageVector HudMessageVector ChatHud






Article:

Open the file chatHud.cs right after the new
MessageVector(HudMessageVector); add the following code:

// All messages are stored in this HudMessageVector, the actual
// MainChatHud only displays the contents of this vector.

new MessageVector(HudMessageVector);
new MessageVector(HudMessageVectorGlobal);
new MessageVector(HudMessageVectorRoomA);
new MessageVector(HudMessageVectorRoomB);
new MessageVector(HudMessageVectorRoomC);
$LastHudTarget = 0;
$ChatRoom = HudMessageVector;
$ChatSelector = "g";
$ChatText = "GLOBAL:";

// this is a gui elements in chatHud.gui
$ChatHud = ChatHud;

function changeChat(%chatSelector){
$ChatSelector = %chatSelector;

if( %chatSelector $= "g" ){
$ChatRoom = HudMessageVector;

ChatHud.setVisible(true);
ChatRoomA.setVisible(false);
ChatRoomB.setVisible(false);
ChatRoomC.setVisible(false);
$ChatHud = ChatHud;
$ChatText = "GLOBAL:";
}
else if( %chatSelector $= "a" ){
$ChatRoom = HudMessageVectorRoomA;
ChatHud.setVisible(false);
ChatRoomA.setVisible(true);
ChatRoomB.setVisible(false);
ChatRoomC.setVisible(false);

$ChatHud = ChatRoomA;
$ChatText = "ROOM A:";
}
else if( %chatSelector $= "b" ){
$ChatRoom = HudMessageVectorRoomB;
ChatHud.setVisible(false);
ChatRoomA.setVisible(false);
ChatRoomB.setVisible(true);
ChatRoomC.setVisible(false);

$ChatHud = ChatRoomB;
$ChatText = "ROOM B:";
}
else if( %chatSelector $= "c" ){
$ChatRoom = HudMessageVectorRoomC;
ChatHud.setVisible(false);
ChatRoomA.setVisible(false);
ChatRoomB.setVisible(false);
ChatRoomC.setVisible(true);

$ChatHud = ChatRoomC;
$ChatText = "ROOM C:";
}
}


In this same file ChatHud.cs make the following changes:

Change
all the HudMessageVector with $ChatRoom or $HudMessageVector or any
variable name you feel comfortable using and all the ChatHud with
$ChatHud in the whole file, this will enable us to instead of
Using a single object we will use a "pointer" (the global variable) to the object so we can change it at any time.

Example:


while( !chatPageDown.isVisible() && HudMessageVector.getNumLines() && 
(HudMessageVector.getNumLines() >= $pref::HudMessageLogSize))


Should look like this.

while( !chatPageDown.isVisible() && $ChatRoom.getNumLines() && ($ChatRoom.getNumLines() 
>= $pref::HudMessageLogSize))




In the function onChatMessage add an extra parameter %chatWindow


function onChatMessage(%message, %voice, %pitch, %chatWindow){...}


and replace the whole if statement

 // Chat goes to the chat HUD.
if (getWordCount(%message)) {...}


with

   // Chat goes to the chat HUD.
if (getWordCount(%message)) {
// save the current variables
// to keep using them later
%tempChatHud = $ChatHud;
%tempHudMessageVect = $ChatRoom;

if( %chatWindow $= "g" ){
$ChatRoom = HudMessageVector;
$ChatHud = ChatHud;
}
else if( %chatWindow $= "a" ){
$ChatRoom = HudMessageVectorRoomA;
$ChatHud = ChatRoomA;
}
else if( %chatWindow $= "b" ){
$ChatRoom = HudMessageVectorRoomB;
$ChatHud = ChatRoomB;
}
else if( %chatWindow $= "c" ){
$ChatRoom = HudMessageVectorRoomC;
$ChatHud = ChatRoomC;
}
$ChatHud.addLine(%message);

$ChatHud = %tempChatHud;
$ChatRoom = %tempHudMessageVect;
}


Add at the end of the file. these are copies of ChatHud you will have to add one for work
For each chatRoomX that you have



//-----------------------------------------------------------------------------
// ChatRoomA methods
// This is the actual message vector/text control which is part of
// the MainChatHud dialog
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

function ChatRoomA::addLine(%this,%text)
{
//first, see if we're "scrolled up"...
%textHeight = %this.profile.fontSize;
if (%textHeight <= 0)
%textHeight = 12;
%chatScrollHeight = getWord(%this.getGroup().getGroup().extent, 1);
%chatPosition = getWord(%this.extent, 1) - %chatScrollHeight +
getWord(%this.position, 1);
%linesToScroll = mFloor((%chatPosition / %textHeight) + 0.5);
if (%linesToScroll > 0)
%origPosition = %this.position;

//add the message...
while( !chatPageDown.isVisible() && $ChatRoom.getNumLines() &&
($ChatRoom.getNumLines() >= $pref::HudMessageLogSize))
{
%tag = $ChatRoom.getLineTag(0);
if(%tag != 0)
%tag.delete();
$ChatRoom.popFrontLine();
}
$ChatRoom.pushBackLine(%text, $LastHudTarget);
$LastHudTarget = 0;

//now that we've added the message, see if we need to reset the position
if (%linesToScroll > 0)
{
chatPageDown.setVisible(true);
%this.position = %origPosition;
}
else
chatPageDown.setVisible(false);
}


//-----------------------------------------------------------------------------

function ChatRoomA::pageUp(%this)
{
// Find out the text line height
%textHeight = %this.profile.fontSize;
if (%textHeight <= 0)
%textHeight = 12;

// Find out how many lines per page are visible
%chatScrollHeight = getWord(%this.getGroup().getGroup().extent, 1);
if (%chatScrollHeight <= 0)
return;

%pageLines = mFloor(%chatScrollHeight / %textHeight) - 1;
if (%pageLines <= 0)
%pageLines = 1;

// See how many lines we actually can scroll up:
%chatPosition = -1 * getWord(%this.position, 1);
%linesToScroll = mFloor((%chatPosition / %textHeight) + 0.5);
if (%linesToScroll <= 0)
return;

if (%linesToScroll > %pageLines)
%scrollLines = %pageLines;
else
%scrollLines = %linesToScroll;

// Now set the position
%this.position = firstWord(%this.position) SPC (getWord(%this.position, 1) +
(%scrollLines * %textHeight));

// Display the pageup icon
chatPageDown.setVisible(true);
}


//-----------------------------------------------------------------------------

function ChatRoomA::pageDown(%this)
{
// Find out the text line height
%textHeight = %this.profile.fontSize;
if (%textHeight <= 0)
%textHeight = 12;

// Find out how many lines per page are visible
%chatScrollHeight = getWord(%this.getGroup().getGroup().extent, 1);
if (%chatScrollHeight <= 0)
return;

%pageLines = mFloor(%chatScrollHeight / %textHeight) - 1;
if (%pageLines <= 0)
%pageLines = 1;

// See how many lines we actually can scroll down:
%chatPosition = getWord(%this.extent, 1) - %chatScrollHeight +
getWord(%this.position, 1);
%linesToScroll = mFloor((%chatPosition / %textHeight) + 0.5);
if (%linesToScroll <= 0)
return;

if (%linesToScroll > %pageLines)
%scrollLines = %pageLines;
else
%scrollLines = %linesToScroll;

// Now set the position
%this.position = firstWord(%this.position) SPC (getWord(%this.position, 1) -
(%scrollLines * %textHeight));

// See if we have should (still) display the pagedown icon
if (%scrollLines < %linesToScroll)
chatPageDown.setVisible(true);
else
chatPageDown.setVisible(false);
}


Now add 4 buttons and 3 GuiMessageVectorCtrl in chatHud.gui this is not the whole code,
Just the part that adds the buttons and the GuiMessageVectorCtrl

...
new GuiControl() {
canSaveDynamicFields = "0";
Profile = "GuiDefaultProfile";
HorizSizing = "relative";
VertSizing = "bottom";
position = "0 0";
Extent = "640 300";
MinExtent = "8 8";
canSave = "1";
Visible = "1";
hovertime = "1000";

new GuiButtonCtrl(Global) {
canSaveDynamicFields = "0";
Profile = "GuiButtonProfile";
HorizSizing = "left";
VertSizing = "bottom";
position = "515 6";
Extent = "38 14";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "changeChat(\"g\");";
hovertime = "1000";
text = "Glob";
groupNum = "-1";
buttonType = "PushButton";
};
new GuiButtonCtrl(RoomA) {
canSaveDynamicFields = "0";
Profile = "GuiButtonProfile";
HorizSizing = "left";
VertSizing = "bottom";
position = "515 21";
Extent = "38 14";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "changeChat(\"a\");";
hovertime = "1000";
text = "RoomA";
groupNum = "-1";
buttonType = "PushButton";
};
new GuiButtonCtrl(RoomB) {
canSaveDynamicFields = "0";
Profile = "GuiButtonProfile";
HorizSizing = "left";
VertSizing = "bottom";
position = "515 36";
Extent = "38 14";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "changeChat(\"b\");";
hovertime = "1000";
text = "RoomB";
groupNum = "-1";
buttonType = "PushButton";
};
new GuiButtonCtrl(RoomC) {
canSaveDynamicFields = "0";
Profile = "GuiButtonProfile";
HorizSizing = "left";
VertSizing = "bottom";
position = "515 51";
Extent = "38 14";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
Command = "changeChat(\"c\");";
hovertime = "1000";
text = "RoomC";
groupNum = "-1";
buttonType = "PushButton";
};
new GuiBitmapBorderCtrl(OuterChatHud) {
canSaveDynamicFields = "0";
Profile = "ChatHudBorderProfile";
HorizSizing = "width";
VertSizing = "bottom";
position = "0 0";
Extent = "512 72";
MinExtent = "8 8";
canSave = "1";
Visible = "1";
hovertime = "1000";

new GuiBitmapCtrl() {
canSaveDynamicFields = "0";
Profile = "GuiDefaultProfile";
HorizSizing = "width";
VertSizing = "height";
position = "8 8";
Extent = "496 56";
MinExtent = "8 8";
canSave = "1";
Visible = "1";
hovertime = "1000";
bitmap = "./hudfill.png";
wrap = "0";
};
new GuiButtonCtrl(ChatPageDown) {
canSaveDynamicFields = "0";
Profile = "GuiButtonProfile";
HorizSizing = "left";
VertSizing = "top";
position = "460 42";
Extent = "36 14";
MinExtent = "8 8";
canSave = "1";
Visible = "0";
hovertime = "1000";
text = "Dwn";
groupNum = "-1";
buttonType = "PushButton";
};
new GuiScrollCtrl(ChatScrollHud) {
canSaveDynamicFields = "0";
Profile = "ChatHudScrollProfile";
HorizSizing = "width";
VertSizing = "height";
position = "8 8";
Extent = "496 56";
MinExtent = "8 8";
canSave = "1";
Visible = "1";
willFirstRespond = "1";
hScrollBar = "alwaysOff";
vScrollBar = "alwaysOff";
constantThumbHeight = "0";
childMargin = "0 0";

new GuiMessageVectorCtrl(ChatHud) {
canSaveDynamicFields = "0";
Profile = "ChatHudMessageProfile";
HorizSizing = "width";
VertSizing = "height";
position = "1 -382";
Extent = "492 16";
MinExtent = "8 8";
canSave = "1";
Visible = "1";
lineSpacing = "0";
lineContinuedIndex = "10";
allowedMatches[0] = "http";
allowedMatches[1] = "tgeserver";
matchColor = "0 0 255 255";
maxColorIndex = "5";
};
new GuiMessageVectorCtrl(ChatRoomA) {
canSaveDynamicFields = "0";
Profile = "ChatHudMessageProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "1 1";
Extent = "492 53";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
lineSpacing = "0";
lineContinuedIndex = "10";
allowedMatches[0] = "http";
allowedMatches[1] = "tgeserver";
matchColor = "0 0 255 255";
maxColorIndex = "9";
};
new GuiMessageVectorCtrl(ChatRoomB) {
canSaveDynamicFields = "0";
Profile = "ChatHudMessageProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "0 0";
Extent = "497 57";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
lineSpacing = "0";
lineContinuedIndex = "10";
allowedMatches[0] = "http";
allowedMatches[1] = "tgeserver";
matchColor = "0 0 255 255";
maxColorIndex = "9";
};
new GuiMessageVectorCtrl(ChatRoomC) {
canSaveDynamicFields = "0";
Profile = "ChatHudMessageProfile";
HorizSizing = "right";
VertSizing = "bottom";
position = "0 0";
Extent = "496 56";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
lineSpacing = "0";
lineContinuedIndex = "10";
allowedMatches[0] = "http";
allowedMatches[1] = "tgeserver";
matchColor = "0 0 255 255";
maxColorIndex = "9";
};
};
};
...


In the file messageHud.cs in the function

function MessageHud_Edit::eval(%this){...} 


around line 87 add one parameter to the commandToServer

      if(MessageHud.isTeamMsg)
commandToServer('teamMessageSent', %text);
else
******* commandToServer('messageSent', %text, $ChatSelector);




In the file common/client/message.cs change the function clientCmdChatMessage with this

function clientCmdChatMessage(%sender, %voice, %pitch, %msgString, %a1, %a2, %a3, %a4, 
%a5, %a6, %a7, %a8, %a9, %a10)
{
onChatMessage(detag(%msgString), %voice, %pitch, %a3);
}


Ok
so we are done with the client side, at this point if you start your
game you should see a chat window with four buttons on the side, if you
choose any of these buttons the screen should change. we do not have to
mod the server because thankfuly we are using one of the extra
paramenters that the messaging system has available :) and there for
the parameter of which chat room the message should go to has been
passed all the way back to each client that recieves the message and
has been taken care of in clientCmdChatMessage.

If you want to know where the messages are handled in the server go to
common/server/commands.cs and look at the function serverCmdMessageSent(%client, %text, 
%chatSelector){...}


Hope this will help someone.

JuanMa




Sunday, September 30, 2007

Avatar Selection

Ok, so this is the next "big" thing I am working on: Avatar Selection. I have something running already, but still have some bugs I need to work out, and I also need to test it on clients since I have been doing all the work on the local client. You should know that is always a pain to make things work on clients...




This should be all scriptable.

Lets get started!

There are several things we have to do in order to get an avatar selection screen. Lets first take a look at the things that you should have working already and that I will NOT be covering in this resource.

First you should have at least two workable models with their animations. By this I mean models such as the Ork from GG, or the Elf. For this resource I will be using our own models from SDSC.

Make sure you get images of your player models to add them in the avatar selection window.

I think that is all we need for now.

Now the first thing we have to change is the button to start the game, we are going to push to the canvas a new window. here it is the code for the window. It contains tree imageButtonCrls, change the image path to direct it to your image files...

(Code ...)

Create a Global variable that will run on the client side to identify the character that we want to play with. I know globals are evil, but for the sake of this tutorial we are going to make it like this sicen I have been experiencing some problems when calling this function from the GUI an passing parameters to the functions. I will try to come with a more elegant solution to the problem, but for now this should do.

(Code...)

Make sure you call the mission selection window after you have selected the avatar.
The construction of the avatar, or the definition of the datablock will not happen until the games starts running.

You should have a fully functional selection window to use different avatars.

Enjoy.
--------------------------
Working out the bugs

1) Global variable does not work on non-local client
2) models show up, but the animations doesn't work.
3) ...

Thursday, August 30, 2007

Quick Teleporter




First of all there is a resource that has been around for some time click here. I have never used it, so I do not know how it works.

Also I have been working on a generic trigger call teleporter, it should be a cheap way to achieve similar results. This will not show fancy particle emitter or lights. You can add it on top of this later

This particular trigger assumes that you will have a single dynamic field that will contain the coordinates.

First of all go to your server directory server/scripts/triggers.cs and add this code at the end of the file:


datablock TriggerData(Teleporter)
{
tickPeriodMS = 100;
};

//-----------------------------------------------------------------------------
function Teleporter::onEnterTrigger(%this,%trigger,%obj)
{
echo("Teleporter::onEnterTrigger --IN");
Parent::onEnterTrigger(%this,%trigger,%obj);

// Print out some debug info ;)
// Get the number of dynamic field in the trigger
echo("# Of Indx : " @ %trigger.getDynamicFieldCount());
// in my case I just have one so I use the first index
echo("Index 0 : " @ %trigger.getDynamicField(0));

// here is where we do the real work
%str = %trigger.getDynamicField(0);
// it should remove the name of the dynamic field and leave the coordinates
%coords = removeField(%str, 0);

// for educational purposes print out the string and the coordinates
echo("The String : " @ %str);
echo("Coordinates : " @ %coords);

// this transform will take you to the place that you want!!
%obj.setTransform(%coords);
}
//-----------------------------------------------------------------------------

function Teleporter::onLeaveTrigger(%this,%trigger,%obj)
{
Parent::onLeaveTrigger(%this,%trigger,%obj);
}

function Teleporter::onTickTrigger(%this,%trigger)
{

Parent::onTickTrigger(%this,%trigger);
}


The trigger should look like this on your *.mis file where coord is the dynamic data field


new Trigger(TestTrigger) {
canSaveDynamicFields = "1";
position = "647.288 -1344.81 112.729";
rotation = "1 0 0 0";
scale = "1 1 1";
dataBlock = "Teleporter";
polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
coord = "-9 -1414 50 0 0 1 0";
};



Hope it helps

Juanma

Edited on Aug 30, 2007 11:30

Friday, August 17, 2007

Terrain Problem

I have been working on a project where our main level is an island, I had problems before with the water block: when I looked beyond (-1024, -1024, 0, 0) the water block would disappear. I solved this by translating all my mission objects (4096 4096 0 0) this means I put all my objects on the terrain kitty-corner from the original one.

I am almost done with the map, but I want to use the terrain editor or texture painter and it would not do anything because the terrain editable area is still where my initial mission was. I only get a blue brush and can't edit the terrain :(

How do I move the Green box to my side of the map?