Difference between revisions of "MERVBot Tutorial"

From ASSS Wiki
Jump to: navigation, search
(Example of reading any text from a .txt and printing it to pilot line by line: updated - check my comment about max length being 255 (assuming \0, for [256]).)
m (fixed tutorial link)
 
(73 intermediate revisions by 9 users not shown)
Line 1: Line 1:
Here is the ever-popular MERVBot Tutorial by Underlord:
+
This tutorial is based on the ever-popular MERVBot Tutorial by Underlord. It has since been updated to reflect new changes with MervBot. To see examples of how to use this instruction, see [[MERVBot Example Code]].
  
<div id="merv_toc" style="font-size: 12px; margin: 2em;">
+
This tutorial also assumes that you have a basic knowledge of C++. If you don't, check out cplusplus.com's great [http://www.cplusplus.com/doc documentation].
Setup a MERVBot bot and project in visual c++<br />
 
0) <a href="#0">Setting up a MERVBot bot</a><br />
 
<br />
 
Command.cpp<br />
 
&nbsp;1) <a href="#1">Player commands</a> (!play, !squadA vs squadB)<br />
 
<br />
 
Spawn.cpp<br />
 
&nbsp;2) <a href="#2">Event descriptions</a> (describe events in spawn.cpp)<br />
 
&nbsp;2) <a href="#2">Messaging </a>(&quot;*arena hi&quot;, &quot;:player:*scorereset)<br />
 
&nbsp;3) <a href="#3">MervBot Timer</a> (do this in 10 seconds)(countdown[n])<br />
 
&nbsp;4) <a href="#4">Writing Functions</a> (bool IsInCenter(Player *p))<br />
 
<br />
 
Useful operations<br />
 
&nbsp;6) <a href="#6">Cycling players</a><br />
 
&nbsp;7) <a href="#7">Check if pilot is in safe zone</a><br />
 
&nbsp;8) <a href="#8">Random numbers</a><br />
 
&nbsp;9) <a href="#9">Time without using countdown[n]</a><br />
 
10) <a href="#10">Storing data for pilots</a><br />
 
11) <a href="#11">Output data in messages</a><br />
 
12) <a href="#12">Input/Output to files</a><br />
 
13) <a href="#13">Programming commands</a><br />
 
14) <a href="#14">Useful player data</a><br />
 
15) <a href="#15">Bot built in functions</a><br />
 
<br />
 
Example Code<br />
 
<div style="margin-left: 2em">
 
a) <a href="#15a">No antiwarp in center of map</a><br />
 
b) <a href="#15b">Setting freq size depending on how many pilots in game</a><br />
 
c) <a href="#15c">Tracking kills and announcing when pilot gets 10 kills in a row without dying </a><br />
 
d) <a href="#15d">Warp pilot to coord when they are in a certain region</a><br />
 
e) <a href="#15e">Structures within structures</a><br />
 
f) <a href="#15f">Tracking flag data</a><br />
 
g) <a href="#15g">Way to do simple /!spam feature</a><br />
 
h) <a href="#15h">Implementing a simple stack to do &quot;next in line for several 'boxes' at once&quot;</a><br />
 
i) <a href="#15i">Reading any text from a .txt and printing it to pilot line by line</a><br />
 
j) <a href="#15j">Example of printing player stats grid</a><br />
 
k) <a href="#15k">Example of checking if any pilots are within a region</a><br />
 
l) <a href="#15l">Example of functions to get a pilot's struct id info from a name or *player info</a><br />
 
m) <a href="#15m">Example of creating a logfile name using date and squad names</a><br />
 
n) <a href="#15n">Sending messages to playing freqs or public and logging depending on status</a><br />
 
o) <a href="#15o">Example of reading in all player/freqs to struct data</a><br />
 
p) <a href="#15p">Example of finding MVP from struct data</a><br />
 
q) <a href="#15q">Print time stamp of event</a><br />
 
r) <a href="#15r">Simple way to track player bomb/bullet damage stats</a><br />
 
s) <a href="#15s">Simple way to print those stats</a><br />
 
t) <a href="#15t">Make bot spectate specific coordinates</a>
 
</div>
 
</div>
 
  
 
==Setting up a MERVBot (plugin)==
 
==Setting up a MERVBot (plugin)==
  
MERVBot download site: &nbsp;http://catid.sscentral.com/
+
[http://mervbot.com MERVBot download site]
  
MERVBot Forum: &nbsp;http://www.ssforum.net/ &nbsp;(Development - MervBot forum)
 
  
 +
===Obtaining MERVBot===
  
===Download [http://catid.sscentral.com/files/MERVBot.zip MERVBot Build 37 (DLL 6.5)]===
+
* Download the [http://mervbot.com/files/MERVBot.rar latest build].
 +
* Unrar MERVBot.rar into a new folder. (example c:\program files\continuum\mervbot)
 +
* Unzip src.zip into &quot;src&quot; subfolder of that new folder (example c:\program files\continuum\mervbot\src)
  
Unzip MERVBot.zip into a new folder. (example c:\program files\continuum\mervbot)<br />
+
===Preparing to write a plugin===
Unzip src.zip into &quot;src&quot; subfolder of that new folder (example c:\program files\continuum\mervbot\src)
 
 
 
 
 
===Download [http://catid.sscentral.com/files/Tutorial.zip DLL-plugin Tutorial]===
 
  
 
''Note:'' if you only want to execute someone's premade plugin (.dll), skip to [[MERVBot Tutorial#Run your bot dll|step 4]], otherwise continue to learn how to make your own bot
 
''Note:'' if you only want to execute someone's premade plugin (.dll), skip to [[MERVBot Tutorial#Run your bot dll|step 4]], otherwise continue to learn how to make your own bot
  
Unzip Tutorial.zip (containing spawn.h, spawn.cpp, and command.cpp) into &quot;/tutorial&quot; subfolder of that new folder. (example c:\program files\continuum\mervbot\src\tutorial).
+
Download [http://www.mervbot.com/files/Tutorial.rar DLL-plugin Tutorial] and unzip Tutorial.zip (containing spawn.h, spawn.cpp, and command.cpp) into a &quot;tutorial&quot; subfolder of that new folder. (example c:\program files\continuum\mervbot\src\tutorial).
  
 
''File descriptions:''
 
''File descriptions:''
<ul>
+
* spawn.h = declare/initialize globals
<li>spawn.h = declare/initialize globals
+
* command.cpp = code for commands coming into bot (ie /!help, /!play, etc)
<li>command.cpp = code for commands coming into bot (ie /!help, /!play, etc)
+
* spawn.cpp = code that interacts with bot spawns
<li>spawn.cpp = code for main part of bot
 
</ul>
 
 
 
  
 
===Microsoft Visual c++===
 
===Microsoft Visual c++===
Line 103: Line 50:
 
===Run your bot dll===
 
===Run your bot dll===
  
To run your bot you need your DLL (mybot.dll), Commands.txt, MERVBot.exe, MERVBot.ini, Operators.txt, Spawns.txt, subspace.bin, and zlib.dll all in one folder (example c:\program files\continuum\mervbot).
+
To run your bot you need your DLL (mybot.dll), Commands.txt, MERVBot.exe, MERVBot.ini, Operators.txt, Spawns.txt, and zlib.dll all in one folder (example c:\program files\continuum\mervbot).
  
 
<ol>
 
<ol>
<li>Edit spawns.txt (only one line of text in file needed)
+
<li>Edit spawns.txt. '''Read every word of spawns.txt to find out what needs to go in there.'''
 +
 
 
<br>
 
<br>
<br>
 
bot_name : pw_for_bot_name : arena: dll_plugin : optional_staff_password<br />
 
 
 
''Example:''
 
''Example:''
 
<pre>2v2-Bot-League : botpw : 2v2a : 2v2league : staffpw</pre>
 
<pre>2v2-Bot-League : botpw : 2v2a : 2v2league : staffpw</pre>
<ul>
+
 
<li>You should create your bot name and pw using your Continuum client as you would create any new name.
+
''Note:'' The bot will attempt to create the name if it doesn't exist already.
<li>There is no * in front of staffpw.
 
</ul>
 
  
  
Line 123: Line 66:
 
<br>
 
<br>
 
<pre>[Login]
 
<pre>[Login]
Zone=216.33.98.254:21000 // make that your zone IP:PORT available from zone.dat in Continuum dir
+
Zone=216.33.98.254:21000 // your zone IP:PORT available from zone.dat in Continuum dir
 
</pre>
 
</pre>
  
  
<li>Edit operators.txt
+
<li>Edit operators.txt. '''Read every word of operators.txt to find out what needs to go in there.'''<br />
<br>
 
<br>
 
access_level : name :<br />
 
 
          
 
          
 
''Example:''
 
''Example:''
Line 140: Line 80:
  
  
<li>Run MERVBot.exe
+
<li>Make sure the bot is on vip.txt or has smod+ access, then run MERVBot.exe.
<br>
 
<br>
 
Double click MERVBot.exe, the bot should now enter the zone. ''Note:'' Bot needs to be on vip.txt or have moderator+ access to enter a zone.
 
<br>
 
<br>
 
<li>Modify your MERVBot code
 
<br>
 
<br>
 
You can open your MERVBot project by opening &quot;mybot.dsw&quot; file with visual c++.
 
(example c:\program files\continuum\mervbot\src\mybot\mybot.dsw)
 
 
 
Edit the spawn.h, spawn.cpp, and command.cpp to create your plugin, then build, copy your updated DLL to your MERVBot.exe folder and then execute the bot.
 
  
Use the tutorial to get ideas on how to implement certain types of features into the bot.
+
<li>You can now edit your plugin code by opening &quot;mybot.dsw&quot; (example c:\program files\continuum\mervbot\src\mybot\mybot.dsw) in Microsoft Visual C++. Edit the spawn.h, spawn.cpp, and command.cpp to create your plugin, then build, copy your updated DLL to your MERVBot.exe folder and then execute the bot. Use the tutorial to get ideas on how to implement certain types of features into the bot.
  
 
</ol>
 
</ol>
Line 248: Line 176:
 
MERVBot is event based, so when making a bot you need to decide what will happen at certain events. Normal plugins need to consider what happens when bot enters arena, player enters arena, player leaves arena, player events like kill, shipchange, teamchange, spec, move then any other relevant events to your bot. Just worry about events that are relevant to the tasks your bot is doing.
 
MERVBot is event based, so when making a bot you need to decide what will happen at certain events. Normal plugins need to consider what happens when bot enters arena, player enters arena, player leaves arena, player events like kill, shipchange, teamchange, spec, move then any other relevant events to your bot. Just worry about events that are relevant to the tasks your bot is doing.
  
MERVBot sends events to botInfo::gotEvent() in spawn.cpp. Each supported event is already present and categorized in gotEvent(), along with the paramters that MERVBot sends with the event.  
+
MERVBot sends events to botInfo::gotEvent() in spawn.cpp. Each supported event is already present and categorized in gotEvent(), along with the paramters that MERVBot sends with the event. When a plugin wants the bot to do something, it sends tell(event) to the bot.
 +
 
 +
See dllcore.h for a list of current events and their descriptions. Dllcore.h also contains functions (like makeFollowing) to make events to send back to the bot via tell().
  
See dllcore.h for a list of current events and their descriptions.
+
<pre>tell(makeFollowing(false));</pre>
  
==Messaging - How to use the messaging system==
+
==The Messaging System==
  
 
Private message - void sendPrivate(Player *player, char *msg);
 
Private message - void sendPrivate(Player *player, char *msg);
Line 293: Line 223:
 
Note: to have bot print several lines of text fast it needs sysop in the
 
Note: to have bot print several lines of text fast it needs sysop in the
 
arena (sysop in arena bot first spawns to also) otherwise it'll print slow to avoid being
 
arena (sysop in arena bot first spawns to also) otherwise it'll print slow to avoid being
kicked for spam<br />
+
kicked for spam
 +
 
 +
===Output of data in messages===
 +
<p>An example of using normal strings to output data/messages.</p>
 +
<pre>
 +
// does *arena X pilots left in game
 +
// NOTE: variable temp needs to be defined with some value
 +
 
 +
String s = "*arena ";
 +
      s += temp;
 +
      s += " pilots left in the game.";
 +
 
 +
sendPublic(s);
 +
</pre>
 +
Or,
 +
<pre>
 +
//NOTE: this can be considered inefficient.
 +
 
 +
sendPublic("*arena " + (String)temp + " pilots left in the game");
 +
</pre>
 +
 
 +
<p>An example using sprintf to align/space data, where output data will be in this approximate format.</p>
 +
<pre>
 +
// output data will be in this approximate format (not lined up perfectly because of html)
 +
// --------------------------------------------------------------------------------------
 +
// Squad: squadname      PTS    FPTS    K    D  DMG DEALT TAKEN  F  FK    FLT
 +
// --------------------------------------------------------------------------------------
 +
// PlayerA              10000      500  116  101      9999 99999  10 150 980:55
 +
// PlayerB                500      200    7    5      9999 99999  5  3  0:04
 +
 
 +
char str[255];
 +
sendPublic("*arena--------------------------------------------------------------------------------");
 +
 
 +
sprintf(str, "*arena Squad: %-20s  PTS    FPTS  K  D  DMG DEALT  TAKEN  F  FK  FLT",
 +
        freqs[freq].freqname
 +
        );
 +
 
 +
sendPublic(str);
 +
 
 +
sendPublic("*arena--------------------------------------------------------------------------------");
 +
 
 +
            // assuming existing freqs struct with data
 +
            for (pilot=freqs[freq].playercount-1; pilot>=0; pilot--)
 +
            {
 +
                // on freq squad so print stats
 +
                char outString[255];
 +
 
 +
                sprintf(outString, "*arena %-20s %12d %8d %3d %3d %10d %6d %2d %3d %3d:%02d",
 +
                      freqs[freq].pilots[pilot].name,
 +
                      freqs[freq].pilots[pilot].points,
 +
                      freqs[freq].pilots[pilot].flagpoints,
 +
                      freqs[freq].pilots[pilot].kills,
 +
                      freqs[freq].pilots[pilot].deaths,
 +
                      freqs[freq].pilots[pilot].dmgdealt,
 +
                      freqs[freq].pilots[pilot].dmgtaken,
 +
                      freqs[freq].pilots[pilot].flags,
 +
                      freqs[freq].pilots[pilot].flagkills,
 +
                      freqs[freq].pilots[pilot].flagtime /60,
 +
                      freqs[freq].pilots[pilot].flagtime %60
 +
                      );
 +
               
 +
                sendPublic(outString);
 +
            }
  
==Timer - How to use the timing function==
+
            // Notes: sprintf format = sprintf(output char string, spacing, variables)
 +
            // Notes: s = chars, d = integer, - = left align, right align default
 +
            // Notes: doing %02d = put 0 in front if not 2 digits, %3d:%02d makes 0:04 format
 +
</pre>
 +
 
 +
==Time==
  
 
Each time MERVBot sends an EVENT_Tick to a plugin (once a second), the default handler code decrements each value in an array of countdowns. You can modify the number of countdowns and add code to occur at a specific value for one of the countdowns.
 
Each time MERVBot sends an EVENT_Tick to a plugin (once a second), the default handler code decrements each value in an array of countdowns. You can modify the number of countdowns and add code to occur at a specific value for one of the countdowns.
Line 334: Line 331:
  
 
You can then have events (such as EVENT_PlayerDeath) change the value of a countdown to make the bot do something a set time after an event occurs.
 
You can then have events (such as EVENT_PlayerDeath) change the value of a countdown to make the bot do something a set time after an event occurs.
 +
 +
=== Tracking time not using countdown[n] ===
 +
 +
This is a solution to a common problem of determining the amount of time it takes for something to occur. Using basic math, we record a start-time B, and an end-time E, both in the unit of seconds, we calculate the time elapsed by E-B.
 +
 +
Lucky for us, Windows provides a function called GetTickCount() that is a measurement of time (milliseconds) that we can use for such cases.
 +
 +
So:
 +
<pre>
 +
int begin = GetTickCount();
 +
 +
// do some code here.
 +
 +
int end = GetTickCount();
 +
 
 +
int delta = (end - begin) / 1000;  // elapsed time converted to seconds from milliseconds
 +
</pre>
 +
 +
=== Obtaining the current time ===
 +
 +
''Requirements:'' Include <time.h>.
 +
 +
Use:
 +
<pre>
 +
char u[100];
 +
time_t t=time(NULL);
 +
tm *tmp = localtime(&t);
 +
strftime(u,99,"%c",tmp);
 +
sendPublic("Current date and time: " + u);
 +
</pre>
  
 
==Writing Functions==
 
==Writing Functions==
Line 378: Line 405:
 
If you're not familiar with prototypes, notice it is similar to that in your spawn.cpp, but without the botInfo::, and a trailing ;.
 
If you're not familiar with prototypes, notice it is similar to that in your spawn.cpp, but without the botInfo::, and a trailing ;.
  
As a side-note, with C++ we are able to pass variables by reference. We'd modify our example function accordingly. If you are unfamiliar with this concept, I suggest you read up on some C++ resources.
+
===Function notes===
<pre>
 
        bool botInfo::closeto(Player *p, int& x, int& y, int& tolerance)  // note the &
 
</pre>
 
  
To use our original example we'd implement it accordingly:
+
Remember that you can pass variables [http://www.cplusplus.com/doc/tutorial/tut2-3.html by reference]. If variables are passed by reference, any changes a function makes to the variables will remain after the function returns.
<pre>
 
        bool is_close = closeto(p,x,y,tolerance);  // if x is changed in closeto() its saved here
 
</pre>
 
  
From time-to-time you will need to pass an array to a function. An example illustrating this is:
+
From time to time you will need to pass an array to a function. An example illustrating this is:
 
<pre>
 
<pre>
 
int freqs[5]; // declare our example data.
 
int freqs[5]; // declare our example data.
Line 399: Line 420:
 
</pre>
 
</pre>
  
==Cycling players- How to search through the players in the arena==
+
==Cycling through players==
<p>With MervBot all player related data is stored in a linked-list. To search through the players in the arena, use the following example:
+
 
 +
MERVBot stores player-related data in a linked list. A linked list is a datatype that stores its data in a series of structures linked to each other, hence the name.
 +
 
 +
To search through the players in the arena, just start at the first link, then continue through all the following links until you reach the end:
 
<pre>
 
<pre>
  _listnode <Player> *parse = playerlist->head;
+
_listnode <Player> *parse = playerlist->head; //set to first link of the player linked list
  
  while (parse)
+
while (parse) //parse will be NULL when we reach the last link
    {
+
{
        Player *p = parse->item;
+
Player *p = parse->item; //item is the actual data stored in the link
  
        // do functionality here
+
// do functionality here
        // Example 1: sendPrivate(p,"*watchdamage"); // turns on all pilot's watchdamage
+
// Example 1: sendPrivate(p,"*watchdamage"); // turns on all pilot's watchdamage
        // Example 2: if (p->safety != 0) sendPrivate(p,"*spec"); // spec all pilots in safe zone
+
// Example 2: if (p->safety != 0) sendPrivate(p,"*spec"); // spec all pilots in safe zone
  
        parse = parse->next
+
parse = parse->next; //set parse to the next link
    }
+
}
</pre></p>
+
</pre>
<p>As an example of use, assuming our bot has smod+ privilages, the following code will set all non-spectator players to a specific ship. First begin by adding the following function prototype to the spawn.h in the botInfo class.
+
 
 +
For example, assuming our bot has smod+ privilages, the following code will set all non-spectator players to a specific ship. First begin by adding the following function prototype to the spawn.h in the botInfo class:
 
<pre>
 
<pre>
  void handleCmdSetShip(BYTE ship);
+
  void handleCmdSetShip(enum Ship_Types ship);
 
</pre>
 
</pre>
  
 
In spawn.cpp add:
 
In spawn.cpp add:
 
 
<pre>
 
<pre>
void botInfo::handleCmdSetShip(BYTE ship)
+
void botInfo::handleCmdSetShip(enum Ship_Types ship)
 
{
 
{
  // This is NOT an efficient piece of source code by ANY means.
+
//Note that the parameter ship is of the Ship_Types enum,
  // Note, a suggestion I would recommend is to add validation
+
//so its value is hopefully restricted to the proper types.
  //  to the variable ship, keeping it in the range of 0 to 9,
 
  //  where 0 - Warbird, and 9 - Spectator.
 
  
 
_listnode <Player> *parse = playerlist->head;
 
_listnode <Player> *parse = playerlist->head;
Line 434: Line 456:
 
{
 
{
 
Player *p = parse->item;
 
Player *p = parse->item;
 +
 
if ( p->ship != ship && p->ship != SHIP_Spectator )
 
if ( p->ship != ship && p->ship != SHIP_Spectator )
{
+
sendPrivate(p, "*setship " + (String)ship);
sendPrivate(p, 0x00, "*setship " + (String)ship);
+
 
}
 
 
parse = parse->next;
 
parse = parse->next;
 
}
 
}
Line 443: Line 465:
 
</pre>
 
</pre>
  
To use:
+
To use, just call the function with the appropriate Ship_Type from the enum in clientprot.h:
 
 
 
<pre>
 
<pre>
  handleCmdSetShip(SHIP_Warbird); //SHIP constants are in clientprot.h
+
  handleCmdSetShip(SHIP_Warbird);
 
</pre>
 
</pre>
</p>
 
 
==Checking if pilot is in a safe zone==
 
<p>MervBot keeps track of whether a player is in safe or not by accessing a member of the Player class.<br />
 
* if (p->safety != 0)  // pilot is in a safe zone<br />
 
* if (p->safety == 0)  // pilot is NOT in a safe zone<br /></p>
 
<p>An example goes as follows, in spawn.cpp:
 
<pre>
 
...
 
EVENT_PlayerMove:
 
{
 
  Player *p = (Player*)event.p[0];
 
  
  if ( p->safety ) // player is in safe zone.
+
==Random numbers==
    {
 
        // do something.
 
    }
 
  
  if ( !p->safety ) // player NOT in safe zone.
+
===Generating a random number===
    {
 
        // do something.
 
    }
 
}
 
...
 
</pre>
 
</p>
 
  
==Random number==
+
To use this method, these two includes must be used:
<p>To use these examples, two required include statements must be used:
 
 
<pre>
 
<pre>
 
#include "time.h"    //provides time() function.
 
#include "time.h"    //provides time() function.
 
#include "stdlib.h"  //provides srand() and rand() functions.
 
#include "stdlib.h"  //provides srand() and rand() functions.
 
</pre>
 
</pre>
</p>
 
  
<p>'''Example 1''': Generate a completely random number.</p>
 
 
Use:
 
Use:
 
<pre>
 
<pre>
Line 495: Line 491:
 
</pre>
 
</pre>
  
<p>'''Example 2''': Pick a random pilot.</p>
+
===Picking a random pilot===
<p>'''Note:''' A required user-defined function, getInGame(), must be created and declared for this example to work.</p>
+
 
 +
''Note:'' A required user-defined function, getInGame(), must be created for this example to work.
  
 
<pre>
 
<pre>
  int temp = GetTickCount() % getInGame();  // getInGame() = how many pilots in arena
+
int temp = GetTickCount() % getInGame();  // getInGame() = how many pilots in arena
  
  Player *rabbit = NULL;
+
Player *rabbit = NULL;
  
    _listnode <Player> *parse = playerlist->head;
+
_listnode <Player> *parse = playerlist->head;
    while (parse)
+
while (parse)
    {
+
{
        Player *p = parse->item;
+
Player *p = parse->item;
  
        if (p->ship != SHIP_Spectator) // if player is not a spectator.
+
if (p->ship != SHIP_Spectator) // if player is not a spectator
        {
+
if ( !(--temp) ) // and if we've hit the randomly-selected pilot
          if ( !(--temp) ) // pointer-arithmetic. decrement temp, if its 0, this is the rnd pilot.
+
{
            {
+
rabbit = p;
              rabbit = p;
+
break;
              break;
+
}
            }
+
parse = parse->next;
        }
+
}
    parse = parse->next;
 
    }
 
 
</pre>
 
</pre>
  
==Tracking time not using countdown[n]==
+
==Storing data for pilots==
<P>This is a solution to a common problem of determining the amount of time it takes for something to occur. Using basic math, we record a start-time B, and an end-time E, both in the unit of seconds, we calculate the time elapsed by E-B.</p>
 
 
 
<p>Lucky for us, C++ provides a function called GetTickCount() that is a measurement of time (milliseconds) that we can use for such cases.</p>
 
<p>
 
So:
 
<pre>
 
  int begin = GetTickCount();
 
 
 
  // do some code here.
 
 
 
  int end  = GetTickCount();
 
 
 
  int delta = end - begin;  // the change of time in milliseconds
 
 
 
  int delta_seconds = delta / 1000; // the change of time in seconds.
 
</pre>
 
</p>
 
  
==Storing data for pilots==
+
There are several ways to store data for pilots (ie tracking flagtime or kills in a period of time). Note that these methods are all purely internal to the bot, and don't effect anything beyond the plugin in any way.
<p>There are several ways to store data for pilots (ie tracking flagtime or kills in a period of time)</p>
 
  
<p>1) get/setTag - use if you only want to track data until pilot leaves arena then its erased<br />
+
# Built-in get/setTag: Tracks data until player leaves the arena, then automatically deletes data.
built in tags track by an ID that is reset when pilot leaves/enters arena, so loses track of data once they leave arena</p>
+
# Modified perm get/setTag: Tracks data until bot leaves arena, then automatically deletes data. (Advantage: easier to sort by player)
 +
# Custom Structs: Tracks data until plugin deletes it. (Advantage: easier to sort by freqs)
  
<p>2) modified perm get/setTag - use if you want to track all pilots even if they leave (advantage - easier to sort by player)<br />also can track near unlimited amount of pilots</p>
+
''Note:'' 2 and 3 are similar in effect, mostly the difference is in how you are able to search through data you need to decide which method of storing data is best for each bot depending on what it does.
  
<p>3) structs - use to track all pilots even if they leave, (advantage -
+
===Built-in get/setTag method===
easier to sort by freqs), have to specify bound of players</p>
 
  
<p>note: 2 and 3 are similar in effect, mostly the difference is in how you are able to search through data you need to decide which method of storing data is best for each bot depending on what it does beware using modified perm get/setTag if bot is in an arena for long periods of time, data is not reset so the linkedlist could get huge</p>
+
Player tags simply tag a player with a number. Define the wanted values in '''spawn.h''' at the top:
<br />
 
<p>Initialize these following values in '''spawn.h''' at the very top:<br />
 
1.) '''Built in get/setTag method'''<br />
 
 
<pre>
 
<pre>
#define DMG_DEALT        0
+
#define DMG_DEALT        0
#define DMG_TAKEN        1
+
#define DMG_TAKEN        1
 
</pre>
 
</pre>
// in spawn.cpp initialize the values on arena-enter and player-enter<br />
+
 
 +
In spawn.cpp, initialize the values on ArenaEnter and PlayerEnter:
 
<pre>
 
<pre>
 
case EVENT_ArenaEnter:
 
case EVENT_ArenaEnter:
  {
+
{
  ...
+
// ...
  
    _listnode <Player> *parse = playerlist->head;
+
// do for all pilots in arena when bot enters
 +
_listnode <Player> *parse = playerlist->head;
 +
while (parse)
 +
{
 +
  Player *p = parse->item;  // get pilot
  
    while (parse) // do for all pilots in arena when bot enters
+
  set_tag(p, DMG_DEALT, 0); // initialize to 0
    {
+
  set_tag(p, DMG_TAKEN, 0);
      Player *p = parse->item;  // get pilot
+
  sendPrivate(p, "*watchdamage");  // optionally turn on player *watchdamage
  
      set_tag(p, DMG_DEALT, 0); // initialize to 0
+
  parse = parse->next;  // get next pilot
 
+
}
      set_tag(p, DMG_TAKEN, 0);
+
}
 
 
      sendPrivate(p,"*watchdamage");  // optionally turn on player *watchdamage
 
 
 
      parse = parse->next;  // get next pilot
 
    }
 
  }
 
 
</pre>
 
</pre>
 
<pre>
 
<pre>
 
case EVENT_PlayerEntering:
 
case EVENT_PlayerEntering:
  {
+
{
    set_tag(p, DMG_DEALT, 0); // initialize to 0
+
// ...
+
set_tag(p, DMG_DEALT, 0); // initialize to 0
    set_tag(p, DMG_TAKEN, 0);
+
set_tag(p, DMG_TAKEN, 0);
 
+
sendPrivate(p,"*watchdamage");
    sendPrivate(p,"*watchdamage");
+
}
  }
 
 
</pre>
 
</pre>
// then somewhere edit the tag values
+
 
 +
Then somewhere edit the tag values:
 
<pre>
 
<pre>
 
case EVENT_WatchDamage:
 
case EVENT_WatchDamage:
  {
+
{
      // sets tag for k (shooter) to be old value plus damage dealt currently
+
// sets tag for k (shooter) to be old value plus damage currently dealt
      set_tag(k, DMG_BOMB_DEALT, get_tag(k, DMG_BOMB_DEALT) + damage);
+
 
  }
+
int old_damage = get_tag(k, DMG_BOMB_DEALT);
 +
set_tag(k, DMG_BOMB_DEALT, old_damage + damage);
 +
}
 
</pre>
 
</pre>
The following demonstrates how to retrieve the tag values as a command in spawn.h.
+
 
 +
The following demonstrates how to retrieve the tag values as a command in command.cpp:
 
<pre>
 
<pre>
if (c->check("showstats")) {
+
if (c->check("showstats"))
            int temp = get_tag(p, DMG_TOTAL_DEALT);
+
{
 +
int temp = get_tag(p, DMG_TOTAL_DEALT);
  
            String s = "You've done ";
+
String s = "You've done ";
            s += temp;
+
s += temp;
            s += " damage so far!";
+
s += " damage so far!";
  
            sendPrivate(p,s);
+
sendPrivate(p,s);
 
  }
 
  }
 
</pre>
 
</pre>
Destroy the tags when the player leaves arena.
+
 
<pre>
+
===Modified permanent get/setTag method===
case EVENT_PlayerLeaving:
+
 
  {
+
This method is the same as get/setTag with some modifications to the tag code to retain them after the player leaves. Beware of using this method if bot is in an arena for long periods of time, linkedlist could get huge.
    killTags(p);
+
 
  }
 
</pre>
 
</p>
 
<p>2.) '''Modified permanent get/setTag method'''<br />
 
// same as get/setTag with some modifications to the tag code, then can use tags exactly as above
 
  
 
// spawn.h, add char name[20]; into struct PlayerTag
 
// spawn.h, add char name[20]; into struct PlayerTag
Line 625: Line 597:
 
struct PlayerTag
 
struct PlayerTag
 
{
 
{
    Player *p;
+
Player *p;
    char name[20];
+
char name[20];
    int index;
+
int index;
    int data;
+
int data;
 
};
 
};
 
</pre>
 
</pre>
 +
 
In spawn.cpp:
 
In spawn.cpp:
 
<pre>
 
<pre>
    case EVENT_PlayerLeaving:
+
case EVENT_PlayerLeaving:
    {
+
{
        Player *p = (Player*)event.p[0];
+
    Player *p = (Player*)event.p[0];
  
        // killTags(p);  // remove so tag not deleted on arena exit
+
    // killTags(p);  // remove so tag not deleted on arena exit
  
    ...
+
// ...
 
</pre>
 
</pre>
 +
 
Locate in spawn.cpp and modify accordingly:
 
Locate in spawn.cpp and modify accordingly:
 
<pre>
 
<pre>
    int botInfo::get_tag(Player *p, int index)
+
int botInfo::get_tag(Player *p, int index)
 +
{
 +
    _listnode <PlayerTag> *parse = taglist.head;
 +
    PlayerTag *tag;
 +
 
 +
    while (parse)
 
     {
 
     {
        _listnode <PlayerTag> *parse = taglist.head;
+
      tag = parse->item;
        PlayerTag *tag;
 
 
 
        while (parse)
 
        {
 
          tag = parse->item;
 
  
          // if (tag->p == p)
+
      // if (tag->p == p)
          if (strcmp(tag->name,p->name)==0)  // now tracking by player name, not ID
+
      if (strcmp(tag->name,p->name)==0)  // now tracking by player name, not pointer
          if (tag->index == index)
+
      if (tag->index == index)
            return tag->data;
+
        return tag->data;
  
          parse = parse->next;
+
      parse = parse->next;
        }
 
        return 0;
 
 
     }
 
     }
</pre>
+
     return 0;
<pre>
+
}
    void botInfo::set_tag(Player *p, int index, int data)
 
     {
 
        _listnode <PlayerTag> *parse = taglist.head;
 
        PlayerTag *tag;
 
  
        while (parse)
+
void botInfo::set_tag(Player *p, int index, int data)
        {
+
{
          tag = parse->item;
+
    _listnode <PlayerTag> *parse = taglist.head;
 +
    PlayerTag *tag;
  
          //if (tag->p == p)
+
    while (parse)
          if (strcmp(tag->name,p->name)==0) // now tracking by player name, not ID
+
    {
          if (tag->index == index)
+
      tag = parse->item;
          {
 
            tag->data = data;
 
            return;
 
          }
 
          parse = parse->next;
 
        }
 
  
        tag = new PlayerTag;
+
      //if (tag->p == p)
        // tag->p = p; // not tracking by ID anymore
+
      if (strcmp(tag->name,p->name)==0) // now tracking by player name, not pointer
        strncpy(tag->name, p->name, 20); // tracking by player name
+
      if (tag->index == index)
        tag->index = index;
+
      {
 
         tag->data = data;
 
         tag->data = data;
         taglist.append(tag);
+
         return;
 +
      }
 +
      parse = parse->next;
 
     }
 
     }
 +
 +
    tag = new PlayerTag;
 +
    // tag->p = p; // not tracking by pointer anymore
 +
    strncpy(tag->name, p->name, 20); // tracking by player name
 +
    tag->index = index;
 +
    tag->data = data;
 +
    taglist.append(tag);
 +
}
 
</pre>
 
</pre>
</p>
+
 
<p>3.) '''Using structs, implement in spawn.h:'''<br />
+
===Using structs===
 +
 
 +
In '''spawn.h''':
 
<pre>
 
<pre>
 
class botInfo
 
class botInfo
 
{
 
{
struct freqdata  
+
struct freqdata  
  {
+
{
  int kills;
+
int kills, deaths;
  int deaths;
+
};
  };
+
// ...
...
 
 
};
 
};
 
</pre>
 
</pre>
 +
 
To make use of this structure, implement accordingly:
 
To make use of this structure, implement accordingly:
<pre>freqdata freqs[100]; // 100 of those structs</pre>
+
<pre>
 +
freqdata freqs[100]; // 100 of those structs
 +
</pre>
 
Access the data in spawn.cpp using
 
Access the data in spawn.cpp using
<pre>freqs[56].kills = 1;</pre>
+
<pre>
</p>
+
freqs[56].kills = 1;
 +
</pre>
  
==Output of data/messages==
+
See CPlusPlus.com's [http://www.cplusplus.com/doc/tutorial/tut3-5.html Structures] tutorial for a more comprehensive guide. Note that, as shown on the bottom of the page, you can have structures within structures. Thus, for example, you could have a structure for each freq with a structure for each player nested within them.
<p>An example of using normal strings to output data/messages.</p>
 
<pre>
 
// does *arena X pilots left in game
 
// NOTE: variable temp needs to be defined with some value
 
  
String s = "*arena ";
+
==Input/Output to files==
      s += temp;
 
      s += " pilots left in the game.";
 
  
sendPublic(s);
+
For reading and/or writing to files with C++ you must have the required include statement as follows:
</pre>
 
Or,
 
 
<pre>
 
<pre>
//NOTE: this can be considered inefficient.
+
#include <fstream>
 
+
using namespace std;
sendPublic("*arena " + (String)temp + " pilots left in the game");
 
 
</pre>
 
</pre>
  
<p>An example using sprintf to align/space data, where output data will be in this approximate format.</p>
+
===File stream input===
<pre>
+
The following example will show you how to read a file, duel.ini, line by line.
// output data will be in this approximate format (not lined up perfectly because of html)
 
// --------------------------------------------------------------------------------------
 
// Squad: squadname      PTS    FPTS    K    D  DMG DEALT TAKEN  F  FK    FLT
 
// --------------------------------------------------------------------------------------
 
// PlayerA              10000      500  116  101      9999 99999  10 150 980:55
 
// PlayerB                500      200    7    5      9999 99999  5  3  0:04
 
  
char str[255];
+
<pre>#include "stdlib.h" // for atoi()</pre>
sendPublic("*arena--------------------------------------------------------------------------------");
 
  
sprintf(str, "*arena Squad: %-20s  PTS    FPTS  K  D  DMG DEALT  TAKEN  F  FK  FLT",
+
<pre>
        freqs[freq].freqname
+
  ifstream file("duel.ini");
        );
 
  
sendPublic(str);
+
  if (!file.good()) // if there was an error opening the file
 +
    sendPublic("*arena Error opening file for reading"); // or add your own error handler
 +
  else
 +
  {
 +
    char line[256];
  
sendPublic("*arena--------------------------------------------------------------------------------");
+
    // read in MaxBoxes=X
 +
    while (file.getline(line, 256))
 +
    {
 +
   
 +
      if (CMPSTART("MaxBoxes=", line)) //Does the line begin with MaxBoxes= ?
 +
      {
 +
        MAX_BOXES = atoi(&(line[9]));  //If so, read the value into an integer, using atoi.
 +
        break;
 +
      }
 +
    }
  
            // assuming existing freqs struct with data
+
    file.close();
            for (pilot=freqs[freq].playercount-1; pilot>=0; pilot--)
+
  }
            {
+
</pre>
                // on freq squad so print stats
 
                char outString[255];
 
  
                sprintf(outString, "*arena %-20s %12d %8d %3d %3d %10d %6d %2d %3d %3d:%02d",
+
===File stream output===
                      freqs[freq].pilots[pilot].name,
+
The following code example will demonstrate how to append to a file, duelleaguestat.inc.
                      freqs[freq].pilots[pilot].points,
+
<pre>
                      freqs[freq].pilots[pilot].flagpoints,
+
ofstream file("duelleaguestat.inc", ios::app);   // app = put all data at end of file
                      freqs[freq].pilots[pilot].kills,
 
                      freqs[freq].pilots[pilot].deaths,
 
                      freqs[freq].pilots[pilot].dmgdealt,
 
                      freqs[freq].pilots[pilot].dmgtaken,
 
                      freqs[freq].pilots[pilot].flags,
 
                      freqs[freq].pilots[pilot].flagkills,
 
                      freqs[freq].pilots[pilot].flagtime /60,
 
                      freqs[freq].pilots[pilot].flagtime %60
 
                      );
 
               
 
                sendPublic(outString);
 
            }
 
  
            // Notes: sprintf format = sprintf(output char string, spacing, variables)
+
if (!file.good()) // if there was an error opening the file
            // Notes: s = chars, d = integer, - = left align, right align default
+
  sendPublic("*arena Error opening file.");
            // Notes: doing %02d = put 0 in front if not 2 digits, %3d:%02d makes 0:04 format
+
else
 +
{
 +
  file << squad1<< endl;  // squad1 = char[20]
 +
  file << " vs "<< endl;
 +
  file << squad2<< endl;  // squad2 = char[20]
 +
  file.close();
 +
}
 
</pre>
 
</pre>
  
==Input/Output to files==
+
Similarly, you are able to write an output of a String to a file:
         
+
<pre>
<blockquote>Input to file<br />
+
// key is converting String to (char*) to file write
<blockquote>// example reading from duel.ini looking for line that starts with MaxBoxes= then taking the next char as value to store as <br />
+
String str = freqs[freq].slotname[slot];
// MAX_BOXES (ie duel.ini = MaxBoxes=5)<br />
+
str += ", Repels: " + (String)(int) t->repel;
#include &lt;fstream&gt;<br />
+
file << endl;
using namespace std;<br />
+
file << (char*) str;
              <br />
+
</pre>
ifstream file(&quot;duel.ini&quot;);<br />
 
&nbsp;&nbsp;&nbsp; char line[256];<br />
 
              <br />
 
&nbsp;&nbsp;&nbsp; // read in MaxBoxes=X<br />
 
&nbsp;&nbsp;&nbsp; while (file.getline(line, 256))<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
if (CMPSTART(&quot;MaxBoxes=&quot;, line))<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; MAX_BOXES = atoi(&amp;(line[9]));<br />
 
&nbsp;&nbsp;&nbsp; break;<br />
 
}<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
              </blockquote>
 
Output to file<br />                                                   
 
                                                                       
 
                                                                       
 
                                    <blockquote>// normal char output<br />
 
                                                                       
 
                                                                       
 
      <blockquote>#include &lt;fstream&gt;<br />
 
  
 +
===Input with GetPrivateProfileString===
  
using namespace std;<br />
+
GetPrivateProfileString(), a function provided by Windows for reading INI files, will automatically find an INI key (like "MaxBoxes=") in a file for you. See the [http://msdn.microsoft.com/library MSDN Library] for help on this function. This next example will show how to read input using GetPrivateProfileString() based on the rampage plugin.
  
                <br />
+
The file format for rampage.ini is like this:
 +
<pre>
 +
7=is on a killing spree! (6:0)
 +
10=is opening a can of booya! (9:0)
 +
</pre>
  
ofstream file(&quot;duelleaguestat.inc&quot;, ios::app); &nbsp; // app = put all data at end of file<br />
+
In '''rampageini.cpp''':
 +
<pre>
 +
#include "rampageini.h"
 +
#define WIN32_LEAN_AND_MEAN
 +
#include <windows.h>
  
                <br />
+
#define NUM_RANKS 10
 +
#define BUFFER_LEN 256
  
file &lt;&lt; squad1&lt;&lt; endl; &nbsp;// squad1 = char[20]<br />
+
struct RampageSettings
 +
{
 +
char quotes[NUM_RANKS][BUFFER_LEN];
 +
};
 +
 +
void LoadSettings(RampageSettings &setts);
  
file &lt;&lt; &quot; vs &quot;&lt;&lt; endl;<br />
+
static char path[BUFFER_LEN];
  
file &lt;&lt; squad2&lt;&lt; endl; &nbsp;// squad2 = char[20]<br />     
+
char *rank_type[NUM_RANKS] = { "7", "10" };
                                                                       
 
                                                                       
 
  </blockquote>
 
                                                                       
 
                                                      // how to output String's
 
to file (key is converting String to (char*) to file write)<br />       
 
                                                                       
 
                                                                       
 
<blockquote>
 
String str = freqs[freq].slotname[slot];<br />
 
  
str += &quot;, Repels: &quot; + (String)(int) t-&gt;repel;<br />
+
void LoadSettings(RampageSettings &setts)
 +
{
 +
GetCurrentDirectory(BUFFER_LEN - 64, path);
 +
strcat(path, "\rampage.ini");
  
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
+
for (int i = 0; i < NUM_RANKS; ++i)
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
+
{
&nbsp;&nbsp;&nbsp; <br />
+
GetPrivateProfileString("Comments", rank_type[i], "-ERROR-",
 
+
setts.quotes[i], BUFFER_LEN, path);
outf &lt;&lt; endl;<br />
+
}
 
+
}
outf &lt;&lt; (char*) str;<br />                                       
 
                                                                       
 
                                          </blockquote>
 
 
 
                                                        <br />
 
// date and time stamp<br />                                           
 
                                                                       
 
                                      <blockquote>#include &quot;time.h&quot;<br />
 
                                                                                      <br />
 
char u[100];<br />
 
time_t t=time(NULL);<br />
 
tm *tmp = localtime(&amp;t);<br />
 
strftime(u,99,&quot;%c&quot;,tmp);<br />
 
sendPublic(&quot;Date and time: &quot; + (String) u);<br />
 
 
 
                                                                                     
 
                </blockquote>
 
&nbsp;</blockquote>
 
</blockquote>
 
&nbsp;<blockquote>Example reading input from file using &quot;GetPrivateProfileString&quot; (from rampage plugin)<br />
 
                                                  <blockquote>format of rampage.ini<br />
 
        <blockquote>7=is on a killing spree! (6:0)<br />10=is opening a can of whoop-ass! (9:0)<br />
 
                                                      </blockquote>
 
read input<br />
 
                                                      <blockquote>rampageini.h<br />
 
                                                        <blockquote>#pragma once<br />
 
 
 
#ifndef RAMPAGEINI_H<br />
 
#define RAMPAGEINI_H<br />
 
                                                          <br />
 
#define NUM_RANKS 10<br />
 
#define BUFFER_LEN 256<br />
 
                                                          <br />
 
struct RampageSettings<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; char quotes[NUM_RANKS][BUFFER_LEN];<br />
 
};<br />
 
                                                          <br />
 
void LoadSettings(RampageSettings &amp;setts);<br />
 
                                                          <br />
 
#endif&nbsp;&nbsp;&nbsp; // RAMPAGEINI_H<br />
 
                                                          </blockquote>
 
rampageini.cpp<br />
 
                                                          <blockquote>#include &quot;rampageini.h&quot;<br />
 
static char buffer[BUFFER_LEN];<br />
 
static char path[BUFFER_LEN];<br />
 
#include &quot;../algorithms.h&quot;<br />
 
#define WIN32_LEAN_AND_MEAN<br />
 
#include &lt;windows.h&gt;<br />                                       
 
                                                                       
 
                <br />
 
char *rank_type[10] = {<br />
 
&nbsp;&nbsp; &nbsp;&quot;7&quot;,<br />
 
&nbsp;&nbsp; &nbsp;&quot;10&quot;,<br />
 
};<br />                                                               
 
                                                                <br />
 
void LoadSettings(RampageSettings &amp;setts)<br />
 
{<br />
 
&nbsp;&nbsp; &nbsp;GetCurrentDirectory(BUFFER_LEN - 64, path);<br />
 
&nbsp;&nbsp; &nbsp;strcat(path, &quot;\rampage.ini&quot;);<br />                 
 
                                                                       
 
                                      <br />
 
&nbsp;&nbsp; &nbsp;for (int i = 0; i &lt; NUM_RANKS; ++i)<br />
 
&nbsp;&nbsp; &nbsp;{<br />
 
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; GetPrivateProfileString(&quot;Comments&quot;,
 
rank_type[i], &quot;-ERROR-&quot;, setts.quotes[i], BUFFER_LEN, path);<br />
 
&nbsp;&nbsp; &nbsp;}<br />
 
}<br />                                                                 
 
                                                                </blockquote>
 
&nbsp;</blockquote>&nbsp;</blockquote>&nbsp;</blockquote>
 
 
 
==Simple programming commands==
 
<p>The following are some brief C++ tid-bits.</p>
 
<p>Simple Commands:
 
* declare / initialize variables
 
** Example: int r = 10; int q; q = 5;
 
* if ( condition ) { }
 
** Example: if (a > b) { a++; }
 
* if ( condition ) { } else { }
 
** Example: if (b <= 0) { b--; } else { a++; }
 
* while ( condition ) { }
 
** Example: while (b > 0) { b--; }
 
* for ( initialize ; condition ; increment/decrement ) { }
 
** Example: for (a=1; a < 10; a++) { a = a + b; }</p>                                                                       
 
<p>Arrays:
 
* single dimension:
 
** int teams[100];  // create 100 hundred teams 0-99
 
** teams[50] = 1;   
 
* multi-dimensional:
 
** int teams[100][50];  // multidimensional array
 
** teams[99][49] = 2;
 
* variable (or dynamic) size :
 
** String *list = new String[amount+1]; // string array with size amount (variable) + 1;
 
** list[amount-1] = "hi";
 
** '''Note''': Remember to '''delete []list''';
 
</p>
 
<p>Structures
 
<pre>
 
struct name
 
  {
 
  ... // structure variables and functions.
 
  }; // NOTE: trailing ;
 
 
</pre>
 
</pre>
Example:
 
<pre>
 
struct freqdata { int kills; int deaths; };
 
 
freqdata freqs[100]; // 100 of those structs
 
  
freqs[56].kills = 1;  // access struct
+
==Player data==
</pre>
 
</p>
 
<p>Switches:
 
<pre>
 
switch (variable)           
 
    {                           
 
        case n:             
 
        { }                     
 
        break;                       
 
                                         
 
        case m:           
 
        {  }                         
 
        break;           
 
       
 
        default:
 
        break;
 
    }
 
</pre>
 
Example:
 
<pre>
 
switch (p->ship)
 
  {
 
    case SHIP_Warbird:
 
    {
 
      sendPrivate(p, "You're in a warbird");
 
    }
 
    break;
 
  
    default:
+
As stated earlier in the tutorial, MervBot stores useful player data internally as Player objects, see player.h for implementation details.
    break;
 
  }
 
</pre>
 
</p>
 
  
==Useful Player data==
+
* p->name = player name stored as char[20] (''Note:'' SubSpace protocol allows for usernames to be 19+ in length, do not rely on this for player-name comparisions.)
<p>As stated earlier in the tutorial, MervBot stores useful player data internally as Player objects, see player.h for implementation details.</p>
 
<p>
 
* p->name = player name stored as char[20]
 
** '''Note:''' SubSpace protocol allows for usernames to be 19+ in length, do not rely on this for player-name comparisions.
 
 
* p->squad = player squad stored as char[20]
 
* p->squad = player squad stored as char[20]
 
* p->ship = ship (0-9) enumerated as SHIP_Warbird, SHIP_Spectator, etc..
 
* p->ship = ship (0-9) enumerated as SHIP_Warbird, SHIP_Spectator, etc..
* p->safety = if ship is in safety zone (boolean)
+
* p->safety = whether ship is in safety zone (boolean)
 
* p->bounty = player bounty
 
* p->bounty = player bounty
 
* p->energy = player energy (have bot with *energy on to get accurate readings)
 
* p->energy = player energy (have bot with *energy on to get accurate readings)
Line 1,000: Line 812:
 
* p->score.wins = player kills from f2
 
* p->score.wins = player kills from f2
 
* p->score.losses = player deaths from f2
 
* p->score.losses = player deaths from f2
</p>
 
  
==Bot built in functions==
+
Just access the respective member of the Player class to check the player's property.
<blockquote>// useful MervBot commands to control what the bot is doing<br />
 
                                                                                                                            <br />
 
// player.cpp<br />
 
Player::move(Sint32 x, Sint32 y) &nbsp;// example &nbsp;me-&gt;move(512,512) - bot moves to coord 512 512<br />
 
Player::clone(Player *p) // example &nbsp;me-&gt;clone(p) <br />
 
                                                                                                                            <br />
 
// dllcore.cpp (descriptions of functions in dllcore.h)<br />
 
                                                                                                                            <br />
 
BotEvent makeEcho&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (char *m);<br />
 
BotEvent makeSay&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (int t, int s, int i, char *m);<br />
 
                                                                                                                            <br />
 
BotEvent makeShip&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (int s);<br />
 
BotEvent makeTeam&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (int t);<br />
 
BotEvent makeGrabFlag&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (int f);<br />
 
BotEvent makeSendPosition&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (bool reliable);<br />
 
BotEvent makeDropFlags&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ();<br />
 
                                                                                                                            <br />
 
BotEvent makeDeath&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (Player *p);<br />
 
BotEvent makeAttach&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (Player *p);<br />
 
BotEvent makeDetach&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ();<br />
 
BotEvent makeFollowing&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (bool f);<br />
 
BotEvent makeFlying&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (bool f);<br />
 
BotEvent makeBanner&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (BYTE *b);<br />
 
BotEvent makeDropBrick&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ();<br />
 
BotEvent makeFireWeapon&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (void *weapon_info);<br />
 
                                                                                                                            <br />
 
BotEvent makeToggleObjects&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (Uint16 player, Uint16 *objects, int num_objects);<br />
 
                                                                                                                            <br />
 
BotEvent makeSpawnBot&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
(char *name, char *password, char *staff, char *arena);<br />
 
BotEvent makeChangeArena&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (char *name);<br />
 
BotEvent makeChangeSettings&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; (_linkedlist &lt;String&gt; *settings);<br />
 
                                                                                                                            <br />
 
// example how to use them:<br />
 
                                                                                                                            <br />
 
tell(makeFollowing(false));<br />
 
                                                      <br />
 
// look in Commands.txt , command.cpp (core), or /!help to bot to see all bot external commands (example /!go &lt;arena&gt;)<br />
 
                                                      </blockquote>
 
  
==Example Code==
+
For example, in spawn.cpp, to check whether a player is in a safety zone:
<!-- Example Code Sections have specific    -->
 
<!-- internal commenting system That        -->
 
<!-- go like this:                          -->
 
<!-- .equals.x4 <TITLE> .equals.x4          -->
 
<!-- <!-- EXAMPLE L: BEGIN -->              -->
 
<!-- CODE / ETC HERE.                        -->
 
<!-- <br /><!-- EXAMPLE L: END  -->        -->
 
==== No antiwarp in center of the map ====
 
<!-- EXAMPLE A: BEGIN -->
 
<p>'''Example A''': No antiwarp in center of map. Warn the player, and revoke the prize.</p>
 
<p>In order for this code to work correctly, the bot must have smod+ privilages.</p>
 
<p>Lets first implement two functions which we will need to accomplish this task:
 
 
<pre>
 
<pre>
bool closeto(Player *p, int x, int y, int tolerance) {
+
EVENT_PlayerMove:
    return (abs((p->tile.x) - x) < tolerance) && (abs((p->tile.y) - y) < tolerance); }
 
 
 
inline int abs(int n) {
 
    if (n < 0)    return -n;
 
    else        return n; }
 
</pre>
 
 
 
We should define the radius of antiwarp checking, this can be done several ways, for sake of simplicity, here is a quick-plop-in for '''spawn.h''':
 
<pre>
 
class botInfo
 
{
 
bool CONNECTION_DENIED;
 
        ...
 
        // Put bot data here
 
        int radius;
 
 
 
public:
 
botInfo(CALL_HANDLE given)
 
{
 
        ...
 
        // Put initial values here
 
        radius = 35;
 
        ...
 
</pre>
 
Locate and add into '''spawn.cpp''' accordingly:
 
<pre>
 
case EVENT_PlayerMove:
 
 
{
 
{
 
    Player *p = (Player*)event.p[0];
 
 
    // no anti in center
 
    if ((p->ship != SHIP_Spectator) && (p->awarp)) {
 
        if (closeto(p, 512, 512, radius)){
 
            sendPrivate(p, "*prize #-20");
 
            sendPrivate(p, "*warn Antiwarp is not allowed in center.");
 
        }
 
    }
 
  ...
 
</pre>
 
Just as a word of caution, players may at times be flooded with *prize #-20, and *warn statements under certain conditions.
 
</p>
 
<br /><!-- EXAMPLE A: END  -->
 
==== Setting freq size depending on how many pilots in game ====
 
<!-- EXAMPLE B: BEGIN -->
 
<p>'''Example B''': Setting freq size depending on how many pilots in game.</p>
 
<p>In order for this code to work correctly, the bot must have Sysop or Arena-Owner privilages.</p>
 
<p>In '''spawn.cpp''' (Note: this source code has assumptions, please review comments before implementing.):
 
<pre>
 
case EVENT_Tick:
 
  {
 
  ...
 
 
  // NOTE: assuming countdown[0] initialized to > 0  in spawn.h, freqchange=0;
 
  if (countdown[0] == 0)
 
  {
 
        _listnode <Player> *parse = playerlist->head;
 
        int count = 0;
 
 
        while (parse)
 
        {
 
            Player *p = parse->item;
 
 
            if (p->ship != SHIP_Spectator)
 
            ++count;
 
 
            parse = parse->next;
 
        }
 
 
        if ((count > 24) && (freqchange != 4))
 
        {
 
            sendPublic("?set team:maxperteam:4"); //Sysop command to modify arena config.
 
            String s;
 
            s = "Max freq size 4  (";
 
            s += count;
 
            s += " pilots in game)";
 
            sendPublic(s);
 
            freqchange = 4;
 
        }
 
 
        if ((count < 25) && (count > 14) && (freqchange != 3))
 
        {
 
            sendPublic("?set team:maxperteam:3"); //Sysop command to modify arena config.
 
            String s;
 
            s = "Max freq size 3  (";
 
            s += count;
 
            s += " pilots in game)";
 
            sendPublic(s);
 
            freqchange = 3;
 
        }
 
 
    countdown[0] = 120; // reset timer to 120 seconds.
 
    }
 
</pre></p><br /><!-- EXAMPLE B: END  -->
 
==== Tracking kills and announcing when pilot gets 10 kills in a row without dying ====
 
<!-- EXAMPLE C: BEGIN -->
 
<p>'''Example C''': Tracking kills and announcing when pilot gets 10 kills in a row without
 
dying.</p>
 
<p>In order for this code to work correctly, the bot must have smod+ privilages.</p>
 
<p>Locate EVENT_PlayerDeath in '''spawn.cpp''' (see note.):
 
<pre>
 
EVENT_PlayerDeath:
 
{
 
    Player *p = (Player*)event.p[0],
 
          *k = (Player*)event.p[1];
 
    Uint16 bounty = (Uint16)(Uint32)event.p[2];
 
    Uint16 flags = (Uint16)event.p[3];
 
 
    // NOTE: assuming tags are setup (see storing data section).
 
    set_tag(p, KILLS, 0);  // pilot died, reset to 0 kills in a row
 
    set_tag(k, KILLS, get_tag(k, KILLS) + 1);  // pilot killed someone, increment kills in a row by 1
 
 
    if (get_tag(k, KILLS) == 10)
 
        sendPublic("*arena (String) k->name + " has gotten 10 kills.");
 
...
 
  }
 
</pre></p>
 
<br /><!-- EXAMPLE C: END  -->
 
==== Warp pilot to coord when they are in a certain region ====
 
<!-- EXAMPLE D: BEGIN -->
 
<p>'''Example D''': Warp pilot to coord when they are in a certain region.</p>
 
<p>In order for this code to work correctly, the bot must have smod+ privilages.</p>
 
<p>Lets first implement two functions which we will need to accomplish this task:
 
<pre>
 
bool closeto(Player *p, int x, int y, int tolerance) {
 
    return (abs((p->tile.x) - x) < tolerance) && (abs((p->tile.y) - y) < tolerance); }
 
 
inline int abs(int n) {
 
    if (n < 0)    return -n;
 
    else        return n; }
 
</pre>
 
</p>
 
<p>In '''spawn.cpp''', EVENT_PlayerMove:
 
<pre>
 
case EVENT_PlayerMove:
 
  {
 
 
   Player *p = (Player*)event.p[0];
 
   Player *p = (Player*)event.p[0];
  
   if (closeto(p, 509, 509, 2)) // if pilot within 2 of map coord 509,509
+
   if ( p->safety ) // player is in safe zone.
      {
+
    {
        sendPrivate(p, "*warpto 509 504");  // warp to coord 509,504
+
        // do something.
      }
+
    }
  ...
 
</pre></p>
 
<br /><!-- EXAMPLE D: END  -->
 
==== Structures within structures ====
 
<!-- EXAMPLE E: START  -->
 
<p>'''Example E''': Structures within structures (spawn.h botinfo).</p>
 
<p>Implement the following in the spawn.h:
 
<pre>
 
struct playerstats
 
{
 
  char name[20];
 
  
   int kills;
+
   if ( !p->safety ) // player NOT in safe zone.
  int deaths;
+
    {
  Uint16 points;
+
        // do something.
  Uint16 flagpoints;
+
    }
  int flagtime;
+
}
  int cflagtime;
 
  int flags;
 
  int flagkills;
 
 
 
  int dmgdealt;
 
  int dmgtaken;
 
};
 
 
 
struct freqdata
 
{
 
  playerstats pilots[100];
 
 
 
  int freqpoints;
 
  char freqname[20];
 
  int freqflagpoints;
 
  Uint16 freqteam;
 
  int freqflagtime;
 
 
 
  int flags;
 
  int kills;
 
  int deaths;
 
  int flagkills;
 
 
 
  int dmgdealt;
 
  int dmgtaken;
 
 
 
  int playercount;
 
};
 
 
 
//... (large jump down in spawn.h)
 
 
 
// Put bot data here <- locate and add after:
 
freqdata freqs[100];
 
 
 
//... <- perhaps some other variables.. scroll down past the next }
 
}
 
 
void Clear(); // A user-defined function. Add this.
 
 
 
void clear_objects(); //already exists, provided by Catid.
 
void object_target(Player *p); //already exists, provided by Catid.
 
 
 
// ...  spawn.h continues.
 
 
</pre>
 
</pre>
</p>
 
<p>Implement the following in '''spawn.cpp''':
 
<pre>
 
void botInfo::Clear()
 
{
 
  // initialize/clear struct data
 
  for (int n=99; n>=0; n--)
 
  {
 
    freqs[n].freqteam=-1;
 
    freqs[n].freqpoints=0;
 
    freqs[n].freqflagpoints=0;
 
    freqs[n].playercount=0;
 
    freqs[n].flags=0;
 
    freqs[n].kills=0;
 
    freqs[n].deaths=0;
 
    freqs[n].freqflagtime=0;
 
    freqs[n].flagkills=0;
 
    freqs[n].dmgdealt=0;
 
    freqs[n].dmgtaken=0;
 
  
    for (int m = 99; m>=0; m--)
+
=== Obtaining a player pointer using name comparison ===
    {
 
      freqs[n].pilots[m].deaths=0;
 
      freqs[n].pilots[m].kills=0;
 
      freqs[n].pilots[m].points=0;
 
      freqs[n].pilots[m].flagpoints=0;
 
      freqs[n].pilots[m].flagtime=0;
 
      freqs[n].pilots[m].cflagtime=0;
 
      freqs[n].pilots[m].flags=0;
 
      freqs[n].pilots[m].flagkills=0;
 
      freqs[n].pilots[m].dmgdealt=0;
 
      freqs[n].pilots[m].dmgtaken=0;
 
    }
 
}
 
}
 
</pre></p>
 
<p>                                                               
 
To access private data:
 
<pre>freqs[1].pilots[2].kills++;</pre>
 
-OR-
 
<pre>int freq = p->team;
 
freqs[freq].deaths++;</pre></p>
 
<br /><!-- EXAMPLE E: END  -->
 
==== Tracking flag data ====
 
<!-- EXAMPLE F: START  -->
 
<p>'''Example F''': Tracking flag data.</p>
 
f) Tracking flag data<br />                                         
 
                                                                       
 
<blockquote>Example GetPilot() function &nbsp;(using structs from example e)<br /><blockquote>bool botInfo::GetPilot(Player *p)<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // get a pilots freq/pilot id from struct<br />
 
&nbsp;&nbsp;&nbsp; for (freq=freqcount-1; freq&gt;=0; freq--)<br />
 
if (p-&gt;team == freqs[freq].freqteam)<br />
 
&nbsp;&nbsp;&nbsp; for (pilot = freqs[freq].playercount-1; pilot&gt;=0; pilot--)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
if (strcmp(p-&gt;name,freqs[freq].pilots[pilot].name)==0)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return true;<br /><br />
 
&nbsp;&nbsp;&nbsp; return false;<br />
 
}<br />
 
</blockquote></blockquote>
 
&nbsp;
 
<blockquote>Example way to track flag data using above struct/functions<br /><blockquote>
 
case EVENT_FlagGrab:<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; if (GetPilot(p)) &nbsp;// function<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; freqs[freq].pilots[pilot].flags++;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; freqs[freq].flags++;<br /><br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
if (freqs[freq].pilots[pilot].flags &lt; 2) // didnt have a flag before,
 
first flag<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; freqs[freq].pilots[pilot].cflagtime = GetTickCount();
 
&nbsp;// time stamp when picked up flag<br />
 
&nbsp;&nbsp;&nbsp; }<br /></blockquote></blockquote>
 
&nbsp;<blockquote>Example way to track flag data using built in get/set tag (from catid flagbot)<br />
 
                                                                       
 
                                                                       
 
                                              <blockquote>
 
case EVENT_FlagGrab:<br />
 
  
 +
Since Player pointers are internal to MERVBot, it is necessary to find a way of obtaining a Player pointer from the identifying information given by the game. One of the simpler ways is just to compare the names after converting to lowercase.
  
{<br />
+
''Note'': Using pilot names as vital comparisions should be used with caution. See [http://cypherjf.sscentral.com/articles/bots-as-clients/ Bot-Issues] by CypherJF.
  
 
&nbsp;&nbsp;&nbsp; set_tag(p, TAG_STAT_FS, get_tag(p, TAG_STAT_FS) + 1);<br />
 
 
 
&nbsp;&nbsp;&nbsp; set_tag(p, TAG_FLAGTIMER, GetTickCount());<br />
 
 
 
}<br />                           
 
                                                                       
 
                                                                       
 
                    </blockquote>&nbsp;</blockquote>&nbsp;<blockquote>Get current flag times using struct format<br />
 
                                                                       
 
                                                                       
 
                                                  <blockquote>void botInfo::SetFlagTimes()<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // set current flagtime for pilots/freqs<br />
 
&nbsp;&nbsp;&nbsp; _listnode &lt;Player&gt; *parse = playerlist-&gt;head;<br />
 
&nbsp;&nbsp;&nbsp; <br />
 
&nbsp;&nbsp;&nbsp; while (parse)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
Player *p = parse-&gt;item;<br /> 
 
                                                                       
 
                                                                       
 
                                                  <br />
 
if (GetPilot(p))<br />
 
&nbsp;&nbsp;&nbsp; if (freqs[freq].pilots[pilot].flags &gt; 0)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (PilotOnSquad(p))<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; freqs[freq].freqflagtime += (GetTickCount() - freqs[freq].pilots[pilot].cflagtime)/1000;<br />
 
                                                                       
 
                                                                       
 
                                                    <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
freqs[freq].pilots[pilot].flagtime += (GetTickCount() - freqs[freq].pilots[pilot].cflagtime)/1000;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
freqs[freq].pilots[pilot].cflagtime = GetTickCount();<br />
 
&nbsp;&nbsp;&nbsp; }<br />       
 
                                                                       
 
                                                                       
 
                                          <br />
 
parse = parse-&gt;next;<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
}<br />                                                                 
 
                                                                       
 
                                                          </blockquote>
 
// side note: &nbsp;case EVENT_FlagDrop: {} gets called anytime theres a teamkill<br />
 
                                                                       
 
                                                                       
 
                                                  </blockquote>                                                               
 
<br /><!-- EXAMPLE F: END  -->
 
==== Example way to do simple /!spam feature (allowed 1x/60s) ====
 
<!-- EXAMPLE G: START  -->
 
<p>'''Example G''': Example way to do simple /!spam feature (allowed 1x/60s).</p>
 
g) Example way to do simple /!spam feature (allowed 1x/60s)<br />   
 
                                                                       
 
                                                                       
 
                                            <blockquote>declare and initialize variables in spawn.h<br />
 
                                                                       
 
                                                                       
 
                                                    <blockquote>class botInfo<br />
 
{<br />
 
bool spamready;<br />
 
int SPAM_TIME;<br />                                                   
 
                                                                       
 
                                                                       
 
<br />
 
public:<br />
 
&nbsp;&nbsp;&nbsp; botInfo(CALL_HANDLE given)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; spamready = true;<br />
 
&nbsp;&nbsp;&nbsp; SPAM_TIME = 60;<br />                               
 
                                                                       
 
                                                                       
 
                    </blockquote>
 
spawn.cpp - mark as spamready=true when 60 seconds up<br />         
 
                                                                       
 
                                                                       
 
                                          <blockquote>case EVENT_Tick:<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; if (countdown[0] == 1) &nbsp;{<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; spamready = true; &nbsp;// ready to spam again<br />
 
&nbsp;&nbsp;&nbsp; }<br />       
 
                                                                       
 
                                                                       
 
                                              </blockquote>           
 
                                                                       
 
                                                                       
 
                                            <br />
 
command.cpp - handle !spam command<br />                             
 
                                                                       
 
                                                                       
 
                          <blockquote>case OP_Player:<br />
 
{&nbsp;&nbsp;&nbsp; // Player-level commands<br />                     
 
                                                                       
 
                                                                       
 
                                  <br />
 
else if (c-&gt;check(&quot;spam&quot;))<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
// zone announcement &quot;Need pilots to duel in ?go arena -pilotname&quot;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (spamready == true)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String s;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; s += &quot;*zone Need pilots to duel in ?go &quot;;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += arena;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += &quot; - &quot;;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += p-&gt;name;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; sendPublic(s);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; spamready=false;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; countdown[0] = SPAM_TIME * 60; // next spam time limit<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else if (countdown[0] &lt; 0)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; sendPrivate(p,&quot;Spam ability disabled.&quot;);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String s;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += SPAM_TIME;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; s += &quot; Minute timer between announcements. &quot;;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += countdown[0] / 60;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += &quot;:&quot;;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (countdown[0] % 60 &lt; 10)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += &quot;0&quot;;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; s += countdown[0] % 60;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; s += &quot; minutes left before next spam allowed.&quot;;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; sendPrivate(p, s);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; }<br />       
 
                                                                       
 
                                                                       
 
                                                </blockquote>&nbsp;</blockquote>         
 
<br /><!-- EXAMPLE G: END    -->
 
==== Implementing a simple stack to do "next in line for several 'boxes' at once" ====
 
<!-- EXAMPLE H: START  -->
 
<p>'''Example H''': Example of implementing a simple stack to do "next in line for several 'boxes' at once".</p>
 
<p>In '''spawn.h''' declare the following variables and structs:
 
 
<pre>
 
<pre>
class botInfo
+
// return Player* info (or NULL if not found) from p->name info
 +
Player * botInfo::GetPilot(char *name)
 
{
 
{
  //...
+
// get pilot from a name, return as TempPlayer
 +
_listnode <Player> *parse = playerlist->head;
  
  // Put bot data here  <- locate and add after
+
//convert search name to lowercase
  Player *next[99][99];
+
char nname[20], pname[20];
  int MAX_NEXT;
+
strncpy(nname, name, 20);
  int nextcount[99];
+
tolower(nname);
  
public:
+
while (parse)
    botInfo(CALL_HANDLE given)
+
{
    {
+
Player *p = parse->item;
    //...
 
  
    // Put initial values here <- locate and add after
+
// convert to lowercase to compare
    MAX_NEXT = 8;
+
strncpy(pname,p->name,20);
 
+
tolower(pname);
//... spawn.h continues on.
+
if (strcmp(pname,nname)==0)
</pre>
+
return p;
In '''spawn.cpp''' implement (remember to add the function prototype to spawn.h as well.):
+
<pre>
+
parse = parse->next;
void botInfo::MoveUp(int pos, int box)
+
}
{
 
    // moves up the next line for that box and decrement box's nextcount
 
    if (nextcount[box] > 0)
 
        nextcount[box]--;
 
 
 
    for (pos = pos; pos < MAX_NEXT - 1; pos++)
 
    {
 
        next[box][pos] = next[box][pos + 1];
 
    }
 
  
    next[box][MAX_NEXT] = 0;
+
return NULL; //player not found
 
}
 
}
 
</pre>
 
</pre>
</p>
 
<br /><!-- EXAMPLE H: END    -->
 
  
==== Example of reading any text from a .txt and printing it to pilot line by line ====
+
==Bot built in functions==
<!-- EXAMPLE I: START  -->
 
<p>'''Example I''': Example of reading any text from a .txt and printing it to pilot line by line.</p>
 
<p>Required include:
 
<pre>#include <fstream>
 
using namespace std;  // bad coding practice, but for ease of use, we'll use it.
 
</pre>
 
Example of use, in command.cpp:
 
<pre>
 
case OP_Player:
 
{
 
    //...
 
 
 
    if (c->check("staff"))
 
    {
 
        // read in data line by line from the file staff.txt (max length 255)
 
        ifstream file("staff.txt");
 
        char line[256];
 
  
        while (file.getline(line, 256))
+
Here are some useful MervBot commands to control what the bot is doing.
        {
 
            sendPrivate(p, line);
 
        }
 
        file.close();
 
    }
 
  
    //... command.cpp continues.
+
Player.cpp:
</pre>
+
* Player::move(Sint32 x, Sint32 y) moves a player to the coordinates specified by x and y
</p>
+
* Player::clone(Player *p) clones a player into a player class
<br /><!-- EXAMPLE I: END    -->
 
  
==== Example of printing player stats grid ====
+
Look in Commands.txt , command.cpp (core), or /!help to bot to see all bot external commands (example /!go &lt;arena&gt;).
<!-- EXAMPLE J: START  -->
 
<p>'''Example J''': Example of printing player stats grid.</p>
 
j) Example of printing player stats grid<br />                       
 
                                                                       
 
                                                                       
 
<blockquote>// spawn.cpp &nbsp; (see &quot;structures within structures&quot; example for variable declarations, varibale freqcount = # of freqs)<br />
 
                                                                       
 
                                                                       
 
                                                                    <br />
 
void botInfo::DisplayPlayers()<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // Display Match player/freq stats in this format &nbsp;(not aligned b/c of html but aligned in bot)<br />
 
&nbsp;&nbsp;&nbsp; // ---------------------------------------------------<br />
 
&nbsp;&nbsp;&nbsp; // Squad: squad_name_1 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
K&nbsp; D TK DMG DEALT TAKEN<br />
 
&nbsp;&nbsp;&nbsp; // ---------------------------------------------------<br />
 
            &nbsp;&nbsp;&nbsp; // Player_1 &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      0&nbsp; 0&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;
 
      0<br /> &nbsp;&nbsp;&nbsp; // Player_2 &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      0&nbsp; 0&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;
 
      0<br /> &nbsp;&nbsp;&nbsp; // TOTAL:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      0&nbsp; 0&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;
 
      0<br />
 
&nbsp;&nbsp;&nbsp; // ---------------------------------------------------<br />
 
&nbsp;&nbsp;&nbsp; // Squad: squad_name_2 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
K&nbsp; D TK DMG DEALT TAKEN<br />
 
  
&nbsp;&nbsp;&nbsp; // ---------------------------------------------------<br />
+
''LVZ Object toggling commands in plugins are to go here.''
              &nbsp;&nbsp;&nbsp; // Player_3 &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      0&nbsp; 0&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;
 
      0<br /> &nbsp;&nbsp;&nbsp; // Player_4 &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      0&nbsp; 0&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;
 
      0<br /> &nbsp;&nbsp;&nbsp; // Player_5 &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      0&nbsp; 0&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;
 
      0<br />  &nbsp;&nbsp;&nbsp; // TOTAL:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      0&nbsp; 0&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;
 
      0<br />
 
&nbsp;&nbsp;&nbsp; // ---------------------------------------------------<br />
 
                                                                       
 
                                                                       
 
                                                                    <br />
 
&nbsp;&nbsp;&nbsp; for (freq=freqcount-1; freq&gt;=0; freq--)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
char str[255];<br />             
 
                                                                       
 
                                                                       
 
                                                    <br />
 
sendFreqs(&quot;---------------------------------------------------&quot;);<br />
 
                                                                       
 
                                                                       
 
                                                                    <br />
 
sprintf(str, &quot;Squad: %-20s K&nbsp; D TK DMG DEALT TAKEN&quot;, freqs[freq].freqname);<br />
 
sendFreqs(str);<br />             
 
                                                                       
 
                                                                       
 
                                                      <br />
 
sendFreqs(&quot;---------------------------------------------------&quot;);<br />
 
                                                                       
 
                                                                       
 
                                                                    <br />
 
for (pilot=freqs[freq].playercount-1; pilot &gt;= 0; pilot--)<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; sprintf(str, &quot;%-20s
 
%8d %2d %2d %9d %5d&quot;, freqs[freq].pilots[pilot].name, freqs[freq].pilots[pilot].kills,<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; freqs[freq].pilots[pilot].deaths, freqs[freq].pilots[pilot].teamkills,
 
freqs[freq].pilots[pilot].dmgdealt,<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; freqs[freq].pilots[pilot].dmgtaken);<br />
 
&nbsp;&nbsp;&nbsp; sendFreqs(str);<br />
 
}<br />                           
 
                                                                       
 
                                                                       
 
                                        <br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
      sprintf(str, &quot;TOTAL:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 
      %2d %2d %2d %9d %5d&quot;, freqs[freq].kills, freqs[freq].deaths, <br />
 
&nbsp;&nbsp;&nbsp; freqs[freq].teamkills, freqs[freq].dmgdealt, freqs[freq].dmgtaken);<br />
 
sendFreqs(str);<br />
 
&nbsp;&nbsp;&nbsp; }<br />                                             
 
                                                                       
 
                                                                       
 
                    <br />
 
&nbsp;&nbsp;&nbsp; sendFreqs(&quot;---------------------------------------------------&quot;);<br />
 
}<br />                                                                 
 
                                                                       
 
                                                                       
 
  </blockquote>                                                       
 
<br /><!-- EXAMPLE J: END    -->
 
==== Checking if any pilots are within a region ====
 
<!-- EXAMPLE K: START    -->
 
<p>'''Example K''': Example of checking if any pilots are within a region.</p>
 
k) Example of checking if any pilots are within a region                                                       
 
                                                                       
 
                                                                       
 
          <blockquote>// see GetPilotName(name) function in other example, returns *player as TempPlayer from name<br />
 
// closeto() function from several previous examples<br />             
 
                                                                       
 
                                                                       
 
                                                      <br />
 
bool botInfo::FreqAInBox()<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // return true if teamA has a pilot in the box, otherwise false<br />
 
&nbsp;&nbsp;&nbsp; for (int tempplayercount = freqs[0].playercount-1; tempplayercount &gt;= 0; tempplayercount--)<br />
 
if (GetPilotName(freqs[0].pilots[tempplayercount].name))<br />
 
&nbsp;&nbsp;&nbsp; if (closeto(TempPlayer,
 
coordX, coordY, 73) &amp;&amp; (TempPlayer-&gt;ship != SHIP_Spectator))<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return true;<br />
 
                                                                       
 
                                                                       
 
<br />
 
&nbsp;&nbsp;&nbsp; return false;<br />
 
}<br />                                                                 
 
                                                                       
 
                                                                       
 
    </blockquote>   
 
<br /><!-- EXAMPLE K: END      -->
 
==== Functions to get a pilot's struct id info from a name or *player info ====
 
<!-- EXAMPLE L: START    -->
 
<p>'''Example L''': Example of functions to get a pilot's struct id info from a name or *player info.</p>
 
 
 
l) Example of functions to get a pilot's struct id info from a name or *player info<br />
 
                                                                       
 
                                                                       
 
<blockquote>// see struct examples for variable info<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
<br />
 
// return struct freq/pilot id from *player info<br />
 
bool botInfo::GetPilot(Player *p)<br />
 
 
 
{<br />
 
 
 
&nbsp;&nbsp;&nbsp; // return freq, pilot of a player p<br />
 
 
 
&nbsp;&nbsp;&nbsp; for (freq=freqcount-1; freq&gt;=0; freq--)<br />
 
 
 
if (p-&gt;team == freqs[freq].freqteam)<br />
 
 
 
&nbsp;&nbsp;&nbsp; for (pilot = freqs[freq].playercount-1; pilot&gt;=0; pilot--)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
if (strcmp(p-&gt;name,freqs[freq].pilots[pilot].name)==0)<br />
 
 
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return true;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
<br />
 
 
 
&nbsp;&nbsp;&nbsp; return false;<br />
 
 
 
}<br />                                                                 
 
                                                                       
 
                                                                       
 
      <br />
 
// return *player as TempPlayer info from p-&gt;name info<br />bool botInfo::GetPilotName(char *name)<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // get pilot from a name, return as TempPlayer<br />
 
&nbsp;&nbsp;&nbsp; _listnode &lt;Player&gt; *parse = playerlist-&gt;head;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
          <br />
 
&nbsp;&nbsp;&nbsp; while (parse)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
Player *p = parse-&gt;item;<br /> 
 
                                                                       
 
                                                                       
 
                                                                       
 
      <br />
 
// convert both to lowercase to compare<br />
 
char pname[20];&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; <br />
 
strncpy(pname,p-&gt;name,20);<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
        <br />
 
char nname[20];&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; <br />
 
strncpy(nname,name,20);<br />     
 
                                                                       
 
                                                                       
 
                                                                       
 
  <br />
 
tolower(pname);<br />
 
tolower(nname);<br />             
 
                                                                       
 
                                                                       
 
                                                                    <br />
 
if (strcmp(pname,nname)==0)<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; TempPlayer = p;<br />
 
&nbsp;&nbsp;&nbsp; return true;<br />
 
}<br />                           
 
                                                                       
 
                                                                       
 
                                                      <br />
 
parse = parse-&gt;next;<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; return false;<br />
 
}<br />
 
<br />
 
// note: better to implement these functions as passing values by reference instead of using global variables<br />
 
// just easier to not have to be declaring different int freq, int pilot all the time<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
</blockquote>                                                         
 
<br /><!-- EXAMPLE L: END      -->
 
==== Creating a logfile name using date and squad names ====
 
<!-- EXAMPLE M: START    -->
 
<p>'''Example M''': Example of creating a logfile name using date and squad names.</p>
 
m) Example of creating a logfile name using date and squad names<br />
 
                                                                       
 
                                                                       
 
<blockquote>&nbsp;&nbsp;&nbsp; // create log file name (squadA and squadB external char[20] variables)<br />
 
&nbsp;&nbsp;&nbsp; char u[100];<br />
 
&nbsp;&nbsp;&nbsp; time_t t=time(NULL);<br />
 
&nbsp;&nbsp;&nbsp; tm *tmp = localtime(&amp;t);<br />
 
&nbsp;&nbsp;&nbsp; strftime(u,99,&quot;%y&quot;,tmp);<br />
 
&nbsp;&nbsp;&nbsp; logname = &quot;c:\Program Files\Continuum\logs\&quot;;<br />
 
&nbsp;&nbsp;&nbsp; logname += u;<br />
 
&nbsp;&nbsp;&nbsp; logname += &quot;y&quot;;<br />
 
&nbsp;&nbsp;&nbsp; strftime(u,99,&quot;%m&quot;,tmp);<br />
 
&nbsp;&nbsp;&nbsp; logname += u;<br />
 
&nbsp;&nbsp;&nbsp; logname += &quot;m&quot;;<br />
 
&nbsp;&nbsp;&nbsp; strftime(u,99,&quot;%d&quot;,tmp);<br />
 
&nbsp;&nbsp;&nbsp; logname += u;<br />
 
&nbsp;&nbsp;&nbsp; logname += &quot;d&quot;;<br />
 
&nbsp;&nbsp;&nbsp; logname += squadA;<br />
 
&nbsp;&nbsp;&nbsp; logname += &quot; vs &quot;;<br />
 
&nbsp;&nbsp;&nbsp; logname += squadB;<br />
 
&nbsp;&nbsp;&nbsp; strftime(u,99,&quot;%I&quot;,tmp);<br />
 
&nbsp;&nbsp;&nbsp; logname += u;<br />
 
&nbsp;&nbsp;&nbsp; logname += &quot;h&quot;;<br />
 
&nbsp;&nbsp;&nbsp; strftime(u,99,&quot;%M&quot;,tmp);<br />
 
&nbsp;&nbsp;&nbsp; logname += u;<br />
 
&nbsp;&nbsp;&nbsp; logname += &quot;m&quot;;<br />
 
&nbsp;&nbsp;&nbsp; logname += &quot;.txt&quot;;<br />                             
 
                                                                       
 
                                                                       
 
                                            <br />
 
// example name created: &nbsp;03y01m27dBLACKDRaGON vs Integral05h08m.txt<br />
 
// format &nbsp;year, month, day, squadA vs squadB, hour, minute<br /> 
 
                                                                       
 
                                                                       
 
</blockquote>
 
<br /><!-- EXAMPLE M: END      -->
 
==== Sending messages to playing freqs or public and logging depending on status ====
 
<!-- EXAMPLE N: START    -->
 
<p>'''Example N''': Example of sending messages to playing freqs or public and logging depending on status.</p>
 
n) Example of sending messages to playing freqs or public and logging depending on status<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
  <blockquote>// teamA, teamB, logname global variables<br />
 
void botInfo::sendFreqs(char *msg)<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; char *mmsg = &quot;*arena&quot;;<br />
 
&nbsp;&nbsp;&nbsp; String s = msg;<br />
 
&nbsp;&nbsp;&nbsp; <br />
 
&nbsp;&nbsp;&nbsp; if (teammsgs == false)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
s.prepend(&quot;*arena &quot;,7);<br />
 
sendPublic(s);<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; else<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
sendTeamPrivate(8025,msg);<br />
 
sendTeamPrivate(teamA,msg);<br />
 
sendTeamPrivate(teamB,msg);<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; if (gameon == true)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
ofstream outf(logname, ios::app);<br />
 
outf &lt;&lt; msg &lt;&lt; endl;<br />
 
outf.close();<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
}<br />                                                                 
 
                                                                       
 
                                                                       
 
          </blockquote>                                               
 
<br /><!-- EXAMPLE N: END      -->
 
==== Reading in all player/freqs to struct data ====
 
<!-- EXAMPLE O: START    -->
 
<p>'''Example O''': Example of reading in all player/freqs to struct data.</p>
 
o) Example of reading in all player/freqs to struct data<br />       
 
                                                                       
 
                                                                       
 
                                                                    <blockquote>// see structures within structures example for freqs[] declaration<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
      <br />
 
// to get freqs in a game where there are several freqs<br />
 
void botInfo::GetFreqs()<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // read pilots into freq struct data from ingame and on playing freqs<br />                     
 
                                                                       
 
                                                                       
 
                                                        <br />
 
&nbsp;&nbsp;&nbsp; _listnode &lt;Player&gt; *parse = playerlist-&gt;head;<br />
 
&nbsp;&nbsp;&nbsp; <br />
 
&nbsp;&nbsp;&nbsp; while (parse)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
Player *p = parse-&gt;item;<br /> 
 
                                                                       
 
                                                                       
 
                                                                       
 
  <br />
 
if (p-&gt;ship != SHIP_Spectator)<br />
 
&nbsp;&nbsp;&nbsp; if (closeto(p, coordX, coordY, 73))<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // look for freq in struct<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; bool foundfreq=false;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
      <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; freq=freqcount-1;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
      <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; while ((freq&gt;=0) &amp;&amp; (foundfreq==false))<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; if (p-&gt;team == freqs[freq].freqteam)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
foundfreq=true;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
strncpy(freqs[freq].pilots[freqs[freq].playercount].name,
 
p-&gt;name, 20);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
freqs[freq].playercount++;&nbsp;&nbsp;&nbsp;
 
<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; freq--;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
      <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // didnt find freq in struct so add new freq<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (foundfreq == false)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (manualsquads == false)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
strncpy(freqs[freqcount].freqname,
 
p-&gt;squad, 20);<br />                                                 
 
                                                                       
 
                                                                       
 
                            <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
if (freqcount == 0)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; teamA = p-&gt;team;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; strncpy(squadA,
 
p-&gt;squad, 20);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; teamB = p-&gt;team;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; strncpy(squadB,
 
p-&gt;squad, 20);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
if (p-&gt;team == teamA)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; strncpy(freqs[freqcount].freqname,squadA,20);<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
      <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
else if (p-&gt;team == teamB)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; strncpy(freqs[freqcount].freqname,squadB,20);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; freqs[freqcount].freqteam = p-&gt;team;<br />       
 
                                                                       
 
                                                                       
 
                                                                    <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; strncpy(freqs[freqcount].pilots[0].name, p-&gt;name, 20);<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
                                                                       
 
                                                                       
 
                                                                       
 
    <br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; freqs[freqcount].playercount++;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; freqcount++;<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
parse = parse-&gt;next;<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
}<br />                                                                 
 
                                                                       
 
                                                                       
 
                                                    <br />
 
// to get freqs in a game where there are only two teams<br />
 
void botInfo::GetFreqs()<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // read pilots into freq struct data from ingame and on playing freqs<br />
 
&nbsp;&nbsp;&nbsp; _listnode &lt;Player&gt; *parse = playerlist-&gt;head;<br />
 
&nbsp;&nbsp;&nbsp; <br />
 
&nbsp;&nbsp;&nbsp; while (parse)<br />
 
&nbsp;&nbsp;&nbsp; {<br />
 
Player *p = parse-&gt;item;<br /> 
 
                                                                       
 
                                                                       
 
                                                                       
 
                                          <br />
 
if ((p-&gt;ship != SHIP_Spectator)
 
&amp;&amp; ((p-&gt;team == teamA) || (p-&gt;team == teamB)))<br />
 
{<br />
 
&nbsp;&nbsp;&nbsp; // freq 100, team A<br />
 
&nbsp;&nbsp;&nbsp; // set freq<br />
 
&nbsp;&nbsp;&nbsp; freq = 0;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
                                            <br />
 
&nbsp;&nbsp;&nbsp; if (p-&gt;team == teamB)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; freq = 1;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
                                              <br />
 
&nbsp;&nbsp;&nbsp; // number of pilots on freq counted so far, starts 0<br />
 
&nbsp;&nbsp;&nbsp; pilot = freqs[freq].playercount;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
                                              <br />
 
&nbsp;&nbsp;&nbsp; // pilot name<br />
 
&nbsp;&nbsp;&nbsp; strncpy(freqs[freq].pilots[pilot].name, p-&gt;name, 20);<br />
 
&nbsp;&nbsp;&nbsp; // time stamp for playing time<br />
 
&nbsp;&nbsp;&nbsp; freqs[freq].pilots[pilot].cplaying_time = GetTickCount();<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
                                              <br />
 
&nbsp;&nbsp;&nbsp; // slot name<br />
 
&nbsp;&nbsp;&nbsp; if (freqs[freq].playercount &lt; NUMBER_PILOTS)<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
 
strncpy(freqs[freq].slotname[pilot], p-&gt;name, 20);<br />
 
<br />
 
&nbsp;&nbsp;&nbsp; // increment freq player count<br />
 
&nbsp;&nbsp;&nbsp; freqs[freq].playercount++;<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
                                              <br />
 
&nbsp;&nbsp;&nbsp; // if freq not already have name, give it player squad name<br />
 
&nbsp;&nbsp;&nbsp; if ((manualsquads == false) &amp;&amp; (strlen(p-&gt;squad) &gt; 0))<br />
 
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; strncpy(freqs[freq].freqname, p-&gt;squad, 20);<br />
 
                                                                       
 
                                                                       
 
                                                                       
 
                                              <br />
 
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // set player ship<br />
 
&nbsp;&nbsp;&nbsp; freqs[freq].pilots[pilot].ship = p-&gt;ship + 1;<br />
 
}<br />
 
parse = parse-&gt;next;<br />
 
&nbsp;&nbsp;&nbsp; }<br />
 
}<br />                                                       
 
                                                                       
 
                                                                       
 
                      </blockquote>                                   
 
<br /><!-- EXAMPLE O: END      -->
 
==== Finding MVP from struct data (2*kills - deaths formula) ====
 
<!-- EXAMPLE P: BEGIN -->
 
<p>'''Example P''': Example of finding MVP from struct data (2*kills - deaths formula).</p>
 
<p>
 
<pre>
 
int highest = -20;
 
int mvp = 0;
 
 
 
for (pilot = freqs[mvpteam].playercount-1; pilot >=0; pilot--)
 
  {
 
    if (((freqs[mvpteam].pilots[pilot].kills * 2) - freqs[mvpteam].pilots[pilot].deaths) > highest)
 
      {
 
        mvp = pilot;
 
        highest = (freqs[mvpteam].pilots[pilot].kills * 2) - freqs[mvpteam].pilots[pilot].deaths;
 
      }
 
  }
 
</pre>
 
</p>
 
<br /><!-- EXAMPLE P: END  -->
 
==== Print time stamp of event ====
 
<!-- EXAMPLE Q: BEGIN -->
 
<p>'''Example Q''': Print time stamp of event.</p>
 
<p><pre>#include "time.h" // required include</pre>Use:
 
<pre>
 
char u[100];
 
time_t t=time(NULL);
 
tm *tmp = localtime(&t);
 
strftime(u,99,"%c",tmp);
 
sendPublic("Current date and time: " + (String) u);
 
</pre></p>
 
<br /><!-- EXAMPLE Q: END  -->
 
==== Simple way to track player bomb/bullet damage stats ====
 
<!-- EXAMPLE R: BEGIN -->
 
<p>'''Example R''': Simple way to track player bomb/bullet damage stats.</p>
 
<p>'''Note''': see data section for how to setup set_tag.</p>
 
<p>'''Note''': see clientprot.h for weapon information.</p>
 
<p>In '''spawn.cpp''':
 
<pre>
 
case EVENT_WatchDamage:
 
{
 
    if (PLAYING)  // if tracking stats
 
    {
 
        if ((wi.type == PROJ_PBomb) && (p->name != k->name))
 
        {
 
            set_tag(k, DMG_BOMB_DEALT, get_tag(k, DMG_BOMB_DEALT) + damage);
 
            set_tag(k, DMG_TOTAL_DEALT, get_tag(k, DMG_TOTAL_DEALT) + damage);
 
            set_tag(p, DMG_BOMB_TAKEN, get_tag(p, DMG_BOMB_TAKEN) + damage);
 
            set_tag(p, DMG_TOTAL_TAKEN, get_tag(p, DMG_TOTAL_TAKEN) + damage);
 
        }
 
        else if (wi.type == PROJ_BBullet)
 
        {
 
            set_tag(k, DMG_BULLET_DEALT, get_tag(k, DMG_BULLET_DEALT) + damage);
 
            set_tag(k, DMG_TOTAL_DEALT, get_tag(k, DMG_TOTAL_DEALT) + damage);
 
            set_tag(p, DMG_BULLET_TAKEN, get_tag(k, DMG_BULLET_TAKEN) + damage);
 
            set_tag(p, DMG_TOTAL_TAKEN, get_tag(k, DMG_TOTAL_TAKEN) + damage);
 
        }
 
    }
 
</pre></p><br />
 
<!-- EXAMPLE R: END  -->
 
==== Simple way to print those stats ====
 
<!-- EXAMPLE S: START -->
 
<p>'''Example S''': Simple way to print those stats.</p>
 
<p><pre>
 
case OP_Moderator:
 
{
 
  if (c->check("showstats"))
 
    {
 
      sendPublic("Showing stats:");
 
     
 
      _listnode <Player> *parse = playerlist->head;           
 
 
 
      while (parse)
 
{
 
  Player *p = parse->item;
 
   
 
          if (get_tag(p, DMG_TOTAL_DEALT) > 0)
 
  {
 
    char str[256];
 
    sprintf(str, "(%-20s Dmg Dealt: Total %0004d, Bomb %0004d, Bullet %0004d  Dmg TAKEN: Total %0004d, Bomb %0004d, Bullet %0004d)",
 
    p->name, get_tag(p,DMG_TOTAL_DEALT), get_tag(p,DMG_BOMB_DEALT), get_tag(p,DMG_BULLET_DEALT),
 
    get_tag(p,DMG_TOTAL_TAKEN), get_tag(p,DMG_BOMB_TAKEN), get_tag(p,DMG_BULLET_TAKEN));
 
    sendPublic(str);
 
    }
 
        parse = parse->next;
 
}
 
    }
 
}
 
</pre></p><br />
 
<!-- EXAMPLE S: END  -->
 
==== Make bot spectate specific coordinates ====
 
<!-- EXAMPLE T: START  -->
 
<p>'''Example T''': Make bot spectate specific coordinates.</p>
 
<p>
 
<pre>
 
// make bot spectate the coord 512,600
 
// possible use - capturing weapon packets in a specific region
 
  
tell(makeFollowing(false));
+
[[Category:Guides]]
tell(makeFlying(true));
 
me->move(512 * 16, 600 * 16);
 
tell(makeSendPosition(true));
 
</pre></p>
 
<br /><!-- EXAMPLE T: END  -->
 

Latest revision as of 05:38, 3 August 2009

This tutorial is based on the ever-popular MERVBot Tutorial by Underlord. It has since been updated to reflect new changes with MervBot. To see examples of how to use this instruction, see MERVBot Example Code.

This tutorial also assumes that you have a basic knowledge of C++. If you don't, check out cplusplus.com's great documentation.

Setting up a MERVBot (plugin)

MERVBot download site


Obtaining MERVBot

  • Download the latest build.
  • Unrar MERVBot.rar into a new folder. (example c:\program files\continuum\mervbot)
  • Unzip src.zip into "src" subfolder of that new folder (example c:\program files\continuum\mervbot\src)

Preparing to write a plugin

Note: if you only want to execute someone's premade plugin (.dll), skip to step 4, otherwise continue to learn how to make your own bot

Download DLL-plugin Tutorial and unzip Tutorial.zip (containing spawn.h, spawn.cpp, and command.cpp) into a "tutorial" subfolder of that new folder. (example c:\program files\continuum\mervbot\src\tutorial).

File descriptions:

  • spawn.h = declare/initialize globals
  • command.cpp = code for commands coming into bot (ie /!help, /!play, etc)
  • spawn.cpp = code that interacts with bot spawns

Microsoft Visual c++

  1. Start Visual Studios 6.0.
  2. Click the Drop Down Menu labeled "File" at the top left of your screen.
  3. Click "New".
  4. On the next screen that comes up, choose from the Project tab, then Win32 Dynamic-Link Library
  5. Select the "/src" folder as the base folder (example c:\program files\continuum\mervbot\src)
  6. Name your project "mybot". This will make a "mybot" subfolder in your "src" folder. Click OK. (example creates c:\program files\continuum\mervbot\src\mybot)
  7. Choose to create an "Empty DLL project".
  8. Click "Finish".
  9. Click the Drop Down Menu labbled "Project".
  10. Click "Add To Project Files"
  11. Copy only spawn.h, spawn.cpp, and command.cpp from the "tutorial" folder into the this new folder. (example from c:\program files\continuum\mervbot\src\tutorial to c:\program files\continuum\mervbot\src\mybot)
  12. Click the Drop Down Menu labelled "Build".
  13. Click "Build (dll name)" - where (dll name) is "mybot"
  14. Go into your "mybot" folder and look for a folder named "Debug" (example c:\program files\continuum\mervbot\src\mybot\debug)
  15. Your new DLL will be in that folder. (example mybot.dll)
  16. Copy mybot.dll to your base folder that has mervbot.exe in it (example c:\program files\continuum\mervbot)


Run your bot dll

To run your bot you need your DLL (mybot.dll), Commands.txt, MERVBot.exe, MERVBot.ini, Operators.txt, Spawns.txt, and zlib.dll all in one folder (example c:\program files\continuum\mervbot).

  1. Edit spawns.txt. Read every word of spawns.txt to find out what needs to go in there.
    Example:
    2v2-Bot-League : botpw : 2v2a : 2v2league : staffpw

    Note: The bot will attempt to create the name if it doesn't exist already.


  2. Edit MERVBot.ini

    [Login]
    Zone=216.33.98.254:21000	// your zone IP:PORT available from zone.dat in Continuum dir
    


  3. Edit operators.txt. Read every word of operators.txt to find out what needs to go in there.
    Example:
    4:my_name:
    4:another_sysop:
    3:other_person:
    


  4. Make sure the bot is on vip.txt or has smod+ access, then run MERVBot.exe.
  5. You can now edit your plugin code by opening "mybot.dsw" (example c:\program files\continuum\mervbot\src\mybot\mybot.dsw) in Microsoft Visual C++. Edit the spawn.h, spawn.cpp, and command.cpp to create your plugin, then build, copy your updated DLL to your MERVBot.exe folder and then execute the bot. Use the tutorial to get ideas on how to implement certain types of features into the bot.

Player Commands - (command.cpp)

This section describes how to implement player commands into your plugin. Commands are sent to the botInfo::gotCommand function in command.cpp.

Example (makes bot reply to !test with "hi"):

void botInfo::gotCommand(Player *p, Command *c) {
	switch (p->access)
	{
        case OP_Moderator:
                {
                     // handle moderator-operator commands here.
                }
	case OP_Player: //appropriate staff rank here.
		{
			if (c->check("test")) //replace "test" with whatever command you want
			{
				//put your command code here
				sendPrivate(p,"hi"); //example
			}
		}

How to have commands with numerical parameters

Example (!test #):

	if (c->check("test")) { // reads in test #, default to 1 if invalid number input
		int temp = 1;

		if (isNumeric(c->final))
			temp = atoi(c->final);

How to have player name as input

Example (!rank player):

	if (c->check("rank"))
	{
		String player_name = c->final;

		if (player_name.IsEmpty()) // default name to self if invalid name
			player_name = p->name;

How to have multi-parameter input

Use the CRT function sscanf() to scan the string for the values.

Example (!squads squadA vs squadB or !squads teamA:squadA:teamB:squadB):

else if (c->check("squads"))
{
	char squadA[20], squadB[20];
	int teamA, teamB;

	strncpy(squadA, "", 20);
	strncpy(squadB, "", 20);

	int n_found;

	//Note: %[A-Za-z ] is equivalent to %s, but allows an internal space.

	//scan the string for the two squads separated by " vs "
	n_found = sscanf(c->final, "%[A-Za-z ] vs %[A-Za-z ]", squadA, squadB);

	//if that fails, scan the string for freqA:squadA:freqB:squadB
	if (n_found < 2)
		sscanf(c->final, "%d:%[A-Za-z ]:%d:%[A-Za-z ]", &teamA, squadA, &teamB, squadB);
}

Help Menu

When a player sends !help to the bot, MERVBot calls botInfo::gotHelp() in each plugin loaded.

void botInfo::gotHelp(Player *p, Command *c)
{
	if (!*c->final)
	{
	sendPrivate(p, "4v4 Bot General Commands:");
	sendPrivate(p, "------------------------");
	sendPrivate(p, "!caps - get captain names");
	sendPrivate(p, "!roster <squad> - get roster of a squad");
	sendPrivate(p, "!schedule- get current schedule");
	sendPrivate(p, "!score - get current score");

Event Calls

MERVBot is event based, so when making a bot you need to decide what will happen at certain events. Normal plugins need to consider what happens when bot enters arena, player enters arena, player leaves arena, player events like kill, shipchange, teamchange, spec, move then any other relevant events to your bot. Just worry about events that are relevant to the tasks your bot is doing.

MERVBot sends events to botInfo::gotEvent() in spawn.cpp. Each supported event is already present and categorized in gotEvent(), along with the paramters that MERVBot sends with the event. When a plugin wants the bot to do something, it sends tell(event) to the bot.

See dllcore.h for a list of current events and their descriptions. Dllcore.h also contains functions (like makeFollowing) to make events to send back to the bot via tell().

tell(makeFollowing(false));

The Messaging System

Private message - void sendPrivate(Player *player, char *msg);

Examples:

sendPrivate(p,"hi");

String s="test";
sendPrivate(p,s);

String s="test";
s += "ing";
sendPrivate(p,s);

char captain1[20];
char captain2[20];
strncpy(captain1,"",20);
strncpy(captain2,"",20);
sendPrivate(p,(String) captain1 + " and " + (String) captain2 + " are the captains.");

Team message - void sendTeamPrivate(Uint16 team, char *msg);

Examples:

a) sendTeamPrivate(8025,"hi spec freq");
b) Uint16 test=0; sendTeamPrivate(test,"hi freq 0");

Public message - void sendPublic(char *msg);

Example: sendPublic("*arena " + (String) p->name + " is now a captain");

Chat channel message - void sendChannel(char *msg);

Example: sendChannel("hi chat channel");

Remote private message - void sendRemotePrivate(char *name, char *msg);

Example: sendRemotePrivate("Player01", "hi");

Note: to have bot print several lines of text fast it needs sysop in the arena (sysop in arena bot first spawns to also) otherwise it'll print slow to avoid being kicked for spam

Output of data in messages

An example of using normal strings to output data/messages.

 // does *arena X pilots left in game
 // NOTE: variable temp needs to be defined with some value

 String s = "*arena ";
       s += temp;
       s += " pilots left in the game.";

 sendPublic(s);

Or,

 //NOTE: this can be considered inefficient.

 sendPublic("*arena " + (String)temp + " pilots left in the game");

An example using sprintf to align/space data, where output data will be in this approximate format.

// output data will be in this approximate format (not lined up perfectly because of html)
// --------------------------------------------------------------------------------------
// Squad: squadname       PTS     FPTS    K    D  DMG DEALT TAKEN   F  FK    FLT
// --------------------------------------------------------------------------------------
// PlayerA              10000      500  116  101       9999 99999  10 150 980:55
// PlayerB                500      200    7    5       9999 99999   5   3   0:04

char str[255];
sendPublic("*arena--------------------------------------------------------------------------------");

sprintf(str, "*arena Squad: %-20s   PTS     FPTS   K   D  DMG DEALT  TAKEN  F  FK  FLT",
         freqs[freq].freqname
        );

sendPublic(str);

sendPublic("*arena--------------------------------------------------------------------------------");

            // assuming existing freqs struct with data
            for (pilot=freqs[freq].playercount-1; pilot>=0; pilot--)
            {
                // on freq squad so print stats
                char outString[255];

                sprintf(outString, "*arena %-20s %12d %8d %3d %3d %10d %6d %2d %3d %3d:%02d",
                       freqs[freq].pilots[pilot].name,
                       freqs[freq].pilots[pilot].points,
                       freqs[freq].pilots[pilot].flagpoints,
                       freqs[freq].pilots[pilot].kills,
                       freqs[freq].pilots[pilot].deaths,
                       freqs[freq].pilots[pilot].dmgdealt,
                       freqs[freq].pilots[pilot].dmgtaken,
                       freqs[freq].pilots[pilot].flags,
                       freqs[freq].pilots[pilot].flagkills,
                       freqs[freq].pilots[pilot].flagtime /60,
                       freqs[freq].pilots[pilot].flagtime %60
                       );
                
                sendPublic(outString);
            }

            // Notes: sprintf format = sprintf(output char string, spacing, variables)
            // Notes: s = chars, d = integer, - = left align, right align default
            // Notes: doing %02d = put 0 in front if not 2 digits, %3d:%02d makes 0:04 format

Time

Each time MERVBot sends an EVENT_Tick to a plugin (once a second), the default handler code decrements each value in an array of countdowns. You can modify the number of countdowns and add code to occur at a specific value for one of the countdowns.

Setup number of timers and initialize in spawn.h:

class botInfo
{
	#define COUNTDOWNS 10 		// how many countdowns you want
	int countdown[COUNTDOWNS];	// this gives you 10 timers

// unrelated code
 
	public:
	botInfo(CALL_HANDLE given)
	{
	countdown[0] = 0;
	countdown[1] = 60; // 60 seconds
	//
	// initialize values
	//
	countdown[9] = 5*60; // 5 minutes

Using timer functions in spawn.cpp:

case EVENT_Tick:
{
	for (int i = 0; i < COUNTDOWNS; ++i) //cycles through each countdown you have
		--countdown[i]; //note that countdowns will continue decrementing past 0.

	if (countdown[1] == 2) // when timer #1 hits two seconds
	{
	// do stuff here when timer #1 hits 2 seconds
	// example: sendPublic("two seconds left, setting timer to 1 minute");
	// example: countdown[1] = 60; // change timer #1 value
	}

You can then have events (such as EVENT_PlayerDeath) change the value of a countdown to make the bot do something a set time after an event occurs.

Tracking time not using countdown[n]

This is a solution to a common problem of determining the amount of time it takes for something to occur. Using basic math, we record a start-time B, and an end-time E, both in the unit of seconds, we calculate the time elapsed by E-B.

Lucky for us, Windows provides a function called GetTickCount() that is a measurement of time (milliseconds) that we can use for such cases.

So:

	int begin = GetTickCount();

	// do some code here.

	int end = GetTickCount();
  
	int delta = (end - begin) / 1000;  // elapsed time converted to seconds from milliseconds

Obtaining the current time

Requirements: Include <time.h>.

Use:

 char u[100];
 time_t t=time(NULL);
 tm *tmp = localtime(&t);
 strftime(u,99,"%c",tmp);
 sendPublic("Current date and time: " + u);

Writing Functions

For this example, we will take the function called closeto, which determines if a player exists in an specific radius around a point. Now to apply this function to a MervBot plugin, you need to write it into the spawn.cpp - at the top of the file in the //////// DLL "import" //////// setion, as below:

//////// DLL "import" ////////

bool closeto(Player *p, int x, int y, int tolerance) 
{
	// Requires the function abs() to be declared elsewhere.
	// Return if player p is in area of square with center x, y
	//   and radius = tolerance
	return (abs((p->tile.x) - x) - tolerance) && (abs((p->tile.y) - y) - tolerance);
}


If you want your function to have access to the data from spawn.h botInfo class, you make the function apart of it. To do this, we add the botInfo:: infront of the function name, in spawn.cpp.

//////// DLL "import" ////////

bool botInfo::closeto(Player *p, int x, int y, int tolerance) 
    {
	 ...
    }

In spawn.h, add your method's prototype without botInfo::, it will look like this:

//...
botInfo(CALL_HANDLE given)
 {
//  ...
 } 
  bool closeto(Player *p, int x, int y, int tolerance); // Your function prototype.

  void clear_objects(); //provided by Catid, and already exists.
  void object_target(Player *p); //provided by Catid, and already exists.
//  ...
};

If you're not familiar with prototypes, notice it is similar to that in your spawn.cpp, but without the botInfo::, and a trailing ;.

Function notes

Remember that you can pass variables by reference. If variables are passed by reference, any changes a function makes to the variables will remain after the function returns.

From time to time you will need to pass an array to a function. An example illustrating this is:

	int freqs[5]; // declare our example data.

	// call function - notice freqs and not freqs[5] or freqs[].
	my_function(freqs); //You're not passing the array itself, just a pointer to the array.

	// function - notice freqs[] and not freqs[5] or freqs
	void my_function(int freqs[]) {} //You're specifying that the freqs parameter is an array

Cycling through players

MERVBot stores player-related data in a linked list. A linked list is a datatype that stores its data in a series of structures linked to each other, hence the name.

To search through the players in the arena, just start at the first link, then continue through all the following links until you reach the end:

_listnode <Player> *parse = playerlist->head;	//set to first link of the player linked list

while (parse)	//parse will be NULL when we reach the last link
{
	Player *p = parse->item;	//item is the actual data stored in the link

	// do functionality here
	// Example 1: sendPrivate(p,"*watchdamage"); // turns on all pilot's watchdamage
	// Example 2: if (p->safety != 0) sendPrivate(p,"*spec"); // spec all pilots in safe zone

	parse = parse->next;	//set parse to the next link
}

For example, assuming our bot has smod+ privilages, the following code will set all non-spectator players to a specific ship. First begin by adding the following function prototype to the spawn.h in the botInfo class:

 void handleCmdSetShip(enum Ship_Types ship);

In spawn.cpp add:

void botInfo::handleCmdSetShip(enum Ship_Types ship)
{
	//Note that the parameter ship is of the Ship_Types enum,
	//so its value is hopefully restricted to the proper types.

	_listnode <Player> *parse = playerlist->head;
	while (parse)
	{
		Player *p = parse->item;

		if ( p->ship != ship && p->ship != SHIP_Spectator )
				sendPrivate(p, "*setship " + (String)ship);

		parse = parse->next;
	}
}

To use, just call the function with the appropriate Ship_Type from the enum in clientprot.h:

 handleCmdSetShip(SHIP_Warbird);

Random numbers

Generating a random number

To use this method, these two includes must be used:

#include "time.h"    //provides time() function.
#include "stdlib.h"  //provides srand() and rand() functions.

Use:

    srand(time(NULL)); // seed random number generator.

    rand(); // randomize.

    int temp = (int) (51 * ((float)rand()/RAND_MAX));
       // the above line returns a random integer between 0 and 51.
       // Note: RAND_MAX is a global constant defined in stdlib.h

Picking a random pilot

Note: A required user-defined function, getInGame(), must be created for this example to work.

int temp = GetTickCount() % getInGame();  // getInGame() = how many pilots in arena

Player *rabbit = NULL;

_listnode <Player> *parse = playerlist->head;
while (parse)
{
	Player *p = parse->item;

	if (p->ship != SHIP_Spectator) // if player is not a spectator
	if ( !(--temp) ) // and if we've hit the randomly-selected pilot
	{
		rabbit = p;
		break;
	}
	parse = parse->next;
}

Storing data for pilots

There are several ways to store data for pilots (ie tracking flagtime or kills in a period of time). Note that these methods are all purely internal to the bot, and don't effect anything beyond the plugin in any way.

  1. Built-in get/setTag: Tracks data until player leaves the arena, then automatically deletes data.
  2. Modified perm get/setTag: Tracks data until bot leaves arena, then automatically deletes data. (Advantage: easier to sort by player)
  3. Custom Structs: Tracks data until plugin deletes it. (Advantage: easier to sort by freqs)

Note: 2 and 3 are similar in effect, mostly the difference is in how you are able to search through data you need to decide which method of storing data is best for each bot depending on what it does.

Built-in get/setTag method

Player tags simply tag a player with a number. Define the wanted values in spawn.h at the top:

#define DMG_DEALT        0
#define DMG_TAKEN        1

In spawn.cpp, initialize the values on ArenaEnter and PlayerEnter:

case EVENT_ArenaEnter:
{
	// ...

	// do for all pilots in arena when bot enters
	_listnode <Player> *parse = playerlist->head;
	while (parse)
	{
	   Player *p = parse->item;  // get pilot

	   set_tag(p, DMG_DEALT, 0); // initialize to 0
	   set_tag(p, DMG_TAKEN, 0);
	   sendPrivate(p, "*watchdamage");  // optionally turn on player *watchdamage

	   parse = parse->next;  // get next pilot
	}
}
case EVENT_PlayerEntering:
{
//	...
	set_tag(p, DMG_DEALT, 0); // initialize to 0
	set_tag(p, DMG_TAKEN, 0);
	sendPrivate(p,"*watchdamage");
}

Then somewhere edit the tag values:

case EVENT_WatchDamage:
{
	// sets tag for k (shooter) to be old value plus damage currently dealt

	int old_damage = get_tag(k, DMG_BOMB_DEALT);
	set_tag(k, DMG_BOMB_DEALT, old_damage + damage);
}

The following demonstrates how to retrieve the tag values as a command in command.cpp:

if (c->check("showstats"))
{
	int temp = get_tag(p, DMG_TOTAL_DEALT);

	String s = "You've done ";
	s += temp;
	s += " damage so far!";

	sendPrivate(p,s);
 }

Modified permanent get/setTag method

This method is the same as get/setTag with some modifications to the tag code to retain them after the player leaves. Beware of using this method if bot is in an arena for long periods of time, linkedlist could get huge.


// spawn.h, add char name[20]; into struct PlayerTag

struct PlayerTag
{
	Player *p;
	char name[20];
	int index;
	int data;
};

In spawn.cpp:

	case EVENT_PlayerLeaving:
	{
	    Player *p = (Player*)event.p[0];

	    // killTags(p);  // remove so tag not deleted on arena exit

//	...

Locate in spawn.cpp and modify accordingly:

int botInfo::get_tag(Player *p, int index)
{
    _listnode <PlayerTag> *parse = taglist.head;
    PlayerTag *tag;

    while (parse)
    {
      tag = parse->item;

      // if (tag->p == p)
      if (strcmp(tag->name,p->name)==0)  // now tracking by player name, not pointer
      if (tag->index == index)
        return tag->data;

      parse = parse->next;
    }
    return 0;
}

void botInfo::set_tag(Player *p, int index, int data)
{
    _listnode <PlayerTag> *parse = taglist.head;
    PlayerTag *tag;

    while (parse)
    {
      tag = parse->item;

      //if (tag->p == p)
      if (strcmp(tag->name,p->name)==0) // now tracking by player name, not pointer
      if (tag->index == index)
      {
        tag->data = data;
        return;
      }
      parse = parse->next;
    }

    tag = new PlayerTag;
    // tag->p = p; // not tracking by pointer anymore
    strncpy(tag->name, p->name, 20); // tracking by player name
    tag->index = index;
    tag->data = data;
    taglist.append(tag);
}

Using structs

In spawn.h:

class botInfo
{
	struct freqdata 
	{
		int kills, deaths;
	};
// ...
};

To make use of this structure, implement accordingly:

	freqdata freqs[100]; // 100 of those structs

Access the data in spawn.cpp using

freqs[56].kills = 1;

See CPlusPlus.com's Structures tutorial for a more comprehensive guide. Note that, as shown on the bottom of the page, you can have structures within structures. Thus, for example, you could have a structure for each freq with a structure for each player nested within them.

Input/Output to files

For reading and/or writing to files with C++ you must have the required include statement as follows:

#include <fstream>
using namespace std;

File stream input

The following example will show you how to read a file, duel.ini, line by line.

#include "stdlib.h" // for atoi()
  ifstream file("duel.ini");

  if (!file.good()) // if there was an error opening the file
    sendPublic("*arena Error opening file for reading"); // or add your own error handler
  else
  {
    char line[256];

    // read in MaxBoxes=X
    while (file.getline(line, 256))
    {
     
      if (CMPSTART("MaxBoxes=", line)) //Does the line begin with MaxBoxes= ?
      {
        MAX_BOXES = atoi(&(line[9]));  //If so, read the value into an integer, using atoi.
        break;
      }
    }

    file.close();
  }

File stream output

The following code example will demonstrate how to append to a file, duelleaguestat.inc.

 ofstream file("duelleaguestat.inc", ios::app);   // app = put all data at end of file

 if (!file.good()) // if there was an error opening the file
   sendPublic("*arena Error opening file.");
 else
 {
   file << squad1<< endl;  // squad1 = char[20]
   file << " vs "<< endl;
   file << squad2<< endl;  // squad2 = char[20]
   file.close();
 }

Similarly, you are able to write an output of a String to a file:

 // key is converting String to (char*) to file write
 String str = freqs[freq].slotname[slot];
 str += ", Repels: " + (String)(int) t->repel;
 file << endl;
 file << (char*) str;

Input with GetPrivateProfileString

GetPrivateProfileString(), a function provided by Windows for reading INI files, will automatically find an INI key (like "MaxBoxes=") in a file for you. See the MSDN Library for help on this function. This next example will show how to read input using GetPrivateProfileString() based on the rampage plugin.

The file format for rampage.ini is like this:

 7=is on a killing spree! (6:0)
 10=is opening a can of booya! (9:0)

In rampageini.cpp:

#include "rampageini.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#define NUM_RANKS 10
#define BUFFER_LEN 256

struct RampageSettings
{
	char quotes[NUM_RANKS][BUFFER_LEN];
};
 
void LoadSettings(RampageSettings &setts);

static char path[BUFFER_LEN];

char *rank_type[NUM_RANKS] = { "7", "10" };

void LoadSettings(RampageSettings &setts)
{
	GetCurrentDirectory(BUFFER_LEN - 64, path);
	strcat(path, "\rampage.ini");

	for (int i = 0; i < NUM_RANKS; ++i)
	{
		GetPrivateProfileString("Comments", rank_type[i], "-ERROR-",
					setts.quotes[i], BUFFER_LEN, path);
	}
}

Player data

As stated earlier in the tutorial, MervBot stores useful player data internally as Player objects, see player.h for implementation details.

  • p->name = player name stored as char[20] (Note: SubSpace protocol allows for usernames to be 19+ in length, do not rely on this for player-name comparisions.)
  • p->squad = player squad stored as char[20]
  • p->ship = ship (0-9) enumerated as SHIP_Warbird, SHIP_Spectator, etc..
  • p->safety = whether ship is in safety zone (boolean)
  • p->bounty = player bounty
  • p->energy = player energy (have bot with *energy on to get accurate readings)
  • p->flagCount = how many flags player is holding
  • p->team = player frequency
  • p->(burst, repel, thor, brick, decoy, rocket, portal) = how many items of that type player has
  • p->(stealth, cloak, xradar, awarp, ufo, flash, safety, shields, supers) = if player has that item on (boolean)
  • p->score.killPoints = player kill points
  • p->score.flagPoints = player flag points
  • p->score.wins = player kills from f2
  • p->score.losses = player deaths from f2

Just access the respective member of the Player class to check the player's property.

For example, in spawn.cpp, to check whether a player is in a safety zone:

EVENT_PlayerMove:
{
   Player *p = (Player*)event.p[0];

   if ( p->safety ) // player is in safe zone.
     {
        // do something.
     }

   if ( !p->safety ) // player NOT in safe zone.
     {
        // do something.
     }
}

Obtaining a player pointer using name comparison

Since Player pointers are internal to MERVBot, it is necessary to find a way of obtaining a Player pointer from the identifying information given by the game. One of the simpler ways is just to compare the names after converting to lowercase.

Note: Using pilot names as vital comparisions should be used with caution. See Bot-Issues by CypherJF.

// return Player* info (or NULL if not found) from p->name info
Player * botInfo::GetPilot(char *name)
{
	// get pilot from a name, return as TempPlayer
	_listnode <Player> *parse = playerlist->head;

	//convert search name to lowercase
	char nname[20], pname[20];
	strncpy(nname, name, 20);
	tolower(nname);

	while (parse)
	{
		Player *p = parse->item;

		// convert to lowercase to compare
		strncpy(pname,p->name,20);
		tolower(pname);
		if (strcmp(pname,nname)==0)
			return p;
		
		parse = parse->next;
	}

	return NULL;	//player not found
}

Bot built in functions

Here are some useful MervBot commands to control what the bot is doing.

Player.cpp:

  • Player::move(Sint32 x, Sint32 y) moves a player to the coordinates specified by x and y
  • Player::clone(Player *p) clones a player into a player class

Look in Commands.txt , command.cpp (core), or /!help to bot to see all bot external commands (example /!go <arena>).

LVZ Object toggling commands in plugins are to go here.