An Introduction to the Lithtech Engine
by Ashley Matheson

Welcome to the first in what I hope will be a series of mod tutorials/discussions for Shogo (and hopefully this will be useful to you Blood 2 types as well).

Let me give you a brief background about myself first (after all, you should know a little about the person writing all this stuff). My name is Ash Matheson and I've been working with the Lithtech Engine since the Engine SDK was publicly released in January (of 1998). I've come to appreciate all the hard work that Mike, Brad, Jeremy and the rest of the guys have put into the engine. It is a work of beauty. I've also been involved in writing some technical documentation for the engine, so I've had a fair bit of exposure to the engine in all it's glory. It truly is a sight to behold.

Anyway, enough rambling. Most of you folks out there are chomping at the bit, getting ready to start developing your own mods. However, since the source code hasn't been released yet, it's pretty hard to start writing about mods. But I can start talking about what we are going to need to know in order to prepare ourselves for developing mods. And that's the topic of this discussion.

I'm a programmer. I'm not a level editor, nor a graphic artist (I do however do 3D, but that's a whole other story). So the focus of my tutorials will always be programming (with maybe a slide or two into model editing and level editing here and there).

So, what should you, as the budding developer, know about developing mods for Lithtech? Well, first off, you are going to need some tools in front of you. So far, I've only used Visual C++ 5.0 to compile programs with the SDK. However, any other C++ compiler that will generate Win 32 DLL's should work. Remember, that's a big SHOULD. I'll be getting said compilers (all the free ones, that is ...) and trying them when the source code is made available.

Next, I would advise digging out any books you have on C++ and giving them a read over. Sure you can create your own add-ons in C, but understand that there is a lot of FREE code in the Shogo source. And the Monolith boys are using C++ pretty extensively. So get up to speed on C++. It's more than worth it.

OK. As stated above, the goal of this document is not to create a mod, but to help you understand what you are going to have to know about Lithtech in order to create a mod. It'll be pretty short this time around, because I'm just going to talk mostly theory. In the next couple of supplements (up until the release of the source code) that's what I'm going to concentrate on. There will be snippets of code, but since it's pretty certain that you won't have access to the development tools, I'll try and keep the discussions simple.

From a developers standpoint, you are only going to be developing two dll's. The CSHELL.DLL and OBJECT.LTO files. Let's talk about what each of them is individually.

The first thing to understand is that the Lithtech engine is Client/Server. Right from the ground up. There is a Client portion of the program that handles menus, displaying of the world, User Interface, Heads up display, initiating Network communications, connecting to hosts ... anything that can be considered local to the player. Think about it this way, the client will handle things that no one else needs to be aware of. On the Server side, we handle the physics of world objects, placement of object in the world, story line advancement ... anything that has to do with the world and the players in the world (that everyone can see).

That's the brief overview of Client/Server. From the filenames, you may be able to get an idea as to what DLL goes where. However, to make everything crystal clear ...

CSHELL.DLL is the Client SHELL DLL. In this DLL, you define the behavior of how the client behaves. You determine what the Display looks like, what menus get displayed, what (and how) the first person weapons look like, as well as network connection and score display. Additionally, the Client Shell (What I'll be referring to from now on instead of CSHELL.DLL. It's easier to understand that way) does our world rendering for us. The server doesn't do any rendering at all (most of you already knew that, but it's best to say it for those of you that don't know).

OBJECT.LTO is a part of the Server. It, in part, defines the behavior of how the server acts, but additionally, defines all the object that you will have available in your world. All your Bad-guys, Non-Player Characters, weapons, ammo, Health, Power-ups ... you get the idea .. are all defined in this DLL. Additionally, OBJECT.LTO is used by the world editor for placement of objects in the game. DEdit (the world editor) will read your OBJECT.LTO DLL and find all the objects that have published properties and allow you to place then in your level as well as set their attributes. I'm going to keep this discussion pretty high level, but in the next tutorial, I certainly will be looking at how you go about creating a very simple world object you can use in your level.

Additionally, if you've looked through the files in your Shogo directory, you'll have undoubtedly found the following:

Shogo.exe
ShogoSrv.exe
Client.exe
I think everyone by know (if you've at least played the game) knows what SHOGO.EXE is. It's the launcher program. It brings up the following window:

That and some Network connection stuff is all this front end does. It has very little to do with actual game play. The game is actually run by a call to CLIENT.EXE. That is, for all intents and purposes, the Lithtech Engine (as well as all the DLL's that go with it).

OK. Before you go looking for those two files to start mucking with them, you're out of luck. You won't find them. Both the OBJECT.LTO and CSHELL.DLL files are compressed and stored inSHOGO.REZ (as well as in SOGOP.REZ)

So that leads us into another set of questions. When are we going to be able to have access to the tools that will allow us to create worlds, models and customize the way the game is played? According to Matt Saettler, Director of 3rd Party Development :

".., the mod tools will be out shortly after the Patch is released.

The patch is due Nov. 15."

Now let's start talking about putting in some code that will actually do something. Let's start off on the Server side of things. If you remember, I said that OBJECT.LTO defines how the server acts as well as defining world object, their properties and behaviors. There's a couple of other very important things that it does, and we will talk about them shortly.

Most everything in Lithtech is C++ oriented. Personally, I think that's a good thing. That being the case, inheritance plays a huge part in Lithtech. You define your sever implementation as a derived class of a base Lithtech Server class. OK. That may sound like a lot, but it's not. If you're familiar with OOP, this next section may bore you a bit, but I'm hoping it's a good read anyway.

Lithtech defines a base class CServerShellDE. This base class manages all the low level server functionality and via virtual functions allows you to extend what the server can do. Here's a bit of what CServerShellDE looks like:

    // Notification when new clients come in.
    // You must create an object to represent the client.
    // It uses the object's position to determine what the client can see.
    virtual void    OnAddClient(HCLIENT hClient) {}
    virtual void    OnRemoveClient(HCLIENT hClient) {}

    virtual LPBASECLASS OnClientEnterWorld(HCLIENT hClient) {return NULL;}
    virtual void        OnClientExitWorld(HCLIENT hClient) {}

    // Called before and after you switch worlds.
    virtual void    PreStartWorld(DBOOL bSwitchingWorlds) {}
    virtual void    PostStartWorld() {}

    // Incoming message notification.
    virtual void OnMessage       (HCLIENT hSender,
                                  DBYTE messageID,
                                  HMESSAGEREAD hMessage) {}

    virtual void OnObjectMessage (LPBASECLASS pSender,
                                  DDWORD messageID,
                                  HMESSAGEREAD hMessage) {}

    // Command notification.
    virtual void    OnCommandOn(HCLIENT hClient, int command) {}
    virtual void    OnCommandOff(HCLIENT hClient, int command) {}

    // Update loop callback.. do whatever you like in here.
    // Time since the last Update() call is passed in.
    virtual void    Update(DFLOAT timeElapsed) {}

That's a lot of virtual functions. And as you can see, most of them don't do a lot. So what are we looking at here? Let's quickly review each virtual function and see what it does.

OnAddClient
OnRemoveClient

These two virtual methods are run whenever the Lithtech server finds that a client has attached themselves to the server (OnAddClient) or the client has disconnected from the server (OnRemoveClient). Understand that these two functions are completely different than the next two virtual methods we are going to see.

OnClientEnterWorld
OnClientExitWorld

These two virtual methods are run whenever the Lithtech server detects that a client has entered the world or has left the world. There's a big difference between these methods and the methods defined above. There are two entry points into any world that a Lithtech Engine is serving. First, when the person tries to connect to the server and make sure they can get a reliable connection (the OnAddClient method). Then, when the client has loaded the world and is ready to play, the OnClientEnterWorld is called. The reverse stands true. When the Client decides to leave a game, and they exit the level, the OnClientExitWorld method is called, then the OnRemoveClient method is called.

Why do this? Well, imagine that you are trying to connect to a server that is full. The OnAddClient on the server can have an internal table with a list of the names, IP's, scores, ... whatever of currently on-line players. If the server is full, it can then tell the client that it cannot join the game. Also, it can send back a list of all the users that are on this server. I think you get the idea.

PreStartWorld
PostStartWorld

Another twin set of functions. The first, PreStartWorld is called before the Server starts to load a new (or first) world into the engine. This is a good point to send out a message to all the clients currently attached with a message indicating a level change, estimated time to load, ... you get the idea.

The second of the twin functions, PostStartWorld, is called after the Server has finished loading the world. Again, this could be a good opportunity to send a "Level beginning now' message to all the clients. Or to inform all the clients that the server is ready to handle the game. Also, if your game has any time limits/frag limits, now is a good time to set them.

OnMessage
OnObjectMessage

One big thing in Lithtech is Messages. If you want objects to communicate to each other, you need to send a message from one to another. The same thing goes for the server talking to the Clients that are attached to it (and vice versa). If, for example, you have a client that wants to send a message to everyone (the famous 'say' commmand), you first need to send a message to the server from the client saying "Hey, I have a message!", as well as the string that is the message. The server then broadcasts that string to all the attached clients with another message.

OK, now that you get an idea about messages, what does OnMessage and OnObjectMessage do? OnMessage handles generic messages from Clients. OnObjectMessage handles messages to the server from Objects in the world (Doors, crates, that exploding wall, ... you get the picture).

Now there are a set of parameters that go along with this. OnMessage's parameter list looks like this:

HCLIENT hSender, DBYTE messageID, HMESSAGEREAD hMessage

hSender is the Client (which is of type HCLIENT, a Handle to a Lithtech Client), messageID is a unique Identifier for the message received, and hMessage, a Handle to a Message. With the Handle to the Message, we can read in any further information. For example, in the 'say' example, we would have a message ID of, say 12, indicating a 'say' message has been received from the client hSender. We then use hMessage to get the string that was sent. We will see how this can be done in a future installment.

OnCommandOn
OnCommandOff

If you are used to windows programming, you may have seen something very similar to this. In Windows programming, when you press a key , you get a WM_KEYDOWN message. When you release the key that was depressed, you get a WM_KEYUP message. Well, these two virtual methods work in the same vein.

In Lithtech, you bind Actions to Keys, Mouse buttons, Joystick Buttons, ... whatever. That's what the autoexec.cfg file is all about. What we have here is a process when an action key is either depressed or released, you will get a notification of it. OnCommandOn works like the WM_KEYDOWN, and OnCommandOff works like WM_KEYUP. Again, there are arguments that go along with these functions:

HCLIENT hClient, int command

where once again, we see a hClient variable of type HCLIENT (the client that pressed the key) and an int value of the command that was activated.

Update

Finally (for now) we see the Update method. This is where the game updates itself every 'frame'. This is where you can process user movement (from the server), update objects in the world (respawn weapons), or whatever else you want to do in the world. Also, it's a good place to update any level timers you may have.

So, that, in a nutshell, is what a Lithtech CServerShellDE base class looks like. From this, we will create our server. But not quite yet. There's another thing that we need to do that is directly related to any server. We need some way to represent our player (especially in a multi-player game) in the world. Let's look at that, and another base class, next.

We are going to need an object in the world that will represent 'us'. This could be a model, a camera, a light, whatever we want it to be. But somehow we need a way to depict it in our world. So what we are going to do is create a Player class that represents the player in the world. But to do this, we also need to look at the next base class in our list, the aptly name base class BaseClass.

BaseClass only has two virtual functions that you can override. They are:

EngineMessageFn(DDWORD messageID, void *pData, DFLOAT lData);
ObjectMessageFn(HOBJECT hSender, DDWORD messageID, HMESSAGEREAD hRead);

Once again, this should look very familiar. We've got similar methods defined for the server that handle when the BaseClass object receives a message from the server, and when the BaseClass object receives a message from another object (of class HOBJECT).

So what have we got so far? Two base classes. One that define our server and another that defines an object in the world. Believe it or not, that's a good part of what we are going to need to create our games.

Let's now look at what we need to do to get a server alive. Remember, this is not the code for Shogo. This is simply how the Lithtech engine works. From this we will eventually see how the Shogo server works (when they release the source code to the public).

Let's see the definition of the Server Shell object that we are going to create. For demonstration purposes, we're going to call this server CSampleServerShell :

    #ifndef __SAMPLE_SERVERSHELL_H__
    #define __SAMPLE_SERVERSHELL_H__


    class CSampleServerShell : public CServerShellDE
    {
        public:

            virtual LPBASECLASS OnClientEnterWorld(HCLIENT hClient);
            virtual void        OnClientExitWorld(HCLIENT hClient);

            virtual void        OnMessage(HCLIENT hSender,
                                          DBYTE messageID,
                                          HMESSAGEREAD hRead);

    };

    #endif  // __SAMPLE_SERVERSHELL_H__

From this, you can see that we have created a new class (this code snippet is from a file called sample_servershell.h) derived from the base class CServerShellDE. In this derived class, we override the methods OnClientEnterWorld, OnClientExitWorld and OnMessage.

And that's it. Well, that's it for the definition of the new server shell. Now we need to look at Implementing these methods. We switch over the file sample_servershell.cpp and take a look-see:

    // --------------------------------------------------------------- //
    // CSampleServerShell member functions.
    // --------------------------------------------------------------- //

    LPBASECLASS CSampleServerShell::OnClientEnterWorld(HCLIENT hClient)
    {
        ObjectCreateStruct createStruct;
        LPBASECLASS pObject;

        // Create an object to represent the client.
        INIT_OBJECTCREATESTRUCT(createStruct);

        pObject = GetServerDE()->CreateObject(
                                GetServerDE()->GetClass("Player"),
                                &createStruct;);

        ((Player*)pObject)->m_hClient = hClient;

        // Store the object pointer with the client for future reference.
        GetServerDE()->SetClientUserData(hClient, pObject);

        return pObject;
    }

OK. Here in the OnClientEnterWorld we see that a new client has attached to the server. What we are going to do here is create a new 'Player' object when the client attaches to the server. To do that, we need some new structures. First off, we have a structure called ObjectCreateStruct. Gee, with a name like that, I wonder if it is a Struct that tells us how to Create and Object? You betcha! Here's what the ObjectCreateStruct actually looks like:

unsigned short  m_ObjectType;
DDWORD          m_Flags;
DVector         m_Pos;
DVector         m_Scale;
DRotation       m_Rotation;
D_WORD          m_ContainerCode;            // Container code if it's a container.
                                            // It's in here because you can only
                                            // set it at creation time.

DDWORD          m_UserData;                 // User data


char    m_Filename[MAX_CS_FILENAME_LEN+1];  // This is the model, sound, or
                                            // sprite filename.It also can be
                                            // the WorldModel name. This can
                                            // be zero-length when it's not
                                            // needed.

char    m_SkinName[MAX_CS_FILENAME_LEN+1];  // This can be zero-length.. if
                                            // you set it for an OT_MODEL,
                                            // it's the skin filename.

// Server only info.
char    m_Name[MAX_CS_FILENAME_LEN+1];      // This object's name.
float   m_NextUpdate;                       // This will be the object's
                                            // starting NextUpdate.
float   m_fDeactivationTime;                // Amount of time before object
                                            // deactivates self

There's a lot of things that we can put into this structure. I won't go into detail here, but I will in a future installment. However, one thing that you will see is a macro called OBJECTCREATESTRUCT. This was called on the variable createStruc (remember, it's of type ObjectCreateStruct. Also, we have a variable pObject that is of type LPBASECLASS. LPBASECLASS is a generic object that can hold any world object (Polymorphism, anyone ...?).

Next, we have an example of one of the most important functions available in Lithtech. GetServerDE() is THE server command. It returns a pointer to the server, and allows us to access all the neato-wiz-bang functionality of the server.

Waitaminute, I hear you asking. We've already got access to the server through the virtual functions, right?

Well, kinda-sorta. The server we implemented is the server SHELL. It's what the server expects us to handle. There are functions the server has available to you, the application programmer, that make your life less of a living hell. And you access these functions through a call to GetServerDE(). All it does is return a pointer to the Server object, thus allowing us access to those methods that Lithtech has made available. And one of those function is the CreateObject method. CreateObject allows us to create a new object on the server, whenever we make the call to this object. Here's the line once again (just to jog your memory);

pObject = GetServerDE()-<gtCreateObject;(GetServerDE()-<gtGetClass;("Player"), &createStruct;);

So, the server function CreateObject (accessed through GetServerDE()-<gt;) creates a new object of type "Player", through yet another server function call of GetClass. Also, it passes in the createStruct that we defined earlier. This is used to create a new player object. We will look at the player object very soon.

Now what do we do when we decide to leave the world?

void CSampleServerShell::OnClientExitWorld(HCLIENT hClient)
{
    LPBASECLASS pObject;

    // Get rid of their object.
    pObject = (LPBASECLASS)GetServerDE()->GetClientUserData(hClient);
    if(pObject)
    {
        GetServerDE()->RemoveObject(pObject->m_hObject);
        GetServerDE()->SetClientUserData(hClient, DNULL);
    }
}

This is pretty simple. First off, we make sure that the client that just left was valid. If it was, we remove the object with a call to RemoveObject, and then set that client to Nothing. However, instead of using a NULL to do that, we use a DNULL. Why DNULL? Remember that at some point in the near future, games made with the Lithtech engine will be portable to other systems. And it's more than possible that certain constants will be defined differently. So, for consistency sake ...

Finally, we need to handle any messages coming from the server. We want the server to handle any rotation messages for the object. There's a reason we are going to do it this way.

We want the client to handle rotation of the user's viewpoint immediately. So we process all turning commands on the client as fast as is possible. We then send to the server a message ID of 1 to indicate that the user has turned in a new direction. We pass all that information along and the server then set's the player's object on the server to match that rotation. This way, you can immediately rotation on the client side, and if there is any lag present in the system, that should be OK. Movement will be handled on the server, and we will see that in the Player class.

void CSampleServerShell::OnMessage(HCLIENT hSender, DBYTE messageID,
                                   HMESSAGEREAD hRead)
{
    LPBASECLASS pObject;
    DRotation rotation;

    // Is it a rotation message?
    if(messageID == 1)
    {
        // Ok, get the client's object.
        pObject = (LPBASECLASS)GetServerDE()->GetClientUserData(hSender);
        if(pObject)
        {
            // Set the object's rotation.
            GetServerDE()->ReadFromMessageRotation(hRead, &rotation;);
            GetServerDE()->SetObjectRotation(pObject->m_hObject, &rotation;);
        }
    }
}

OK. A quick discussion about what just went on. We talked about WHY we did this, now we need to talk about HOW we did this. The Server will get from the client a message ID of 1, which indicates that a rotation has occurred. Additionally with that message there will be rotation information passed along. To get that information, we need yet another server function called ReadFromMessageRotation. This reads in the rotation values sent to the server (represented by hRead) and is stored into the variable rotation (which is why we have the '&&' in front of the rotation variable.

Next, we want to set the Player object's rotation, on the server, to match what was sent from the client. This is done with the SetObjectRotation method (which again is another handy-dandy server function).

And that's a good part of the server. Now we have one last thing to look at, and that is the Player object. Or at least what the server is going to represent as our player. Let's look at the Player header file (Player.h)

  class Player : public BaseClass
  {
    public:

        Player();
        DDWORD EngineMessageFn(DDWORD messageID, void *pData, DFLOAT lData);

        // The client associated with this player.
        HCLIENT m_hClient;

  };

As you can see, the player is derived from the BaseClass class. Since BaseClass has an overrideable EngineMessageFn method, we override it (you will see why very shortly).

And, other than the typical constructor, we have a variable that represents a pointer to the client. This gets set when the client enters the world, and you never know, we may have need of it, to directly access this player's client. It's a good idea to have, in any case.

Let's see the implementation of this Player class.

  Player::Player() : BaseClass(OT_CAMERA)
  {
  }

OK. This is a constructor for the Player Class. The only thing to notice here is that we create the Player object and call the constructor for the BaseClass ancestor. Note that we pass in the Constant OT_CAMERA. This tells BaseClass that we are creating a camera object that will represent our player. There are a LOT of different types of objects we can create. But for now, let's leave it as being a camera.

Next, we have the EngineMessageFn to deal with. This method gets passed messages from the engine (No surprise there, that's why it's named that way). But what kind of messages can we expect the engine to send this class. For now, we're only going to concern ourselves with three of them: MID_PRECREATE, MID_INITIALUPDATE and MID_UPDATE. Let's discuss them briefly.

MID_PRECREATE

This message is sent to the object just before the engine creates the object. So here you can set any properties that are relevant to the object.

MID_INITIALUPDATE

This message is sent to the object after the object has been created and just before it is initially updated. To update the object, you must call the Server function SetNextUpdate, which tells the engine when it should update the object next.

MID_UPDATE

This message is sent to the object whenever it needs to be updated. This is where we are going to process any user movement commands.

Now that we've talked theory, it's time to look at it in practice.

  DDWORD Player::EngineMessageFn(DDWORD messageID, void *pData, DFLOAT lData)
  {
    float moveSpeed = 120.0f;
    float terminalVelocity = 400.0f;
    DVector velocity, forward, right, up, temp;
    DRotation rotation;
    ObjectCreateStruct *pObjectCreateStruct;
    DVector dims;

    // See serverobj_de.h (near line 100) for descriptions of the messages.
    switch(messageID)
    {
        case MID_PRECREATE:
        {
            pObjectCreateStruct = (ObjectCreateStruct*)pData;

            pObjectCreateStruct->m_Flags |= FLAG_SOLID;

            break;
        }

        case MID_INITIALUPDATE:
        {
            // Tell LithTech to update us as soon as possible.
            GetServerDE()->SetNextUpdate(m_hObject, 0.001f);

            // Give us some dimensions.
            VEC_SET(dims, 14, 50, 25);
            GetServerDE()->SetObjectDims(m_hObject, &dims;);

            break;
        }

        case MID_UPDATE:
        {
            // Check what commands are on and apply movement.
            GetServerDE()->GetVelocity(m_hObject, &velocity;);
            GetServerDE()->GetObjectRotation(m_hObject, &rotation;);
            GetServerDE()->GetRotationVectors(&rotation;, &up;, &right;, &forward;);

            if(GetServerDE()->IsCommandOn(m_hClient, COMMAND_FORWARD))
            {
                VEC_MULSCALAR(temp, forward, moveSpeed);
                VEC_ADD(velocity, velocity, temp);
            }

            if(GetServerDE()->IsCommandOn(m_hClient, COMMAND_BACKWARD))
            {
                VEC_MULSCALAR(temp, forward, -moveSpeed);
                VEC_ADD(velocity, velocity, temp);
            }

            // Slow down for when they aren't pressing keys.
            VEC_MULSCALAR(velocity, velocity, 0.8f);
            if(VEC_MAG(velocity) < 0.05f)

                VEC_SET(velocity, 0, 0, 0);

            // Update the object's velocity.
            GetServerDE()->SetVelocity(m_hObject, &velocity;);

            // Register for another update..
            GetServerDE()->SetNextUpdate(m_hObject, 0.001f);
            break;
        }
    }

    // Pass the message on down.
    return BaseClass::EngineMessageFn(messageID, pData, lData);
  }

Oops, one or two more things to talk about here. At the very top of this method, you see variables that are being created from the types DVector and DRotation. Once again, these are new structures defined in Lithtech. A DVector is a vector, short and sweet. I'm not going to talk about what a vector is. If you don't know what a vector is, and you can't find any information on the subject, e-mail me and I'll help you out. DRotation is a little more involved. Instead of storing the rotation as a set of degrees, it's stored as a Euler angle. What's a Euler angle? Well, it's an awful lot like a vector, but instead of storing a direction, each vector component stores a rotation angle around each of the three major axis (X, Y and Z).

That's a quick little math lesson. If there's a call for it, in a later installment, I'll discuss the 3D math being used in Lithtech.

Let's look at each message, starting with MID_PRECREATE. This message handler does nothing more than set the object's flag to be solid. If an object's solid, you can't pass through walls, floors, other objects. In essence, it makes you an object that can collide with other things.

In MID_INITIALUPDATE, we set the next update of this object to be very soon (0.001 of a second, to be precise). This way we guarantee that we get a MID_UPDATE message. But I'm getting ahead of myself. The very last thing that we do is set the dimensions for this object. As it stands, our player, or the player's camera, has a small bounding box used for collisions. We need to make it a little bigger (to help out with clipping problems). We set a DVector variable to define the width, length and height of a box to be used as a 'collision' box.

Finally, we have the MID_UPDATE message. This is where we can move out player around. First, we get the object's current Velocity and rotation with calls to GetVelocity and GetObjectRotation (yet more handy Server functions ...).

Now that we have these, we can use them to move our player (if we need to). We check to see if the user has the action keys bound to the Actions "COMMAND_FORWARD" or "COMMAND_BACKWARD" depressed with the call to IsCommandOn. If either of these commands are active, we move the character along or back along the forward vector (depending upon which key is being pressed).

Next, we slow down the player's movement by 20% (to give the illusion of friction through the air). We then check to see if the player has slowed down enough to be considered a 'stop' ( to avoid really small values of forward or backward velocities causing a 'drift' effect). Otherwise we continue on and add the new velocity to the current velocity. Finally, we set the next update to a very small value (read that as being ASAP).

OK. I think that's enough for one installment. It's a brief introduction into how the Lithtech Engine works and what it takes to create a server. In the next installment, I'll talk more in-depth about the OBJECT.LTO file as well as the CSHELL.DLL (which we only briefly discussed). Remember, these are the keys to developing MODS for Shogo.

Until then ...

Ash (Madman99) Matheson
madman99@nbnet.nb.ca

IGN.com | GameSpy | Comrade | Arena | FilePlanet | ModCenter | GameSpy Technology
TeamXbox | Planets | Vaults | VE3D | CheatsCodesGuides | GameStats | 3D Gamers
AskMen.com | Rotten Tomatoes | GamerMetrics | Direct2Drive
By continuing past this page, and by your continued use of this site, you agree to be bound by and abide by the User Agreement.
Copyright 1996-2007, IGN Entertainment, Inc.   About Us | Support | Advertise | Privacy Policy | User Agreement Subscribe to RSS Feeds RSS Feeds
IGN's enterprise databases running Oracle, SQL and MySQL are professionally monitored and managed by Pythian Remote DBA.




- Current News
- News Archive
- POTD Archive
- Forums


- Main Index
- Send Question
- Archives


- Main Index
- 3D Support
- Adversaries
- Arsenal
- Characters
- Cheat Codes
- Developers
- Gameplay
- Influences
- Introduction
- MCA
- Mission Pack
- Release Info
- Storyline


- Add-ons & Maps
- Audio
- Images
- LithTech Tools
- Patches
- Shogo Demo
- Source Code
- Transcripts
- Videos


- Main Index
- Anime 101
- Avault Chat
- Coding
- Editing Tutorials
- Editorials
- Interviews
- LAN Parties
- LithTech Coding
- Legacy of the Fallen
  Materials
- MAD Listing
- Mailbag
- Mod Coding
- Polls
- Radio Show
- Rants
- Server Listing
- Shogo Fan Art
-
Shogo Links
- Shogo Review
- Shogo Shoot Out
- Testimonies
- Walkthroughs
- Web Chat!
- Weekly Download


- BloodBath
- BS Productions
- Defending The
  Monolith
- Imminent Chaos
- Legacy
- Macross TC
- Otaku
- Prizm Dash
- Shogo:Mod
- ShogoPaint
- ShogoSwitch
- ShogoTech
- SnS CTF
- The Next Level
- TOW Arena


- The Staff
- Contacting Us