Multiplayer Programming The Crazy Carnage Game

You made it to the final chapter! Looking back, just think about all you have learned with DarkBASIC, from the basics of writing a program to 3D graphics. It has not always been easy! Now it is time to put all that you have learned to good use. In this chapter, I will show you how to write a complete game. Not just any game, but a 3D game. And that is not all—this game will support two players over a network! The network line can be a local area network (LAN) or it can be over the Internet with a phone line or cable/DSL. Regardless of the way you connect, you will be able to challenge a friend in the other room or across the globe to a game because DarkBASIC provides a consistent multiplayer programming interface. This multiplayer feature is based on DirectPlay—a component of DirectX 8.1.

This chapter will be the final one in your quest to learn about DarkBASIC, and I am confident that it will be a fun chapter for you! Specifically, I'll get into the multiplayer aspects of DarkBASIC and the use of memory blocks, and I'll add a few more 3D commands that build on the information from the last chapter. The final result is a multiplayer game called Crazy CARnage. I'm sure you have played vehicle combat games like Twisted Metal: Black, Star Wars: Demolition, and Vigilante 8. Crazy CARnage was based loosely on this game genre. Although I can't possibly begin to write a game as advanced and creative as Twisted Metal: Black in a single chapter, this game will at least show you how multiplayer games work and give you a good, solid overview of how to write a complete game.

Introduction to Multiplayer Games

Multiplayer games seem to be the wave of the future. Everyone from Microsoft to EA is making them now. Some of the most notable multiplayer games are Quake III, Half-Life, Ultima Online, Real War: Rogue States, and Everquest. Each of these games uses multiplayer capabilities in different ways to achieve the same goal: FUN.

Single Player Versus Multiplayer

There is a question hanging around in the gaming world. Do consumers want multiplayer or single-player games? Multiplayer puts the world of human AI at your fingertips. There is no need to program the computer to simulate human behavior when you can just have humans do it for you. Single-player games, however, do have their place. Sometimes you just feel like playing a game without having to find someone else to play it with you. Single-player games are more story-driven, but that is changing as well.

Multiplayer Support in DarkBASIC

DarkBASIC uses the DirectPlay aspects of DirectX 8.1 to establish multiplayer games. DirectPlay is the network portion of DirectX. It supports all of the functions required to play a network game.

DarkBASIC has many different commands to provide multiplayer support. For example, with just a few commands, you can create a multiplayer connection. DarkBASIC supports two models of multiplayer connectivity—the client/server model and the peer-to-peer model. Each starts with a host (server) and a client; the difference is in how they pass around the packets. The default game settings use the peer-to-peer model.

A packet is a collection of data to be processed that is sent from one computer to another.

The Client/Server Model

The client/server model designates one computer as the master controller and the rest of the computers as slaves. Each slave sends its packets to the master controller, which then dictates where the packets are sent. Imagine that there are three computers in a game—A, B, and C. If computer B is designated the master, computers A and C send their packets to B, which then sends the packets to the appropriate computer (A or C). Figure 18.1 shows the layout of the client/server model.

click to expand
Figure 18.1: A client/server model

Anyone who has been in networking is very familiar with this model. This type of network connectivity is seen in systems such as Novell, UNIX, and Citrix, which generally include a server that hosts all the files and clients that connect to the server to retrieve files.

The Peer-to-Peer Model

The peer-to-peer model differs drastically from the client/server model. Although in DarkBASIC you start with the client/server for connection, that is where the similarities end. When all of the computers are connected to the game, there is no designated master controller. Every computer can send packets to any other computer. Imagine computers A, B, and C in this scenario. Computer A could send all of its packets to both computers B and C. However, Computer A might have information only for Computer B, in which case it would send that data only to Computer B. The same applies to computers B and C. Figure 18.2 illustrates the peer-to-peer model.

click to expand
Figure 18.2: A peer-to-peer model

You will find the peer-to-peer model mainly in Microsoft networking. In this model, one computer can talk to another computer without transferring any information through a central server. This is why, in a Microsoft network, you can connect from any computer to any other computer on the network without logging on to a master server. Higher functionalities of Microsoft networking merge both the client/server model and the peer-to-peer model.

Network connection is just a fancy term for the mode of transportation for network packets.


Packet Transport

Once you are familiar with the network layout, you need to understand how the packets are transported around the network. There are many different types of transportation for packets. DarkBASIC supports four different types of network connections—serial (NULL modem), modem, IPX/SPX Internet Packet Exchange/Sequenced Packet Exchange), and TCP/IP (Transmission Control Protocol/Internet Protocol ). In this book I will only cover one—TCP/IP, which is the most widely used protocol on networks today.

The TCP IP Protocol

TCP/IP can be considered the taxicab of the Internet. Every time you view a Web page, TCP/IP transports the HTML packets to your Web browser. Every time you check your e-mail, TCP/IP transports the e-mail packets to your e-mail client. If you think about it, TCP/IP is really quite amazing.

TCP/IP transports your packets by using the IP address. Every computer that is connected to a network using TCP/IP has an IP address. Currently, this address consists of four numbers ranging from 1 to 255, each separated by a period. A typical IP address is 209.232.223.40. Figure 18.3 shows a typical TCP/IP network.

click to expand
Figure 18.3: A typical TCP/IP network

You're probably thinking, "If TCP/IP connects via numbered addresses, why do I type http://www.microsoft.com to get to Microsoft's Web site?" Well, there is a feature of the Internet known as DNS (Domain Name System) that converts IP addresses into numbers. Your computer keeps a repository that matches IP addresses to domain names so you do not have to memorize all those numbers. If your computer doesn't know an IP address, it checks with another computer for the answer. Domain names are like nicknames. You only have one real name (such as Robert), but you can have many different nicknames (such as Bob, Pooh Bear, and Dogface). You only have one real IP address (such as 192.168.0.50), but you can have many different domain names (such as www.myaddress.com and www.bobshouse.com).

Selecting the Correct Transport

Now that you know about TCP/IP protocol, it's time to learn how to select it. This is a pretty easy task; you just need to know what number it is by using PERFORM CHECKLIST FOR NET CONNECTIONS. This command will fill the checklist with all the transports available. CHECKLIST STRING$ will contain the name of the protocol associated with that number. You will have to search for or specifically select TCP/IP.


Establishing a Network Game

After you select the connection, you must set up the game. A network game requires a minimum of two people. To that end, you need to call two different paths to create a network game—the client and server paths. There is only a difference of a single command between the two paths, but it's important to understand both of them.

Establishing the Server Path

In the client/server model, the server is the computer that is hosting the game. In the peer-to-peer model, the server is the starting point for the game, but everyone acts as a server after the game begins.

Before the game is established, you need to set the connection type that you got from PERFORM CHECKLIST FOR CONNECTIONS. Use the SET NET CONNECTION Connection Number command followed by a blank space in quotes: " ". You need the " " to establish that this computer is a host. The full command might look like this:

SET NET CONNECTION ConnectNumber, " "

The connection number variable, ConnectNumber, is the number you got from the PERFORM CHECKLIST FOR CONNECTIONS command.

After you set the game connection, you need to create a net game. To do so, you must use the CREATE NET GAME command, which looks like this:

CREATE NET GAME GameName, PlayerName, NumberOfPlayers, Flag

This command sets up DarkBASIC to accept other players into the game. GameName is the name of the game being hosted. PlayerName is your name in the game. NumberOfPlayers is the maximum number of players that you want to allow the game to include. You can have a maximum of 256 players including yourself in a game. Flag determines whether the game is connected in a peer-to-peer or client/server model. If Flag is a 1, the game is set to peer-to-peer. If Flag is a 2, the game is set to client/server. After the game is established, the server just waits for players.

Establishing the Client Path

Anyone that is not hosting a game is a client, and must connect to the host to play. Client computers first need to establish the connection by using the SET NET CONNECTION command, like this:

SET NET CONNECTION ConnectionNumber, AddressData

This is the same command used to establish the connection for the server, but you take a different approach in this instance. AddressData can either be blank ("") or it can have an address, such as 192.168.0.1 or http://www.mycomputer.com. AddressData is always a string and therefore its value must always be enclosed in quotes. If you leave AddressData blank, a dialog box will ask for any needed address data when the command is run. Figure 18.4 shows the Locate Session dialog box.

click to expand
Figure 18.4: The TCP/IP Locate Session dialog box

Finding the games on the host is almost as easy as finding the connections. After you have established the connection, use the PERFORM CHECKLIST FOR NET SESSIONS command. This will return a list of all the games running on the host computer. When you know what session to play, use the following command to establish a connection to the game.

JOIN NET GAME SessionNumber, Playername

SessionNumber is the number of the game you found using the PERFORM CHECKLIST FOR NET SESSIONS command. Playername is a string that contains your name.

Making the Connection

The following section of code will connect two computers using DarkBASIC. When you are running the program, you will have to specify whether the computer is the hosting machine or the client machine. If the computer is the client machine, the program will ask for the host's IP address. Figure 18.5 shows the output of a machine that is hosting a session. You will find this program on the CD in the SourcesChapter18NetworkConnect folder.

click to expand
Figure 18.5: The NetworkConnect program demonstrates a host machine connected to a client machine.

REMSTART
---------------------------------
Beginner's Guide To DarkBASIC Game Programming
Copyright (C)2002 Jonathan S. Harbour and Joshua R. Smith
Chapter 8- NetworkConnect Program
---------------------------------
REMEND
SYNC ON
SYNC RATE 30
' Find the TCP/IP connection number
TcpIpNumber = FindTCPIPConnectionNumber()
PRINT "Simple Network Connection program!"
PRINT
SYNC
' Get their Name
INPUT "Please Enter Your Name: ", MyName$
SYNC
IF MyName$ = ""
 PRINT "You need to enter a name!"
 WAIT KEY
 END
ENDIF

' Find out who the host and clients are..
PRINT "(1) I'm the Host"
PRINT "(2) I'm the Client"
SYNC
A$ = ""
Answer = 0
' Get Host or Client
WHILE Answer = 0
 A$ = INKEY$()
 IF A$ = "1" THEN Answer = 1
 IF A$ = "2" THEN Answer = 2
ENDWHILE

' Do this if I'm the host..
IF Answer = 1
 PRINT "Creating net session. Please wait"
 SYNC
 SLEEP 200
 SET NET CONNECTION TcpIpNumber, " "
 CREATE NET GAME "Sample Net session", MyName$, 16, 1
ENDIF

' Do this if I'm the client.
IF Answer = 2
 Input "Please enter the Hosts IP Address: ",AddressData$
 PRINT "Connecting to net session. Please wait"
 SYNC
 SET NET CONNECTION TcpIpNumber, AddressData$
 PERFORM CHECKLIST FOR NET SESSIONS
 NumberOfGames =CHECKLIST QUANTITY()
 IF NumberOfGames = 0
 PRINT "No session found at that address"
 SYNC
 WAIT KEY
 END
 ENDIF
 JOIN NET GAME 1, MyName$
 PRINT "Connected to session!"
 SYNC
ENDIF
NumberOfPlayers = 0
LastNumberOfPlayers = 0
PRINT "Press Escape To Quit"
SYNC
' Wait until the escape key it hit.
WHILE ESCAPEKEY()=0
 ' Look for more Net Players to display
 PERFORM CHECKLIST FOR NET PLAYERS
 NumberOfPlayers = CHECKLIST QUANTITY()
 ' If a player has entered or left.. display that.
 IF LastNumberOfPlayers <> NumberOfPlayers
 IF NumberOfPlayers = 1
 NewString$ = "There is "+STR$(NumberOfPlayers)
 ELSE
 NewString$ = "There are "+STR$(NumberOfPlayers)
 ENDIF
 NewString$ = NEWSTRING$ + " computers connected!"
 PRINT NewString$
 LastNumberOfPlayers = NumberOfPlayers
 ENDIF
 SYNC
ENDWHILE
END

' This function will determine which NET CONNECTION number
' is TCP/IP.
FUNCTION FindTCPIPConnectionNumber()
 Flag = 0
 CLS
 PERFORM CHECKLIST FOR NET CONNECTIONS 
 FOR x = 1 TO CHECKLIST QUANTITY()
 Service$ = CHECKLIST STRING$(x)
 IF LEFT$(Service$,15)="Internet TCP/IP"
 Flag = x
 ENDIF
 NEXT x
ENDFUNCTION Flag


Passing the Data

After you have established the game, DarkBASIC provides you with many ways to pass data back and forth between the computers in the game. Passing data is a lot like passing notes in class. The data is written at your end, passed through the network, and read at the other end. If the other end has something to say to you, it performs the same series of events. The only difference between passing data and passing notes is that the teacher won't take your data and read it in front of the class.

Getting the Number and Names of Players

The very first piece of data you will probably want to obtain is the number of players and their names. You pass this data differently than the other data you send because it already resides on your computer. The PERFORM CHECKLIST FOR NET PLAYERS command will fill the checklist with the names and number of players in the game at the time the command is called. You can use this command to check to see whether someone else is in the game before you start it.

The checklist for PERFORM CHECKLIST FOR NET PLAYERS contains valuable information about the players. CHECKLIST STRING$ (PlayerNumber) contains a string with that player's name. CHECKLIST VALUE A (PlayerNumber) contains a unique ID (or number) that was assigned to the player by the computer when he joined the game. This number will not change for the duration of the game; however, it is not the same on all computers in the game. CHECKLIST VALUE B(PlayerNumber) contains a special universal ID that is assigned to the player when he joins the game. This ID is the same for all of the computers on the network. CHECKLIST VALUE C(PlayerNumber) returns a 1 if you are the player. CHECKLIST VALUE D(PlayerNumber) returns a 1 if the player is the host of the game.

Sending Information

There are quite a few commands for sending different types of information over the network. Each command follows a basic form, as shown in the following line of code.

SEND NET MESSAGE TYPE PlayerNumber, Value

Net message is DarkBASIC's terminology for a network packet. Any information sent over the network is considered a net message. You will see all packets from this point on referred to as net messages.

PlayerNumber is the number of the player to whom you want to send the data. If you want to send the information to everyone except yourself, PlayerNumber would be 0. Table 18.1 lists all the commands and what type of data they send.

Table 18.1: Network Message Send Commands

Command

Data Type Sent

SEND NET MESSAGE INTEGER

Integer

SEND NET MESSAGE FLOAT

Float

SEND NET MESSAGE STRING

String

SEND NET MESSAGE MEMBLOCK

Memblock

SEND NET MESSAGE BITMAP

Bitmap

SEND NET MESSAGE IMAGE

Image

SEND NET MESSAGE SOUND

Sound

SEND NET MESSAGE MESH

3D mesh

Each of the commands sends a specific type of data. Some of the commands take longer to get to another computer than others. The SEND NET MESSAGE INTEGER command sends a small packet, whereas SEND NET MESSAGE IMAGE can send a very large packet containing all the data in the image. You'll notice that the last five commands have a flag parameter after them. This flag is there in case of network slowdown. If it is set to 1, it guarantees that the data will get to the other computer. DarkBASIC will drop any packets that don't have this flag if there is not enough time to send them.

Reading Information

There are many commands for reading the data that is available to the computer. Because you don't want to look for packets every turn, I will cover the NET MESSAGE EXISTS command first. This command returns a 1 if any messages are waiting to be processed. If no messages are waiting, there is no need to go through the process of reading them.

When you know that at least one message exists, you need to get that message. The GET NET MESSAGE command opens the packet so you can read it. For every message that comes to the computer, you must call a GET NET MESSAGE command. That is why when I am reading net messages; I simply do a loop while NET MESSAGE EXISTS is equal to 1.

After you have the message, and before you process it, you might want to know whom it is to and from. The NET MESSAGE PLAYER TO and NET MESSAGE PLAYER FROM commands return the player number to whom and from whom the message is sent, respectively. You should only use these commands if you need to know whom the message is from or to whom it is directed. In a client/server model, you will only get messages that are meant for you, so you can effectively ignore the NET MESSAGE PLAYER TO command. If your game requires you to keep track of whom the message is from (in the case of games with more than two players), the NET MESSAGE PLAYER FROM command will return the ID of the player who sent the message.

If the computer knows the message is for you and has dealt with whom it is from, you might want to know what type of message it is. If the wrong read message is called, the data will not be valid for what you read. NET MESSAGE TYPE returns an integer that dictates the type of net message that is waiting. Table 18.2 lists the return values and the types of data they represent.

Table 18.2: Network Message Return Values

Return Value

Data Type

1

Integer

2

Float

3

String

4

Memblock

5

Bitmap

6

Image

7

Sound

8

Mesh

The data type of the message waiting dictates what command you must call to retrieve the message. The commands will return the appropriate values for your game to process. NET MESSAGE INTEGER(), NET MESSAGE FLOAT(), and NET MESSAGE STRING$ all return values appropriate to the data type sent. The remaining five commands (NET MESSAGE MEMBLOCK, NET MESSAGE BITMAP, NET MESSAGE IMAGE, NET MESSAGE SOUND, and NET MESSAGE MESH) each have a parameter to indicate where to place the data (as referenced in Table 18.3).

Table 18.3: Network Message Types

Command

Data Type

NET MESSAGE INTEGER

Integer

NET MESSAGE FLOAT

Float

NET MESSAGE STRING$

String

NET MESSAGE MEMBLOCK

Memblock

NET MESSAGE BITMAP

Bitmap

NET MESSAGE IMAGE

Image

NET MESSAGE SOUND

Sound

NET MESSAGE MESH

Mesh

Let s Communicate

The ChatClient program demonstrates how to use the NET MESSAGE commands by creating a chat client. This program recycles some of the source code found in the NetworkConnect program because that code does not change. Figure 18.6 shows the host's point of view of the chat client with some sample chat. You will find this program on the CD in the SourcesChapter18ChatClient folder.

click to expand
Figure 18.6: The ChatClient program demonstrates using NET MESSAGE commands to create a chat client.

REMSTART
---------------------------------
Beginner's Guide To DarkBASIC Game Programming
Copyright (C)2002 Jonathan S. Harbour and Joshua R. Smith
Chapter 18- ChatClient Program
---------------------------------

REMEND
SYNC ON
SYNC RATE 30

' Keep track of what was said and who said it.
DIM ChatText$(32)
DIM PlayersName$(1)
DIM NumberOfPlayers(1)
DIM LastNumberOfPlayers(1)
' Find the TCP/IP connection number
TcpIpNumber = FindTCPIPConnectionNumber()
PRINT "Simple network chat client!"
PRINT
SYNC
' Get their Name
INPUT "Please Enter Your Name: ", MyName$
PlayersName$(1) = MyName$
SYNC
SYNC
IF MyName$ = ""
 PRINT "You need to enter a name!"
 WAIT KEY
 END
ENDIF
' Find out who the host and clients are..
PRINT "(1) I'm the Host"
PRINT "(2) I'm the Client"
SYNC
SYNC
A$ = ""
Answer = 0
' Get Host or Client
WHILE Answer = 0
 A$ = INKEY$()
 IF A$ = "1" THEN Answer = 1
 IF A$ = "2" THEN Answer = 2
ENDWHILE
' Do this if I'm the host..
IF Answer = 1
 PRINT "Creating net session. Please wait"
 SYNC
 SLEEP 200
 SET NET CONNECTION TcpIpNumber, " "
 CREATE NET GAME "Sample Net session", MyName$, 16, 1
ENDIF
' Do this if I'm the client.
IF Answer = 2
 Input "Please enter the Hosts IP Address: ",AddressData$
 PRINT "Connecting to net session. Please wait"
 SYNC
 SET NET CONNECTION TcpIpNumber, AddressData$
 PERFORM CHECKLIST FOR NET SESSIONS
 NumberOfGames =CHECKLIST QUANTITY()
 IF NumberOfGames = 0
 PRINT "No session found at that address"
 SYNC
 WAIT KEY
 END
 ENDIF
 JOIN NET GAME 1, MyName$
 PRINT "Connected to session!"
 SYNC
ENDIF
' Do the chat client
ChatClient()
END

' Ths function will determine which NET CONNECTION number
' is TCP/IP.
FUNCTION FindTCPIPConnectionNumber()
 Flag = 0
 CLS
 PERFORM CHECKLIST FOR NET CONNECTIONS
 FOR x = 1 TO CHECKLIST QUANTITY()
 Service$ = CHECKLIST STRING$(x)
 IF LEFT$(Service$,15)="Internet TCP/IP"
 Flag = X
 ENDIF
 NEXT x
ENDFUNCTION Flag

' This function does all the chat client functionality.
FUNCTION ChatClient()
 ' Clears the chat text from the array..
 ClearChatText()
 ' Displays the initial players in the room.
 PERFORM CHECKLIST FOR NET PLAYERS
 NumberOfPlayers(1) = CHECKLIST QUANTITY()
 FOR x = 1 TO NumberOfPlayers(1)
 AddUserMessage(CHECKLIST STRING$(x))
 NEXT x

' Send a comming in message
C$ = PlayersName$(1)+" has joined."
SEND NET MESSAGE STRING 0,C$
' Displays the chat text..
DisplayChatText()
' Set the entry buffers.
A$ = ""
B$ = ""
C$ = ""
CLEAR ENTRY BUFFER
' Capture Text Input and process it accordingly
 WHILE ESCAPEKEY()=0
 CheckIncomingMessages()
 A$ = INKEY$()
 IF ASC(A$) = 8
 C$ = C$ + ENTRY$()
 C$ = LEFT$(C$,LEN(C$)-1)
 CLEAR ENTRY BUFFER
 CLS
 DisplayChatText()
 ENDIF
 B$ = C$ + ENTRY$()
 TEXT 10,460,B$
 IF RETURNKEY()=1 AND B$ <> ""
 SLEEP 250
' Send Remote Message
 D$ = PlayersName$(1)+": "+B$
 SEND NET MESSAGE STRING 0,D$
' Display Local Message
 AddStringToChat(D$)
 D$ = ""
 B$ = ""
 C$ = ""
 CLEAR ENTRY BUFFER
' Display New Chat Window
 DisplayChatText()
 ENDIF
 SYNC
 ENDWHILE
ENDFUNCTION

' Scans the incoming messages for strings
' and displays them.
FUNCTION CheckIncomingMessages()
 GET NET MESSAGE
 IF NET MESSAGE EXISTS()=0 THEN EXITFUNCTION
 WHILE NET MESSAGE EXISTS()<>0
 MsgType = NET MESSAGE TYPE()
 IF MsgType = 3
 Msg$ = NET MESSAGE STRING$()
 AddStringToChat(Msg$)
 DisplayChatText()
 ENDIF
 GET NET MESSAGE
 ENDWHILE
ENDFUNCTION

' Message to display if a User has joined
FUNCTION AddUserMessage(Name$)
 NewString$ = Name$+" is here."
 AddStringToChat(NewString$)
ENDFUNCTION

' Adds a string to the ChatText$ array
FUNCTION AddStringToChat(a$)
 FOR x = 1 TO 32
 IF ChatText$(x) = ""
 ChatText$(x) = a$
 EXITFUNCTION
 ENDIF
 NEXT x
 FOR x = 32 TO 2
 y = x - 1
 ChatText$(y) = ChatText$(x)
 NEXT x
 ChatText$(32) = a$
ENDFUNCTION
' Clears the ChatText$ Variables
FUNCTION ClearChatText()
 FOR x = 1 to 32
 ChatText$(x) = ""
 NEXT x
ENDFUNCTION

' Displays the chat text on the screen
FUNCTION DisplayChatText()
 CLS
 SET CURRENT BITMAP 0
 CENTER TEXT 320,10,"Chat Client"
 FOR x = 1 TO 32
 TEXT 10,10+(x*15),ChatText$(x)
 NEXT x
ENDFUNCTION


Additional Multiplayer Commands

There are a few more multiplayer commands to cover. These commands give you a little extra data, as well as control over what is occurring. You don't need to use all of the commands to play a multiplayer game, but they do provide you with extra information.

The NET BUFFER SIZE() Command

The NET BUFFER SIZE() command returns how many messages are waiting to be received. This is the virtual pile of notes on your desk. If you don't want to process messages every time, you can use this command to determine how many are waiting before you process them. However, there is a limit to the number of packets that can be waiting. If you go over that limit, you will start losing them.

The FREE NET GAME Command

The FREE NET GAME command frees the current game so you can create a new one. Even though you can have multiple games from PERFORM CHECKLIST FOR NET SESSIONS, you can only have one game per application. Therefore, you need to free the game before starting a new one.

The NET GAME LOST() Command

The NET GAME LOST() command lets you know whether you have lost the current game. You should run this command to see whether the current net game has been freed. Once you know that it has, your program can quit the current game it is playing. There is no need to process or send any more net messages if no one is listening to them.

The CREATE NET PLAYER Command

The CREATE NET PLAYER command allows you to add your own players into the game. The command syntax is CREATE NET PLAYER Playername$, where PlayerName$ is a string containing the name of the player you want to create. When you create or join a net game, a player is automatically created for you. This command allows you to add secondary or AI players or other local players to the same net game.

The FREE NET PLAYER Command

The FREE NET PLAYER command allows you to free a player from a net game. The command syntax is FREE NET PLAYER PlayerNumber, where PlayerNumber is the number of the player found in the PERFORM CHECKLIST FOR NET PLAYERS command. This is useful if you are dropping a local or AI player from the game because they have been destroyed or are no longer needed.

The NET PLAYER CREATED() Command

The NET PLAYER CREATED() command lets you know whether a net player was created by a CREATE NET PLAYER command. This command returns the number of the new net player that was created.

The NET PLAYER DESTROYED() Command

The NET PLAYER DESTROYED() command lets you know whether a net player was destroyed by a FREE NET PLAYER command. This command returns the number of the player that was destroyed.


Memory Blocks

One of the most power aspects of DarkBASIC Professional is the ability to create and manipulate memory blocks (memblocks). A memblock can be any size and can contain any data. Memblocks are powerful tools for passing multiple bits of information over a single network packet. They are a defined size that can be broken up into multiple bits of information. Figure 18.7 shows one possible example of a memory block.

click to expand
Figure 18.7: A sample memblock

Memory blocks (memblocks) are chunks of memory allocated to store multiple types of information in one location. They have a specific size but do not conform to any specific data type. Memblocks can contain many different types of information in the same memory block.

Creating Memory Blocks

Creating a memblock is similar to creating an image, bitmap, or sound. You simply call the MAKE MEMBLOCK Memblock Number, Size in Bytes command. Make sure that you set the size of the memblock large enough to fit all the data you will be storing in it. The size is measured in bytes, so if you are placing four floats in the memory block, you should allocate the size as 12 bytes (43).

Destroying Memory Blocks

Destroying a memblock is very important. If you do not destroy a memblock when you are finished using it, you can create a memory leak in your program. To destroy a memblock, just use the DELETE MEMBLOCK Memblock Number command. This command will destroy the memblock and free any memory associated with it.

Writing Data to Memory Blocks

Writing data into a memblock is pretty easy. DarkBASIC provides four different commands to write data into memblocks: WRITE MEMBLOCK BYTE, WRITE MEMBLOCK WORD, WRITE MEMBLOCK DWORD, and WRITE MEMBLOCK FLOAT. The syntax for each command looks like this:

WRITE MEMBLOCK BYTE MemblockNumber, Location, Value
WRITE MEMBLOCK WORD MemblockNumber, Location, Value
WRITE MEMBLOCK DWORD MemblockNumber, Location, Value
WRITE MEMBLOCK FLOAT MemblockNumber, Location, Value

Each of these commands has three parameters. The first parameter is the memblock number. This is the number you designated during the CREATE MEMBLOCK command. The second parameter is the location in the memblock to write the data. When you created the memory block you had to assign it a size that designated the maximum amount of data (in bytes) that can be placed into the memory block. Each value placed in a memblock takes up a specific amount of bytes. You do not want to overwrite any data that was previously written into a memblock by overwriting the bytes that contain that data. Table 18.4 contains the list of commands and the number of bytes of memory each uses.

Table 18.4: Memory Block WRITE Commands

Command

Data Size

WRITE MEMBLOCK BYTE

1

WRITE MEMBLOCK WORD

2

WRITE MEMBLOCK DWORD

4

WRITE MEMBLOCK FLOAT

4

The third parameter of the WRITE MEMBLOCK commands is the value of the data itself. Figure 18.8 illustrates a memblock with data and the commands used to place that data into the memblock.

click to expand
Figure 18.8: A memblock with data and commands

Reading Data from a Memory Block

Reading from a memory block is as easy as writing to one. There are four commands for reading from each memblock, and each command takes two parameters. The first parameter is the memblock number, and the second is the position in the memblock to start reading. Table 18.5 lists the commands for reading from a memblock. These commands return the values that you request from the memblock.

Table 18.5: Memblock Read Commands

Command Data

Size

MEMBLOCK BYTE

1

MEMBLOCK WORD

2

MEMBLOCK DWORD

4

MEMBLOCK FLOAT

4

Miscellaneous Memory Block Commands

There are a few other memblock commands that don't fall into the create/destroy or read/write categories. However, these commands are still useful for manipulating memblocks.

Copying Part of a Memory Block

The COPY MEMBLOCK From, To, PosFrom, PosTo, Bytes command copies the contents of one memory block to another. You can specify the locations in the memblock you are copying the data from and to, along with the size of the memory to copy.

Determining the Existence of a Memory Block

The MEMBLOCK EXISTS(Memblock Number) command indicates whether a memblock has been allocated. If the memblock has been allocated, this command returns a 0.

Retrieving the Size of a Memory Block

The GET MEMORYBLOCK SIZE(Memblock Number) command returns the size of the specified memblock.

Using Memblocks

Using memblocks is a tough concept to understand. The Memblock program shows you how to use them. This program asks for a few inputs, places the data into memblocks, and then returns the data stored in the memblock.

You can write this program using all variables, but the memblock is the most efficient way to collect data and send it as one net message. You will see the use of memblocks with net messages in the Crazy CARnage game. Figure 18.9 shows the output of the Memblock program. You can find the source for this program on the CD in the SourcesChapter18Memblock folder.

click to expand
Figure 18.9: The Memblock example demonstrates the use of memblocks.


REMSTART
---------------------------------
Beginner's Guide To DarkBASIC Game Programming
Copyright (C)2002 Jonathan S. Harbour and Joshua R. Smith
Chapter 18- Memblock Program
---------------------------------
REMEND

CLS
' Create Memblock
MAKE MEMBLOCK 1,25
' Get the numbers
INPUT "Enter an byte (0-255)",MyByte
INPUT "Enter an word (0-65535)",MyWord
INPUT "Enter an dword (0-4294967295)",MyDWord
INPUT "Enter an float (A number with a . in it)",MyFloat#
' Make the Byte entered less than or equal to 255
WHILE MyByte > 255
 MyByte = MyByte - 255 ENDWHILE
' Make the Byte entered less than or equal to 65535
WHILE MyWord > 65535
 MyByte = MyByte - 65535
ENDWHILE
' Make the Byte entered less than or equal to 4294967295
WHILE MyDWord > 4294967295
 MyByte = MyByte - 4294967295
ENDWHILE
' Write the memory blocks
WRITE MEMBLOCK BYTE 1,0,MyByte
WRITE MEMBLOCK WORD 1,1,MyWord
WRITE MEMBLOCK DWORD 1,3,MyDWord
WRITE MEMBLOCK FLOAT 1,7,MyFloat#
' Clear the vars (to show memblocks are working)
MyByte = 0
MyWord = 0
MyDWord = 0
MyFloat# = 0.0
' Read the vars from the memblock
MyByte = MEMBLOCK BYTE(1,0)
MyWord = MEMBLOCK WORD(1,1)
MyDWord = MEMBLOCK DWORD(1,3)
MyFloat# = MEMBLOCK FLOAT(1,7)
' Display the vars gathered.
PRINT "Byte = "+STR$(MyByte)
PRINT "Word = "+STR$(MyWord)
PRINT "DWord = "+STR$(MyDWord)
PRINT "Float = "+STR$(MyFloat#)
'Delete Memory Block
DELETE MEMBLOCK 1
WAIT KEY
END


The Crazy CARnage Game!

"Gentlemen, start your engines!"

Are you ready to roll? It's time to work on the game! This will be the final project in the book. It's been a long road with many learning curves along the way, but I am confident that you will be able to understand this game without any problem. Crazy CARnage is a multiplayer car combat game. As I was writing this chapter, Crazy CARnage took many forms. One version had a level editor; another version was only single-player. Throughout the development of any game, you will make many revisions. It's the final product that counts, and it is up to you to decide on the final vision for a game.

Crazy CARnage uses two car models from the DarkMATTER collection. They are the Buggy models. It's so much fun to fight with buggies. Crazy CARnage also uses a skybox and ground texture from the DarkMATTER collection. This collection is an excellent source of material for those who are not necessarily artistically inclined. As you can tell, I'm not the world's best artist. I generated all of the other textures in the game.

  Tip

If you have the full version of DarkBASIC, make sure you have the latest patch. You can find it at http://www.darkbasicpro.com. This game might not run correctly on the original version of DarkBASIC because it was developed with the 1.13 patch release.

You can find all the source for Crazy CARnage on the CD in the Chapter 18 source directory. All graphics, 3D models, sounds, and textures are located in this directory as well. The files are located in subfolders that are referenced through the game. If you copy the game to your hard drive to modify it, make sure you copy all the subfolders as well. After I walk you through the coding of this game, I will give you a list of add-ons and features I would love to see in the game. If you add any of them, feel free to e-mail the authors with a link to the game. We'd both love to see it.

The Menus

This game can be broken into two major sections. The first section is the menus. These menus help you decide which computer is the host and which is the client. Each player must decide his name and whether he will be a host or a client. Figure 18.10 displays the screen where you enter your name. This is all part of the menu section.

click to expand
Figure 18.10: The player name entry screen

The Game

After the games are connected from the host and client menus, you enter the second section—the actual gameplay. The up, down, left, and right arrows allow you to maneuver the car around the screen. The spacebar fires your weapon. You can display a radar by holding down the Shift key. The first player to lose all his health (shown in the red health bar) loses the game. There are boxes with power-ups scattered throughout the level; these power-ups include health, speed, weapon increase, and freeze. Figure 18.11 displays the game interface.

click to expand
Figure 18.11: The Crazy CARnage game is all-out fun!

When a player loses all of his health, the game is over, and the winner is declared. The winner of the game is the player who still has health remaining. After that, you return to the main menu. The game contains many TYPE and DIM commands. Good luck typing the program; if you get too frustrated, you can find the code listing on the CD in the SourcesChapter18CrazyCarnage folder.

SYNC RATE 30
SYNC ON
DISABLE ESCAPEKEY
AUTOCAM OFF

' Special Type Defines
' This Type Contains the Players Information
' Their Name (NAME$)
' And If they are that player (IAM)
DIM PlayerInfoName$(2)
DIM PlayerInfoIam(2)

' This type references all the objects
' Laid through the level
' ObjectNumber is the DarkBASIC Object Number
' ObjectType is the type of object
' 3 Health
' 4 Speed
' 5 Weapon
' 6 Freeze
DIM SpecialObjectsObjectNumber(256)
DIM SpecialObjectsObjectType(256)

' This contains all the information about a
' project (what the car files)
' ObjectNumber is the DarkBASIC object number
' ObjectSpeed is how fast the projectile is going.
' ObjectTimer is the time the projectile has to live
' ObjectOwner is who fired the projectile
' ObjectDoneDamage indicates if the object has hit anything.
' ObjectDamage is how much damage the object can/will do.
' ObjectType is the type of projectile being shot.
DIM ProjectileObjectNumber(30)
DIM ProjectileObjectSpeed(30)
DIM ProjectileObjectTimer(30)
DIM ProjectileObjectLive(30)
DIM ProjectileObjectOwner(30)
DIM ProjectileObjectDoneDamage(30)
DIM ProjectileObjectDamage(30)
DIM ProjectileObjectType(30)

' Type type contains all the variables about the car.
' ObjectStartX is the starting X position of the car
' ObjectStartZ is the starting Z position of the car.
' There is no ObjectStartY because the car starts
' on the ground.
' CarWaponPower is how much Damage a shot from the
' car does.
' CarAngle is the Angle the car is facing.
' CarSpeed is the speed the car is going.
' CarMaxSpeed is how fast the car can go.
' CarHealth is how much health the car has left.
' ObjectNumber is the DarkBASIC object number
DIM CarInfoObjectStartX(2)
DIM CarInfoObjectStartZ(2)
DIM CarInfoCarWeaponPower(2)
DIM CarInfoCarAngle(2)
DIM CarInfoCarSpeed(2)
DIM CarInfoCarMaxSpeed(2)
DIM CarInfoCarHealth(2)
DIM CarInfoObjectNumber(2)

' Global Dims
' This Contains your name
DIM YourName$(1)
' This contains what connection number
' TCP/IP is
DIM ConnectType(1)
' This contains if a network connection
' was established.
DIM NoNetwork(1)
DIM MyPlayerNumber(1)
' Contains what player number They am
' either 1 or 2 and is the opposite of
' MyPlayerNumber
DIM TheirPlayerNumber(1)
' Fire Counter to delay firing
DIM FireCounter(1)
' How many Special Objects are out there.
DIM SpecialObjCount(1)
' Stores where the other player quit or not.
DIM HeQuit(1)
' Determines if the radar needs to be displayed.
DIM ShowTheRadar(1)
' Timer to freeze my car if need be.
DIM FreezeTimer(1)
' I am the host this is 1
DIM IAmHost(1)

' Initialize the global vars.
ShowTheRadar(1) = 0
FreezeTimer(1) = 0
SpecialObjCount(1) = 0
MyPlayerNumber(1) = 1
TheirPlayerNumber(1)= 2
NoNetwork(1) = 1
IAmHost(1) = 1
' Determing which connection is TCP/IP
CType = StandardNetworkStuff()
ConnectType(1) = CType
' Process Menus and Play the game.
WHILE ProcessMenus()=1
 PlayGame()
ENDWHILE
END

The ProcessMenus() function displays all the initial menus for connecting the game. In the previous examples in this chapter, you used a simple text menu. However, most games don't use simple PRINT and INPUT menus. They have fancy graphics and menu selections. The ProcessMenus() function calls all the sub-functions for generating the look of the menu. You'll notice that this function returns a 1 or a 0 (based on the RetValue variable). This tells the main part of the program whether or not to play.

' Processes all the game menus
FUNCTION ProcessMenus()
 Flag = 0
 RetValue = 1
' We need to loop until we have a game established..
' Once a game is establiched (or the player quits)
' Flag is set to 1.
' RetValue is used to determine if we need to play
' a game or not.
WHILE Flag=0
 ' Display the Main Menu.
 Command = MainMenu()
 ' If they quit the main menu..
 ' Don't play the game.
 IF Command = 3
 Flag = 1
 RetValue = 0
 ENDIF
 IF Command = 1
 NoNetwork(1) = 1
 Flag = 1
 RetValue = 1
 ENDIF
 ' Load up the Multiplayer menus
 IF Command = 2
 NoNetwork(1) = 0
 ' Get their name
 ReturnBack = GetNameScreen()
 IF ReturnBack <> -1
 MenuCommand = -1
 ' Find out whose hosting and whose a client..
 HostCommand = HostMenu()
 IF HostCommand = 1
 ' If we are hosting wait for the clients
 MenuCommand = WaitForHostMenu()
 ENDIF
 IF HostCommand = 2
 ' If we are a client, look for the host.
 MenuCommand = ConnectToHostMenu()
 ENDIF
 IF HostCommad = 3
 Flag = 1
 RetValue = 0
 ENDIF
 IF MenuCommand = -1
 Flag = 0
 ELSE
 Flag = 1
 ENDIF
 ELSE
 Flag = 0
 ENDIF
 ENDIF
ENDWHILE
ENDFUNCTION RetValue

The PlayGame() function is the heart of Crazy CARnage. This function contains the main game loop and calls all the sub-functions required to play the game. All the images used in the interface (HUD—Heads-Up Display) are loaded in this function, which is executed after you've made your choices in the main menus. All the graphics for the HUD are located in the HUD subfolder. Also included in this function is the processing of the Shift key to display the radar.

REMSTART
Play the main game...
REMEND
FUNCTION PlayGame()
 CarInfoCarWeaponPower(1) = 1
 CarInfoCarWeaponPower(2) = 1
 CarInfoCarMaxSpeed(1) = 1
 CarInfoCarMaxSpeed(2) = 1
' Generates the Terrain for the Level
 GenerateLevel()
' Loads all the images for the Heads up Display (HUD)
 LOAD IMAGE "HudHud_All.bmp",20
 LOAD IMAGE "HudRadar.bmp",21
 LOAD BITMAP "HudGreenBar.bmp",2
 LOAD BITMAP "HudRedBar.bmp",3
 CREATE BITMAP 4,170,170

 SET CURRENT BITMAP 0

' Initialized all the variables, models and sounds
 InitAllProjectiles()
 LoadSounds()
 LoadCarModels()
 InitialSpecialObjects()

SetCarLocation()
MyNumber = MyPlayerNumber(1)
TheirNumber = TheirPlayerNumber(1)
CarInfoCarAngle(MyNumber) = 0
CarInfoCarSpeed(MyNumber)=0
SendLocationPacket_Var = 0
FireCounter(1) = 0
Done = 0
HeQuit(1) = 0
MyObjectNumber = CarInfoObjectNumber(MyNumber)
TheirObjectNumber = CarInfoObjectNumber(TheirNumber)

' Main game loop... Exited when Done != 1
WHILE Done=0
 ' Check to see if radar should be displayed.
 IF SHIFTKEY()=1
 ShowTheRadar(1) = 1
 ELSE
 ShowTheRadar(1) = 0
 ENDIF
 ' Check to see if they quit.
 IF ESCAPEKEY()=1
 SendIQuitPacket()
 DONE = -1
 ENDIF
 ' If they quit, I should quit too..
 IF HeQuit(1) = 1
 DONE = -1
 ENDIF
' Process all the projectiles.
ProcessProjectiles()
' Process all the network packets
IF NoNetwork(1) = 0
 GET NET MESSAGE
 IF NET MESSAGE EXISTS()<>0
 ProcessNetMessages()
 ENDIF
 IF NET GAME LOST() = 1
 DONE = -1
 ENDIF
ENDIF
 ' Save the COORDs of my car.
 XLOC# = OBJECT POSITION X(MyObjectNumber)
 YLOC# = OBJECT POSITION Y(MyObjectNumber)
 ZLOC# = OBJECT POSITION Z(MyObjectNumber)
 YROT# = OBJECT ANGLE Y(MyObjectNumber)
 ' Set the camera to follow my car.
 SET CAMERA TO FOLLOW XLOC#, YLOC#,ZLOC#, YROT#, -12, 5, 1 ,1
 ' Reset my cars speed.
 CarInfoCarSpeed(MyNumber) = 0
 CarInfoCarSpeed(TheirNumber) = 0
 ' Check to see if I'm frozen
 IF FreezeTimer(1) > 0
 SecondsLeft = FreezeTimer(1)-Timer()
 SecondsLeft = SecondsLeft / 1000
 IF SecondsLeft < 0 THEN SecondsLeft = 0
 NewString$ = "Frozen: "+STR$(SecondsLeft)+" seconds."
 TEXT 0,300,NewString$
 IF TIMER() > FreezeTimer(1)
 FreezeTimer(1) = 0
 ENDIF
 IF TIMER() < 10000
 FreezeTimer(1) = 0
 ENDIF
 ENDIF
 ' Check to see if my car is moving or turning.
 SendLocationPacket_Var = 0
 SendLocationPacket_Var = DriveCar(MyNumber)
 LookForSpaceBarFire(MyNumber)
 CarInfoCarAngle(MyNumber) = WRAPVALUE(CarInfoCarAngle(MyNumber))
 ' Move and turn my car based on the values determined above.
 YROTATE OBJECT MyObjectNumber,CarInfoCarAngle(MyNumber)
 MOVE OBJECT MyObjectNumber,CarInfoCarSpeed(MyNumber)
 ' IF my car is colliding, move it back.
 IF OBJECT COLLISION(MyObjectNumber,TheirObjectNumber) = 1
 MOVE OBJECT MyObjectNumber, CarInfoCarSpeed(MyNumber)*-1
 ENDIF
 ' IF my car collides with the edge of the world.
 XLOC# = OBJECT POSITION X(MyObjectNumber)
 ZLOC# = OBJECT POSITION Z(MyObjectNumber)
 IF XLOC# <=0 OR XLOC# >=320
 MOVE OBJECT MyObjectNumber, CarInfoCarSpeed(MyNumber)*-1
 ENDIF
 IF ZLOC# <=0 OR ZLOC# >=320
 MOVE OBJECT MyObjectNumber, CarInfoCarSpeed(MyNumber)*-1
 ENDIF
 ' Tell the other player I've moved..
 IF SendLocationPacket_Var = 1 THEN SendLocationPacket()

 ' Check for collisions to me...
 CheckForOtherCollisions(MyNumber)
 ' Check for collisions for them..
 CheckForOtherCollisions(TheirNumber)
 ' Display the HUD
 DisplayHud()
 ' Display the players names.
 DisplayPlayers()
 ' Determine the winnner
 ' Set's Done to the Player number
 ' if there is a winner.
 IF Done <> -1
 Done = GetWinner()
 ENDIF
 'Display What we've processed.
 SYNC
 ENDWHILE

 ' Send one final damage state so the other side knows who lost..
 SendDamageState()
 ' Clean up everything allocated..
 DELETE IMAGE 20
 DELETE IMAGE 21
 DELETE BITMAP 3
 DELETE BITMAP 2
 DELETE BITMAP 4
 DELETE SOUND 1
 DELETE SOUND 2
 DELETE SOUND 3
 KillSpecialObject()
 KillLevel()
 ' Reset the camera
 ResetCamera()
 ' If we have a winner display them.
 IF Done <> -1
 DisplayWinner(Done)
 ENDIF
 ' Free the net game so we can start anotherone.
 IF NET GAME EXISTS() = 1 THEN FREE NET GAME
 ' Delete the cars.
 DELETE OBJECT 2
 DELETE OBJECT 1
ENDFUNCTION

The next two functions, GenerateLevel() and KillLevel(), deal with creating the world. GenerateLevel() loads the terrain, the terrain texture, and the background skybox. KillLevel() unloads the terrain, the terrain texture, and the background skybox. The terrain and terrain texture are located in the Terrain subfolder. The skybox is located in the Background subfolder.

' Loads the Terrain, Terrain Texture, and
' Skybox Texture.
FUNCTION GenerateLevel()
 ' Load the Terrain Texture
 LOAD IMAGE "Terrain	exture.bmp",10

 ' Make the Terrain
 MAKE MATRIX 1,320,320,10,10
 PREPARE MATRIX TEXTURE 1, 10, 1, 1

 ' Load the Skybox
 LOAD OBJECT "Backgroundsky01.x",3
 ' Turn off collision with the skybox as
 ' the car is inside the sky box and will
 ' always collide with it otherwise.
 SET OBJECT COLLISION OFF 3

 ' Set the Position, scale, and CULL of the
 ' Skybox.
 POSITION OBJECT 3,160,0,160
 SCALE OBJECT 3,5000,5000,5000
ENDFUNCTION

FUNCTION KillLevel()
 ' Free the Terrain
DELETE MATRIX 1
 ' Free the Skybox Object
 DELETE OBJECT 3
 ' Free the Terrain Texture
 DELETE IMAGE 10
ENDFUNCTION

The next five functions—MainMenu(), HostMenu(), GetNameScreen(), ConnectToHostMenu(), and WaitForHostMenu()—are the menus that are called by the ProcessMenu() function. The MainMenu() function has two options—to play the game and to quit. The HostMenu() function has three options—to host the game, to join the game, or to quit. The GetNameScreen() function gets the player's name and stores it in the YourName$(1) variable. The ConnectToHostMenu() function establishes the client-side connection to the game. The WaitForHostMenu() function establishes the host connection to the game. All the menus load their main graphics from the Menu subfolder. Each menu also loads the skybox (and rotates it) from the Background subfolder.

' Display the Main Menu
FUNCTION MainMenu()
 ' Sets the ambient Light
 SET AMBIENT LIGHT 100
 ' Loads Resources for this menu
 LOAD OBJECT "Backgroundsky01.x",3
 LOAD IMAGE "Menumenu.bmp",1
 ' Set Variables
 Flag = 0
 BackgroundAngle = 0
 Black = RGB(0,0,0)
 Orange = RGB(255,102,0)
 SET CURSOR 10,10
 ' Loops until Flag <> 0
 WHILE Flag=0
 IF ESCAPEKEY()=1 THEN Flag = 3
 BackgroundAngle = BackgroundAngle + 1
 BackgroundAngle = WRAPVALUE(BackgroundAngle)
 YROTATE OBJECT 3,BackGroundAngle
 MX = MOUSEX()
 MY = MOUSEY()
 ' Check to see if Multiplayer was selected.
 IF MX >230 AND MX <500 AND MY >180 AND MY <225
 INK Orange,Black
 BOX 230,180,500,225
 IF MOUSECLICK() = 1 THEN Flag = 2
 ENDIF
 ' Check to see if Quit was selected.
 IF MX >230 AND MX <310 AND MY >390 AND MY <435
 INK Orange,Black
 BOX 230,390,310,435
 IF MOUSECLICK() = 1 THEN Flag = 3
 ENDIF
 ' Paste the main menu image on the screen.
 PASTE IMAGE 1,0,0,1
 SYNC
 ENDWHILE
 ' Free Resources used in this menu.
 DELETE IMAGE 1
 DELETE OBJECT 3
ENDFUNCTION Flag

' Display the Host or Client Menu
FUNCTION HostMenu()
 ' Sets the ambient Light
 SET AMBIENT LIGHT 100
 ' Loads Resources for this menu
 LOAD OBJECT "Backgroundsky01.x",3
 LOAD IMAGE "MenuMultiplayer.bmp",1
 ' Set Variables
 Flag = 0
 BackgroundAngle = 0
 Black = RGB(0,0,0)
 Orange = RGB(255,102,0)
 SET CURSOR 10,10

 ' This loop ask the player to Host or
 ' Join a game.
 ' Loops until FLAG <> 0
 WHILE Flag=0
 BackgroundAngle = BackgroundAngle + 1
 BackgroundAngle = WRAPVALUE(BackgroundAngle)
 YROTATE OBJECT 3,BackGroundAngle
 MX = MOUSEX()
 MY = MOUSEY()
 ' Check to see if host was selected.
 IF MX >230 AND MX <400 AND MY >110 AND MY <155
 INK Orange,Black
 BOX 230,110,400,155
 IF MOUSECLICK() = 1 THEN Flag = 1
 ENDIF
 ' Check to see if join was selected.
 IF MX >230 AND MX <500 AND MY >180 AND MY <225
 INK Orange,Black
 BOX 230,180,500,225
 IF MOUSECLICK() = 1 THEN Flag = 2
 ENDIF
 ' Check to see if quit was selected.
 IF MX >230 AND MX <310 AND MY >390 AND MY <435
 INK Orange,Black
 BOX 230,390,310,435
 IF MOUSECLICK() = 1 THEN Flag = 3
 ENDIF

 PASTE IMAGE 1,0,0,1
 SYNC
 ENDWHILE
 ' Free Resources used in this menu.
 DELETE IMAGE 1
 DELETE OBJECT 3
ENDFUNCTION Flag

FUNCTION GetNameScreen()
 ' Sets the ambient Light
 SET AMBIENT LIGHT 100
 ' Loads Resources for this menu
 LOAD OBJECT "Backgroundsky01.x",3
 LOAD IMAGE "MenuNameOnly.bmp",1
 ' Set Variables
 Flag = 0
 BackgroundAngle = 0
 BLACK = RGB(0,0,0)
 WHITE = RGB(255,255,255)
 INK WHITE,BLACK
 SET CURSOR 10,10
 B$ = ""
 C$ = ""
 CLEAR ENTRY BUFFER
 ' This loop collects the players name.
 ' Loops until FLAG <> 0
 WHILE Flag=0
 IF ESCAPEKEY()=1 THEN Flag = -1
 BackgroundAngle = BackgroundAngle + 1
 BackgroundAngle = WRAPVALUE(BackgroundAngle)
 YROTATE OBJECT 3,BackGroundAngle
 IF RETURNKEY()=1 AND B$ <> ""
 WHILE RETURNKEY()=1
 ENDWHILE
 Flag = 1
 ENDIF
 CENTER TEXT 320,260,"Enter Your Name"
 A$ = INKEY$()
 IF ASC(A$) = 8
 C$ = C$ + ENTRY$()
 C$ = LEFT$(C$,LEN(C$)-1)
 CLEAR ENTRY BUFFER
 ENDIF

 B$ = C$ + ENTRY$()
 CENTER TEXT 320,280,B$

 PASTE IMAGE 1,0,0,1
 SYNC
 ENDWHILE
 ' Free Resources used in this menu.
 YourName$(1) = LEFT$(B$,LEN(B$)-1)

 DELETE IMAGE 1
 DELETE OBJECT 3
 CLEAR ENTRY BUFFER
ENDFUNCTION Flag

FUNCTION ConnectToHostMenu()
 ' Check for a valid network connection
 CType = ConnectType(1)
 IF CType = 0 THEN EXITFUNCTION 0
 IAmHost(1) = 0
 ' Sets the ambient Light
SET AMBIENT LIGHT 100
' Loads Resources for this menu
LOAD OBJECT "Backgroundsky01.x",3
LOAD IMAGE "MenuNameOnly.bmp",1
' Set Variables
MyPlayerNumber(1) = 2
TheirPlayerNumber(1) = 1
MyNumber = MyPlayerNumber(1)
TheirNumber = TheirPlayerNumber(1)
MyName$ = YourName$(1)
Flag = 0
BackgroundAngle = 0
Black = RGB(0,0,0)
White = RGB(255,255,255)
INK White,Black SET CURSOR 10,10
B$ = ""
C$ = ""
CLEAR ENTRY BUFFER
' This loop get's the IP address
' of the host you are connecting to.
' It loops until FLAG <> 0
WHILE Flag=0
 IF ESCAPEKEY()=1 THEN Flag = -1
 BackgroundAngle = BackgroundAngle + 1
 BackgroundAngle = WRAPVALUE(BackgroundAngle)
 YROTATE OBJECT 3,BackGroundAngle
 IF RETURNKEY()=1
 WHILE RETURNKEY()=1
 ENDWHILE
 Flag = 1
 ENDIF
 CENTER TEXT 320,260,"Enter Host Address"
 A$ = INKEY$()
 IF ASC(A$) = 8
 C$ = C$ + ENTRY$()
 C$ = LEFT$(C$,LEN(C$)-1)
 CLEAR ENTRY BUFFER
 ENDIF
 B$ = C$ + ENTRY$()
 CENTER TEXT 320,280,B$
 PASTE IMAGE 1,0,0,1
 SYNC
 ENDWHILE
 CLEAR ENTRY BUFFER
 ' Free Resources used in this menu.
 ' if ESCAPE was pressed.
 IF Flag = -1
 DELETE IMAGE 1
 DELETE OBJECT 3
 EXITFUNCTION Flag
 ENDIF
 SET NET CONNECTION CType,B$
 ' This loop waits until there are
 ' two players in the game. This will
 ' rarely ever loop more than once.
 ' because you are the client (the
 ' second player to join).
 ' It loops until Flag <> 0
 Flag = 0
 WHILE Flag=0
 IF ESCAPEKEY()=1 THEN Flag = -1
 PERFORM CHECKLIST FOR NET SESSIONS
 IF CHECKLIST QUANTITY() = 1
 JOIN NET GAME 1,MyName$
 Flag = 1
 ENDIF
 BackgroundAngle = BackgroundAngle + 1
 BackgroundAngle = WRAPVALUE(BackgroundAngle)
 YROTATE OBJECT 3,BackGroundAngle
 CENTER TEXT 320,260,"Connecting to Client"
 PASTE IMAGE 1,0,0,1
 SYNC
 ENDWHILE
 ' Free Resources used in this menu.
 ' if ESCAPE was pressed.
 IF Flag = -1
 DELETE IMAGE 1
 DELETE OBJECT 3
 EXITFUNCTION Flag
 ENDIF
 GetPlayerNames()
 DELETE IMAGE 1
 DELETE OBJECT 3
ENDFUNCTION Flag

FUNCTION WaitForHostMenu()
 ' Check for a valid network connection
 CType = ConnectType(1)
 IF CType = 0 THEN EXITFUNCTION 0
 IAmHost(1) = 1
 SET NET CONNECTION CType," "
 MyName$ = YourName$(1)
 CREATE NET GAME "DualRacer", MyName$, 2
 ' Sets the ambient Light
 SET AMBIENT LIGHT 100
 ' Loads Resources for this menu
 LOAD OBJECT "Backgroundsky01.x",3
 LOAD IMAGE "MenuNameOnly.bmp",1
 ' Set Variables
 MyPlayerNumber(1)=1
 TheirPlayerNumber(1)=2
 MyNumber = MyPlayerNumber(1)
 TheirNumber = TheirPlayerNumber(1)
 MyName$ = YourName$(1)
 Flag = 0
 BackgroundAngle = 0
 Black = RGB(0,0,0)
 White = RGB(255,255,255)
 INK White,Black SET CURSOR 10,10
 ' This look waits until two players are joined in the
 ' game created on this computer.
 ' It loops until Flag <> 0
WHILE Flag=0
 IF ESCAPEKEY()=1 THEN Flag = -1
 BackgroundAngle = BackgroundAngle + 1
 BackgroundAngle = WRAPVALUE(BackgroundAngle)
 YROTATE OBJECT 3,BackGroundAngle
 PERFORM CHECKLIST FOR NET PLAYERS
 NumOfPlayers = CHECKLIST QUANTITY()
 IF CHECKLIST QUANTITY() = 2
 Flag = 1
 ENDIF
 IF RETURNKEY()=1
 Flag = 3
 ENDIF
 CENTER TEXT 320,260,"Waiting for Client"
 NewString$ = "Players Waiting: "+STR$(NumOfPlayers)
 CENTER TEXT 320,280, NewString$
 PASTE IMAGE 1,0,0,1
 SYNC
 ENDWHILE

 ' Free the resources if ESC was pressed
 ' Includes freeing the game.
 IF Flag = -1
 FREE NET GAME
 DELETE IMAGE 1
 DELETE OBJECT 3
 EXITFUNCTION Flag
 ENDIF
 ' Loads the players names into the correct
 ' variables.
 GetPlayerNames()
 ' Free the resources if ESC was pressed
 DELETE IMAGE 1
 DELETE OBJECT 3
ENDFUNCTION Flag

The next four functions cover in-game displays. These functions deal with displaying player names, the HUD, and the radar. The GetPlayerNames() function stores the players’ names in the PlayerInfo(…) structures. The DisplayPlayers() function displays the players’ names on the screen. The DisplayHUD() function displays all the 2D graphics on the screen. The DisplayRadar() function displays the radar.

' Records the players names into the
' PlayerInfo... globals
FUNCTION GetPlayerNames
 PERFORM CHECKLIST FOR NET PLAYERS
 NUMOFPLAYERS = CHECKLIST QUANTITY()
 FOR x = 1 to NUMOFPLAYERS
 PlayerInfoName$(x) = CHECKLIST STRING$(x)
 IF PlayerInfoName$(x) = YourName$(1)
 PlayerInfoIam(x) = 1
 ELSE
 PlayerInfoIam(x) = 0
 ENDIF
 NEXT x
ENDFUNCTION

' Displays the players name on the screen
FUNCTION DisplayPlayers
 BLACK = RGB(0,0,0)
 WHITE = RGB(255,255,255)
 INK WHITE,BLACK
 FOR x = 1 to 2
 TEXT 10,(X*10)+20,PlayerInfoName$(x)
 NEXT x
ENDFUNCTION

' Displays the heads up display on the screen.
FUNCTION DisplayHud
 MyNumber = MyPlayerNumber(1)
 TheirNumber = TheirPlayerNumber(1)

 PASTE IMAGE 20,0,0,1

 COPY BITMAP 3,0,0,199,11,0,20,440,220,452
 COPY BITMAP 3,0,0,199,11,0,20,460,220,472

 ' Displays the health of both cars
 XSIZE = CarInfoCarHealth(MyNumber)*2
 IF XSIZE <> 0
 COPY BITMAP 2,0,0,XSIZE-1,11,0,20,440,20+XSIZE,452
 ENDIF
 XSIZE = CarInfoCarHealth(TheirNumber)*2
 IF XSIZE <> 0
 COPY BITMAP 2,0,0,XSIZE-1,11,0,20,460,20+XSIZE,472
 ENDIF

 If ShowTheRadar(1)=1 THEN DisplayRadar()

ENDFUNCTION

' Display the RADAR on the screen.
FUNCTION DisplayRadar()
 MyNumber = MyPlayerNumber(1)
 TheirNumber = TheirPlayerNumber(1)

 MyObjNumber = CarInfoObjectNumber(MyNumber)
 MyXPos = OBJECT POSITION X(MyObjNumber)
 MyYPos = OBJECT POSITION Z(MyObjNumber)

 TheirObjNumber = CarInfoObjectNumber(TheirNumber)
 TheirXPos = OBJECT POSITION X(TheirObjNumber)
 TheirYPos = OBJECT POSITION Z(TheirObjNumber)

 MyXPos = MyXPos / 2
 MyYPos = MyYPos / 2
 TheirXPos = TheirXPos / 2
 TheirYPos = TheirYPos / 2
 MyXPos = MyXPos+5
 MyYPos = MyYPos+5
 TheirXPos = TheirXPos+5
 TheirYPos = TheirYPos+5

 ' Dipslay the Players positions
 Grn = RGB(0,255,0)
 Red = RGB(255,0,0)
 Wht = RGB(255,255,255)
 SET CURRENT BITMAP 4
 PASTE IMAGE 21,0,0,0
 X1 = MyXPos-2
 X2 = MyXPos+2
 Y1 = MyYPos-2
 Y2 = MyYPos+2
 INK Green, Black
 BOX X1,Y1,X2,Y2
 X1 = TheirXPos-2
 X2 = TheirXPos+2
 Y1 = TheirYPos-2
 Y2 = TheirYPos+2
 INK Red, Black
 BOX X1,Y1,X2,Y2

 INK White, Black
 ' Display all projectiles being fired.
 FOR x = 1 TO 30
 PObj = ProjectileObjectNumber(x)
 IF OBJECT EXIST(Pobj)
 ProX = OBJECT POSITION X(Pobj)
 ProY = OBJECT POSITION Z(Pobj)
 ProX = ProX/2
 ProY = ProY/2
 ProX = ProX+5
 ProY = ProY+5
 X1 = ProX-2
 X2 = ProX+2
 Y1 = ProY-2
 Y2 = ProY+2
 BOX X1,Y1,X2,Y2
 ENDIF
 NEXT x
 SET CURRENT BITMAP 0
 COPY BITMAP 4,0,0,169,169,0,465,75,634,244
ENDFUNCTION

The next five functions deal with the bullets (projectiles) fired from the car. You'll see the number 30 show up a few times in these functions. That is because there are only 30 projectiles on the screen at any one time. Each player has 15 projectiles they can fire at any given time.

The InitAllProjectiles() function sets all the projectiles to their initial states. The ProcessProjectiles() function is the heart of what all the projectiles do. It processes their movements and checks to see whether they have dissipated. The DamagePlayer(PlayerNum, DamageDone) function applies damage to the specified player based on the damage done by the projectile. When this function is called, DamageDone is passed from the particle structures.

The ExplodeProjectile(ProjectileNum) function explodes a projectile when it is called. This function is called when a projectile has hit a player. The FireProjectTile(PlayerNum, ProjectileType) function sets up all the variables necessary to fire a projectile, creates the object, and sets it in motion.

' Sets all the projectiles to a base state
FUNCTION InitAllProjectiles
 FOR x = 1 to 30
 ProjectileObjectNumber(x) = 49+x
 ProjectileObjectSpeed(x) = 0
 ProjectileObjectLive(x) = 0
 ProjectileObjectTimer(x) = 0
 ProjectileObjectOwner(x) = 0
 ProjectileObjectDoneDamage(x) = 0
 ProjectileObjectDamage(x) = 0
 ProjectileObjectType(x) = 0
 NEXT x
ENDFUNCTION

' Process all the projectiles.
FUNCTION ProcessProjectiles
 FOR x = 1 to 30
 ObjNumber = ProjectileObjectNumber(x)
 ObjSpeed = ProjectileObjectSpeed(x)
 ObjTimer = ProjectileObjectTimer(x)
 ' Make sure the object exists
 ' if it does not, that projectile
 ' has not been fired yet.
 IF OBJECT EXIST(ObjNumber)=1
 ' Move the projetile forward
 MOVE OBJECT ObjNumber, ObjSpeed
 ' Decrease the Projectiles timer.
 ProjectileObjectTimer(x) = ObjTimer - 1

 ' Check for the end of the projectile
 IF ProjectileObjectTimer(x) <= 10
 GHOST OBJECT ON ObjNumber
 FADE OBJECT ObjNumber,25
 ENDIF
 ' Free the projectile if it's at the end..
 IF ProjectileObjectTimer(x) <= 0
 ProjectileObjectLive(x) = 0
 ProjectileObjectOwner(x) = 0
 DELETE OBJECT ObjNumber
 ENDIF
 ENDIF
 NEXT x
ENDFUNCTION

' This function is called with a projectile
' collides with another player
FUNCTION DamagePlayer(PlayerNum , DamageDone)
 MyNumber = MyPlayerNumber(1)
 ' You can't get hit by your own projectile.
 IF PlayerNum <> MyNumber THEN EXITFUNCTION
 ' Reduce their health.
 CarInfoCarHealth(PlayerNum) = CarInfoCarHealth(PlayerNum) - DamageDone
 ' If the health is less then 0 make it equal to 0
 IF CarInfoCarHealth(PlayerNum) < 0
 CarInfoCarHealth(PlayerNum) = 0
 ENDIF
 ' Send the damage information to the other player.
 SendDamageState()
ENDFUNCTION

' This function is called when the projectile collides with a player.
FUNCTION ExplodeProjectile(ProjectileNum)
 ProjectileObjectTimer(ProjectileNum) = 10
 ProjectileObjectSpeed(ProjectileNum) = 0
 ProjectileObjectDoneDamage(ProjectileNum) = 1
ENDFUNCTION

' The is called when a projectile needs to be fired.
FUNCTION FireProjectTile(PlayerNum, ProjectileType)
 Flag = 0
 ' This set of IF and for statements determine the
 ' next particle availible to you.
 IF PlayerNum = 1
 FOR x = 1 to 15
 IF ProjectileObjectLive(x) = 0
 Flag = x
 x = 30
 ENDIF
 NEXT x
 ELSE
 FOR x = 16 to 30
 IF ProjectileObjectLive(x) = 0
 Flag = x
 x = 30
 ENDIF
 NEXT x
 ENDIF
 ' If no particles are availible leave this function
 ' You don't want to fire blank particles.
 IF Flag = 0
 EXITFUNCTION Flag
 ENDIF
 ' Setup the Data for the Particle.
 ProjectileObjectOwner(Flag) = PlayerNum
 ProObjectNum = ProjectileObjectNumber(Flag)
 CarObjectNumber = CarInfoObjectNumber(PlayerNum)
 XPos# = OBJECT POSITION X(CarObjectNumber)
 YPos# = OBJECT POSITION Y(CarObjectNumber)
 ZPos# = OBJECT POSITION Z(CarObjectNumber)
 XRot# = OBJECT ANGLE X(CarObjectNumber)
 YRot# = OBJECT ANGLE Y(CarObjectNumber)
 ZRot# = OBJECT ANGLE Z(CarObjectNumber)
 ' Create the Particle VIA the MAKE OBJECT SPHERE
 ' command.
 MAKE OBJECT SPHERE ProObjectNum, 2
 ' SCALE POSITION and Size the particle.
 SCALE OBJECT ProObjectNum, 25,25,25
 POSITION OBJECT ProObjectNum,XPos#, YPos# + 1 , ZPOos#
 ROTATE OBJECT ProObjectNum, XRot#, YRot#, ZRot#
 MOVE OBJECT ProObjectNum, -2
 ' Turn on the collision for the particle.
 SET OBJECT COLLISION ON ProObjectNum
 SET OBJECT COLLISION TO BOXES ProObjectNum
 ' Set the remaining values for the Particle.
 CarMaxSpeed = CarInfoCarMaxSpeed(PlayerNum)*-1
 ProjectileObjectSpeed(Flag) = CarMaxSpeed-1
 ProjectileObjectTimer(Flag) = 100
 ProjectileObjectLive(Flag) = 1
 ProjectileObjectDamage(Flag) = CarInfoCarWeaponPower(PlayerNum)
 ProjectileObjectDoneDamage(Flag) = 0
 ProjectileObjectType(Flag) = ProjectileType
 ' Play the FIRE Sound.
 PLAY SOUND 1
ENDFUNCTION FLAG

If the PlayGame() function is the heart of this game, then the next six functions are the circulatory system. These are the veins and arteries of the program, passing the data around the network. The SendLocationPacket() function is called every time your car is moved. This command tells the other game to move the car on your behalf. The SendProjectilePacket(ObjectNum, ProjectileType) function is called every time a projectile is fired. This tells the other game to fire a projectile from the car on your behalf. The SendIQuitPacket() function is called when a player hits the Esc key. This lets the other game know that the player has quit. The SendDamageState() function is called whenever your projectiles hit your opponent. This lets the other game know that you have hit the other player.

The ProcessNetMessages() function is the brains of the operation. It takes all the messages sent from the other computer and processes them. It keeps track of the command that was sent through the memblock message and processes it accordingly. The StandardNetworkStuff() function finds the connection number for TCP/IP, which is needed to connect games via TCP/IP.

' Sends location packets..
FUNCTION SendLocationPacket()
 ' If there is no network.. don't send the packet.
 IF NoNetwork(1) = 1 THEN EXITFUNCTION
 MAKE MEMBLOCK 1,60
 ' Collectes the location information.
 MyNumber = MyPlayerNumber(1)
 MySpeed = CarInfoCarSpeed(MyNumber)
 MyObjectNumber = CarInfoObjectNumber(MyNumber)
 XPos# = OBJECT POSITION X(MyObjectNumber)
 YPos# = OBJECT POSITION Y(MyObjectNumber)
 ZPos# = OBJECT POSITION Z(MyObjectNumber)
 XRot# = OBJECT ANGLE X(MyObjectNumber)
 YRot# = OBJECT ANGLE Y(MyObjectNumber)
 ZRot# = OBJECT ANGLE Z(MyObjectNumber)

 MyHealth = CarInfoCarHealth(MyNumber)

 ' Writes all the location information into the memblcok.
 WRITE MEMBLOCK DWORD 1,1,1
 WRITE MEMBLOCK FLOAT 1,5,XPos#
 WRITE MEMBLOCK FLOAT 1,9,YPos#
 WRITE MEMBLOCK FLOAT 1,13,ZPos#
 WRITE MEMBLOCK FLOAT 1,17,XRot#
 WRITE MEMBLOCK FLOAT 1,21,YRot#
 WRITE MEMBLOCK FLOAT 1,25,ZRot#
 WRITE MEMBLOCK DWORD 1,29,MyHealth
 ' Sends the packet..
 SEND NET MESSAGE MEMBLOCK 0,1,1
 DELETE MEMBLOCK 1
ENDFUNCTION

' Sends projectile packets.
FUNCTION SendProjectilePacket(ObjectNum,ProjectileType)
 ' If there is no network.. don't send the packet.
 IF NoNetwork(1) = 1 THEN EXITFUNCTION
 MAKE MEMBLOCK 1,50
 ' Collects the Projectile information.
 MyNumber = MyPlayerNumber(1)
 NewNumber = ProjectileObjectNumber(ObjectNum)
 MyObjectNumber = CarInfoObjectNumber(MyNumber)
 XPos# = OBJECT POSITION X(MyObjectNumber)
 YPos# = OBJECT POSITION Y(MyObjectNumber)
 ZPos# = OBJECT POSITION Z(MyObjectNumber)
 XRot# = OBJECT ANGLE X(MyObjectNumber)
 YRot# = OBJECT ANGLE Y(MyObjectNumber)
 ZRot# = OBJECT ANGLE Z(MyObjectNumber)

 MyHealth = CarInfoCarHealth(MyNumber)

 ' Writes all the location
 ' and firing information
 ' into the memblcok.
 WRITE MEMBLOCK DWORD 1,1,2
 WRITE MEMBLOCK FLOAT 1,5,XPos#
 WRITE MEMBLOCK FLOAT 1,9,YPos#
 WRITE MEMBLOCK FLOAT 1,13,ZPos#
 WRITE MEMBLOCK FLOAT 1,17,XRot#
 WRITE MEMBLOCK FLOAT 1,21,YRot#
 WRITE MEMBLOCK FLOAT 1,25,ZRot#
 WRITE MEMBLOCK DWORD 1,29,ProjectileType
 WRITE MEMBLOCK DWORD 1,33,ObjectNum
 WRITE MEMBLOCK DWORD 1,37,MyHealth
 ' Sends the packet..
 SEND NET MESSAGE MEMBLOCK 0,1,1
 DELETE MEMBLOCK 1
ENDFUNCTION

 ' Sends the Quitting packet..

FUNCTION SendIQuitPacket()
 IF NoNetwork(1) = 1 THEN EXITFUNCTION
 MAKE MEMBLOCK 1,50
 WRITE MEMBLOCK DWORD 1,1,4
 ' Sends the packet..
 SEND NET MESSAGE MEMBLOCK 0,1,1
 DELETE MEMBLOCK 1
ENDFUNCTION

 ' Sends Damages State packets.
FUNCTION SendDamageState()
 ' If there is no network.. don't send the packet.
 IF NoNetwork(1) = 1 THEN EXITFUNCTION
 MAKE MEMBLOCK 1,50
 ' Collects damage information.
 MyNumber = MyPlayerNumber(1)
 MyHealth = CarInfoCarHealth(MyNumber)
 ' Writes the damage information to the MEMBLOCK.
 WRITE MEMBLOCK DWORD 1,1,5
 WRITE MEMBLOCK DWORD 1,5,MyHealth
 ' Sends the packet..
 SEND NET MESSAGE MEMBLOCK 0,1,1
 DELETE MEMBLOCK 1
ENDFUNCTION

' Processes all the network messages (Packets).
FUNCTION ProcessNetMessages()
 ' If there is no network.. don't process messages.
 IF NoNetwork(1) = 1 THEN EXITFUNCTION
 MAKE MEMBLOCK 2,50
 TheirNumber = TheirPlayerNumber(1)
 ' Loops while we still have some network messages to read..
 WHILE NET MESSAGE EXISTS()=1
 ' Get the message..
 NET MESSAGE MEMBLOCK 2

 ' The first DWORD in the message is the command
 CmdNumber = MEMBLOCK DWORD(2,1)
 ' This was a location packet..
 IF CmdNumber = 1
 TheirObjNum = CarInfoObjectNumber(TheirNumber)
 XPos# = MEMBLOCK FLOAT(2,5)
 YPos# = MEMBLOCK FLOAT(2,9)
 ZPos# = MEMBLOCK FLOAT(2,13)
 XRot# = MEMBLOCK FLOAT(2,17)
 YRot# = MEMBLOCK FLOAT(2,21)
 ZRot# = MEMBLOCK FLOAT(2,25)

 NewHealth = MEMBLOCK DWORD(2,29)
 CarInfoCarHealth(TheirNumber) = NewHealth

 POSITION OBJECT TheirObjNum, XPos#, YPos#, ZPos#
 ROTATE OBJECT TheirObjNum, XRot#, YRot#, ZRot#
 CarInfoCarAngle(TheirNumber) = YRot
 ENDIF
 ' This was a Projectile Packet
 IF CmdNumber = 2
 TheirObjNum = CarInfoObjectNumber(TheirNumber)
 XPos# = MEMBLOCK FLOAT(2,5)
 YPos# = MEMBLOCK FLOAT(2,9)
 ZPos# = MEMBLOCK FLOAT(2,13)
 XRot# = MEMBLOCK FLOAT(2,17)
 YRot# = MEMBLOCK FLOAT(2,21)
 ZRot# = MEMBLOCK FLOAT(2,25)

 NewHealth = MEMBLOCK DWORD(2,37)
 CarInfoCarHealth(TheirNumber) = NewHealth

 POSITION OBJECT TheirObjNum, XPos#, YPos#, ZPos#
 ROTATE OBJECT TheirObjNum, XRot#, YRot#, ZRot#
 ProjectileType = MEMBLOCK DWORD(2,29)
 FireProjectTile(TheirNumber,ProjectileType)
 ENDIF
 ' This was a Quit Packet
 IF CmdNumber = 4
 HeQuit(1) = 1
 ENDIF
 ' This was a damage packet.
 IF CmdNumber = 5
 NewHealth = MEMBLOCK DWORD(2,5)
 CarInfoCarHealth(TheirNumber) = NewHealth
 ENDIF
 ' Get the next Message
 GET NET MESSAGE
 ENDWHILE
 DELETE MEMBLOCK 2
ENDFUNCTION
' Determine what connection number
' TCP/IP is.
FUNCTION StandardNetworkStuff()
 Flag = 0
 CLS
 PERFORM CHECKLIST FOR NET CONNECTIONS
 FOR x = 1 TO CHECKLIST QUANTITY()
 Service$ = CHECKLIST STRING$(x)
 IF LEFT$(Service$,15)="Internet TCP/IP"
 Flag = x
 ENDIF
 NEXT x
ENDFUNCTION Flag

The next three functions focus on the special objects. All the object textures are loaded from the Pieces subfolder. The InitialSpecialObjects() function initializes all the special objects and their placements using the DATA statements provided. To change the position of any special object in the world, just modify the DATA statements. The ProcessSpecialObject(ObjectIndex, Player) function affects a player based on the object with which they have collided. The KillSpecialObject() command frees all the special objects after a game is finished.

' Data Format
' Object Number, Xpos, Zpos, ObjectType

' Health Powerups
DATA 55,250,3,31
DATA 25,150,3,31
DATA 178,192,3,31
DATA 299,170,3,31
DATA 49,21,3,31
DATA 62,130,3,31
DATA 71,49,3,31
DATA 99,112,3,31
DATA 57,38,3,31
DATA 22,245,3,31
' Speed Powerups
DATA 15,131,4,32
DATA 263,42,4,32
' Weapon Powerups
DATA 10,310,5,33
DATA 310,10,5,33
DATA 10,10,5,33
DATA 310,310,5,33
DATA 130,18,5,33
DATA 301,22,5,33
DATA 41,108,5,33
DATA 163,23,5,33
' Freeze Powerups
DATA 47,180,6,34
DATA 220,20,6,34
DATA 77,111,6,34
DATA 125,132,6,34

' Initializes all the special objects and their placements
FUNCTION InitialSpecialObjects()
 NewObjectNum = 500
 SpecialObjCount(1) = 0
 ObjCount = 1

 ' Load all the textures
 LOAD IMAGE "PiecesHealth.bmp",31
 LOAD IMAGE "PiecesSpeed.bmp",32
 LOAD IMAGE "PiecesWeapon.bmp",33
 LOAD IMAGE "PiecesFreeze.bmp",34

 FOR x = 1 TO 24
 READ Xloc
 READ Zloc
 READ ObjType
 READ TexNum
 MAKE OBJECT BOX NewObjectNum,2,2,2
 TEXTURE OBJECT NewObjectNum,TexNum
 POSITION OBJECT NewObjectNum, Xloc,1,Zloc
 SpecialObjectsObjectNumber(ObjCount) = NewObjectNum
 SpecialObjectsObjectType(ObjCount) = ObjType
 SET OBJECT COLLISION ON NewObjectNum
 ObjCount = ObjCount + 1
 NewObjectNum = NewObjectNum + 1
 NEXT x
 SpecialObjCount(1) = 24
ENDFUNCTION

' Process the effects of special objects on the car.
FUNCTION ProcessSpecialObject(ObjectIndex,Player)
 TheirNumber = TheirPlayerNumber(1)
 MyNumber = MyPlayerNumber(1)
 PHealth = CarInfoCarHealth(Player)
 SpObjNum = SpecialObjectsObjectNumber(ObjectIndex)
 SELECT SpecialObjectsObjectType(ObjectIndex)
 ' Health Object
 CASE 3
 IF PHealth = 100 THEN EXITFUNCTION 0
 IF PHealth > 75
 CarInfoCarHealth(Player) = 100
 ELSE
 CarInfoCarHealth(Player) = PHealth + 25
 ENDIF
 IF OBJECT EXIST(SpObjNum)
 DELETE OBJECT SpObjNum
 ENDIF
 ENDCASE
 ' Speed Object
 CASE 4
 IF CarInfoCarMaxSpeed(Player) = 2 THEN EXITFUNCTION 0
 CarInfoCarMaxSpeed(Player) = CarInfoCarMaxSpeed(Player) + 1
 IF OBJECT EXIST(SpObjNum)
 DELETE OBJECT SpObjNum
 ENDIF
 ENDCASE
 ' Weapon Power UP
 CASE 5
 WepPow =CarInfoCarWeaponPower(Player)
 IF WepPow = 5 THEN EXITFUNCTION 0
 CarInfoCarWeaponPower(Player) = WepPow + 1
 IF OBJECT EXIST(SpObjNum)
 DELETE OBJECT SpObjNum
 ENDIF
 ENDCASE
 ' Speed Power UP.
 CASE 6
 IF Player = TheirNumber
 FreezeTimer(1) = TIMER()+10000
 ENDIF
 IF OBJECT EXIST(SpObjNum)
 DELETE OBJECT SpObjNum
 ENDIF
 ENDCASE
 ENDSELECT
ENDFUNCTION 1

' Frees all unused special objects.
FUNCTION KillSpecialObject()
 SpecialCount = SpecialObjCount(1)
 FOR x = 1 TO SpecialObjCount(1)
 ObjNum = 499+x
 IF OBJECT EXIST(ObjNum)
 DELETE OBJECT ObjNum
 ENDIF
 NEXT x
 ' Frees the texturs as well.
 DELETE IMAGE 31
 DELETE IMAGE 32
 DELETE IMAGE 33
 DELETE IMAGE 34
ENDFUNCTION

The next six functions all deal with players’ cars. The LoadCarModels() function loads all the cars into their respective objects. The car models are all located in the Models subfolder; each car model, in turn, is contained in a separate subfolder under that. The Beach Bug subfolder contains Player 1's car. The Beach Bug2 subfolder contains Player 2's car.

The LoadSounds() function loads all the sounds used in the game. The SetCarLocation() function places the two cars in the world. The DriveCar(CarNumber) function controls the movement of the car on the screen. Just like in the real world, you must be moving before you can turn. The LookForSpaceBarFire(CarNumber) function checks whether the spacebar has been pressed. If it has, the function fires a projectile.

The CheckForOtherCollisions(PlayerNumber) function drives the game. This function detects collisions between the players and the projectiles or special objects.

' Loads all the car models
FUNCTION LoadCarModels()
 ' Loads the models
 LOAD OBJECT "modelsBeach BugH-Beach Bug-Move.x",1
 LOAD OBJECT "modelsBeach Bug 2H-Beach Bug 2-Move.x",2
 ' Set's their collision on
 SET OBJECT COLLISION TO BOXES 1
 SET OBJECT COLLISION TO BOXES 2
 SET OBJECT COLLISION ON 1
 SET OBJECT COLLISION ON 2
 ' Set their ObjectNumber
 CarInfoObjectNumber(1) = 1
 CarInfoObjectNumber(2) = 2
 ' Set their Health
 CarInfoCarHealth(1) = 100
 CarInfoCarHealth(2) = 100
ENDFUNCTION

' Loads all the sounds used.
FUNCTION LoadSounds()
 LOAD SOUND "soundsFIRE.WAV",1
 LOAD SOUND "soundsHIT.WAV",2
 LOAD SOUND "soundsPICKUP.WAV",3
ENDFUNCTION

' Positions the car in the world.
FUNCTION SetCarLocation()
 XLoc# = 25
 ZLoc# = 25
 ObjNum = CarInfoObjectNumber(1)
 POSITION OBJECT ObjNum,XLoc#,0,ZLoc#
 XLoc# = 295
 ZLoc# = 295
 ObjNum = CarInfoObjectNumber(2)
 POSITION OBJECT ObjNum,XLoc#,0,ZLoc#
 ' Send an object packet so the other
 ' player knows where you are.
 SendLocationPacket()
ENDFUNCTION

' This function drives the car around
' the world.
FUNCTION DriveCar(CarNumber)
 ' If you are frozen, you can't drive.
 IF FreezeTimer(1) <> 0 THEN EXITFUNCTION 0
 ' Set the variables.
 MyNumber = CarNumber
 SendLocationPacket_var = 0
 MaxCarSpeed = CarInfoCarMaxSpeed(MyNumber)
 ' Check for Forward motion
 IF UPKEY()=1
 CarInfoCarSpeed(MyNumber)=MaxCarSpeed*-1
 SendLocationPacket_var = 1
 ENDIF
 ' Check for backwards motion
 IF DOWNKEY()=1
 CarInfoCarSpeed(MyNumber)=MaxCarSpeed*1
 SendLocationPacket_var = 1
 ENDIF
 CurAngle = CarInfoCarAngle(MyNumber)
 ' Check for Backwards motion and
 ' Turning Left
 IF LEFTKEY()=1 AND CarInfoCarSpeed(MyNumber)>0
 CarInfoCarAngle(MyNumber) = CurAngle +5
 IF CarInfoCarAngle(MyNumber) > 360
 CarInfoCarAngle(MyNumber) = 5
 ENDIF
 SendLocationPacket_var = 1
 ENDIF
 ' Check for Forwards motion and
 ' Turning Left
 IF LEFTKEY()=1 AND CarInfoCarSpeed(MyNumber)<0
 CarInfoCarAngle(MyNumber) = CurAngle -5
 IF CarInfoCarAngle(MyNumber) < 0
 CarInfoCarAngle(MyNumber) = 355
 ENDIF
 SendLocationPacket_var = 1
 ENDIF
 ' Check for Backwards motion and
 ' Turning Right
 IF RIGHTKEY()=1 AND CarInfoCarSpeed(MyNumber)>0
 CarInfoCarAngle(MyNumber) = CurAngle -5
 IF CarInfoCarAngle(MyNumber) < 0
 CarInfoCarAngle(MyNumber) = 355
 ENDIF
 SendLocationPacket_var = 1
 ENDIF
 ' Check for Forwards motion and
 ' Turning Right
 IF RIGHTKEY()=1 AND CarInfoCarSpeed(MyNumber)<0
 CarInfoCarAngle(MyNumber) = CurAngle +5
 IF CarInfoCarAngle(MyNumber) > 360
 CarInfoCarAngle(MyNumber) = 5
 ENDIF
 SendLocationPacket_var = 1
 ENDIF
ENDFUNCTION SendLocationPacket_var

' Detects if the space bar was pressed
' and if it was, fires a projectile.
FUNCTION LookForSpaceBarFire(CarNumber)
 MyNumber = CarNumber
 ' Checks the fire counter to space out
 ' the projectiles.
 IF SPACEKEY()=1 AND FireCounter(1) = 0
 Flag = FireProjectTile(MyNumber,1)
 IF Flag <> 0
 SendProjectilePacket(FLAG,1)
 ENDIF
 FireCounter(1) = 5
 ENDIF
 IF FireCounter(1)>0
 FireCounter(1) = FireCounter(1) -1
 ENDIF
ENDFUNCTION

' Checks for collisions with other objects.
FUNCTION CheckForOtherCollisions(PlayerNumber)
 TheirNumber = TheirPlayerNumber(1)
 MyNumber = MyPlayerNumber(1)
 CarObjNum = CarInfoObjectNumber(PlayerNumber)
 ' If the player isn't colliding, Quit..
 ObjectNumColide = OBJECT COLLISION(CarObjNum,0)
 IF ObjectNumColide = 0 THEN EXITFUNCTION
 Flag = 0
 ' Check if the player is colliding with
 ' Projectiles
 FOR x = 1 to 30
 PObjNum = ProjectileObjectNumber(x)
 PObjOwn = ProjectileObjectOwner(x)
 PObjDmg = ProjectileObjectDamage(x)
 IF ObjectNumColide = PObjNum and PObjOwn <> PlayerNumber
 SET OBJECT COLLISION OFF PObjNum
 DamagePlayer(PlayerNumber , PObjDmg)
 STOP SOUND 1
 PLAY SOUND 2
 ExplodeProjectile(x)
 Flag = 1
 ENDIF
 NEXT x
 ' If they collided with a projectile quit..
 ' No need to process further.
 IF Flag = 1 THEN EXITFUNCTION
 ' Check if the player is colliding with
 ' Special Objects
 Flag = 0
 FOR x = 1 TO 24
 IF ObjectNumColide = SpecialObjectsObjectNumber(x)
 Flag = x
 x = 25
 ENDIF
 NEXT x

 IF Flag <> 0
 NewFlag = ProcessSpecialObject(Flag,PlayerNumber)
 ENDIF
ENDFUNCTION

The last three functions deal with winning and winning screens. The GetWinner() function determines whether there is a winner. If so, the function returns the winner's number. The DisplayWinner(WinnerNumber) function displays the winner on the winning screen. Both players can see who won the game. To leave this screen, the player simply presses the Esc key. The ResetCamera() function resets the camera for the winning screen.

' Determines the winner.
FUNCTION GetWinner()
 MyNumber = MyPlayerNumber(1)
 TheirNumber = TheirPlayerNumber(1)
 IF CarInfoCarHealth(MyNumber) <= 0
 EXITFUNCTION TheirNumber
 ENDIF
 IF CarInfoCarHealth(TheirNumber) <= 0
 EXITFUNCTION MyNumber
 ENDIF
ENDFUNCTION 0

' Displays the winning screen.
FUNCTION DisplayWinner(WinnerNumber)
 ' Sets the ambient light
 SET AMBIENT LIGHT 100
 ' Loads the skybox.
 LOAD OBJECT "Backgroundsky01.x",3
 Black = RGB(0,0,0)
 White = RGB(255,255,255)
 INK White,Black
 POSITION OBJECT CarInfoObjectNumber(WinnerNumber), 0,0,5
 MyNumber = MyPlayerNumber(1)
 TheirNumber = TheirPlayerNumber(1)
 IF WinnerNumber = MyNumber
 IF PlayerInfoIam(1) = 1
 WinnerText$ = PlayerInfoName$(1)
 ELSE
 WinnerText$ = PlayerInfoName$(2)
 ENDIF
 ENDIF
 IF WinnerNumber = TheirNumber
 IF PlayerInfoIam(1) = 0
 WinnerText$ = PlayerInfoName$(1)
 ELSE
 WinnerText$ = PlayerInfoName$(2)
 ENDIF
 ENDIF
 WinnerText$ = WinnerText$ +" is the winner"
 BackgroundAngle = 0
 WinObjNum = CarInfoObjectNumber(WinnerNumber)
 Flag = 0
 ' Waits until the ESCAPEKY is pressed.
 WHILE Flag=0
 BackgroundAngle = BackgroundAngle + 1
 BackgroundAngle = WRAPVALUE(BackgroundAngle)
 YROTATE OBJECT 3,BackGroundAngle
 YROTATE OBJECT WinObjNum ,BackGroundAngle
 CENTER TEXT 320,40,WinnerText$
 IF RETURNKEY()=1 THEN Flag = 1
 SYNC
 ENDWHILE
 ' Free the SkyBox
 DELETE OBJECT 3
ENDFUNCTION

' Resets the camera.
FUNCTION ResetCamera()
 POSITION CAMERA 0,0,0
 ROTATE CAMERA 0,0,0
ENDFUNCTION

The Extras

As I said before, I would like to see a few things added to this game. I had several great ideas that would not fit in the book. Here are a few ideas you can use to improve this game and make it that much better.

  • Different cars. My favorite idea is to add more cars to the game. I love VWs, but there are more cars out there that you can use. Tanks, motorcycles, and police cars are just a few from the DarkMATTER collection. When you add the cars, just remember that when the multiplayer game is connected, the other computer needs to know what both cars are.
  • Car damage. Another exciting aspect to add to the game would be car damage. It's not as hard as it sounds. You simply replace the car model with a more damaged model. Just remember that with health power-ups, your car could go back to a healthier state.
  • Different environments. This game offers only one type of background and one type of texture for the terrain. It would be neat to have different worlds in which you can battle. The host could select the world before the game begins.
  • Multi-level terrain. This problem is a little tougher to tackle, but you already have a small foothold on it by using the MAKE TERRAIN command. Check out the DarkBASIC Language Reference on the CD for a list of all the different terrain commands available.
  • More power-ups. Weapons boost, speed boost, health, and freeze are nice power-ups, but not very creative. Try adding a few other power-ups, such as one that doubles the size of your car or one that makes the other player's controls go in reverse. The possibilities here are endless.
  • More weapons. I'm using just a torpedo-type weapon, but more weapons would improve the game. I like weapons that people don't expect, such as hover jets or homing missiles.
  • Massively multiplayer capability. A big undertaking for this game would be to make it massively multiplayer. Multiplayer can support two or more players, but what about a game of 32 or 64 cars? That would be massively multiplayer.


Summary

What an extensive chapter. Multiplayer and memblocks are the final components to make your games great. Feel free to modify the Crazy CARnage game to add more special effects and such. Use your imagination. If you come up with something really cool, let me know. I'd like to hear what you have done with the source code.


Quiz

The chapter quiz will help to reinforce the material you learned in this chapter, and will provide feedback on how well you have learned the subjects that were covered. For the answers to the quiz, refer to Appendix A, "Answers to the Chapter Quizzes."

1.

Which component of DirectX does DarkBASIC utilize for multiplayer support?

  1. DirectNetwork
  2. DirectSound
  3. DirectPlay
  4. DirectConnect

c

2.

A packet is…

  1. A series of commands sent to the hard drive
  2. A collection of data sent from one computer to another to be processed
  3. A jacket's cousin
  4. Slang for a packrat

b

3.

Which of the following is not a domain name?

  1. http://www.yahoo.com
  2. slashdot.org
  3. 209.232.223.22
  4. www.cnn.com

c

4.

What is considered the taxicab of the Internet?

  1. TCP/IP
  2. OC/UD
  3. MME/OB
  4. IPX/SPX

a

5.

How many players can participate in a single net game?

  1. 4
  2. 256
  3. 16
  4. Unlimited

b

6.

Which command returns the number of net messages waiting?

  1. MESSAGES WAITING
  2. MESSAGES EXIST
  3. GET NETMESSAGE
  4. NET BUFFER SIZE

d

7.

What does the Create Memblock command do?

  1. Creates a memblock for reading and writing
  2. Deletes an existing memblock
  3. Prints a smiley face on the printer
  4. None of the above

a

8.

How is the size of a memblock measured?

  1. In pixels
  2. In inches
  3. In dollars
  4. In bytes

d

9.

What does the WRITE MEMBLOCK WORD 1, 200, 4 command do?

  1. Writes 4 bytes to memblock 1, starting at byte 1
  2. Writes 200 bytes to memblock 1, starting at byte 4
  3. Writes 1 byte to memblock 4, starting at byte 200
  4. Writes 200 bytes to memblock 4, starting at byte 1

b

10.

GET MEMBLOCK SIZE(Memblock Number) returns what information about a memblock?

  1. Its size in pixels
  2. Its size in inches
  3. Its size in dollars
  4. Its size in bytes

d

Answers

1.

C

2.

B

3.

C

4.

A

5.

B

6.

D

7.

A

8.

D

9.

B

10.

D




Beginner's Guide to DarkBASIC Game Programming
Beginners Guide to DarkBASIC Game Programming (Premier Press Game Development)
ISBN: 1592000096
EAN: 2147483647
Year: 2002
Pages: 203

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net