Load Level Specific Scripts and Datablocks
by Steve Acaster · in Torque 3D Professional · 02/16/2010 (2:12 pm) · 13 replies
If you're making a singleplayer type level, it's quite likely that you'll be using triggers in that level to cause gameplay events ... LOTS of triggers ... lots of triggers that aren't any use in any other level.
In the theme of K.I.S.S. we'll be thinking of level specific files as trigger datablocks and the sort of things which effect/affect gameplay in such a manner. You can extend this to how you see fit.
Just a quick and dirty way to get level specific datablocks to load into a game without having all of the scripts and datablocks for different levels loading up as well.
Working off the Full template - no code changes.
1. Take all of your level specific data (triggers, etc) and place it in a *.cs file with the same name as your mission file. (eg: mylevel.mis = mylevel.cs)
2. Create a folder called "levels" in game/scripts/server. (eg: game/scripts/server/levels)
Place mylevel.cs into this new folder and rename mylevel.cs to mylevel.mis (but keep it a cs file, eg: in the directory it's Name= "mylevel.mis" and the Type= "CS File". Don't add an extra ".cs" or you'll have "mylevel.mis.cs" as the name, and that's really "mylevel.mis.cs.cs" and that ain't right (using a Win7 directory structure)).
3. Create a file called "loadlevels.cs" and slap this scriptette in it.
4. Fire this function off when the level loads up - NOT afterwards or you're trigger data won't get loaded into memory when the trigger objects look for it. In game/scripts/server/gamecore.cs find "function loadstage2":
5. And when you load a level, in the console, after "*** Stage 2 load", it should echo:
"LOAD LEVEL SPECIFIC DATA - ./levels/mylevel.mis.cs".
If there is no specific level file you will get the cyan message:
"Missing file: scripts/server/levels/mylevel.mis.cs!"
And that's it.
As ever, if anyone has a better way of doing things, feel free to post.
In the theme of K.I.S.S. we'll be thinking of level specific files as trigger datablocks and the sort of things which effect/affect gameplay in such a manner. You can extend this to how you see fit.
Just a quick and dirty way to get level specific datablocks to load into a game without having all of the scripts and datablocks for different levels loading up as well.
Working off the Full template - no code changes.
1. Take all of your level specific data (triggers, etc) and place it in a *.cs file with the same name as your mission file. (eg: mylevel.mis = mylevel.cs)
2. Create a folder called "levels" in game/scripts/server. (eg: game/scripts/server/levels)
Place mylevel.cs into this new folder and rename mylevel.cs to mylevel.mis (but keep it a cs file, eg: in the directory it's Name= "mylevel.mis" and the Type= "CS File". Don't add an extra ".cs" or you'll have "mylevel.mis.cs" as the name, and that's really "mylevel.mis.cs.cs" and that ain't right (using a Win7 directory structure)).
3. Create a file called "loadlevels.cs" and slap this scriptette in it.
function loadleveldata()
{
echo("LOAD LEVEL SPECIFIC DATA - " @ "./" @ $Server::MissionFile @ ".cs");
exec("./" @ $Server::MissionFile @ ".cs");
}Remember to exec it from scriptExec.cs.4. Fire this function off when the level loads up - NOT afterwards or you're trigger data won't get loaded into memory when the trigger objects look for it. In game/scripts/server/gamecore.cs find "function loadstage2":
if (!isFile(%file))
{
error("Could not find mission "@ %file);
return;
}
LoadLevelData();//yorks - load the level specific data
// Calculate the mission CRC. The CRC is used by the clients
// to caching mission lighting.
$missionCRC = getFileCRC(%file);5. And when you load a level, in the console, after "*** Stage 2 load", it should echo:
"LOAD LEVEL SPECIFIC DATA - ./levels/mylevel.mis.cs".
If there is no specific level file you will get the cyan message:
"Missing file: scripts/server/levels/mylevel.mis.cs!"
And that's it.
As ever, if anyone has a better way of doing things, feel free to post.
About the author
One Bloke ... In His Bedroom ... Making Indie Games ...
#3
Good post!
One change I'd like to see in T3D is for all SimObjects to consistently invoke script onAdd() and onRemove() functions if defined. Currently, only a select set of classes does this and all of them manually.
If this were changed to this simpler and more consistent form, one could very simply do things like this. Example:
02/18/2010 (12:41 pm)
Good post!
One change I'd like to see in T3D is for all SimObjects to consistently invoke script onAdd() and onRemove() functions if defined. Currently, only a select set of classes does this and all of them manually.
If this were changed to this simpler and more consistent form, one could very simply do things like this. Example:
// In mission file, name the LevelInfo object or give it a class.
new LevelInfo( MyExampleLevel ) {};
// Then in the scripts do
function MyExampleLevel::onAdd()
{
// Put level-specific startup logic here.
}
#4
02/18/2010 (4:12 pm)
Steve, good post. I have one question, do datablocks work in packages the same as functions? Its been a while since I played with them but if you could define the datablocks in a package and activate and deactivate the package then wouldn't that be the way to go. I don't think your method above will clear the datablocks from memory when the mission is over which would mean that they would just build up. Am I wrong on that?
#5
The difference being that the above method probably builds them up mission-by-mission, whilst the original method built them all up immediately as you had to exec them all together at the start.
Anyone know if I can just stick all this stuff into missionCleanup on load? And thus delete it on missionEnd? (nah, it won't be that simple will it.....)
Also anyone know how to find out what actually is in memory?
-----
edit:
I just renamed triggers in 2 missions the same name, and don't get a dual name warning in the console, which proves .... er nothing, but I just wondered if it would.
02/18/2010 (6:23 pm)
I don't think you can just define datablocks on the fly ... I think that's why Singletons are getting used more in the scripts, but all this stuff and Torque's memory management (which has always baffled me!) isn't my area of knowitallism (all I do is script gameplay events - it's just that I've had to learn everything else along the way!), so I'd expect that the datablocks don't get cleared, and thus do build up. The difference being that the above method probably builds them up mission-by-mission, whilst the original method built them all up immediately as you had to exec them all together at the start.
Anyone know if I can just stick all this stuff into missionCleanup on load? And thus delete it on missionEnd? (nah, it won't be that simple will it.....)
Also anyone know how to find out what actually is in memory?
-----
edit:
I just renamed triggers in 2 missions the same name, and don't get a dual name warning in the console, which proves .... er nothing, but I just wondered if it would.
#6
i.e :
then just exec this script in onClientEnterGame:
(NOTE: Although this method will only work if the .mis file has already been added, so not suitable for loading datablocks used by objects in that file)
I haven't got to the point where I need to load/unload datablocks yet. I try and define mine as generically as possible by adding fields that have different values for each trigger to the trigger object, rather than defining them in the datablock itself. So, for example, all my doors and gates use the same base datablock across all missions.
Also, Steve
It is my understanding that objects get deleted at mission end, but datablocks remain persistent. Having said that, I can see no reason why exec-ing a script to destroy unwanted datablocks from endMission wouldn't work. (I'll give it a go and let you know)
Update: Yep, works fine
02/20/2010 (3:14 pm)
I use a script to run startup functions for each level which I achieve by adding a field 'misLoadScript' to the Levelinfo object of the mission :i.e :
new LevelInfo(theLevelInfo) {
....
misLoadScript = "levels/E0M0.cs";
....
};then just exec this script in onClientEnterGame:
exec(theLevelInfo.misLoadScript);
(NOTE: Although this method will only work if the .mis file has already been added, so not suitable for loading datablocks used by objects in that file)
I haven't got to the point where I need to load/unload datablocks yet. I try and define mine as generically as possible by adding fields that have different values for each trigger to the trigger object, rather than defining them in the datablock itself. So, for example, all my doors and gates use the same base datablock across all missions.
Also, Steve
It is my understanding that objects get deleted at mission end, but datablocks remain persistent. Having said that, I can see no reason why exec-ing a script to destroy unwanted datablocks from endMission wouldn't work. (I'll give it a go and let you know)
Update: Yep, works fine
#7
Just had a brainwave - thought why can't I just exec a script with the datablock definitions, etc from the .mis file (obviously, before before any of the mission objects are added).
And it works!!
02/20/2010 (4:42 pm)
Hmmm...Just had a brainwave - thought why can't I just exec a script with the datablock definitions, etc from the .mis file (obviously, before before any of the mission objects are added).
And it works!!
#8
What exactly is your script for deleting the datablocks on endMission? Are you using a scriptObject?
and why does it take me 3 days to recover from a party? uhhh ... brain ...
02/22/2010 (11:32 pm)
@DavidWhat exactly is your script for deleting the datablocks on endMission? Are you using a scriptObject?
and why does it take me 3 days to recover from a party? uhhh ... brain ...
#9
02/23/2010 (7:25 am)
I'm a little late seeing this, looks interesting, i had this problem as well, at first i was making level directory paired with a init.cs but then it only has wakes for scripts but no sleeps or destroys. I wonder if you could add objects like mission clean up for DTS objects.
#10
First I store the name of the level's datablock unload script file in theLevelInfo of the mission : e.g.
Then in endMission() I do this :
It has to run after the missionCleanup objects are deleted.
The script itself just deletes the datablocks by name e.g.
Seems to work well enough.
02/25/2010 (4:42 am)
@SteveFirst I store the name of the level's datablock unload script file in theLevelInfo of the mission : e.g.
endMisCsFile = "levels/E0M0/E0M0CleanUp.cs";
Then in endMission() I do this :
function endMission()
{
//echo("c4 -> endMission() override success");
if (!isObject(MissionGroup))
return;
echo("*** ENDING MISSION");
%file2 = theLevelInfo.endMisCsFile; // <--------
// Inform the game code we're done.
Game.onMissionEnded();
// Inform the clients
for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)
{
// clear ghosts and paths from all clients
%cl = ClientGroup.getObject(%clientIndex);
%cl.endMission();
%cl.resetGhosting();
%cl.clearPaths();
}
// Delete everything
MissionGroup.delete();
MissionCleanup.delete();
$ServerGroup.delete();
// Clean up mission specific datablocks
exec(%file2); // <----------
$ServerGroup = new SimGroup(ServerGroup);
clearServerpaths();
if (isObject(Game))It has to run after the missionCleanup objects are deleted.
The script itself just deletes the datablocks by name e.g.
GateADatablock.delete();
Seems to work well enough.
#11
edit:
Actually does anyone know of a way to get a dump of all the datablocks currently in memory at any one time?
02/25/2010 (7:27 am)
Didn't actually know you could call a delete on datablocks themselves --- though I had one look at the info on memory manager years ago when I first started with TGE, didn't understand a word of it and let sleeping dogs lie since.edit:
Actually does anyone know of a way to get a dump of all the datablocks currently in memory at any one time?
#12
Can you tell me the sense of the opening words:
eheheh tnx ;-)
02/28/2010 (3:46 am)
Steve a question for you that probably will probe my low level in programming :-PCan you tell me the sense of the opening words:
Quote: "If you're making a singleplayer type level..."
eheheh tnx ;-)
#13
02/28/2010 (7:03 am)
If you're making a singleplayer level ... where the game is driven by scripted events ... rather than multiplayer were the game is driven by the conflict with the other players ... or a perpetual virtual world where you'd need to have all datablocks loaded at start.
Associate Steve Acaster
[YorkshireRifles.com]
function LevelDataStart() { //Science! Kicking arse since the Stone Age echo("LevelData Functions Startup"); }Fire this off from the end of game/scripts/server/gameCore.cs/onClientEnterGame() - any sooner and it'll missfire and stuff (like Ai won't spawn).
function GameCore::onClientEnterGame(%game, %client) { //echo (%game @"c4 -> "@ %game.class @" -> GameCore::onClientEntergame"); //..... // Prepare the player object. %game.preparePlayer(%client); LevelDataStart();//yorks startup level functions }edit 18feb10: had different functions in text and code. doh!