Torque 101: Intro to Torque
by Infinitum3D · in Torque Game Engine · 10/05/2006 (10:12 am) · 155 replies
I have just purchased and downloaded the TorqueGameEngineSDK_1.4
I will be documenting my experiences with it here. I am new to Torque and C++, so I hope others here will be able to learn from my mistakes.
UPDATE: Since I make several mistakes, you should always read through the entire thread of posts before trying anything. There are times that I ruin everything and have to start over entirely. Also, I have upgraded to Torque 1.5 since this post.
Thanks!
1. Download and run the TorqueGameEngineSDK-1-4.exe
This will install the Torque SDK on your computer. It will also place a link on the desktop called Torque Game Engine SDK.
This link is to twelve shortcuts;
FPS Starter Kit
Getting Started Tutorial
readMe.html
Racing Starter Kit
Torque Developer network (online)
Torque Game Engine Demo
Torque Game Engine Home Page
Tutorial Base
Torque Show Tool Pro
Torque Documentation (online)
Torque SDK forums (online)
Uninstall Torque Game Engine
I will read up on these and post more soon. I am starting with the ReadMe.html and the getting Started Tutorial.
UPDATE 10/07/06:
The ReadMe.html is just a one page note about where to find help. Look it over or skip straight to the Getting Starting .pdf tutorial.
2. The Getting Started tutorial is excellent. Follow it through, word by word, step by step, from start to finish, even if a section looks like common sense. !!!Be sure to place the TorqueScript in the EXACT location, exactly as the tutorial says!!! This was my first mistake.
All we have left to do is make sure that our 'clientGame.cs' file gets loaded. Open up GameOne/main.cs and find the 'Client scripts' section of the function called initClient. At the bottom of that list, add the following line, which will load our file:
I added the line at the bottom of the page, not at the bottom of the initClient function.
Update 10/11/06:
Next try to make some changes. For example: tdn.garagegames.com/wiki/Beginner
a. Make a new Torque Logo model that is blue instead of red.
b. Make it give 2 points instead of just 1.
c. Make a yellow one that gives 5 points.
Once I have done this, I'll explain how to do it.
Tony
I will be documenting my experiences with it here. I am new to Torque and C++, so I hope others here will be able to learn from my mistakes.
UPDATE: Since I make several mistakes, you should always read through the entire thread of posts before trying anything. There are times that I ruin everything and have to start over entirely. Also, I have upgraded to Torque 1.5 since this post.
Thanks!
1. Download and run the TorqueGameEngineSDK-1-4.exe
This will install the Torque SDK on your computer. It will also place a link on the desktop called Torque Game Engine SDK.
This link is to twelve shortcuts;
FPS Starter Kit
Getting Started Tutorial
readMe.html
Racing Starter Kit
Torque Developer network (online)
Torque Game Engine Demo
Torque Game Engine Home Page
Tutorial Base
Torque Show Tool Pro
Torque Documentation (online)
Torque SDK forums (online)
Uninstall Torque Game Engine
I will read up on these and post more soon. I am starting with the ReadMe.html and the getting Started Tutorial.
UPDATE 10/07/06:
The ReadMe.html is just a one page note about where to find help. Look it over or skip straight to the Getting Starting .pdf tutorial.
2. The Getting Started tutorial is excellent. Follow it through, word by word, step by step, from start to finish, even if a section looks like common sense. !!!Be sure to place the TorqueScript in the EXACT location, exactly as the tutorial says!!! This was my first mistake.
All we have left to do is make sure that our 'clientGame.cs' file gets loaded. Open up GameOne/main.cs and find the 'Client scripts' section of the function called initClient. At the bottom of that list, add the following line, which will load our file:
I added the line at the bottom of the page, not at the bottom of the initClient function.
Update 10/11/06:
Next try to make some changes. For example: tdn.garagegames.com/wiki/Beginner
a. Make a new Torque Logo model that is blue instead of red.
b. Make it give 2 points instead of just 1.
c. Make a yellow one that gives 5 points.
Once I have done this, I'll explain how to do it.
Tony
#62
I've placed this code in my signTriggers.cs file and placed the .cs file in my server\scripts directory.
I load it from my game.cs file using the exec("./signTriggers.cs"); line.
This works for me.
Tony
12/05/2006 (5:29 pm)
OK, to finish my post on signTriggers...I've placed this code in my signTriggers.cs file and placed the .cs file in my server\scripts directory.
I load it from my game.cs file using the exec("./signTriggers.cs"); line.
Quote:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// DefaultTrigger is used by the mission editor. This is also an example
// of trigger methods and callbacks.
datablock TriggerData(YorkvilleTrigger)
{
// The period is value is used to control how often the console
// onTriggerTick callback is called while there are any objects
// in the trigger. The default value is 100 MS.
tickPeriodMS = 5000;
};
datablock TriggerData(TowneTrigger)
{
// The period is value is used to control how often the console
// onTriggerTick callback is called while there are any objects
// in the trigger. The default value is 100 MS.
tickPeriodMS = 5000;
};
datablock TriggerData(MountPleasantTrigger)
{
// The period is value is used to control how often the console
// onTriggerTick callback is called while there are any objects
// in the trigger. The default value is 100 MS.
tickPeriodMS = 5000;
};
//-----------------------------------------------------------------------------
function YorkvilleTrigger::onEnterTrigger(%this,%trigger,%obj)
{
// This method is called whenever an object enters the %trigger
// area, the object is passed as %obj. The default onEnterTrigger
// method (in the C++ code) invokes the ::onTrigger(%trigger,1) method on
// every object (whatever it's type) in the same group as the trigger.
echo("This road leads to Yorkville.");
//print message to client
centerPrint(%obj.client, "This road leads to Yorkville.", 1, 1);
Parent::onEnterTrigger(%this,%trigger,%obj);
}
function YorkvilleTrigger::onLeaveTrigger(%this,%trigger,%obj)
{
// This method is called whenever an object leaves the %trigger
// area, the object is passed as %obj. The default onLeaveTrigger
// method (in the C++ code) invokes the ::onTrigger(%trigger,0) method on
// every object (whatever it's type) in the same group as the trigger.
Parent::onLeaveTrigger(%this,%trigger,%obj);
}
function YorkvilleTrigger::onTickTrigger(%this,%trigger)
{
// This method is called every tickPerioMS, as long as any
// objects intersect the trigger. The default onTriggerTick
// method (in the C++ code) invokes the ::onTriggerTick(%trigger) method on
// every object (whatever it's type) in the same group as the trigger.
echo("This road leads to Yorkville.");
//print message to client
centerPrint(%obj.client, "This road leads to Yorkville.", 1, 1);
// You can iterate through the objects in the list by using these
// methods:
// %this.getNumObjects();
// %this.getObject(n);
Parent::onTickTrigger(%this,%trigger);
}
//-----------------------------------------------------------------------------
function TowneTrigger::onEnterTrigger(%this,%trigger,%obj)
{
// This method is called whenever an object enters the %trigger
// area, the object is passed as %obj. The default onEnterTrigger
// method (in the C++ code) invokes the ::onTrigger(%trigger,1) method on
// every object (whatever it's type) in the same group as the trigger.
echo("This road leads to Towne.");
//print message to client
centerPrint(%obj.client, "This road leads to Towne.", 1, 1);
Parent::onEnterTrigger(%this,%trigger,%obj);
}
function TowneTrigger::onLeaveTrigger(%this,%trigger,%obj)
{
// This method is called whenever an object leaves the %trigger
// area, the object is passed as %obj. The default onLeaveTrigger
// method (in the C++ code) invokes the ::onTrigger(%trigger,0) method on
// every object (whatever it's type) in the same group as the trigger.
Parent::onLeaveTrigger(%this,%trigger,%obj);
}
function TowneTrigger::onTickTrigger(%this,%trigger)
{
// This method is called every tickPerioMS, as long as any
// objects intersect the trigger. The default onTriggerTick
// method (in the C++ code) invokes the ::onTriggerTick(%trigger) method on
// every object (whatever it's type) in the same group as the trigger.
echo("This road leads to Towne.");
//print message to client
centerPrint(%obj.client, "This road leads to Towne.", 1, 1);
// You can iterate through the objects in the list by using these
// methods:
// %this.getNumObjects();
// %this.getObject(n);
Parent::onTickTrigger(%this,%trigger);
}
//-----------------------------------------------------------------------------
function MountPleasantTrigger::onEnterTrigger(%this,%trigger,%obj)
{
// This method is called whenever an object enters the %trigger
// area, the object is passed as %obj. The default onEnterTrigger
// method (in the C++ code) invokes the ::onTrigger(%trigger,1) method on
// every object (whatever it's type) in the same group as the trigger.
echo("This road leads to Mount Pleasant.");
//print message to client
centerPrint(%obj.client, "This road leads to Mount Pleasant.", 1, 1);
Parent::onEnterTrigger(%this,%trigger,%obj);
}
function MountPleasantTrigger::onLeaveTrigger(%this,%trigger,%obj)
{
// This method is called whenever an object leaves the %trigger
// area, the object is passed as %obj. The default onLeaveTrigger
// method (in the C++ code) invokes the ::onTrigger(%trigger,0) method on
// every object (whatever it's type) in the same group as the trigger.
Parent::onLeaveTrigger(%this,%trigger,%obj);
}
function MountPleasantTrigger::onTickTrigger(%this,%trigger)
{
// This method is called every tickPerioMS, as long as any
// objects intersect the trigger. The default onTriggerTick
// method (in the C++ code) invokes the ::onTriggerTick(%trigger) method on
// every object (whatever it's type) in the same group as the trigger.
echo("This road leads to Mount Pleasant.");
//print message to client
centerPrint(%obj.client, "This road leads to Mount Pleasant.", 1, 1);
// You can iterate through the objects in the list by using these
// methods:
// %this.getNumObjects();
// %this.getObject(n);
Parent::onTickTrigger(%this,%trigger);
}
This works for me.
Tony
#63
BackUp! your files before editing!
Following the tutorial Advanced AI for TGE 1.4 by Jeff Loveless.
Download the file advancedai.zip from
www.garagegames.com/uploaded/code/10923.advancedai.zip
Here's what I did...
1. Change the names of SDK/Engine/Game/AiPlayer.cc to AiPlayer_BKUP.cc and AiPlayer.h to AiPlayer_BKUP.h.
2. Copy the new AiPlayer.cc and AiPlayer.h to the SDK/Engine/Game/ folder.
3. Add this line to client/config.sys
//----- Pressing CTRL-B runs the addBot function
moveMap.bind(keyboard, "ctrl b", addBot);
//-----
4. Add this function to the MISC section at the bottom of client\scripts\default.bind.cs
//-----
function addBot(%val)
{
if(%val)
{
commandToServer('AddBot'); }
}
moveMap.bind(keyboard, "ctrl b", addBot);
//-----
5. Add this function to server/scripts/aiPlayer.cs
Place it after the function AIManager::Think ... } <<--after end bracket
//----- PLACE THE "PICK PATH" FUNCTION HERE -----//
before-->> function AIManager::spawn
Here's the code for the Pick Path function;
//-----
function AIManager::pickPath(%this)
{
%pathsName = "MissionGroup/Paths";
%paths = nameToID(%pathsName);
%path = "";
if (%paths != -1) {
%count = %paths.getCount();
if (%count != 0) {
%index = getRandom(%count-1);
%path = %paths.getObject(%index);
} else
error("No paths points found in " @ %pathsName);
}
else
error("Missing paths group " @ %pathsName);
return %path;
}
//-----
and add this line to function AIManager::spawn(%this)
//-----
%path=AIManager::pickPath();
//-----
6. in Server/Scripts/command.cs
add these two functions, even though I don't know if they're important. But they're NOT in my commands.cs file so I'm adding them. Place them AFTER this:
function serverCmdDropCameraAtPlayer(%client)
{
%client.camera.setTransform(%client.player.getEyeTransform());
%client.camera.setVelocity("0 0 0");
%client.setControlObject(%client.camera);
}
//-----Place the two new functions here-----//
//-----
function serverCmdSetPlayerPos(%client,%pos)
{
if (isObject(%client.player))
%client.player.setPlayerPosition(%pos);
}
function serverCmdGetPlayerPos(%client)
{
if (isObject(%client.player))
return %client.player.getPosition();
else
return 0;
}
//-----
7. Add this function at the end of command.cs
//-----
function serverCmdSelectObject(%client, %mouseVec, %cameraPoint)
{
//Determine how far should the picking ray extend into the world? %selectRange = 200;
// scale mouseVec to the range the player is able to select with mouse
%mouseScaled = VectorScale(%mouseVec, %selectRange);
// cameraPoint = the world position of the camera
// rangeEnd = camera point + length of selectable range
%rangeEnd = VectorAdd(%cameraPoint, %mouseScaled);
// Search for anything that is selectable - below are some examples %searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::CorpseObjectType | $TypeMasks::ItemObjectType | $TypeMasks::TriggerObjectType;
// Search for objects within the range that fit the masks above
// If we are in first person mode, we make sure player is not selectable by setting fourth parameter (exempt // from collisions) when calling ContainerRayCast
%player = %client.player;
if ($firstPerson)
{
%scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks, %player);
}
else //3rd person - player is selectable in this case
{
%scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks);
} // a target in range was found so select it
if (%scanTarg)
{
%targetObject = firstWord(%scanTarg);
%client.setSelectedObj(%targetObject);
}
}
$botCounter = 1;
$botCounter++;
function serverCmdAddBot(%client)
{
MissionCleanup.add($bots);
%npcName = "Kork" @ $botCounter;
%path = AIManager::pickPath();
%mrbot = AIPlayer::spawnOnPath(%npcName, %path);
$bots[$botCounter] = %mrbot;
%mrbot.followPath(%path,-1);
%mrbot.setMoveSpeed(0.8);
%mrbot.setSite(60);
%mrbot.incInventory(Crossbow,1);
%mrbot.incInventory(CrossbowAmmo,120);
%mrbot.mountImage(CrossbowImage,0);
%mrbot.setScanningPlayers(true);
MissionCleanup.add(%mrbot);
//%mrbot.setAttackTargetObject(%client.player);
$botCounter++;
}
//-----
Now I have to run Torque to see if it works.
Well, sort-of. CTRL-B does create a new AI Kork. I kept pressing CTRL-B and had a dozen Kork's running around the path in the STRONGHOLD mission, but they didn't attack me. I must have left out some code somewhere.
I'll look into it.
I also want to try to place a path in my own world (from Starter.FPS).
I'll let you know if it works.
Oh yeah, I forgot to delete the .dso's. Let's try again... Nope. I can spawn a bunch of Kork's in STRONGHOLD, but none attack me, and I can't spawn any in my own world. Aw heck... I forgot to re-build. Let's try that.
Still no better. I can only spawn Kork's in STRONGHOLD, not in my own. And they still don't attack me.
But at least they spawn. I wonder if I can set up a trigger to automatically spawn a new Kork.
Tony
12/18/2006 (10:18 am)
12 12 2006 Torque 101: Adding BotsBackUp! your files before editing!
Following the tutorial Advanced AI for TGE 1.4 by Jeff Loveless.
Download the file advancedai.zip from
www.garagegames.com/uploaded/code/10923.advancedai.zip
Quote:
You'll need to add the .cc and .h files to your project and add the .cs to your scripts in client and server directories, re-build, delete DSO's and you're done!
Here's what I did...
1. Change the names of SDK/Engine/Game/AiPlayer.cc to AiPlayer_BKUP.cc and AiPlayer.h to AiPlayer_BKUP.h.
2. Copy the new AiPlayer.cc and AiPlayer.h to the SDK/Engine/Game/ folder.
3. Add this line to client/config.sys
//----- Pressing CTRL-B runs the addBot function
moveMap.bind(keyboard, "ctrl b", addBot);
//-----
4. Add this function to the MISC section at the bottom of client\scripts\default.bind.cs
//-----
function addBot(%val)
{
if(%val)
{
commandToServer('AddBot'); }
}
moveMap.bind(keyboard, "ctrl b", addBot);
//-----
5. Add this function to server/scripts/aiPlayer.cs
Place it after the function AIManager::Think ... } <<--after end bracket
//----- PLACE THE "PICK PATH" FUNCTION HERE -----//
before-->> function AIManager::spawn
Here's the code for the Pick Path function;
//-----
function AIManager::pickPath(%this)
{
%pathsName = "MissionGroup/Paths";
%paths = nameToID(%pathsName);
%path = "";
if (%paths != -1) {
%count = %paths.getCount();
if (%count != 0) {
%index = getRandom(%count-1);
%path = %paths.getObject(%index);
} else
error("No paths points found in " @ %pathsName);
}
else
error("Missing paths group " @ %pathsName);
return %path;
}
//-----
and add this line to function AIManager::spawn(%this)
//-----
%path=AIManager::pickPath();
//-----
6. in Server/Scripts/command.cs
add these two functions, even though I don't know if they're important. But they're NOT in my commands.cs file so I'm adding them. Place them AFTER this:
function serverCmdDropCameraAtPlayer(%client)
{
%client.camera.setTransform(%client.player.getEyeTransform());
%client.camera.setVelocity("0 0 0");
%client.setControlObject(%client.camera);
}
//-----Place the two new functions here-----//
//-----
function serverCmdSetPlayerPos(%client,%pos)
{
if (isObject(%client.player))
%client.player.setPlayerPosition(%pos);
}
function serverCmdGetPlayerPos(%client)
{
if (isObject(%client.player))
return %client.player.getPosition();
else
return 0;
}
//-----
7. Add this function at the end of command.cs
//-----
function serverCmdSelectObject(%client, %mouseVec, %cameraPoint)
{
//Determine how far should the picking ray extend into the world? %selectRange = 200;
// scale mouseVec to the range the player is able to select with mouse
%mouseScaled = VectorScale(%mouseVec, %selectRange);
// cameraPoint = the world position of the camera
// rangeEnd = camera point + length of selectable range
%rangeEnd = VectorAdd(%cameraPoint, %mouseScaled);
// Search for anything that is selectable - below are some examples %searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::CorpseObjectType | $TypeMasks::ItemObjectType | $TypeMasks::TriggerObjectType;
// Search for objects within the range that fit the masks above
// If we are in first person mode, we make sure player is not selectable by setting fourth parameter (exempt // from collisions) when calling ContainerRayCast
%player = %client.player;
if ($firstPerson)
{
%scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks, %player);
}
else //3rd person - player is selectable in this case
{
%scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks);
} // a target in range was found so select it
if (%scanTarg)
{
%targetObject = firstWord(%scanTarg);
%client.setSelectedObj(%targetObject);
}
}
$botCounter = 1;
$botCounter++;
function serverCmdAddBot(%client)
{
MissionCleanup.add($bots);
%npcName = "Kork" @ $botCounter;
%path = AIManager::pickPath();
%mrbot = AIPlayer::spawnOnPath(%npcName, %path);
$bots[$botCounter] = %mrbot;
%mrbot.followPath(%path,-1);
%mrbot.setMoveSpeed(0.8);
%mrbot.setSite(60);
%mrbot.incInventory(Crossbow,1);
%mrbot.incInventory(CrossbowAmmo,120);
%mrbot.mountImage(CrossbowImage,0);
%mrbot.setScanningPlayers(true);
MissionCleanup.add(%mrbot);
//%mrbot.setAttackTargetObject(%client.player);
$botCounter++;
}
//-----
Now I have to run Torque to see if it works.
Well, sort-of. CTRL-B does create a new AI Kork. I kept pressing CTRL-B and had a dozen Kork's running around the path in the STRONGHOLD mission, but they didn't attack me. I must have left out some code somewhere.
I'll look into it.
I also want to try to place a path in my own world (from Starter.FPS).
I'll let you know if it works.
Oh yeah, I forgot to delete the .dso's. Let's try again... Nope. I can spawn a bunch of Kork's in STRONGHOLD, but none attack me, and I can't spawn any in my own world. Aw heck... I forgot to re-build. Let's try that.
Still no better. I can only spawn Kork's in STRONGHOLD, not in my own. And they still don't attack me.
But at least they spawn. I wonder if I can set up a trigger to automatically spawn a new Kork.
Tony
#64
I'm following a tutorial by Tim Newell called "Inventory Popup Tutorial". It can be found at www.garagegames.com/index.php?sec=mg&mod=resource&page=category&qid=115 It's listed as "Basic Information" and is under the category Scripting.
It basically creates a GUI to hold four things as inventory.
1. BACKUP EVERYTHING!!!! I mess things up often. I'm not responsible if you ruin hundreds of hours of game creation. I don't know if the tutorial will work. I don't know what I'm doing. I'm just trying to help.
2. You need some .PNG files to make this work. I use Paint Shop Pro to create mine.
a background image for the popup named InvPanel.png
a 64x64 square for the empty inventory slots named InvEmpty.png
a 64x64 square picture of your item named Inv*insert ingame datablock name here*.png
for example, a crossbow would be InvCrossbow.png
a 20x20 up arrow named InvUp.png
a 20x20 down arrow named InvDown.png
Place these .PNG files in the client/iu/Inv folder (you have to make this folder).
OK, let's code.
3. Open client\scripts\default.bind.cs and scroll down to Item manipulation.
add this code:
//-----
$InvState = false;
function toggleInventory() {
if ($InvState == false) {
commandToServer('DisplayInventory');
$InvState = true;
} else {
Canvas.setContent( PlayGui );
$InvState = false;
}
}
moveMap.bindCmd(keyboard, "i", "toggleInventory();", "");
//-----
4. Open client/config.cs and add this at the end
//-----
moveMap.bindCmd(keyboard, "i", "toggleInventory();", "");
//-----
5. Open \client\init.cs and add after the other Gui exec statements (under // Load up the shell GUIs):
//-----
exec("./ui/InventoryGui.gui");
//-----
and add after the other script exec statements (under // Client scripts):
//-----
exec("./scripts/InventoryGui.cs");
//-----
6. Open client/ui/defaultGameProfiles.cs and add (at the end)
//-----
//-----------------------------------------------------------------------------
// Inventory popup profile
new GuiControlProfile ("InventoryProfile")
{
opaque = false;
fillColor = "128 128 128";
fontColor = "0 0 0";
border = true;
borderColor = "0 0 0";
fontType = "Arial";
fontSize = 18;
};
//-----
7. open server/scripts/commands.cs and add (at the end)
//-----
//-----------------------------------------------------------------------------
function serverCmdDisplayInventory(%client)
{
//Had to use a string hack to get the data to the client since arrays do not seem to
//work in function passing
if (%client.player.hasInventory("Rifle"))
%InvData = "Rifle" @ " " @ %client.player.hasInventory("RifleAmmo") @ " ";
if (%client.player.hasInventory("Crossbow")) {
%InvData = %InvData @ "Crossbow" @ " " @ %client.player.hasInventory("CrossbowAmmo") @ " ";
}
//Send the info to the client to display it
commandToClient(%client, 'PopInventory',%InvData);
}
8. Open server/scripts/inventory.cs and add at the end
//-----
//-----------------------------------------------------------------------------
function ShapeBase::hasInventory(%this, %data) {
return %this.inv[%data];
}
//-----
9. Create a new InventoryGui.cs file in client/scripts with this code
//-----
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-------------------------------------------------
// InventoryGui.cs
//-------------------------------------------------
$InvWordCount = 0;
$CurrentBottomSlot = 4;
new ActionMap(InvMap);
InvMap.bindCmd(keyboard, "i", "toggleInventory();", "");
function InventoryGui::onWake()
{
InvpName.setText("Inventory for " @ $pref::Player::Name);
$enableDirectInput = "1";
activateDirectInput();
InvMap.push();
}
function InventoryGui::onSleep()
{
InvMap.pop();
}
function clientCmdPopInventory(%InvData) {
//String Hack since I couldn't get arrays to pass
$InvWordCount = getWordCount(%InvData);
//reset the Bottom Slot to default
$CurrentBottomSlot = 4;
%InvWdCount = 0;
if ($InvWordCount > 0) {
for (%i = 0; %i < ($InvWordCount/2); %i++) {
$WeaponTokens[%i] = getWord(%InvData,%InvWdCount);
%InvWdCount++;
$AmmoTokens[%i] = getWord(%InvData,%InvWdCount);
%InvWdCount++;
}
}
if ($InvWordCount > 0) { //Slot 1
Slot1.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[0] @ ".png");
Slot1Desc.setText($WeaponTokens[0] @ " Ammo: " @ $AmmoTokens[0]);
} else {
Slot1.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot1Desc.setText("Inventory Slot Available");
}
if ($InvWordCount > 2) { //Slot 2
Slot2.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[1] @ ".png");
Slot2Desc.setText($WeaponTokens[1] @ " Ammo: " @ $AmmoTokens[1]);
} else {
Slot2.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot2Desc.setText("Inventory Slot Available");
}
if ($InvWordCount > 4) { //Slot 3
Slot3.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[2] @ ".png");
Slot3Desc.setText($WeaponTokens[2] @ " Ammo: " @ $AmmoTokens[2]);
} else {
Slot3.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot3Desc.setText("Inventory Slot Available");
}
if ($InvWordCount > 6) { //Slot 4
Slot4.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[3] @ ".png");
Slot4Desc.setText($WeaponTokens[3] @ " Ammo: " @ $AmmoTokens[3]);
} else {
Slot4.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot4Desc.setText("Inventory Slot Available");
}
Canvas.setContent( InventoryGui );
}
12/18/2006 (10:21 am)
12 14 2006 Torque 101 : Inventory SystemI'm following a tutorial by Tim Newell called "Inventory Popup Tutorial". It can be found at www.garagegames.com/index.php?sec=mg&mod=resource&page=category&qid=115 It's listed as "Basic Information" and is under the category Scripting.
It basically creates a GUI to hold four things as inventory.
1. BACKUP EVERYTHING!!!! I mess things up often. I'm not responsible if you ruin hundreds of hours of game creation. I don't know if the tutorial will work. I don't know what I'm doing. I'm just trying to help.
2. You need some .PNG files to make this work. I use Paint Shop Pro to create mine.
a background image for the popup named InvPanel.png
a 64x64 square for the empty inventory slots named InvEmpty.png
a 64x64 square picture of your item named Inv*insert ingame datablock name here*.png
for example, a crossbow would be InvCrossbow.png
a 20x20 up arrow named InvUp.png
a 20x20 down arrow named InvDown.png
Place these .PNG files in the client/iu/Inv folder (you have to make this folder).
OK, let's code.
3. Open client\scripts\default.bind.cs and scroll down to Item manipulation.
add this code:
//-----
$InvState = false;
function toggleInventory() {
if ($InvState == false) {
commandToServer('DisplayInventory');
$InvState = true;
} else {
Canvas.setContent( PlayGui );
$InvState = false;
}
}
moveMap.bindCmd(keyboard, "i", "toggleInventory();", "");
//-----
4. Open client/config.cs and add this at the end
//-----
moveMap.bindCmd(keyboard, "i", "toggleInventory();", "");
//-----
5. Open \client\init.cs and add after the other Gui exec statements (under // Load up the shell GUIs):
//-----
exec("./ui/InventoryGui.gui");
//-----
and add after the other script exec statements (under // Client scripts):
//-----
exec("./scripts/InventoryGui.cs");
//-----
6. Open client/ui/defaultGameProfiles.cs and add (at the end)
//-----
//-----------------------------------------------------------------------------
// Inventory popup profile
new GuiControlProfile ("InventoryProfile")
{
opaque = false;
fillColor = "128 128 128";
fontColor = "0 0 0";
border = true;
borderColor = "0 0 0";
fontType = "Arial";
fontSize = 18;
};
//-----
7. open server/scripts/commands.cs and add (at the end)
//-----
//-----------------------------------------------------------------------------
function serverCmdDisplayInventory(%client)
{
//Had to use a string hack to get the data to the client since arrays do not seem to
//work in function passing
if (%client.player.hasInventory("Rifle"))
%InvData = "Rifle" @ " " @ %client.player.hasInventory("RifleAmmo") @ " ";
if (%client.player.hasInventory("Crossbow")) {
%InvData = %InvData @ "Crossbow" @ " " @ %client.player.hasInventory("CrossbowAmmo") @ " ";
}
//Send the info to the client to display it
commandToClient(%client, 'PopInventory',%InvData);
}
8. Open server/scripts/inventory.cs and add at the end
//-----
//-----------------------------------------------------------------------------
function ShapeBase::hasInventory(%this, %data) {
return %this.inv[%data];
}
//-----
9. Create a new InventoryGui.cs file in client/scripts with this code
//-----
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-------------------------------------------------
// InventoryGui.cs
//-------------------------------------------------
$InvWordCount = 0;
$CurrentBottomSlot = 4;
new ActionMap(InvMap);
InvMap.bindCmd(keyboard, "i", "toggleInventory();", "");
function InventoryGui::onWake()
{
InvpName.setText("Inventory for " @ $pref::Player::Name);
$enableDirectInput = "1";
activateDirectInput();
InvMap.push();
}
function InventoryGui::onSleep()
{
InvMap.pop();
}
function clientCmdPopInventory(%InvData) {
//String Hack since I couldn't get arrays to pass
$InvWordCount = getWordCount(%InvData);
//reset the Bottom Slot to default
$CurrentBottomSlot = 4;
%InvWdCount = 0;
if ($InvWordCount > 0) {
for (%i = 0; %i < ($InvWordCount/2); %i++) {
$WeaponTokens[%i] = getWord(%InvData,%InvWdCount);
%InvWdCount++;
$AmmoTokens[%i] = getWord(%InvData,%InvWdCount);
%InvWdCount++;
}
}
if ($InvWordCount > 0) { //Slot 1
Slot1.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[0] @ ".png");
Slot1Desc.setText($WeaponTokens[0] @ " Ammo: " @ $AmmoTokens[0]);
} else {
Slot1.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot1Desc.setText("Inventory Slot Available");
}
if ($InvWordCount > 2) { //Slot 2
Slot2.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[1] @ ".png");
Slot2Desc.setText($WeaponTokens[1] @ " Ammo: " @ $AmmoTokens[1]);
} else {
Slot2.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot2Desc.setText("Inventory Slot Available");
}
if ($InvWordCount > 4) { //Slot 3
Slot3.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[2] @ ".png");
Slot3Desc.setText($WeaponTokens[2] @ " Ammo: " @ $AmmoTokens[2]);
} else {
Slot3.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot3Desc.setText("Inventory Slot Available");
}
if ($InvWordCount > 6) { //Slot 4
Slot4.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[3] @ ".png");
Slot4Desc.setText($WeaponTokens[3] @ " Ammo: " @ $AmmoTokens[3]);
} else {
Slot4.setBitmap("fps/client/ui/Inv/InvEmpty.png");
Slot4Desc.setText("Inventory Slot Available");
}
Canvas.setContent( InventoryGui );
}
#65
// "Arrow Keys"
//--------------------------------------------------------------------
function UpArrowEvt::onMouseDown(%this, %obj)
{
if ($InvWordCount > 8 && $CurrentBottomSlot > 4) {
$CurrentBottomSlot--;
Slot1.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-4] @ ".png");
Slot1Desc.setText($WeaponTokens[$CurrentBottomSlot-4] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-4]);
Slot2.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-3] @ ".png");
Slot2Desc.setText($WeaponTokens[$CurrentBottomSlot-3] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-3]);
Slot3.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-2] @ ".png");
Slot3Desc.setText($WeaponTokens[$CurrentBottomSlot-2] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-2]);
Slot4.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-1] @ ".png");
Slot4Desc.setText($WeaponTokens[$CurrentBottomSlot-1] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-1]);
}
}
function DownArrowEvt::onMouseDown(%this, %obj)
{
if ($InvWordCount > 8 && $CurrentBottomSlot < ($InvWordCount/2)) {
$CurrentBottomSlot++;
Slot1.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-4] @ ".png");
Slot1Desc.setText($WeaponTokens[$CurrentBottomSlot-4] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-4]);
Slot2.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-3] @ ".png");
Slot2Desc.setText($WeaponTokens[$CurrentBottomSlot-3] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-3]);
Slot3.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-2] @ ".png");
Slot3Desc.setText($WeaponTokens[$CurrentBottomSlot-2] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-2]);
Slot4.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-1] @ ".png");
Slot4Desc.setText($WeaponTokens[$CurrentBottomSlot-1] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-1]);
}
}
//-----
10. Now it's time to create the actual GUI screen.
Open Torque and load your mission. (F10) to enter GUI Editor.
The rest is a direct quote from Tim's tutorial.
We can add slots, and maybe use an array of some kind to streamline the code. I'm no programmer so if anyone wants to update things using an array, please do. If you want to add potions, or magic items, or coins or whatever to the list, just go back and follow the steps.
So this does work, sort of. Pressing i does toggle an inventory screen. And it does display "Crossbow Bolts" and an amount. But the number isn't always accurate. It starts out at 1, then if I fire one, it changes to 9. If I pick up extras in STRONGHOLD it says I have 3. Even if I shoot a couple, it still says I have 3.
So there are some things that need corrected, but at least it's a start!
Thanks!
Tony
ps. Thanks to Tim Newell for a great tutorial!!!!
12/18/2006 (10:21 am)
//--------------------------------------------------------------------// "Arrow Keys"
//--------------------------------------------------------------------
function UpArrowEvt::onMouseDown(%this, %obj)
{
if ($InvWordCount > 8 && $CurrentBottomSlot > 4) {
$CurrentBottomSlot--;
Slot1.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-4] @ ".png");
Slot1Desc.setText($WeaponTokens[$CurrentBottomSlot-4] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-4]);
Slot2.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-3] @ ".png");
Slot2Desc.setText($WeaponTokens[$CurrentBottomSlot-3] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-3]);
Slot3.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-2] @ ".png");
Slot3Desc.setText($WeaponTokens[$CurrentBottomSlot-2] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-2]);
Slot4.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-1] @ ".png");
Slot4Desc.setText($WeaponTokens[$CurrentBottomSlot-1] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-1]);
}
}
function DownArrowEvt::onMouseDown(%this, %obj)
{
if ($InvWordCount > 8 && $CurrentBottomSlot < ($InvWordCount/2)) {
$CurrentBottomSlot++;
Slot1.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-4] @ ".png");
Slot1Desc.setText($WeaponTokens[$CurrentBottomSlot-4] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-4]);
Slot2.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-3] @ ".png");
Slot2Desc.setText($WeaponTokens[$CurrentBottomSlot-3] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-3]);
Slot3.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-2] @ ".png");
Slot3Desc.setText($WeaponTokens[$CurrentBottomSlot-2] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-2]);
Slot4.setBitmap("fps/client/ui/Inv/Inv" @ $WeaponTokens[$CurrentBottomSlot-1] @ ".png");
Slot4Desc.setText($WeaponTokens[$CurrentBottomSlot-1] @ " Ammo: " @ $AmmoTokens[$CurrentBottomSlot-1]);
}
}
//-----
10. Now it's time to create the actual GUI screen.
Open Torque and load your mission. (F10) to enter GUI Editor.
The rest is a direct quote from Tim's tutorial.
Quote:
In the grey box under the main window beside the New Control dropdown box you should see a button labeled New... Click it.
In the Gui Name field, type InventoryGui and in the Class: drop down box where it says GuiControl click it and choose GameTSCtrl. Then click Create.
Now we need to save our new Gui Script. Now you will see in the bottom dropdown box your script name. (InventoryGui) Beside it click the button labeled Save. Then click the Dropdown box beside Directory and choose fps/client/ui (this way our new script gets saved with all of the other scripts). Make sure the script is named InventoryGui.gui (it should be) then click Save.
Now to complete the rest of the GUI. First lets add the background for the popup. (my green half transparent png) click the drop down New Control and choose GuiBitmapCtrl. Then put in its Name: field InventoryPanel set its horizSizing to relative and its vertSizing to relative, then set its bitmap field to fps/client/ui/Inv/InvPanel.png then click Apply. Now resize it with the mouse to how you want it to look.
Next lets add the text that in game will be displayed as Inventory for PlayerName. Click the drop down New Control and choose GuiTextCtrl. Then put in its Name: field InvpName set its horizSizing to relative and its vertSizing to relative, then set its profile to InventoryProfile then click Apply. Now resize it with the mouse to be across the top of the popup background.
Next it is time to add the 4 empty png items to the left side of the popup background. Click the drop down New Control and choose GuiBitmapCtrl. Then put in its Name: field Slot1 set its horizSizing to relative and its vertSizing to relative, then set its bitmap field to fps/client/ui/Inv/InvEmpty.png then click Apply. Now resize it with the mouse to how you want it to look. Then repeat this step for the other 3, Slot2, Slot3, Slot4.
Next it is time to add the 4 Text Descriptions( to the right side of the 4 empty png items) that will display the weapon name and ammo. Click the drop down New Control and choose GuiTextCtrl. Then put in its Name: field Slot1Desc set its horizSizing to relative and its vertSizing to relative, then set its profile to InventoryProfile then click Apply. Now resize it with the mouse to how you want it to look. Then repeat this step for the other 3, Slot2Desc, Slot3Desc, Slot4Desc.
The last of the GUI to add is the arrow buttons. First we add the 2 pngs for the up arrow and the down arrow on the right side of the popup background. Click the drop down New Control and choose GuiBitmapCtrl. Then put in its Name: field UpArrow set its horizSizing to relative and its vertSizing to relative, then set its bitmap field to fps/client/ui/Inv/InvUp.png then click Apply. Now resize it with the mouse to how you want it to look. Next click the drop down New Control and choose GuiBitmapCtrl. Then put in its Name: field DownArrow set its horizSizing to relative and its vertSizing to relative, then set its bitmap field to fps/client/ui/Inv/InvDown.png then click Apply. Now resize it with the mouse to how you want it to look.
Now to give these "buttons" functionality we need to add a MouseEvt over each one. Click the drop down New Control and choose GuiMouseEventCtrl. Then put in its Name: field UpArrowEvt set its horizSizing to relative and its vertSizing to relative, then click Apply. Now resize and position it over the area that you want the user to click to get the up arrow to work. Next click the drop down New Control and choose GuiMouseEventCtrl. Then put in its Name: field DownArrowEvt set its horizSizing to relative and its vertSizing to relative, then click Apply. Now resize and position it over the area that you want the user to click to get the down arrow to work.
Now click Save again so that all of our changes from the beginning are saved in our new script. After saving, click the dropdown box with InventoryGui in it and select PlayGui then press F10 to exit the GUI editor.
Restarting Torque may be required before the Inventory will work correctly.
We can add slots, and maybe use an array of some kind to streamline the code. I'm no programmer so if anyone wants to update things using an array, please do. If you want to add potions, or magic items, or coins or whatever to the list, just go back and follow the steps.
So this does work, sort of. Pressing i does toggle an inventory screen. And it does display "Crossbow Bolts" and an amount. But the number isn't always accurate. It starts out at 1, then if I fire one, it changes to 9. If I pick up extras in STRONGHOLD it says I have 3. Even if I shoot a couple, it still says I have 3.
So there are some things that need corrected, but at least it's a start!
Thanks!
Tony
ps. Thanks to Tim Newell for a great tutorial!!!!
#66
Well, I have to get CTRL-B to add bots to MY world, not just to STRONGHOLD. But to do that, I have to learn how it works in STRONGHOLD.
Let's play around with STRONGHOLD a little.
First thing, I have to cut out the snow precipitation. It really kills my framerate.
In server/scripts/game.cs we can comment out the line that runs the environment stuff. Look under "//Load up all datablocks," and place two forward slashes // before the command
Run Torque. OK, that didn't work. It's still snowing. Go back and remove the comment slashes.
Let's just go into Torque and delete out the precipitation emitters for "Heavy Snow" and "Heavy Rain2". While you're at it, change the Name in the ScriptObject - MissionInfo to Stronghold Edit and Save Mission As - StrongholdEdit. That makes it easier to find it later on.
That helps with the frame count.
There are also "Shore" emitters of some kind. I'm getting rid of those too. I don't know what they are, but the red dots fill my screen. If we need them, we can replace them later.
In fact, we can delete out ALL the unimportant stuff. Towers, docks, trees, cottages, audio emitters, cottage fires, campfires, rocks, lights, health patches, crossbows... all gone.
I'm keeping Paths, since that's what we need to study.
File> SaveMissionAs> StrongholdEdit.mis
Ok, let's look at PlayerDropPoints. There are five spawn spheres. And under Paths, there are five markers in one path. Click on the first Marker and in the Inspector, Name it Marker01. Hit Apply. Name each marker in order.
Now, hit ALT-C to switch to camera view, and fly up above the world, looking down. You can see the Path of green dots, with Kork running around it. It's not a perfect match, but Kork basically runs from Marker to Marker.
When you click CTRL-B a new Kork spawns at Marker01.
OK, let's open our world and try to create a path. (F11) to open the Editor. (ALT-C) to switch to camera mode. Fly up above the world, looking down. (F4) to access the Creator.
Mission Objects> Mission> Path
Name it Path1
Mission Objects> Mission> PathMarker
Name it Marker01
Move to the right a little and create another pathMarker named Marker02 and again for Marker03.
In the Upper tree-view, drag each Marker into the Path1 folder.
Now click CTRL-B. Nothing happened. Kork didn't spawn. Why not? I don't know. Let's go back into STRONGHOLD. Save your mission.
OK, in STRONGHOLD, the Markers are in the Path folder, but they aren't PathMarkers, they're just Markers. And the Path folder is in a SimGroup called Paths.
Let's go back into our world.
Well, our Markers are also just called Markers (not PathMarkers) so we must be OK here. Let's make a SimGroup named Paths, put our path in it, and try CTRL-B again. Hey! Kork is running around my path. Even BEFORE I hit CTRL-B. It spawned automatically once I had things in the proper places. CTRL-B works too.!!!
Yay!!!
Save your mission.
If you shoot Kork 3 times, he will die. After about 10 seconds, his dead body fades out. Kork re-spawns automatically.
We can do alot with this.
First of all Kork shouldn't just appear. He could fade in, in the shadows, or in a cave. And new Korks should spawn automatically, even if the first Kork isn't dead yet. But you don't want too many Korks, so there should be a way to limit it.
Something like,
1. Count the number of Korks.
2. If the number of Korks is less than one hundred, then spawn another Kork.
You could add a Genocide feature.
1. Count the number of Korks.
2. If the number of Korks is greater than two, AND less than 100, then spawn another Kork.
3. If the number of Korks is zero, then display a message saying "Korks are extinct!"
If there are less than two Korks, then they can never reproduce, and Korks go extinct.
So we can spawn Korks, but they still don't attack me. I have to work on that.
Tony
12/21/2006 (10:22 am)
12-15-2006 Torque 101 : Adding Bots 2Well, I have to get CTRL-B to add bots to MY world, not just to STRONGHOLD. But to do that, I have to learn how it works in STRONGHOLD.
Let's play around with STRONGHOLD a little.
First thing, I have to cut out the snow precipitation. It really kills my framerate.
In server/scripts/game.cs we can comment out the line that runs the environment stuff. Look under "//Load up all datablocks," and place two forward slashes // before the command
Quote:
// exec("./environment.cs");
Run Torque. OK, that didn't work. It's still snowing. Go back and remove the comment slashes.
Let's just go into Torque and delete out the precipitation emitters for "Heavy Snow" and "Heavy Rain2". While you're at it, change the Name in the ScriptObject - MissionInfo to Stronghold Edit and Save Mission As - StrongholdEdit. That makes it easier to find it later on.
That helps with the frame count.
There are also "Shore" emitters of some kind. I'm getting rid of those too. I don't know what they are, but the red dots fill my screen. If we need them, we can replace them later.
In fact, we can delete out ALL the unimportant stuff. Towers, docks, trees, cottages, audio emitters, cottage fires, campfires, rocks, lights, health patches, crossbows... all gone.
I'm keeping Paths, since that's what we need to study.
File> SaveMissionAs> StrongholdEdit.mis
Ok, let's look at PlayerDropPoints. There are five spawn spheres. And under Paths, there are five markers in one path. Click on the first Marker and in the Inspector, Name it Marker01. Hit Apply. Name each marker in order.
Now, hit ALT-C to switch to camera view, and fly up above the world, looking down. You can see the Path of green dots, with Kork running around it. It's not a perfect match, but Kork basically runs from Marker to Marker.
When you click CTRL-B a new Kork spawns at Marker01.
OK, let's open our world and try to create a path. (F11) to open the Editor. (ALT-C) to switch to camera mode. Fly up above the world, looking down. (F4) to access the Creator.
Mission Objects> Mission> Path
Name it Path1
Mission Objects> Mission> PathMarker
Name it Marker01
Move to the right a little and create another pathMarker named Marker02 and again for Marker03.
In the Upper tree-view, drag each Marker into the Path1 folder.
Now click CTRL-B. Nothing happened. Kork didn't spawn. Why not? I don't know. Let's go back into STRONGHOLD. Save your mission.
OK, in STRONGHOLD, the Markers are in the Path folder, but they aren't PathMarkers, they're just Markers. And the Path folder is in a SimGroup called Paths.
Let's go back into our world.
Well, our Markers are also just called Markers (not PathMarkers) so we must be OK here. Let's make a SimGroup named Paths, put our path in it, and try CTRL-B again. Hey! Kork is running around my path. Even BEFORE I hit CTRL-B. It spawned automatically once I had things in the proper places. CTRL-B works too.!!!
Yay!!!
Save your mission.
If you shoot Kork 3 times, he will die. After about 10 seconds, his dead body fades out. Kork re-spawns automatically.
We can do alot with this.
First of all Kork shouldn't just appear. He could fade in, in the shadows, or in a cave. And new Korks should spawn automatically, even if the first Kork isn't dead yet. But you don't want too many Korks, so there should be a way to limit it.
Something like,
1. Count the number of Korks.
2. If the number of Korks is less than one hundred, then spawn another Kork.
You could add a Genocide feature.
1. Count the number of Korks.
2. If the number of Korks is greater than two, AND less than 100, then spawn another Kork.
3. If the number of Korks is zero, then display a message saying "Korks are extinct!"
If there are less than two Korks, then they can never reproduce, and Korks go extinct.
So we can spawn Korks, but they still don't attack me. I have to work on that.
Tony
#67
I found another tutorial. This one is called Slightly advanced bot scripting
Avg. Rating (of 20) 4.3
Basic Information
Author: Stefan Beffy Moises (Apr 08, 2002)
Categories: Scripting, Tutorials, Example Code
Apparently it's based on this tutorial www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=2215
And it can be found at this location www.garagegames.com/index.php?sec=mg&mod=resource&page=category&qid=127
It's a little old, but I'm going to see if it works. Since we have bots in our game, and can spawn more with CTRL-B, we now need the bots to "hunt".
First thing, as always BACK-UP YOUR FILES!!!
1. Add waypoints. Run Torque, go into the World Editor.
Shapes> Misc> WayPointMarker. I make five of them. Place them in a SimGroup called BotMarkers.
2. Open AIPlayer.cs (in Server/scripts).
add this:
//-----
function AIPlayer::handleWaypointsAndChase(%this)
{
// look for next "available" human player... :-)
%nextClient = ClientGroup.getObject(AIPlayer::getClosestHuman(%this));
$daPlaya = %nextClient.player;
if(!isObject($daPlaya) || !isObject(%this))
{
return;
}
// get his position:
%playPos = $daPlaya.getPosition();
// but first look for some waypoints...
// if there are some waypoints to follow
if(isObject(BotMarkers))
{
%num = getNumberBotMarkers();
// %this.countMyMoves tracks the number of
//waypoints already passed by this AI
if(%this.countMyMoves $= "")
{
//echo number of waypoint once
echo(%num @ " BotMarkers found!");
}
// output player posititon
error("Player Position: " @ %playPos);
// as long as this bot hasn't completed the waypoint path:
if(%this.countMyMoves < BotMarkers.getCount())
{
%dest = BotMarkers.getObject(%this.countMyMoves).position;
echo("Next destination: " @ %dest);
echo("The distance is: " @ getMarkerDistance(%this.countMyMoves, %this));
echo("myMoves along the waypoint path:" SPC %this.countMyMoves);
%this.setMoveDestination(%dest);
%this.move();
%this.countMyMoves++;
}
// okay, finished path, time for hunting the player...
else
{
AIPlayer::shootHimIfYouSeeHim(%this, %playPos);
}
}
// else: No waypoints, so chase the player!
else
{
AIPlayer::shootHimIfYouSeeHim(%this, %playPos);
}
}
//-----
then add this
//-----
function AIPlayer::shootHimIfYouSeeHim(%this, %playPos)
{
%this.setTargetObject($daPlaya);
%iSeeHim = aiPlayer::isObjectInView(%this, $daPlaya);
error(%this.player SPC "isObjectInView:" SPC %iSeeHim);
if(%iSeeHim)
{
error(%this.player @ ": I see him!!!");
%this.setAimLocation($daPlaya.getWorldBoxCenter());
// do we have a weapon?
if ( (%this.player.hasInventory("Crossbow") && %this.player.hasInventory("CrossbowAmmo"))
|| (%this.player.hasInventory("Rifle") && %this.player.hasInventory("RifleAmmo")) )
{
// shoooooot
%this.setTrigger(0,true);
}
else
{
error(%this.player SPC "has no weapon/ammo!");
}
%this.setMoveDestination(%playPos);
%this.move();
}
// no player in sight?
else
{
// clear aim
%this.clearAim();
// check for next nearest player %nextClient = ClientGroup.getObject(AIPlayer::getClosestHuman(%this));
$daPlaya = %nextClient.player; if(!isObject($daPlaya))
{
return;
}
%playPos = $daPlaya.getPosition();
%this.setMoveDestination(%playPos);
%this.setAimLocation($daPlaya.getWorldBoxCenter());
%this.move();
}
}
//-----
Add this to server/scripts/inventory.cs
Note: This was already in my Inventory.cs file
//-----------------------------------------------------------------------------
function ShapeBase::hasInventory(%this, %data) {
return %this.inv[%data];
}
//-----
Add this to AIPlayer.cs
//-----
// this one's provided by Ryan Mette
function aiPlayer::isObjectInView(%this, %object)
{
%player = %this.player;
// default sight range of AI:
%this.sightRange = 70.0;
if (!(isObject(%player) && isObject(%object))) return;
%objPos = %object.getWorldBoxCenter();
%eyePoint = %player.getWorldBoxCenter();
%distance = VectorDist(%objPos, %eyePoint);
error("DISTANCE:" SPC %distance);
if (%distance <= %this.sightRange)
{
// if the object is within 1.5 meters sometimes it can
// fall out of the field of view due to the eye height
if (%distance > 2)
{
%eyeTransform = %player.getEyeTransform();
%eyePoint = firstWord(%eyeTransform)
SPC getWord(%eyeTransform, 1)
SPC getWord(%eyeTransform, 2);
}
%eyeVector = VectorNormalize(%player.getEyeVector());
//make sure we're not looking through walls...
%mask = $TypeMasks::TerrainObjectType |
$TypeMasks::InteriorObjectType |
$TypeMasks::StaticShapeObjectType;
%losResult = containerRayCast(%objPos, %eyePoint, %mask);
%losObject = GetWord(%losResult, 0);
if (!isObject(%losObject))
{
//create the vector from this client to the client
%objVector = VectorNormalize(VectorSub(%objPos, %eyePoint));
// dot product to determine field of view
%dot = VectorDot(%objVector, %eyeVector);
// within field of view
return (%dot > 0.6);
}
}
return false;
}
//-----
//-----
// provided by Jared Hoberock
// I changed it to use ClientGroup instead of its own array like Jared did...
function AIPlayer::getClosestHuman(%this)
{
%botPos = %this.getLocation();
%count = ClientGroup.getCount();
for(%i = 0; %i < %count; %i++)
{
%client = ClientGroup.getObject(%i);
%playPos = %client.player.getPosition();
%tempDist = VectorDist(%playPos, %botPos);
if(%i == 0)
{
%dist = %tempDist;
%index = %i;
}
else
{
if(%dist > %tempDist) {
%dist = %tempDist;
%index = %i;
}
}
}
return %index;
}
//-----
//-----
function getNumberBotMarkers()
{
%count = BotMarkers.getCount();
return %count;
}
function getMarkerDistance(%markernumber, %bot)
{
%botPosition = %bot.getLocation();
%markerObject = BotMarkers.getObject(%markernumber);
%dist = vectorDist( %botPosition, %markerObject.position );
return %dist;
}
//-----
Well, it doesn't do anything. On the plus side, it doesn't seem to hurt anything either. The mission still loads, Kork still spawns, but still doesn't chase me.
Oh well.
More to come.
Tony
12/21/2006 (10:22 am)
12-15-2006 Torque 101 : Adding Bots 3I found another tutorial. This one is called Slightly advanced bot scripting
Avg. Rating (of 20) 4.3
Basic Information
Author: Stefan Beffy Moises (Apr 08, 2002)
Categories: Scripting, Tutorials, Example Code
Apparently it's based on this tutorial www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=2215
And it can be found at this location www.garagegames.com/index.php?sec=mg&mod=resource&page=category&qid=127
It's a little old, but I'm going to see if it works. Since we have bots in our game, and can spawn more with CTRL-B, we now need the bots to "hunt".
First thing, as always BACK-UP YOUR FILES!!!
1. Add waypoints. Run Torque, go into the World Editor.
Shapes> Misc> WayPointMarker. I make five of them. Place them in a SimGroup called BotMarkers.
2. Open AIPlayer.cs (in Server/scripts).
add this:
//-----
function AIPlayer::handleWaypointsAndChase(%this)
{
// look for next "available" human player... :-)
%nextClient = ClientGroup.getObject(AIPlayer::getClosestHuman(%this));
$daPlaya = %nextClient.player;
if(!isObject($daPlaya) || !isObject(%this))
{
return;
}
// get his position:
%playPos = $daPlaya.getPosition();
// but first look for some waypoints...
// if there are some waypoints to follow
if(isObject(BotMarkers))
{
%num = getNumberBotMarkers();
// %this.countMyMoves tracks the number of
//waypoints already passed by this AI
if(%this.countMyMoves $= "")
{
//echo number of waypoint once
echo(%num @ " BotMarkers found!");
}
// output player posititon
error("Player Position: " @ %playPos);
// as long as this bot hasn't completed the waypoint path:
if(%this.countMyMoves < BotMarkers.getCount())
{
%dest = BotMarkers.getObject(%this.countMyMoves).position;
echo("Next destination: " @ %dest);
echo("The distance is: " @ getMarkerDistance(%this.countMyMoves, %this));
echo("myMoves along the waypoint path:" SPC %this.countMyMoves);
%this.setMoveDestination(%dest);
%this.move();
%this.countMyMoves++;
}
// okay, finished path, time for hunting the player...
else
{
AIPlayer::shootHimIfYouSeeHim(%this, %playPos);
}
}
// else: No waypoints, so chase the player!
else
{
AIPlayer::shootHimIfYouSeeHim(%this, %playPos);
}
}
//-----
then add this
//-----
function AIPlayer::shootHimIfYouSeeHim(%this, %playPos)
{
%this.setTargetObject($daPlaya);
%iSeeHim = aiPlayer::isObjectInView(%this, $daPlaya);
error(%this.player SPC "isObjectInView:" SPC %iSeeHim);
if(%iSeeHim)
{
error(%this.player @ ": I see him!!!");
%this.setAimLocation($daPlaya.getWorldBoxCenter());
// do we have a weapon?
if ( (%this.player.hasInventory("Crossbow") && %this.player.hasInventory("CrossbowAmmo"))
|| (%this.player.hasInventory("Rifle") && %this.player.hasInventory("RifleAmmo")) )
{
// shoooooot
%this.setTrigger(0,true);
}
else
{
error(%this.player SPC "has no weapon/ammo!");
}
%this.setMoveDestination(%playPos);
%this.move();
}
// no player in sight?
else
{
// clear aim
%this.clearAim();
// check for next nearest player %nextClient = ClientGroup.getObject(AIPlayer::getClosestHuman(%this));
$daPlaya = %nextClient.player; if(!isObject($daPlaya))
{
return;
}
%playPos = $daPlaya.getPosition();
%this.setMoveDestination(%playPos);
%this.setAimLocation($daPlaya.getWorldBoxCenter());
%this.move();
}
}
//-----
Add this to server/scripts/inventory.cs
Note: This was already in my Inventory.cs file
//-----------------------------------------------------------------------------
function ShapeBase::hasInventory(%this, %data) {
return %this.inv[%data];
}
//-----
Add this to AIPlayer.cs
//-----
// this one's provided by Ryan Mette
function aiPlayer::isObjectInView(%this, %object)
{
%player = %this.player;
// default sight range of AI:
%this.sightRange = 70.0;
if (!(isObject(%player) && isObject(%object))) return;
%objPos = %object.getWorldBoxCenter();
%eyePoint = %player.getWorldBoxCenter();
%distance = VectorDist(%objPos, %eyePoint);
error("DISTANCE:" SPC %distance);
if (%distance <= %this.sightRange)
{
// if the object is within 1.5 meters sometimes it can
// fall out of the field of view due to the eye height
if (%distance > 2)
{
%eyeTransform = %player.getEyeTransform();
%eyePoint = firstWord(%eyeTransform)
SPC getWord(%eyeTransform, 1)
SPC getWord(%eyeTransform, 2);
}
%eyeVector = VectorNormalize(%player.getEyeVector());
//make sure we're not looking through walls...
%mask = $TypeMasks::TerrainObjectType |
$TypeMasks::InteriorObjectType |
$TypeMasks::StaticShapeObjectType;
%losResult = containerRayCast(%objPos, %eyePoint, %mask);
%losObject = GetWord(%losResult, 0);
if (!isObject(%losObject))
{
//create the vector from this client to the client
%objVector = VectorNormalize(VectorSub(%objPos, %eyePoint));
// dot product to determine field of view
%dot = VectorDot(%objVector, %eyeVector);
// within field of view
return (%dot > 0.6);
}
}
return false;
}
//-----
//-----
// provided by Jared Hoberock
// I changed it to use ClientGroup instead of its own array like Jared did...
function AIPlayer::getClosestHuman(%this)
{
%botPos = %this.getLocation();
%count = ClientGroup.getCount();
for(%i = 0; %i < %count; %i++)
{
%client = ClientGroup.getObject(%i);
%playPos = %client.player.getPosition();
%tempDist = VectorDist(%playPos, %botPos);
if(%i == 0)
{
%dist = %tempDist;
%index = %i;
}
else
{
if(%dist > %tempDist) {
%dist = %tempDist;
%index = %i;
}
}
}
return %index;
}
//-----
//-----
function getNumberBotMarkers()
{
%count = BotMarkers.getCount();
return %count;
}
function getMarkerDistance(%markernumber, %bot)
{
%botPosition = %bot.getLocation();
%markerObject = BotMarkers.getObject(%markernumber);
%dist = vectorDist( %botPosition, %markerObject.position );
return %dist;
}
//-----
Well, it doesn't do anything. On the plus side, it doesn't seem to hurt anything either. The mission still loads, Kork still spawns, but still doesn't chase me.
Oh well.
More to come.
Tony
#68
OK, so Kork spawns, but doesn't chase me. No big deal. My original plan was to create a non-violent game. So I don't want Kork to chase me.
I do need to be able to communicate with Kork, as an NPC (Non-Player Character).
I read somewhere that there is an NPCDialog command. Let me look into that.
In the meantime:
My game (if you can call it that) has the following:
1. A character - the default Orc (Kork) carrying a crossbow
2. Terrain - grassy hills, valleys, plateaus, and I drew in a few simple roads using the "sand" texture and a 1x1 brush. I don't have a lake or volcano since I switched from tutorial.base to starter.fps
3. a couple simple villages - I placed my own (original) DIF interiors to create Yorkville, Towne, and Mount Pleasant. Mount Pleasant is actually a misnomer, since it's neither a mountain nor is it particularly pleasant. The character spawns at a point in the middle of the three villages.
4. Road Signs - I have three signs which use triggers to display the name of the village which the road leads to
5. No background music - it was driving me crazy. I'll put it back in later, when I can add some variability. I'd like to use triggers to get the music to change, and maybe have a random background music play when nothing special is happening.
6. No precipitation. I know how to add it, and I will, but it really cuts into my framerate. Again, maybe I'll set a trigger to make it rain or snow later.
7 & 8. No more torque logos or scoreboard - I know how to change the torque logo and scoreboard, so I'm going to use it as a "gold" counter. I'll add that in later also.
9. I have added an "Inventory GUI" - it is functional, but severely limited at this point
10. I've added a "bot spawner" function. It adds Korks when CTRL-B is pressed. This needs to become an automated function. Something like-- every five minutes count AI Korks, if number of AI Korks is less than 5 then spawn a new AI Kork. That's not very realistic, but it keeps the number of AI Korks relatively stable for now. That can always be improved later.
If our "Beta Test Version" is v1.0 then we are currently at v0.007 Just 993 more steps and we'll be ready. At my current rate of progress, I've taken 7 steps in 64 days. Only 25 years until my beta test :)
Tony
12/21/2006 (10:24 am)
12-15-2006 Torque 101 : OK, so Kork spawns, but doesn't chase me. No big deal. My original plan was to create a non-violent game. So I don't want Kork to chase me.
I do need to be able to communicate with Kork, as an NPC (Non-Player Character).
I read somewhere that there is an NPCDialog command. Let me look into that.
In the meantime:
My game (if you can call it that) has the following:
1. A character - the default Orc (Kork) carrying a crossbow
2. Terrain - grassy hills, valleys, plateaus, and I drew in a few simple roads using the "sand" texture and a 1x1 brush. I don't have a lake or volcano since I switched from tutorial.base to starter.fps
3. a couple simple villages - I placed my own (original) DIF interiors to create Yorkville, Towne, and Mount Pleasant. Mount Pleasant is actually a misnomer, since it's neither a mountain nor is it particularly pleasant. The character spawns at a point in the middle of the three villages.
4. Road Signs - I have three signs which use triggers to display the name of the village which the road leads to
5. No background music - it was driving me crazy. I'll put it back in later, when I can add some variability. I'd like to use triggers to get the music to change, and maybe have a random background music play when nothing special is happening.
6. No precipitation. I know how to add it, and I will, but it really cuts into my framerate. Again, maybe I'll set a trigger to make it rain or snow later.
7 & 8. No more torque logos or scoreboard - I know how to change the torque logo and scoreboard, so I'm going to use it as a "gold" counter. I'll add that in later also.
9. I have added an "Inventory GUI" - it is functional, but severely limited at this point
10. I've added a "bot spawner" function. It adds Korks when CTRL-B is pressed. This needs to become an automated function. Something like-- every five minutes count AI Korks, if number of AI Korks is less than 5 then spawn a new AI Kork. That's not very realistic, but it keeps the number of AI Korks relatively stable for now. That can always be improved later.
If our "Beta Test Version" is v1.0 then we are currently at v0.007 Just 993 more steps and we'll be ready. At my current rate of progress, I've taken 7 steps in 64 days. Only 25 years until my beta test :)
Tony
#69
I created a new explodingBarrels.cs file, and exec it.
The console says:
class name = "barrelClass";
##
##
shapeFile = gameonefps/data/shapes/barrels/barrel01.dts";
If I comment out the class name line, then the console says
So is the problem with my shapeFile or is my BarrelClass wrong? A basic syntax error?
Thanks!!!
Tony
not a programmer
12/21/2006 (10:25 am)
I'm trying to get the barrels to "explode" when they're hit by crossbow ammo.I created a new explodingBarrels.cs file, and exec it.
Quote:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
// Exploding Shapes
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
datablock ExplodingBarrelData(explodingBarrel)
{
category = "Barrels";
class name = "BarrelClass";
shapeFile = "gameonefps/data/shapes/barrels/barrel01.dts";
};
//--collision detection of CrossbowBolt to barrel
function explodingBarrel::onCollision(%this, %obj, %col)
{
// only a collision by a CrossbowBolt causes explosion
if (%col.getClassName() $="CrossbowBolt")
{
//--if barrel is hit by CrossbowBolt then delete the barrel
%obj.delete();
}
};
The console says:
Quote:The ## is between class name and shapeFile like this
GameOneFPS/server/scripts/explodingItems.cs Line: 19 - parse error
>>> Advanced script error report. Line 19.
>>> Some error context, with ## on sides of error halt:
class name = "barrelClass";
##
##
shapeFile = gameonefps/data/shapes/barrels/barrel01.dts";
If I comment out the class name line, then the console says
Quote:.
unable to instantiate non-conobject class ExplodingBarrelData
So is the problem with my shapeFile or is my BarrelClass wrong? A basic syntax error?
Thanks!!!
Tony
not a programmer
#70
I created a new explodingItems.cs file. (don't forget to load it using EXEC)
//--------------------------------------------------------------
12/21/2006 (10:26 am)
With alot of help, I was able to make barrels explode. They take damage from the crossbow bolts (and anything else that causes damage). This can be modified to make any shape "destroyable". Big thanks to Fucifer for the help.I created a new explodingItems.cs file. (don't forget to load it using EXEC)
//--------------------------------------------------------------
#71
I created a new explodingItems.cs file. (don't forget to load it using EXEC)
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
// Exploding Shapes
//-----------------------------------------------------------------------------
datablock StaticShapeData(ExplodingBarrelShape)
{
category = "Exploding Objects";
shapeFile = "~/data/shapes/barrels/barrel01.dts";
maxDamage = 100;
destroyedLevel = 10;
mass = 10;
friction = 1;
elasticity = 0.3;
explosion = ExplodingBarrelShapeDataExplosion;
// debrisShapeName = "~/data/shapes/barrel/barrel_debris1.dts";
// debris = StaticShapeDataDebri;
};
function ExplodingBarrelShape::onAdd(%this,%obj)
{
%obj.playThread(0,"ambient");
}
//---------------------------------
//General StaticShapeData functions
//---------------------------------
function StaticShapeData::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
echo("Blowing stuff up");
%obj.applyDamage(%damage);
}
function StaticShapeData::onDamage(%this, %obj, %delta)
{
%damage = %obj.getDamageLevel();
if (%damage >= %this.destroyedLevel)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%obj.setDamageState(Destroyed);
}
}
else
{
if(%obj.getDamageState() !$= "Enabled")
%obj.setDamageState(Enabled);
}
}
function StaticShapeData::onDestroyed(%this, %obj, %prevState)
{
%obj.schedule(300, "delete");
}
//-----------------------------------------------
//-----------------------------------------------
You can modify the amount of damage taken by the barrel by changing the maxdamage and destroyedLevel amounts. maxDamage is the maximum amount of damage done by the source, and the destroyedLevel is the amount of damage that can be taken by the target before being destroyed (like hit points).
Thanks!
Tony
12/21/2006 (10:26 am)
With alot of help, I was able to make barrels explode. They take damage from the crossbow bolts (and anything else that causes damage). This can be modified to make any shape "destroyable". Big thanks to Fucifer for the help.I created a new explodingItems.cs file. (don't forget to load it using EXEC)
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
// Exploding Shapes
//-----------------------------------------------------------------------------
datablock StaticShapeData(ExplodingBarrelShape)
{
category = "Exploding Objects";
shapeFile = "~/data/shapes/barrels/barrel01.dts";
maxDamage = 100;
destroyedLevel = 10;
mass = 10;
friction = 1;
elasticity = 0.3;
explosion = ExplodingBarrelShapeDataExplosion;
// debrisShapeName = "~/data/shapes/barrel/barrel_debris1.dts";
// debris = StaticShapeDataDebri;
};
function ExplodingBarrelShape::onAdd(%this,%obj)
{
%obj.playThread(0,"ambient");
}
//---------------------------------
//General StaticShapeData functions
//---------------------------------
function StaticShapeData::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
echo("Blowing stuff up");
%obj.applyDamage(%damage);
}
function StaticShapeData::onDamage(%this, %obj, %delta)
{
%damage = %obj.getDamageLevel();
if (%damage >= %this.destroyedLevel)
{
if(%obj.getDamageState() !$= "Destroyed")
{
%obj.setDamageState(Destroyed);
}
}
else
{
if(%obj.getDamageState() !$= "Enabled")
%obj.setDamageState(Enabled);
}
}
function StaticShapeData::onDestroyed(%this, %obj, %prevState)
{
%obj.schedule(300, "delete");
}
//-----------------------------------------------
//-----------------------------------------------
You can modify the amount of damage taken by the barrel by changing the maxdamage and destroyedLevel amounts. maxDamage is the maximum amount of damage done by the source, and the destroyedLevel is the amount of damage that can be taken by the target before being destroyed (like hit points).
Thanks!
Tony
#72
1. At the top it says;
// Timeouts for corpse deletion.
$CorpseTimeoutValue = 22 * 1000;
This means it takes 22 seconds to delete the corpse. (22 x 1000ms)
I changed mine to 60 * 1000 so it takes a full minute before the corpse "decomposes". But there's more!
Further down in Player.cs it says;
//Schedule corpse removal. Just keeping the place clean.
%obj.schedule($CorpseTimeoutValue - 1000, "startFade", 1000, 0, true);
%obj.schedule($CorpseTimeoutValue, "delete");
}
This means that it takes one second for the corpse to fade out. (1000 ms)
If we change both from 1000 to 60000, it takes a full minute for it to fade away.
12/21/2006 (10:27 am)
I don't like how quickly the dead AI Kork fades/deletes. I tweaked the settings in server/scripts/player.cs1. At the top it says;
// Timeouts for corpse deletion.
$CorpseTimeoutValue = 22 * 1000;
This means it takes 22 seconds to delete the corpse. (22 x 1000ms)
I changed mine to 60 * 1000 so it takes a full minute before the corpse "decomposes". But there's more!
Further down in Player.cs it says;
//Schedule corpse removal. Just keeping the place clean.
%obj.schedule($CorpseTimeoutValue - 1000, "startFade", 1000, 0, true);
%obj.schedule($CorpseTimeoutValue, "delete");
}
This means that it takes one second for the corpse to fade out. (1000 ms)
If we change both from 1000 to 60000, it takes a full minute for it to fade away.
#73
I've learned that Torque has a "built-in" inventory system. It's minimal but still useful. If you look at \server\scripts\game.cs, in the GameConnection function (where it creates the player) it establishes a basic inventory set-up. It spawns the Player with the Crossbow and 10 bolts. (just for the record, a bolt is an arrow used by a crossbow).
// Starting equipment
%player.setInventory(Crossbow,1); // Give the player 1 crossbow
%player.setInventory(CrossbowAmmo,10); // Give the player 10 crossbowAmmo
%player.mountImage(CrossbowImage,0); // Set the starting weapon image as the crossbow
So this is called Hard Coding. It's using code to directly tell the game what you want. It's inflexible, no variables, and only used for very limited things.
Now look in \server\scripts\inventory.cs The first thirty lines or so explain how this inventory system works.
This basically says that your inventory will be made up of ShapeBase objects which are StaticShapes plus datablocks.
You have to code a datablock for each item you want to use in your inventory. For example, in crossbow.cs you can find the datablock for CrossbowAmmo
//-----------------------------------------------------------------------------
// Ammo Item
datablock ItemData(CrossbowAmmo)
{
// Mission editor category
category = "Ammo";
// Add the Ammo namespace as a parent. The ammo namespace provides
// common ammo related functions and hooks into the inventory system.
className = "Ammo";
// Basic Item properties
shapeFile = "~/data/shapes/crossbow/ammo.dts";
mass = 1;
elasticity = 0.2;
friction = 0.6;
// Dynamic properties defined by the scripts
pickUpName = "crossbow bolts";
maxInventory = 20;
};
//-------------------------------------------------------------------------------
The most important features are:
category is where it will be in the mission editor shapes directory
className is the name that will be used in other functions
shapeFile] is the .dts file
[b]pickUpName is the name used in scripts
We can make a new weapon item by creating a new .cs file called Swords.cs
(don't forget to load it using the EXEC line)
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Weapon Item. This is the item that exists in the world, i.e. when it's
// been dropped, thrown or is acting as re-spawnable item. When the weapon
// is mounted onto a shape, the **WEAPON**Image is used.
datablock ItemData(shortSword)
{
// Mission editor category
category = "Weapon";
// Hook into Item Weapon class hierarchy. The weapon namespace
// provides common weapon handling functions in addition to hooks
// into the inventory system.
className = "Weapon";
// Basic Item properties
shapeFile = "~/data/shapes/swords/shortSword.dts"; need a shortSword.dts
mass = 1;
elasticity = 0.2;
friction = 0.6;
emap = true;
// Dynamic properties defined by the scripts
pickUpName = "a short sword";
image = shortSwordImage;
itemType="melee"; //different than crossbow
};
//---How is this sword different than Ammo?
//---A sword gets held by the player, so it needs an IMAGE
//---to be mounted to the player[/i]. Therefore we need a datablock for the shortSwordImage
//--------------------------------------------------------------------------
// shortSword image which does all the work. Images do not normally exist in
// the world, they can only be mounted on ShapeBase objects.
datablock ShapeBaseImageData(shortSwordImage)
{
// Basic Item properties
shapeFile = "~/data/shapes/swords/shortSword.dts";
emap = true;
...more...
12/21/2006 (10:28 am)
12-21-2006 Torque 101: setInventoryI've learned that Torque has a "built-in" inventory system. It's minimal but still useful. If you look at \server\scripts\game.cs, in the GameConnection function (where it creates the player) it establishes a basic inventory set-up. It spawns the Player with the Crossbow and 10 bolts. (just for the record, a bolt is an arrow used by a crossbow).
// Starting equipment
%player.setInventory(Crossbow,1); // Give the player 1 crossbow
%player.setInventory(CrossbowAmmo,10); // Give the player 10 crossbowAmmo
%player.mountImage(CrossbowImage,0); // Set the starting weapon image as the crossbow
So this is called Hard Coding. It's using code to directly tell the game what you want. It's inflexible, no variables, and only used for very limited things.
Now look in \server\scripts\inventory.cs The first thirty lines or so explain how this inventory system works.
Quote:
// This inventory system is totally scripted, no C++ code involved.
// It uses object datablock names to track inventory and is generally
// object type, or class, agnostic. In other words, it will inventory
// any kind of ShapeBase object, though the throw method does assume
// that the objects are small enough to throw :)
//
// For a ShapeBase object to support inventory, it must have an array
// of inventory max values:
//
// %this.maxInv[GunAmmo] = 100;
// %this.maxInv[SpeedGun] = 1;
//
// where the names "SpeedGun" and "GunAmmo" are datablocks.
//
// For objects to be inventoriable, they must provide a set of inventory
// callback methods, mainly:
//
// onUse
// onThrow
// onPickup
//
// Example methods are given further down. The item.cs file also contains
// example inventory items.
This basically says that your inventory will be made up of ShapeBase objects which are StaticShapes plus datablocks.
You have to code a datablock for each item you want to use in your inventory. For example, in crossbow.cs you can find the datablock for CrossbowAmmo
//-----------------------------------------------------------------------------
// Ammo Item
datablock ItemData(CrossbowAmmo)
{
// Mission editor category
category = "Ammo";
// Add the Ammo namespace as a parent. The ammo namespace provides
// common ammo related functions and hooks into the inventory system.
className = "Ammo";
// Basic Item properties
shapeFile = "~/data/shapes/crossbow/ammo.dts";
mass = 1;
elasticity = 0.2;
friction = 0.6;
// Dynamic properties defined by the scripts
pickUpName = "crossbow bolts";
maxInventory = 20;
};
//-------------------------------------------------------------------------------
The most important features are:
category is where it will be in the mission editor shapes directory
className is the name that will be used in other functions
shapeFile] is the .dts file
[b]pickUpName is the name used in scripts
We can make a new weapon item by creating a new .cs file called Swords.cs
(don't forget to load it using the EXEC line)
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Weapon Item. This is the item that exists in the world, i.e. when it's
// been dropped, thrown or is acting as re-spawnable item. When the weapon
// is mounted onto a shape, the **WEAPON**Image is used.
datablock ItemData(shortSword)
{
// Mission editor category
category = "Weapon";
// Hook into Item Weapon class hierarchy. The weapon namespace
// provides common weapon handling functions in addition to hooks
// into the inventory system.
className = "Weapon";
// Basic Item properties
shapeFile = "~/data/shapes/swords/shortSword.dts"; need a shortSword.dts
mass = 1;
elasticity = 0.2;
friction = 0.6;
emap = true;
// Dynamic properties defined by the scripts
pickUpName = "a short sword";
image = shortSwordImage;
itemType="melee"; //different than crossbow
};
//---How is this sword different than Ammo?
//---A sword gets held by the player, so it needs an IMAGE
//---to be mounted to the player[/i]. Therefore we need a datablock for the shortSwordImage
//--------------------------------------------------------------------------
// shortSword image which does all the work. Images do not normally exist in
// the world, they can only be mounted on ShapeBase objects.
datablock ShapeBaseImageData(shortSwordImage)
{
// Basic Item properties
shapeFile = "~/data/shapes/swords/shortSword.dts";
emap = true;
...more...
#74
// for first person rendering.
mountPoint = 0;
eyeOffset = "0.1 0.4 -0.6";
// When firing from a point offset from the eye, muzzle correction
// will adjust the muzzle vector to point to the eye LOS point.
// Since this weapon doesn't actually fire from the muzzle point,
// we need to turn this off.
correctMuzzleVector = false;
// Add the WeaponImage namespace as a parent, WeaponImage namespace
// provides some hooks into the inventory system.
className = "WeaponImage";
// Projectile && Ammo.
item = Sword;
// ammo = CrossbowAmmo;
// projectile = FireBall;
// projectileType = Projectile;
// we are a HAND TO HAND weapon so we have a custom look anim
customLookAnim = "h1root"; // as a test
// Here are the Attacks we support
hthNumAttacks = 3;
hthAttack[0] = OneHandedAttackSwing;
hthAttack[1] = OneHandedAttackSlice;
hthAttack[2] = OneHandedAttackThrust;
// Images have a state system which controls how the animations
// are run, which sounds are played, script callbacks, etc. This
// state system is downloaded to the client so that clients can
// predict state changes and animate accordingly. In this case we are a
// HAND to HAND weapon and there is no ammo but we can use the
// reload time to limit how often the weapon can be fired
// Initial start up state
stateName[0] = "Preactivate";
stateTransitionOnLoaded[0] = "Activate";
// Activating the gun. Called when the weapon is first mounted
stateName[1] = "Activate";
stateTransitionOnTimeout[1] = "Ready";
stateTimeoutValue[1] = 0.6;
//stateSequence[1] = "Activate";
// Ready to fire, just waiting for the trigger
stateName[2] = "Ready";
stateTransitionOnTriggerDown[2] = "Swing";
// Fire the weapon. Calls the fire script which does the actual work.
stateName[3] = "Swing";
stateTransitionOnTimeout[3] = "Reset";
stateTimeoutValue[3] = 0.2;
stateFire[3] = true;
stateAllowImageChange[3] = false;
//stateSequence[3] = "Swing";
stateScript[3] = "onSwing";
stateSound[3] = SwordUseSound;
// Play the relead animation, and transition into
stateName[4] = "Reset";
stateTransitionOnTimeout[4] = "Ready";
stateTimeoutValue[4] = 0.8;
stateAllowImageChange[4] = false;
//stateSequence[4] = "Reset";
stateEjectShell[4] = false;
stateSound[4] = SwordResetSound;
};
//-----------------------------------------------------------------------------
function SwordImage::onSwing(%this, %obj, %slot)
{
// default hand to hand weapon code
WeaponImage::onSwingHandToHand(%this, %obj, %slot);
return;
}
//-----------------------------------------------------------
OK, you need to make sure you have the right .dts files and sounds. Use the console to find out if it works. The tilda key (~) opens the console.
I don't know if this works or not. I haven't tried it yet because I don't have any sword.dts files or sound effects yet.
Tony
12/21/2006 (10:28 am)
// Specify mount point & offset for 3rd person, and eye offset// for first person rendering.
mountPoint = 0;
eyeOffset = "0.1 0.4 -0.6";
// When firing from a point offset from the eye, muzzle correction
// will adjust the muzzle vector to point to the eye LOS point.
// Since this weapon doesn't actually fire from the muzzle point,
// we need to turn this off.
correctMuzzleVector = false;
// Add the WeaponImage namespace as a parent, WeaponImage namespace
// provides some hooks into the inventory system.
className = "WeaponImage";
// Projectile && Ammo.
item = Sword;
// ammo = CrossbowAmmo;
// projectile = FireBall;
// projectileType = Projectile;
// we are a HAND TO HAND weapon so we have a custom look anim
customLookAnim = "h1root"; // as a test
// Here are the Attacks we support
hthNumAttacks = 3;
hthAttack[0] = OneHandedAttackSwing;
hthAttack[1] = OneHandedAttackSlice;
hthAttack[2] = OneHandedAttackThrust;
// Images have a state system which controls how the animations
// are run, which sounds are played, script callbacks, etc. This
// state system is downloaded to the client so that clients can
// predict state changes and animate accordingly. In this case we are a
// HAND to HAND weapon and there is no ammo but we can use the
// reload time to limit how often the weapon can be fired
// Initial start up state
stateName[0] = "Preactivate";
stateTransitionOnLoaded[0] = "Activate";
// Activating the gun. Called when the weapon is first mounted
stateName[1] = "Activate";
stateTransitionOnTimeout[1] = "Ready";
stateTimeoutValue[1] = 0.6;
//stateSequence[1] = "Activate";
// Ready to fire, just waiting for the trigger
stateName[2] = "Ready";
stateTransitionOnTriggerDown[2] = "Swing";
// Fire the weapon. Calls the fire script which does the actual work.
stateName[3] = "Swing";
stateTransitionOnTimeout[3] = "Reset";
stateTimeoutValue[3] = 0.2;
stateFire[3] = true;
stateAllowImageChange[3] = false;
//stateSequence[3] = "Swing";
stateScript[3] = "onSwing";
stateSound[3] = SwordUseSound;
// Play the relead animation, and transition into
stateName[4] = "Reset";
stateTransitionOnTimeout[4] = "Ready";
stateTimeoutValue[4] = 0.8;
stateAllowImageChange[4] = false;
//stateSequence[4] = "Reset";
stateEjectShell[4] = false;
stateSound[4] = SwordResetSound;
};
//-----------------------------------------------------------------------------
function SwordImage::onSwing(%this, %obj, %slot)
{
// default hand to hand weapon code
WeaponImage::onSwingHandToHand(%this, %obj, %slot);
return;
}
//-----------------------------------------------------------
OK, you need to make sure you have the right .dts files and sounds. Use the console to find out if it works. The tilda key (~) opens the console.
I don't know if this works or not. I haven't tried it yet because I don't have any sword.dts files or sound effects yet.
Tony
#75
There are two types of game developers; Those that draw a map then build a world to match it, and those that build their world then draw a map of it. I belong to the first group. I have a map for my world, a 800x600 worldMap.png in my client\ui folder. I'm trying to add a "Display Map" feature using the M key to toggle the map of the world on/off. This isn't going to draw a map or modify the map or anything like that. It will simply toggle a picture of the map on/off . We have to create a GUI and bind the M key to it.
1. Back up!
2. Go into \client\scripts\default.bind.cs and at the bottom add this code:
//------------------
//--Map Toggle
//------------------
$worldmapState = false;
function toggleWorldmap() {
if ($worldmapState == false) {
commandToServer('DisplayWorldmapGui');
$worldmapState = true;
} else {
Canvas.setContent( PlayGui );
$worldmapState = false;
}
}
moveMap.bindCmd(keyboard, "m", "toggleWorldmap();", "");
//-----
3. Go into client\init.cs and add this to the SHELL GUI exec section:
exec("./ui/WorldMapGui.gui");
and under //Client scripts
exec("./scripts/WorldMap.cs");
4. In config.sys:
moveMap.bindCmd(keyboard, "m", "toggleWorldmap();", "");
5. Create a WorldmapGui.cs file:
//-------------------------------------------------
// WorldMapGui.cs
//-------------------------------------------------
new ActionMap(worldMap);
worldMap.bindCmd(keyboard, "m", "toggleWorldMap();", "");
function WorldMapGui::onWake()
{
Canvas.setContent( WorldMapGui );
}
function InventoryGui::onSleep()
{
Canvas.setContent(PlayGui );
}
5. Create a new GUI called WorldMapGui, set the bitmap as our worldmap.png.
This doesn't work yet. I'll have to ask why in the other forums....
Tony
12/21/2006 (10:29 am)
12 21 2006 Torque 101: Map ToggleThere are two types of game developers; Those that draw a map then build a world to match it, and those that build their world then draw a map of it. I belong to the first group. I have a map for my world, a 800x600 worldMap.png in my client\ui folder. I'm trying to add a "Display Map" feature using the M key to toggle the map of the world on/off. This isn't going to draw a map or modify the map or anything like that. It will simply toggle a picture of the map on/off . We have to create a GUI and bind the M key to it.
1. Back up!
2. Go into \client\scripts\default.bind.cs and at the bottom add this code:
//------------------
//--Map Toggle
//------------------
$worldmapState = false;
function toggleWorldmap() {
if ($worldmapState == false) {
commandToServer('DisplayWorldmapGui');
$worldmapState = true;
} else {
Canvas.setContent( PlayGui );
$worldmapState = false;
}
}
moveMap.bindCmd(keyboard, "m", "toggleWorldmap();", "");
//-----
3. Go into client\init.cs and add this to the SHELL GUI exec section:
exec("./ui/WorldMapGui.gui");
and under //Client scripts
exec("./scripts/WorldMap.cs");
4. In config.sys:
moveMap.bindCmd(keyboard, "m", "toggleWorldmap();", "");
5. Create a WorldmapGui.cs file:
//-------------------------------------------------
// WorldMapGui.cs
//-------------------------------------------------
new ActionMap(worldMap);
worldMap.bindCmd(keyboard, "m", "toggleWorldMap();", "");
function WorldMapGui::onWake()
{
Canvas.setContent( WorldMapGui );
}
function InventoryGui::onSleep()
{
Canvas.setContent(PlayGui );
}
5. Create a new GUI called WorldMapGui, set the bitmap as our worldmap.png.
This doesn't work yet. I'll have to ask why in the other forums....
Tony
#76
12/21/2006 (10:39 am)
Wow youve gotten alot accomplished since last time i checked, can you post a screenshot ( A BIG ONE ) here? of what it looks like please:)
#77
12/21/2006 (5:01 pm)
I can't post a screenshot because I don't have anywhere to upload the pic for display. Sorry.
#78
http://www.mediafire.com or www.rapidshare.com
just upload and post the url here!
would be great!
12/22/2006 (1:26 am)
Post at: http://www.mediafire.com or www.rapidshare.com
just upload and post the url here!
would be great!
#79
12/22/2006 (2:26 am)
Yup, or filefront, fileplanet so on and so on:P
#80
Great to see the progress you're making, Tony! I'm in the same boat, so watching you learn has helped me immensely.
12/22/2006 (8:43 am)
Imageshack is probably the best for quick, painless picture uploads.Great to see the progress you're making, Tony! I'm in the same boat, so watching you learn has helped me immensely.
Torque Owner Infinitum3D
Let's try something else. I've placed a tower (a default .dif that comes with the Starter.FPS) in my world. The tower looks great, all stone, iron, and wood, and it's massive.
The only problem is that it's pitch black inside. It's been portalized for game efficiency. That's pretty neat if you want it that way, but I'd like to see some shadows. Also, I'm sure the artist put alot of work into details inside, so we need to see those too.
Let's look in the front door into the total darkness.
Open the World Editor Creator and click on Mission Objects>Environment. Near the bottom I see Volume Light. Let's click it.
A pop-up opens. Name it innerLight and select the datablock for sgTempleWindowStaticPSDatablock and click OK. It doesn't do much.
Open the Inspector and let's change the datablock. Whoa! There's like, what, two dozen datablocks to choose from?
Let's try sgDefaultLightDataBlock. Nice! We see a little. Move it over into a corner and let's add a couple more. OK, so I now have 4 lights. Try to keep the Z position the same for each. Now I can see, but it's not very realistic. Where does the light come from? We need a flickering torchlight. In Shapes there is a lantern .DTS file that we can place. (I also have a torch.DTS that I use).
That adds a little more realism, but we need to place the lights inside the lanterns. Let's try that.
Open the Inspector and find the position of the lantern. Copy that location and apply it to the innerLight . Actually, let's change the name of innerLight to lanternLight while we're at it. You may need to tweak the location just a bit to get it perfect.
OK, so the light comes from the lantern, but it's not flickering like a true fire. Let's place a particle emitter. Go into the Creator again, Mission Objects>Environment>ParticleEmitter
Name it LanternFlames, datablock is TorchFireEmitterNode, and particleData is TorchFireEmitter. Set it's location to the same as the lanternLight. Adjust it a little if needed.
That's all there is to it.
Save the mission, run it and enter the tower. You now have a flickering lantern.
Remember to place similar objects into simGroup folders for better organizing.
Tony