root/trunk/twcore/src/twcore/core/BotAction.java

Revision 3817, 155.1 KB (checked in by Dexter, 12 days ago)

lastest version

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1package twcore.core;
2
3import java.io.File;
4import java.sql.Connection;
5import java.sql.PreparedStatement;
6import java.sql.ResultSet;
7import java.sql.SQLException;
8import java.sql.Statement;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.Iterator;
12import java.util.List;
13import java.util.Map;
14import java.util.Timer;
15import java.util.TimerTask;
16import java.util.WeakHashMap;
17import java.util.HashMap;
18import java.util.Random;
19
20import twcore.core.command.TempSettingsManager;
21import twcore.core.game.Arena;
22import twcore.core.game.Flag;
23import twcore.core.game.Player;
24import twcore.core.game.Ship;
25import twcore.core.lvz.Objset;
26import twcore.core.net.GamePacketGenerator;
27import twcore.core.net.Email;
28import twcore.core.util.InterProcessCommunicator;
29import twcore.core.util.StringBag;
30import twcore.core.util.Tools;
31import twcore.core.util.ipc.IPCMessage;
32
33
34/**
35 * The main bot utility class, your bot's best and easiest method of performing
36 * actions in the arena, getting information on players, scheduling tasks to be
37 * performed, and much more.  Being familiar with the features of BotAction will
38 * allow you to do just about anything you require.  Every bot has a reference
39 * to BotAction available as m_botAction, thanks to the bot superclass, SubspaceBot.
40 * <p>
41 * <u>Some of BotAction's abilities include:</u><br>
42 *  - Sending public, private, arena, zone and warning messages.<br>
43 *  - Setting ships, freqs, locking/unlocking arenas, spec'ing, warping, and setting doors.<br>
44 *  - Scheduling tasks to run at a future time, or run repeatedly.<br>
45 *  - Creating teams by team size or number of teams, and setshipping or warping all on a freq.<br>
46 *  - Controlling the bot as a playing ship, changing arenas, and getting an arena list.<br>
47 *  - Setting how reliable position data for players is (important!).<br>
48 *  - Showing and hiding LVZ objects from one or all players using the Objset class to keep track.<br>
49 *  - Accessing SQL databases simply and effectively.<br>
50 *  - Sending messages between bots.<br>
51 *  - Getting data about players, flags, the bot itself, CFG files, player iterators, and more.<br>
52 * <p>
53 * Note that this is just SOME of what BotAction can do; it can do much, much more!
54 * <p>
55 * If you are currently viewing the JavaDoc, and wish to see a more logical and in-depth
56 * organization of all BotAction has to offer, <b><u>look at the BotAction source</b></u>.
57 * <p><br>
58 * BotAction Method Directory  (search source by number and title)
59 * <p>
60 * 1. TASK SCHEDULING - scheduling events w/ TimerTasks to run at a later time
61 * 2. MESSAGING - sending single and multi-line messages of all kinds
62 * 3. MISC OPERATIONS - everything else: setting ships & freqs, changing arenas,
63 * SQL DB ops, inter-process communications, etc.
64 * 4. GETTERS - getting accessible data, usually from instances of other classes
65 */
66public class BotAction
67{
68    private Timer               m_timer;            // Timer used to schedule TimerTasks
69    private String              m_arenaName;        // Name of arena bot is currently in
70    private Session             m_botSession;       // Reference to bot's Session
71    private Map<TimerTask, Object> m_timerTasks;    // List of TimerTasks being run (TimerTask -> null)
72    private Arena               m_arenaTracker;     // Arena tracker holding player/flag data
73    private GamePacketGenerator m_packetGenerator;  // Packet creator
74    private Objset              m_objectSet;        // For automation of LVZ object setting
75    private int                 m_botNumber;        // Bot's internal ID number
76    private TempSettingsManager m_tsm;              // Handles Temporary Settings
77    private int                                 DefaultSpectateTime;// Default time between switching from player to play with player spectating
78
79
80    /** Constructor for BotAction.  Don't worry about this, the object has already
81     * been constructed for you.
82     * @param botSession Bot thread this object is attached to
83     * @param packetGenerator Packet generator which is called when packets are sent out
84     * @param arena Arena Tracker object used to represent an arena full of players
85     */
86    public BotAction(GamePacketGenerator packetGenerator, Arena arena, Timer timer, int botNum, Session botSession)
87    {
88        m_timer = timer;
89        m_arenaTracker = arena;
90        m_botSession = botSession;
91        m_timerTasks = Collections.synchronizedMap(new WeakHashMap<TimerTask, Object>());
92        m_packetGenerator = packetGenerator;
93        m_botNumber = botNum;
94        m_objectSet = new Objset();
95        DefaultSpectateTime = getCoreData().getGeneralSettings().getInt( "DefaultSpectateTime" );
96    }
97
98
99    // **********************************************************************************
100    //
101    //                               1. TASK SCHEDULING
102    //
103    // **********************************************************************************
104    /*
105     * Task scheduling is not difficult in TWCore.  It allows you to create events
106     * that will run at a later time, or run repeatedly at regular intervals.
107     *
108     * To schedule a task, create a new internal class for your bot that extends the
109     * TimerTask class, and include a run() method in the class containing the code
110     * you want to run when the TimerTask executes.  Then call either scheduleTask
111     * (for running once) or scheduleTaskAtFixedRate (for running repeatedly) to
112     * schedule the task, and you're done.
113     *
114     * If you ever need to cancel the task, hold a reference to it and call the cancel
115     * method.  Make sure your tasks finish an execution as quickly as possible so as to
116     * avoid delaying other task from running.
117     */
118
119    /**
120     * Schedules a TimerTask to occur once at a future time.  TimerTask is part of
121     * the package java.util.  The only method that a subclass of TimerTask must
122     * override is public void run().
123     * <p>See the Task Scheduling heading in BotAction source to learn about task scheduling.
124     * @param task TimerTask to be executed
125     * @param delayms Length of time before execution, in milliseconds
126     */
127    public void scheduleTask(TimerTask task, long delayms)
128    {
129        m_timerTasks.put(task, null);
130        m_timer.schedule(task, delayms);
131    }
132
133    /**
134     * Schedules a TimerTask to occur repeatedly at an interval.  Execution is fixed-delay
135     * which means each execution will happen at least <b>periodms</b> from the last one whether
136     * it was delayed or not, resulting in more smoothness over time.  TimerTask is part of the
137     * <i>java.util</i> package.  The only method that a TimerTask must override is
138     * <i>public void run()</i>.
139     * <p>See the Task Scheduling heading in BotAction source to learn about task scheduling.
140     * @param task TimerTask to be executed
141     * @param delayms Length of time before execution, in milliseconds
142     * @param periodms Delay between executions after the initial execution, in milliseconds
143     */
144    public void scheduleTask(TimerTask task, long delayms, long periodms) {
145        m_timerTasks.put(task, null);
146        m_timer.schedule(task, delayms, periodms);
147    }
148
149    /**
150     * Schedules a TimerTask to occur repeatedly at an interval.  Execution is fixed-rate
151     * which means tasks can bunch up and execute in rapid succession if there is a delay.
152     * Use when timeliness is important.  TimerTask is part of <i>java.util</i> package.  The
153     * only method that a TimerTask must override is <i>public void run()</i>.
154     * <p>See the Task Scheduling heading in BotAction source to learn about task scheduling.
155     * @param task TimerTask to be executed
156     * @param delayms Length of time before execution, in milliseconds (time in ms. relative to the current time)
157     * @param periodms Delay between executions after the initial execution, in milliseconds
158     */
159    public void scheduleTaskAtFixedRate(TimerTask task, long delayms, long periodms)
160    {
161        m_timerTasks.put(task, null);
162        m_timer.scheduleAtFixedRate(task, delayms, periodms);
163    }
164
165    /**
166     * Cancels a TimerTask cleanly.  You may cancel an individual TimerTask by using
167     * task.cancel()!  BotAction's reference to the TimerTask is automatically freed
168     * when it is no longer referenced elsewhere in the application to prevent a memory leak.
169     * @param task The TimerTask to cancel
170     * @return true if this task is scheduled for one-time execution and has not yet run, or
171     * this task is scheduled for repeated execution. Returns false if the task was scheduled
172     * for one-time execution and has already run, or if the task was never scheduled, or if
173     * the task was already cancelled, or if task is null. (Loosely speaking, this method
174     * returns true if it prevents one or more scheduled executions from taking place.)
175     */
176    public boolean cancelTask(TimerTask task) {
177        m_timerTasks.remove(task);
178        if(task != null ) {
179                return task.cancel();
180        }
181        return false;
182    }
183
184    /**
185     * Cancels all pending TimerTasks.  You could cancel an individual TimerTask by using
186     * task.cancel()!  Or Use cancelTask(TimerTask) instead.  Note that if you cancel a TimerTask
187     * and it has already been cancelled, nothing will happen.
188     * <p>See the Task Scheduling heading in BotAction source to learn about task scheduling.
189     */
190    public void cancelTasks()
191    {
192        synchronized(m_timerTasks) {
193                Iterator<TimerTask> iter = m_timerTasks.keySet().iterator();
194                while(iter.hasNext()) {
195                        TimerTask task = iter.next();
196                        if(task != null)
197                                task.cancel();
198                        iter.remove();
199                }
200        }
201    }
202
203
204
205
206    // **********************************************************************************
207    //
208    //                                  2. MESSAGING
209    //
210    // **********************************************************************************
211    /*
212     * Messages generally consist of three components: a way to identify the target,
213     * which is either the player ID or name; the message itself; and optionally, a
214     * sound code to play along with the message.
215     *
216     * Messages come in several different types: public, private, team-wide, arena-wide,
217     * zone-wide, to an opposing team, on a chat, or as a public macro.
218     *
219     * Private messages also come in two flavors.  A normal private message is to
220     * someone in the arena (/), whereas a remote is to anywhere on the billing
221     * server, and requires the server to look up their location in order to deliver
222     * it.  Therefore, normal private messages cause less server load, but also
223     * will not be received if the player is outside the arena.  Use Smart private
224     * messages if you're unsure of where the player is, and want to guarantee that
225     * they receive the message.  TWCore will try to find the person in the arena,
226     * and if they can't be found, will send it remotely.
227     *
228     * Use private message spams to send many messages at once to a player, such as
229     * for a help display.
230     *
231     * [For the most part, this section should be self-explanatory.]
232     */
233
234    /**
235     * Displays a green arena message to the arena the bot is in.
236     * @param message The message to be displayed.
237     */
238    public void sendArenaMessage(String message)
239    {
240        sendArenaMessage(message, (byte) 0);
241    }
242
243    /**
244     * Displays a green arena message to the arena the bot is in, with a sound code.<p>
245     * <code><pre><u>Sound codes:</u>
246     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
247     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
248     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
249     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
250     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
251     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
252     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
253     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
254     * @param message The message to be displayed.
255     * @param soundCode Sound code to be sent along with the message.
256     */
257    public void sendArenaMessage(String message, int soundCode)
258    {
259        sendUnfilteredPublicMessage("*arena " + message, soundCode);
260    }
261
262    /**
263     * Sends a zone wide advertisement.  Do not use unless absolutely necessary.
264     * @param message The message to be sent
265     */
266    public void sendZoneMessage(String message)
267    {
268        sendZoneMessage(message, (byte) 0);
269    }
270
271    /**
272     * Sends a zone wide advertisement.  Do not use unless absolutely necessary.
273     * Includes a sound code.
274     * <code><pre><u>Sound codes:</u>
275     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
276     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
277     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
278     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
279     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
280     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
281     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
282     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
283     * @param message The away message to be sent
284     * @param soundCode Sound code to be sent along with the message.
285     */
286    public void sendZoneMessage(String message, int soundCode)
287    {
288        sendUnfilteredPublicMessage("*zone " + message, soundCode);
289    }
290
291    /**
292     * Sends a normal (blue) message to the public chat.
293     * @param message The message to be displayed.
294     */
295    public void sendPublicMessage(String message)
296    {
297        sendPublicMessage(message, 0);
298    }
299
300    /**
301     * Sends a normal (blue) message to the public chat with a sound code.
302     * <code><pre><u>Sound codes:</u>
303     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
304     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
305     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
306     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
307     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
308     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
309     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
310     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
311     * @param message The message to be displayed.
312     * @param soundCode Sound code to be sent along with the message.
313     */
314    public void sendPublicMessage(String message, int soundCode)
315    {
316        String temp = message.trim();
317        char firstChar;
318
319        if (temp.length() > 0)
320        {
321            firstChar = message.charAt(0);
322            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
323            {
324                sendUnfilteredPublicMessage(message, soundCode);
325            }
326        }
327    }
328
329    /**
330     * Sends a message to the public chat as a macro message.  Macro messages may
331     * be ignored by players if they choose.  A suggested use for this command would
332     * be to print regular rules displays for new players, allowing experienced players
333     * to ignore them if they choose.
334     * @param message The message to be displayed.
335     */
336    public void sendPublicMacro(String message)
337    {
338        sendPublicMacro(message, 0);
339    }
340
341    /**
342     * Sends a message to the public chat as a macro message.  Macro messages may
343     * be ignored by players if they choose.  A suggested use for this command would
344     * be to print regular rules displays for new players, allowing experienced players
345     * to ignore them if they choose.  This method can also play a sound w/ the macro.
346     * <code><pre><u>Sound codes:</u>
347     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
348     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
349     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
350     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
351     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
352     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
353     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
354     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
355     * @param message The message to be displayed.
356     * @param soundCode Sound code to be sent along with the message.
357     */
358    public void sendPublicMacro(String message, int soundCode)
359    {
360        String temp = message.trim();
361        char firstChar;
362
363        if (temp.length() > 0)
364        {
365            firstChar = message.charAt(0);
366            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
367            {
368                sendUnfilteredPublicMacro(message, soundCode);
369            }
370        }
371    }
372
373    /**
374     * Sends a private message to someone in the same arena.  Do not attempt to use
375     * this method if you are not absolutely certain that the player is in the arena.
376     * If you are at all unsure, use sendSmartPrivateMessage instead.
377     * @param name The name of the player.
378     * @param message The message to be displayed.
379     */
380    public void sendPrivateMessage(String name, String message)
381    {
382        if(name == null)
383            return;
384       
385        int playerID = m_arenaTracker.getPlayerID(name);
386        sendPrivateMessage(playerID, message, (byte) 0);
387    }
388
389    /**
390     * Sends a private message to someone in the same arena.  Do not attempt to use
391     * this method if you are not absolutely certain that the player is in the arena.
392     * If you are at all unsure, use sendSmartPrivateMessage instead.
393     * @param playerID Player ID of the player you wish to send the message to.
394     * @param message The message to be displayed.
395     */
396    public void sendPrivateMessage(int playerID, String message)
397    {
398        sendPrivateMessage(playerID, message, (byte) 0);
399    }
400
401    /**
402     * Sends a private message to someone in the same arena.  Do not attempt to use
403     * this method if you are not absolutely certain that the player is in the arena.
404     * If you are at all unsure, use sendSmartPrivateMessage instead.
405     * <code><pre><u>Sound codes:</u>
406     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
407     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
408     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
409     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
410     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
411     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
412     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
413     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
414     * @param name The name of the player.
415     * @param message The message to be displayed.
416     * @param soundCode Sound code to be sent along with the message.
417     */
418    public void sendPrivateMessage(String name, String message, int soundCode)
419    {
420        if(name == null)
421            return;
422       
423        int playerID = m_arenaTracker.getPlayerID(name);
424        sendPrivateMessage(playerID, message, soundCode);
425    }
426
427    /**
428     * Sends a private message to someone in the same arena.  Do not attempt to use
429     * this method if you are not absolutely certain that the player is in the arena.
430     * If you are at all unsure, use sendSmartPrivateMessage instead.
431     * <code><pre><u>Sound codes:</u>
432     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
433     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
434     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
435     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
436     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
437     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
438     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
439     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
440     * @param playerID Player ID of the player you wish to send the message to.
441     * @param message The message to be displayed.
442     * @param soundCode Sound code to be sent along with the message.
443     */
444    public void sendPrivateMessage(int playerID, String message, int soundCode)
445    {
446        char firstChar;
447
448        if (message.length() > 0)
449        {
450            firstChar = message.charAt(0);
451            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
452            {
453                sendUnfilteredPrivateMessage(playerID, message, soundCode);
454            }
455        }
456    }
457
458    /**
459     * Sends a smart private message.  A smart private message is a private message
460     * sent much like the Continuum client does it.  If a player is not present in the
461     * arena, the message will be sent as a remote private message.  Otherwise, the
462     * message will be sent as a private message.  Using this will help save server
463     * time over remote private messages, and appear more natural to players.
464     * @param name The name of the player.
465     * @param message The message to be displayed.
466     */
467    public void sendSmartPrivateMessage(String name, String message)
468    {
469        if(name == null)
470            return;
471       
472        sendSmartPrivateMessage(name, message, 0);
473    }
474
475    /**
476     * Sends a smart private message.  A smart private message is a private message
477     * sent much like the Continuum client does it.  If a player is not present in the
478     * arena, the message will be sent as a remote private message.  Otherwise, the
479     * message will be sent as a private message.  Using this will help save server
480     * time over remote private messages, and appear more natural to players.
481     * <code><pre><u>Sound codes:</u>
482     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
483     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
484     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
485     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
486     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
487     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
488     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
489     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
490     * @param name The name of the player.
491     * @param message The message to be displayed.
492     * @param soundCode Sound code to be sent along with the message.
493     */
494    public void sendSmartPrivateMessage(String name, String message, int soundCode)
495    {
496        if(name == null)
497            return;
498       
499        int playerID = m_arenaTracker.getPlayerID(name);
500        if (playerID == -1)
501        {
502            sendRemotePrivateMessage(name, message, soundCode);
503        }
504        else
505        {
506            sendPrivateMessage(playerID, message, soundCode);
507        }
508    }
509
510    /**
511     * Sends a remote private message.  Remote private messages look like (Name)> to
512     * the player, even if the player is in the same arena as you.  Try to use smart
513     * private messages instead.
514     * @param name The name of the player.
515     * @param message The message to be displayed.
516     */
517    public void sendRemotePrivateMessage(String name, String message)
518    {
519        if(name == null)
520            return;
521       
522        sendRemotePrivateMessage( name, message, 0 );
523    }
524
525    /**
526     * Sends a remote private message.  Remote private messages look like (Name)> to
527     * the player, even if the player is in the same arena as you.  Try to use smart
528     * private messages instead.
529     * <code><pre><u>Sound codes:</u>
530     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
531     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
532     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
533     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
534     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
535     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
536     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
537     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
538     * @param name The name of the player.
539     * @param message The message to be displayed.
540     * @param soundCode Sound code to be sent along with the message.
541     */
542    public void sendRemotePrivateMessage(String name, String message, int soundCode)
543    {
544        if( message == null )
545            return;
546        if(name == null)
547            return;
548       
549        char firstChar;
550
551        if (message.length() > 0)
552        {
553            firstChar = message.charAt(0);
554            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
555            {
556                m_packetGenerator.sendChatPacket((byte) 7, (byte) soundCode, (short) 0, ":" + name + ":" + message);
557            }
558        }
559    }
560
561    /**
562     * Sends a message to bot's teammates.  If the bot is in spectator mode, it will
563     * speak with the players in spectator mode.
564     * @param message The message to be displayed.
565     */
566    public void sendTeamMessage(String message)
567    {
568        sendTeamMessage(message, (byte) 0);
569    }
570
571    /**
572     * Sends a message to bot's teammates.  If the bot is in spectator mode, it will
573     * speak with the players in spectator mode.  Includes a sound code.
574     * <code><pre><u>Sound codes:</u>
575     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
576     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
577     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
578     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
579     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
580     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
581     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
582     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
583     * @param message The message to be displayed.
584     * @param soundCode Sound code to be sent along with the message.
585     */
586    public void sendTeamMessage(String message, int soundCode)
587    {
588        String temp = message.trim();
589        char firstChar;
590
591        if (temp.length() > 0)
592        {
593            firstChar = message.charAt(0);
594            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
595            {
596                m_packetGenerator.sendChatPacket((byte) 3, (byte) soundCode, (short) 0, message);
597            }
598        }
599    }
600
601    /**
602     * Sends a message to a whole frequency of players. (")
603     * @param frequency The frequency this message is to be sent to.
604     * @param message The message to be displayed.
605     */
606    public void sendOpposingTeamMessageByFrequency(int frequency, String message)
607    {
608        sendOpposingTeamMessageByFrequency(frequency, message, (byte) 0);
609    }
610
611    /**
612     * Sends a message to a whole frequency of players ("), with sound code.
613     * <code><pre><u>Sound codes:</u>
614     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
615     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
616     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
617     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
618     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
619     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
620     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
621     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
622     * @param frequency The frequency this message is to be sent to.
623     * @param message The message to be sent
624     * @param soundCode Sound code to be sent along with the message (0 if none).
625     */
626    public void sendOpposingTeamMessageByFrequency(int frequency, String message, int soundCode)
627    {
628        Iterator<Integer> i;
629        int playerID;
630        char firstChar;
631        String temp = message.trim();
632
633        if (temp.length() > 0)
634        {
635            firstChar = message.charAt(0);
636            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
637            {
638                i = m_arenaTracker.getFreqIDIterator(frequency);
639                if (i != null)
640                {
641                    if( i.hasNext() ) {
642                        playerID = i.next().intValue();
643                        m_packetGenerator.sendChatPacket((byte) 4, (byte) soundCode, (short) playerID, message);
644                    }
645                }
646            }
647        }
648    }
649
650    /**
651     * Sends a message to a whole frequency of players based on the ID of one player
652     * on the frequency.  Includes sound code.
653     * <code><pre><u>Sound codes:</u>
654     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
655     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
656     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
657     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
658     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
659     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
660     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
661     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
662     * @param playerID The id of the player whose frequency this message is to be sent to.
663     * @param message The message to be sent
664     * @param soundCode Sound code to be sent along with the message (0 if none).
665     */
666    public void sendOpposingTeamMessage( int playerID, String message, int soundCode ) {
667
668        char firstChar;
669        String temp = message.trim();
670
671        if (temp.length() > 0)
672        {
673            firstChar = message.charAt(0);
674            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
675            {
676                m_packetGenerator.sendChatPacket((byte) 4, (byte) soundCode, (short)playerID, message);
677            }
678        }
679    }
680
681    /**
682     * Sends a message to a whole frequency of players based on the name of a player
683     * on that frequency.  Includes sound code.
684     * <code><pre><u>Sound codes:</u>
685     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
686     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
687     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
688     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
689     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
690     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
691     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
692     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
693     * @param playerName The name of the player whose frequency this message is to be sent to.
694     * @param message The message to be sent
695     * @param soundCode Sound code to be sent along with the message (0 if none).
696     */
697    public void sendOpposingTeamMessage( String playerName, String message, int soundCode ){
698       
699        if(playerName == null)
700            return;
701       
702        int         playerID = m_arenaTracker.getPlayerID( playerName );
703
704        sendOpposingTeamMessage( playerID, message, soundCode );
705    }
706
707    /**
708     * Sends a chat message to the first chat this bot has joined.
709     * @param message The message to be displayed.
710     */
711    public void sendChatMessage(String message)
712    {
713        sendChatMessage(1, message, (byte) 0);
714    }
715
716    /**
717     * Sends a chat message to a specific chat number out of the chats the bot
718     * has joined.
719     * @param chatNumber Number of the chat to send information to.
720     * @param message The message to be displayed.
721     */
722    public void sendChatMessage(int chatNumber, String message)
723    {
724        sendChatMessage(chatNumber, message, (byte) 0);
725    }
726
727    /**
728     * Sends a chat message to a specific chat number, with a sound code.
729     * <code><pre><u>Sound codes:</u>
730     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
731     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
732     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
733     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
734     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
735     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
736     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
737     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
738     * @param chatNumber Number of the chat to send information to.
739     * @param message The message to be displayed.
740     * @param soundCode Sound code to be sent along with the message.
741     */
742    public void sendChatMessage(int chatNumber, String message, int soundCode)
743    {
744        if( message == null )
745            return;
746        String temp = message.trim();
747        char firstChar;
748
749        if (temp.length() > 0)
750        {
751            firstChar = message.charAt(0);
752            if (firstChar != '/' && firstChar != '*' && firstChar != '?' && firstChar != ';')
753            {
754                m_packetGenerator.sendChatPacket((byte) 9, (byte) soundCode, (short) 0, ";" + chatNumber + ";" + message);
755            }
756        }
757    }
758
759    /**
760     * Sends a squad message to a specific squad.
761     * @param squadName Name of the squad
762     * @param message Message to send to that squad
763     */
764    public void sendSquadMessage(String squadName, String message)
765    {
766        if(squadName == null)
767            return;
768       
769        sendSquadMessage(squadName, message, 0);
770    }
771
772    /**
773     * Sends a squad message to a specific squad with a sound code.
774     * <code><pre><u>Sound codes:</u>
775     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
776     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
777     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
778     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
779     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
780     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
781     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
782     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
783     * @param squadName Name of the squad
784     * @param message Message to send to that squad
785     * @param soundCode Sound code to be sent along with the message.
786     */
787    public void sendSquadMessage(String squadName, String message, int soundCode)
788    {
789        if(squadName == null)
790            return;
791       
792        m_packetGenerator.sendChatPacket((byte) 7, (byte) soundCode, (short) 0, ":#" + squadName + ":" + message);
793    }
794
795    /**
796     * Sends a "?help" alert command.
797     * @param message The message to be sent along with the alert
798     */
799    public void sendHelpMessage(String message)
800    {
801        sendUnfilteredPublicMessage("?help " + message);
802    }
803
804    /**
805     * Sends a "?cheater" alert command.
806     * @param message The message to be sent along with the alert
807     */
808    public void sendCheaterMessage(String message)
809    {
810        sendUnfilteredPublicMessage("?cheater " + message);
811    }
812
813    /**
814     * Sends an alert command as specified.
815     * @param type The type of alert command to be sent
816     * @param message The message to be sent along with the alert
817     */
818    public void sendAlertMessage(String type, String message)
819    {
820        sendUnfilteredPublicMessage("?" + type + " " + message);
821    }
822
823
824    // ***** ULFILTERED MESSAGING *****
825
826    /**
827     * Sends a private message without any filtration.  Use this ONLY in situations
828     * where you are hard coding in commands to be sent.  For the sake of security, use
829     * the filtered methods for *everything* else.
830     * @param name Name of the person the message is to be sent to.
831     * @param message Message to be sent.
832     */
833    public void sendUnfilteredPrivateMessage(String name, String message)
834    {
835        int playerID = m_arenaTracker.getPlayerID(name);
836        sendUnfilteredPrivateMessage(playerID, message, (byte) 0);
837    }
838
839    /**
840     * Sends a private message without any filtration.  Use this ONLY in situations
841     * where you are hard coding in commands to be sent.  For the sake of security, use
842     * the filtered methods for *everything* else.
843     * @param playerID Player ID of the player you wish to send the message to.
844     * @param message Message to be sent.
845     */
846    public void sendUnfilteredPrivateMessage(int playerID, String message)
847    {
848        sendUnfilteredPrivateMessage(playerID, message, (byte) 0);
849    }
850
851    /**
852     * Sends a private message without any filtration.  Use this ONLY in situations
853     * where you are hard coding in commands to be sent.  For the sake of security, use
854     * the filtered methods for *everything* else.
855     * <code><pre><u>Sound codes:</u>
856     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
857     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
858     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
859     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
860     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
861     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
862     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
863     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
864     * @param name Name of the person the message is to be sent to.
865     * @param message Message to be sent.
866     * @param soundCode Sound code to be sent along with the message.
867     */
868    public void sendUnfilteredPrivateMessage(String name, String message, int soundCode)
869    {
870        int playerID = m_arenaTracker.getPlayerID(name);
871        sendUnfilteredPrivateMessage(playerID, message, soundCode);
872    }
873
874    /**
875     * Sends a private message without any filtration.  Use this ONLY in situations
876     * where you are hard coding in commands to be sent.  For the sake of security, use
877     * the filtered methods for *everything* else.
878     * <code><pre><u>Sound codes:</u>
879     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
880     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
881     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
882     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
883     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
884     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
885     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
886     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
887     * @param playerID Player ID of the player you wish to send the message to.
888     * @param message Message to be sent.
889     * @param soundCode Sound code to be sent along with the message.
890     */
891    public void sendUnfilteredPrivateMessage(int playerID, String message, int soundCode)
892    {
893        m_packetGenerator.sendChatPacket((byte) 5, (byte) soundCode, (short) playerID, message);
894    }
895
896    /**
897     * Sends a public message without any filtration.  Use this ONLY in situations
898     * where you are hard coding in commands to be sent.  For the sake of security, use
899     * the filtered methods for *everything* else.
900     * @param message Message to be sent.
901     */
902    public void sendUnfilteredPublicMessage(String message)
903    {
904        sendUnfilteredPublicMessage(message, (byte) 0);
905    }
906
907    /**
908     * Sends a public message without any filtration.  Use this ONLY in situations
909     * where you are hard coding in commands to be sent.  For the sake of security, use
910     * the filtered methods for *everything* else.
911     * <code><pre><u>Sound codes:</u>
912     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
913     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
914     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
915     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
916     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
917     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
918     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
919     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
920     * @param message Message to be sent.
921     * @param soundCode Sound code to be sent along with the message.
922     */
923    public void sendUnfilteredPublicMessage(String message, int soundCode)
924    {
925        if(message.startsWith("*sendto"))return;//This would crash the zone.
926        m_packetGenerator.sendChatPacket((byte) 2, (byte) soundCode, (short) 0, message);
927    }
928
929   /**
930     * Sends a public macro without any filtration.  For the sake of security, use
931     * the filtered macro methods unless you know what you're doing.
932     * @param message Message to be sent.
933     */
934    public void sendUnfilteredPublicMacro(String message)
935    {
936        sendUnfilteredPublicMacro(message, (byte) 0);
937    }
938
939    /**
940     * Sends a public macro without any filtration.  For the sake of security, use
941     * the filtered macro methods unless you know what you're doing.
942     * <code><pre><u>Sound codes:</u>
943     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
944     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
945     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
946     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
947     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
948     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
949     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
950     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
951     * @param message Message to be sent.
952     * @param soundCode Sound code to be sent along with the message.
953     */
954    public void sendUnfilteredPublicMacro(String message, int soundCode)
955    {
956        m_packetGenerator.sendChatPacket((byte) 1, (byte) soundCode, (short) 0, message);
957    }
958
959    /**
960     * Sends a message to a whole frequency of players. (")
961     * @param frequency The frequency this message is to be sent to.
962     * @param message The message to be displayed.
963     */
964    public void sendUnfilteredTargetTeamMessage(int frequency, String message)
965    {
966        sendUnfilteredTargetTeamMessage(frequency, message, 0);
967    }
968
969    /**
970     * Sends a message to a whole frequency of players ("), with sound code.
971     * <code><pre><u>Sound codes:</u>
972     *  1 Beep #1           9 Listen to me   17 Under attack    25 Can't login
973     *  2 Beep #2          10 Baby crying    18 (Gibberish)     26 Beep #3
974     *  3 AT&T             11 Burp           19 Crowd: Oooo!   100 Start techno loop
975     *  4 Violent content  12 Orgasm (!)     20 Crowd: Gee!    101 Stop techno loop
976     *  5 Hallelujah       13 Scream         21 Crowd: Ohhh!   102 Play bad techno once
977     *  6 R.Reagan (long)  14 Fart #1        22 Crowd: Awww!   103 Victory bell
978     *  7 Inconceivable!   15 Fart #2        23 Game sucks     104 Goal! (or: go go go)
979     *  8 W.Churchill      16 Phone ring     24 Sheep bleat </pre></code>
980     * @param frequency The frequency this message is to be sent to.
981     * @param message The message to be sent
982     * @param soundCode Sound code to be sent along with the message (0 if none).
983     */
984    public void sendUnfilteredTargetTeamMessage(int frequency, String message, int soundCode)
985    {
986        Iterator<Integer> i;
987        int playerID;
988        String temp = message.trim();
989
990        if (temp.length() > 0) {
991            i = m_arenaTracker.getFreqIDIterator(frequency);
992            if (i != null) {
993                if( i.hasNext() ) {
994                    playerID = i.next().intValue();
995                    m_packetGenerator.sendChatPacket((byte) 4, (byte) soundCode, (short) playerID, message);
996                }
997            }
998        }
999    }
1000
1001
1002    // ***** MULTI-LINE MESSAGING *****
1003
1004    /**
1005     * Sends the contents of the String array to the player in private messages.
1006     * Note that the player should be in the arena or this may fail.
1007     * @param playerID PlayerID to send the messages to.
1008     * @param spam The array of Strings to send to the player in private messages.
1009     */
1010    public void privateMessageSpam(int playerID, String[] spam)
1011    {
1012        for (int i = 0; i < spam.length; i++)
1013        {
1014            sendPrivateMessage(playerID, spam[i]);
1015        }
1016    }
1017
1018    /**
1019     * Sends the contents of the String array to the player in private messages.
1020     * Note that the player should be in the arena or this may fail.
1021     * @param playerName Player name to send the messages to.
1022     * @param spam The array of Strings to send to the player in private messages.
1023     */
1024    public void privateMessageSpam(String playerName, String[] spam)
1025    {
1026        for (int i = 0; i < spam.length; i++)
1027        {
1028            sendPrivateMessage(playerName, spam[i]);
1029        }
1030    }
1031
1032    /**
1033     * Private message spam but with a generalized collection to allow for dynamic
1034     * help statements, such as those generated by CommandInterpreter.
1035     * <p>This will not work remotely as in REMOTE_PRIVATE_MESSAGE type, because
1036     * playerIDs have no relevance outside of an Arena.  For smart private messages,
1037     * get the player's name and use privateMessageSpam(String, Collection) instead.
1038     * @param playerID ID of the player to be spammed
1039     * @param messages The collection of messages (Need to be String Objects or typed to string objects)
1040     */
1041    public void privateMessageSpam(int playerID, Collection<String> messages)
1042    {
1043        Iterator<String> i = messages.iterator();
1044        while (i.hasNext())
1045        {
1046            sendPrivateMessage(playerID, i.next());
1047        }
1048    }
1049
1050    /**
1051     * Private message spam but with a generalized collection to allow for dynamic
1052     * help statements, such as those generated by CommandInterpreter.
1053     * <p>Will work if player is in or outside of the arena.
1054     * @param playerName Name of the player to be spammed
1055     * @param messages The collection of messages (Need to be String Objects or typed to string objects)
1056     */
1057    public void privateMessageSpam(String playerName, Collection<String> messages)
1058    {
1059        Iterator<String> i = messages.iterator();
1060        while (i.hasNext())
1061        {
1062            sendSmartPrivateMessage(playerName, i.next());
1063        }
1064    }
1065
1066    /**
1067     * Sends the contents of the String array to the player in remote private messages.
1068     * Careful with this one, sending too many across the billing server can cause
1069     * trouble.
1070     * @param playerName Name of the player to send the messages to.
1071     * @param spam The array of Strings to send to the player in private messages.
1072     */
1073    public void remotePrivateMessageSpam(final String playerName, final String[] spam)
1074    {
1075        for (int i = 0; i < spam.length; i++)
1076        {
1077            sendRemotePrivateMessage(playerName, spam[i]);
1078        }
1079    }
1080
1081    /**
1082     * This sends the contents of the String array to the player in smart private
1083     * messages.  Smart private messages are private messages that are sent as private
1084     * messages if the player is in the same arena, and remote private messages if the
1085     * player is not in the arena.
1086     * @param playerName Name of the player to send the messages to.
1087     * @param spam The array of Strings to send to the player in private messages.
1088     */
1089    public void smartPrivateMessageSpam(final String playerName, final String[] spam)
1090    {
1091        int playerID = m_arenaTracker.getPlayerID(playerName);
1092        if (playerID == -1)
1093        {
1094            remotePrivateMessageSpam(playerName, spam);
1095        }
1096        else
1097        {
1098            privateMessageSpam(playerName, spam);
1099        }
1100    }
1101
1102    /**
1103     * Sends multiline arena messages.
1104     * @param spam Array of Strings to arena
1105     */
1106    public void arenaMessageSpam(final String[] spam) {
1107        if(spam != null) {
1108            for(int i = 0; i < spam.length; i++) {
1109                sendArenaMessage(spam[i]);
1110            }
1111        }
1112    }
1113
1114    /**
1115     * Sends an e-mail message
1116     *
1117     * @param recipient E-mail address of recipient
1118     * @param subject Subject of the e-mail
1119     * @param text Body of the message
1120     * @return boolean TRUE if successfull, FALSE if an error was encountered
1121     */
1122    public boolean sendEmailMessage(String recipient, String subject, String text) {
1123        BotSettings cfg = this.getGeneralSettings();
1124        Email email = new Email(cfg.getString("MailHost"), cfg.getInt("MailPort"), cfg.getString("MailUser"), cfg.getString("MailPass"), cfg.getInt("SSL")==1);
1125       
1126        try {
1127                email.send(cfg.getString("MailUser"), recipient, subject, text);
1128               
1129        } catch(Exception e) {
1130                Tools.printStackTrace(e);
1131                return false;
1132        }
1133       
1134        return true;
1135    }
1136
1137
1138
1139
1140    // **********************************************************************************
1141    //
1142    //                                 3. MISC OPERATIONS
1143    //
1144    // **********************************************************************************
1145    /*
1146     * This is the main section of BotAction, covering the general actions you can use:
1147     *
1148     *  - BASIC PLAYER OPERATIONS: Set ships & freqs, warp, send to spec, reset scores,
1149     *    give prizes, add bounty, send warns, record damage to/from a player.
1150     *  - ARENA OPERATIONS: Lock/unlock arena, reset flag game, set timer, set doors,
1151     *    toggle public & private chat on/off, set power of thors in arena.
1152     *  - COMPLEX OPERATIONS: Team creation by team size or # teams, mass warping,
1153     *    mass shipsetting, mass setfreq'ing, old spec@death method, arena data clear.
1154     *  - BOT OPERATIONS: Join & change arenas, move ship, spectate on players, pick up
1155     *    and drop flags, set a banner, force an in-game death, force complete bot
1156     *    termination (logoff), force receiving of all death packets, get list of arenas,
1157     *    add spam protection, increase or decrease reliability of player position data,
1158     *    adjust rate of packets being sent, send and receive files to server.
1159     *  - LVZ OBJECT OPERATIONS: Show & hide one LVZ obj, show or hide a set of objs,
1160     *    and show & hide a set of objects based on objects managed by Objset.
1161     *  - SQL DATABASE OPERATIONS: Run regular, background and high-priority background
1162     *    queries, and automatically-formed table insertions for new records.
1163     *  - INTER-PROCESS COMMUNICATIONS: (For sending messages between bots -- very easy).
1164     *    Send a standard IPC message, transmit a generic object, subscribe bot to an
1165     *    IPC channel, unsubscribe from an IPC channel, and destroy an IPC channel.
1166     *
1167     */
1168
1169    // ***** BASIC PLAYER OPERATIONS *****
1170    /*
1171     * Simple operations that either have a target, or apply to all players.
1172     */
1173
1174    /**
1175     * Changes the ship type of a player to the type specified.  Ship should be
1176     * 1 to 8 (0 does not set a player to spectator, though when using getShipType()
1177     * inside player, 0 will correspond to the spectator ship).  Note that unlike
1178     * the internal packet protocol, this command uses the more accepted public
1179     * numbering system instead of using ship 0 for warbird and ship 8 for spectator.
1180     * @param playerID Player ID of the player you want to change.
1181     * @param shipType Ship type that you wish to set.  Must be a value between 1 and 8.
1182     */
1183    public void setShip(int playerID, int shipType)
1184    {
1185        sendUnfilteredPrivateMessage(playerID, "*setship " + shipType);
1186    }
1187
1188    /**
1189     * Changes the ship type of a player to the type specified.  Ship should be
1190     * 1 to 8 (0 does not set a player to spectator, though when using getShipType()
1191     * inside player, 0 will correspond to the spectator ship).  Note that unlike
1192     * the internal packet protocol, this command uses the more accepted public
1193     * numbering system instead of using ship 0 for warbird and ship 8 for spectator.
1194     * @param playerName The name of the player.
1195     * @param shipType Ship type that you wish to set.  Must be a value between 1 and 8.
1196     */
1197    public void setShip(String playerName, int shipType)
1198    {
1199        sendUnfilteredPrivateMessage(playerName, "*setship " + shipType);
1200    }
1201
1202    /**
1203     * Changes the frequency a player is on.
1204     * <p>You may also set spectators to a freq with this command so that they
1205     * can use teamchat there, but if they then enter the game or have their
1206     * ship set, they will not automatically enter in with that freq.
1207     * @param playerID Player ID of the player you want to change.
1208     * @param freqNum Frequency you want this player to be on.
1209     */
1210    public void setFreq(int playerID, int freqNum)
1211    {
1212        sendUnfilteredPrivateMessage(playerID, "*setfreq " + freqNum);
1213    }
1214
1215    /**
1216     * Changes the frequency a player is on.
1217     * <p>You may also set spectators to a freq with this command so that they
1218     * can use teamchat there, but if they then enter the game or have their
1219     * ship set, they will not automatically enter in with that freq.
1220     * @param playerName The name of the player.
1221     * @param freqNum Frequency you want this player to be on.
1222     */
1223    public void setFreq(String playerName, int freqNum)
1224    {
1225        sendUnfilteredPrivateMessage(playerName, "*setfreq " + freqNum);
1226    }
1227
1228    /**
1229     * Warps a player to the given tile coordinates.  Exact positioning is not
1230     * offered by this command, operating on the 1 to 1024 scheme rather than
1231     * the more accurate 1 to 16384 (scale of 16).  512,512 is nearly center.
1232     * @param playerID PlayerID of the player to be warped
1233     * @param xTiles X coordinate (1 to 1024)
1234     * @param yTiles Y coordinate (1 to 1024)
1235     */
1236   
1237    /**
1238     * Splits a frequence in two teams & warps each team to a location. Example:
1239     * if you want to split freq 0 in two teams and warp them to different coords,
1240     * so a team of freq 0 can go to a coord(like top right) and other team to other(like bottom left).
1241     * Works for even and odd numbers of frequence's size.
1242     * @param freq Frequence number that'll be split in 2 teams
1243     * @param X1 X coordinate in Tiles to first team
1244     * @param Y1 Y coordinate in Tiles to first team
1245     * @param X2 X coordinate in Tiles to second team
1246     * @param Y2 Y coordinate in Tiles to second team
1247     */
1248        public void splitTeam(int freq, int X1, int Y1, int X2, int Y2){
1249               
1250                Iterator<Player> freqIterator = getFreqPlayerIterator(freq); //we need an iterator to freq
1251                int freqSize = getFrequencySize(freq) ; //we need to know the freq size
1252               
1253                /*now, it'll see if the number of size is even or odd. if even, we split it well.
1254                otherwise we put the (size-1)/2...(like the number 5, 5-1 = 4. 4/2 = 2.
1255                then we put 2 members on a location and the other 3(the rest) will go to the other location
1256                */
1257                if(freqSize % 2 == 0 && freqSize != 0){ 
1258                        int i;
1259                        for( i = 1; i <= (freqSize/2) ; i++){//half to a location and...
1260                                Player p = (Player) freqIterator.next();
1261                                warpTo(p.getPlayerName(), X1 , Y1 );
1262                        }
1263                        for(int u = i; u<= freqSize ; u++){ 
1264                                Player p = (Player) freqIterator.next();//and the other half to other
1265                                warpTo(p.getPlayerName(), X2, Y2 );
1266                               
1267                        }
1268                }
1269                //odd number of freqsize
1270                else if(freqSize != 0){
1271                        int i;
1272                        for( i = 1; i <= ((freqSize-1)/2) ; i++){ //the little amount goin to a location
1273                                Player p = (Player) freqIterator.next();
1274                                warpTo(p.getPlayerName(), X1 , Y1 );
1275                        }
1276                        for(int u = i; u<= freqSize ; u++){ //then, the rest goes to other location
1277                                Player p = (Player) freqIterator.next();
1278                                warpTo(p.getPlayerName(), X2, Y2 );
1279                               
1280                        }
1281                }
1282
1283
1284        }
1285
1286    public void warpTo(int playerID, int xTiles, int yTiles)
1287    {
1288        sendUnfilteredPrivateMessage(playerID, "*warpto " + xTiles + " " + yTiles);
1289        Player p = getPlayer(playerID);
1290        if( p != null )
1291            p.updatePlayerPositionManuallyAfterWarp( xTiles, yTiles );
1292        spectatePlayer( playerID );
1293    }
1294
1295    /**
1296     * Warps a player to the given tile coordinates.  Exact positioning is not
1297     * offered by this command, operating on the 1 to 1024 scheme rather than
1298     * the more accurate 1 to 16384 (scale of 16).  512,512 is nearly center.
1299     * @param playerName The name of the player.
1300     * @param xTiles X coordinate (1 to 1024)
1301     * @param yTiles Y coordinate (1 to 1024)
1302     */
1303    public void warpTo(String playerName, int xTiles, int yTiles)
1304    {
1305        sendUnfilteredPrivateMessage(playerName, "*warpto " + xTiles + " " + yTiles);
1306        Player p = getPlayer(playerName);
1307        if( p != null ) {
1308            p.updatePlayerPositionManuallyAfterWarp( xTiles, yTiles );
1309            spectatePlayer(p.getPlayerID());
1310        }
1311    }
1312
1313    /**
1314     * Warps a player to the given tile coordinates with a radius.
1315     * @param playerID PlayerID of the player to be warped
1316     * @param xTiles X coordinate (1 to 1024)
1317     * @param yTiles Y coordinate (1 to 1024)
1318     * @param radius Radius from (xTiles, yTiles) player can be warped
1319     */
1320    public void warpTo(int playerID, int xTiles, int yTiles, int radius){
1321        Random ran = new Random();
1322        int x = 0, y = 0;
1323        while(Math.sqrt( Math.pow(y-yTiles, 2) + Math.pow(x-xTiles,2)) > radius){
1324            x = ran.nextInt( (radius*2) + 1 ) + (xTiles - radius);
1325            y = ran.nextInt( (radius*2) + 1 ) + (yTiles - radius);
1326        }
1327        warpTo(playerID, x, y);
1328    }
1329
1330    /**
1331     * Warps a player to the given tile coordinates with a radius.
1332     * @param playerName The name of the player.
1333     * @param xTiles X coordinate (1 to 1024)
1334     * @param yTiles Y coordinate (1 to 1024)
1335     * @param radius Radius from (xTiles, yTiles) player can be warped
1336     */
1337    public void warpTo(String playerName, int xTiles, int yTiles, int radius){
1338        Random ran = new Random();
1339        int x = 0, y = 0;
1340        while(Math.sqrt( Math.pow(y-yTiles, 2) + Math.pow(x-xTiles,2)) > radius){
1341            x = ran.nextInt( (radius*2) + 1 ) + (xTiles - radius);
1342            y = ran.nextInt( (radius*2) + 1 ) + (yTiles - radius);
1343        }
1344        warpTo(playerName, x, y);
1345    }
1346
1347    /**
1348     * Warps player to a random location as defined in the CFG, and then sets to
1349     * spectate on them in order to update their position.
1350     * @param playerID PlayerID of the player to be warped.
1351     */
1352    public void warpRandomly( int playerID ) {
1353        specificPrize(playerID, Tools.Prize.WARP);
1354        spectatePlayer( playerID );
1355    }
1356
1357    /**
1358     * Warps player to a random location as defined in the CFG, and then sets to
1359     * spectate on them in order to update their position.
1360     * @param playerName The name of the player.
1361     */
1362    public void warpRandomly( String playerName ) {
1363        specificPrize(playerName, Tools.Prize.WARP);
1364        int id = getPlayerID( playerName );
1365        if( id != -1 )
1366            spectatePlayer( id );
1367    }
1368
1369    /**
1370     * Signals the end of the King of the Hill game for this client.
1371     */
1372    public void endKOTH() {
1373        m_packetGenerator.sendEndKoTH();
1374    }
1375
1376    /**
1377     * Issues a /*spec command to the given player, sending them immediately to
1378     * spectator mode.  Note that if this command is issued just once, it will
1379     * lock the player in spectator mode, making it impossible for them to enter
1380     * an arena even if it's unlocked.  The command needs to be issued twice in
1381     * order to let them re-enter when the arena is unlocked -- once to spec and
1382     * lock, and once to unlock.  Alternately you can use specWithoutLock() to
1383     * automatically issue two /*spec commands.
1384     * @param playerName The name of the player to be specced.
1385     */
1386    public void spec(String playerName)
1387    {
1388        sendUnfilteredPrivateMessage(playerName, "*spec");
1389    }
1390
1391    /**
1392     * Issues a /*spec command to the given player, sending them immediately to
1393     * spectator mode.  Note that if this command is issued just once, it will
1394     * lock the player in spectator mode, making it impossible for them to enter
1395     * an arena even if it's unlocked.  The command needs to be issued twice in
1396     * order to let them re-enter when the arena is unlocked -- once to spec and
1397     * lock, and once to unlock.  Alternately you can use specWithoutLock() to
1398     * automatically issue two /*spec commands.
1399     * @param playerID Player ID of the player you want to spec.
1400     */
1401    public void spec(int playerID)
1402    {
1403        sendUnfilteredPrivateMessage(playerID, "*spec");
1404    }
1405
1406    /**
1407     * Sends a player immediately to spectator mode, and does not lock them there.
1408     * If the arena is unlocked, they will be able to re-enter.  This is the
1409     * standard way to spec someone, and is equivalent to issuing two /*spec's.
1410     * @param playerName The name of the player to be specced.
1411     */
1412    public void specWithoutLock(String playerName) {
1413        sendUnfilteredPrivateMessage(playerName, "*spec");
1414        sendUnfilteredPrivateMessage(playerName, "*spec");
1415    }
1416
1417    /**
1418     * Sends a player immediately to spectator mode, and does not lock them there.
1419     * If the arena is unlocked, they will be able to re-enter.  This is the
1420     * standard way to spec someone, and is equivalent to issuing two /*spec's.
1421     * @param playerID Player ID of the player you want to spec.
1422     */
1423    public void specWithoutLock(int playerID) {
1424        sendUnfilteredPrivateMessage(playerID, "*spec");
1425        sendUnfilteredPrivateMessage(playerID, "*spec");
1426    }
1427
1428    /**
1429     * Places all players in the arena into spectator mode.
1430     */
1431    public void specAll()
1432    {
1433        sendUnfilteredPublicMessage("*specall");
1434    }
1435
1436    /**
1437     * Resets the scores of an individual player.
1438     * @param playerName Name of player to be reset.
1439     */
1440    public void scoreReset(String playerName)
1441    {
1442        sendUnfilteredPrivateMessage(playerName, "*scorereset");
1443    }
1444
1445    /**
1446     * Resets the scores of an individual player.
1447     * @param playerID Player ID of player to be reset.
1448     */
1449    public void scoreReset(int playerID)
1450    {
1451        sendUnfilteredPrivateMessage(playerID, "*scorereset");
1452    }
1453
1454    /**
1455     * Resets the scores of all the players in an arena.
1456     */
1457    public void scoreResetAll()
1458    {
1459        sendUnfilteredPublicMessage("*scorereset");
1460    }
1461
1462    /**
1463     * Issues a *shipreset to a specific player, returning their status, energy,
1464     * etc. to the way it was at spawn.  If issued on a player that has just died,
1465     * the player will come back to life at the position of their death.  Note
1466     * that a shipreset does not remove mines (warping to a safe will, however).
1467     * @param playerName Name of the player to be *shipreset
1468     */
1469    public void shipReset(String playerName)
1470    {
1471        sendUnfilteredPrivateMessage(playerName, "*shipreset");
1472    }
1473
1474    /**
1475     * Issues a *shipreset to a specific player, returning their status, energy,
1476     * etc. to the way it was at spawn.  If issued on a player that has just died,
1477     * the player will come back to life at the position of their death.  Note
1478     * that a shipreset does not remove mines (warping to a safe will, however).
1479     * @param playerID Integer ID reference to the player specified.
1480     */
1481    public void shipReset(int playerID)
1482    {
1483        sendUnfilteredPrivateMessage(playerID, "*shipreset");
1484    }
1485
1486    /**
1487     * Issues a *shipreset to all in-game players, returning their status, energy,
1488     * etc. to the way it was at spawn.  Anyone who has just died will come back
1489     * to life at the position of their death.  Note that a shipreset does not
1490     * remove mines (warping to a safe will, however).
1491     */
1492    public void shipResetAll()
1493    {
1494        sendUnfilteredPublicMessage("*shipreset");
1495    }
1496
1497    /**
1498     * Issues one specific prize to a player, using *prize#.  This is the
1499     * standard method of prizing.<p>
1500     * When specifying a prize number, you should use the Tools.Prize define, such
1501     * as Tools.Prize.ROTATION.<p>
1502     * <code><pre><u>Prize numbers</u>:
1503     * 1 = Recharge     8 = Guns              15 = MultiFire    22 = Burst
1504     * 2 = Energy       9 = Bombs             16 = Proximity    23 = Decoy
1505     * 3 = Rotation    10 = Bouncing Bullets  17 = Super!       24 = Thor
1506     * 4 = Stealth     11 = Thruster          18 = Shields      25 = Multiprize
1507     * 5 = Cloak       12 = Top Speed         19 = Shrapnel     26 = Brick
1508     * 6 = XRadar      13 = Full Charge       20 = AntiWarp     27 = Rocket
1509     * 7 = Warp        14 = Engine Shutdown   21 = Repel        28 = Portal</pre></code>
1510     * To take away a prize, or reduce the prize's result by one, use a negative number.
1511     * @param playerName Name of the player.
1512     * @param prizeNum Number of the prize to issue.
1513     */
1514    public void specificPrize(String playerName, int prizeNum)
1515    {
1516        sendUnfilteredPrivateMessage(playerName, "*prize#" + prizeNum);
1517    }
1518
1519    /**
1520     * Issues one specific prize to a player, using *prize#.  This is the
1521     * standard method of prizing.<p>
1522     * When specifying a prize number, you should use the Tools.Prize define, such
1523     * as Tools.Prize.ROTATION.<p>
1524     * <code><pre><u>Prize numbers</u>:
1525     * 1 = Recharge     8 = Guns              15 = MultiFire    22 = Burst
1526     * 2 = Energy       9 = Bombs             16 = Proximity    23 = Decoy
1527     * 3 = Rotation    10 = Bouncing Bullets  17 = Super!       24 = Thor
1528     * 4 = Stealth     11 = Thruster          18 = Shields      25 = Multiprize
1529     * 5 = Cloak       12 = Top Speed         19 = Shrapnel     26 = Brick
1530     * 6 = XRadar      13 = Full Charge       20 = AntiWarp     27 = Rocket
1531     * 7 = Warp        14 = Engine Shutdown   21 = Repel        28 = Portal</pre></code>
1532     * To take away a prize, or reduce the prize's result by one, use a negative number.
1533     * @param playerID Player ID
1534     * @param prizeNum Number of the prize
1535     */
1536    public void specificPrize(int playerID, int prizeNum)
1537    {
1538          sendUnfilteredPrivateMessage(playerID, "*prize#" + prizeNum);
1539    }
1540
1541    /**
1542     * Issues one specific prize to all players, using *prize#.  This is the
1543     * standard method of prizing.<p>
1544     * When specifying a prize number, you should use the Tools.Prize define, such
1545     * as Tools.Prize.ROTATION.<p>
1546     * <code><pre><u>Prize numbers</u>:
1547     * 1 = Recharge     8 = Guns              15 = MultiFire    22 = Burst
1548     * 2 = Energy       9 = Bombs             16 = Proximity    23 = Decoy
1549     * 3 = Rotation    10 = Bouncing Bullets  17 = Super!       24 = Thor
1550     * 4 = Stealth     11 = Thruster          18 = Shields      25 = Multiprize
1551     * 5 = Cloak       12 = Top Speed         19 = Shrapnel     26 = Brick
1552     * 6 = XRadar      13 = Full Charge       20 = AntiWarp     27 = Rocket
1553     * 7 = Warp        14 = Engine Shutdown   21 = Repel        28 = Portal</pre></code>
1554     * To take away a prize, or reduce the prize's result by one, use a negative number.
1555     * @param prizeNum Number of the prize.
1556     */
1557    public void prizeAll(int prizeNum)
1558    {
1559        sendUnfilteredPublicMessage("*prize#" + prizeNum);
1560    }
1561
1562    /**
1563     * Issues one specific prize to an entire frequency of players.
1564     * <p><code><pre><u>Prize numbers</u>:
1565     * 1 = Recharge     8 = Guns              15 = MultiFire    22 = Burst
1566     * 2 = Energy       9 = Bombs             16 = Proximity    23 = Decoy
1567     * 3 = Rotation    10 = Bouncing Bullets  17 = Super!       24 = Thor
1568     * 4 = Stealth     11 = Thruster          18 = Shields      25 = Multiprize
1569     * 5 = Cloak       12 = Top Speed         19 = Shrapnel     26 = Brick
1570     * 6 = XRadar      13 = Full Charge       20 = AntiWarp     27 = Rocket
1571     * 7 = Warp        14 = Engine Shutdown   21 = Repel        28 = Portal</pre></code>
1572     * To take away a prize, or reduce the prize's result by one, use a negative number.
1573     * @param freqID The frequency of players you wish to issue the prizes to.
1574     * @param prizeNum Number of the prize.
1575     */
1576    public void prizeFreq(int freqID, int prizeNum)
1577    {
1578        try
1579        {
1580            for (Iterator<Integer> i = m_arenaTracker.getFreqIDIterator(freqID); i.hasNext();)
1581            {
1582                specificPrize(i.next().intValue(), prizeNum);
1583            }
1584        }
1585        catch (Exception e)
1586        {
1587        }
1588    }
1589
1590    /**
1591     * Adds a specific amount of bounty to everyone in the arena by giving them
1592     * random prizes (each equivalent to 1 bounty).  The prizes are determined
1593     * by the availability and frequency specified in the arena's config file.
1594     * @param number Amount of bounty / random prizes to give.
1595     */
1596    public void giveBounty(int number)
1597    {
1598        sendUnfilteredPublicMessage("*prize " + number);
1599    }
1600
1601    /**
1602     * Adds a specific amount of bounty to a player in the arena by giving them
1603     * random prizes (each equivalent to 1 bounty).  The prizes are determined
1604     * by the availability and frequency specified in the arena's config file.
1605     * @param name Name of the player to give the bounty to.
1606     * @param number Amount of bounty / random prizes to give.
1607     */
1608    public void giveBounty(String name, int number)
1609    {
1610        sendUnfilteredPrivateMessage(name, "*prize " + number);
1611    }
1612
1613    /**
1614     * Adds a specific amount of bounty to a player in the arena by giving them
1615     * random prizes (each equivalent to 1 bounty).  The prizes are determined
1616     * by the availability and frequency specified in the arena's config file.
1617     * @param playerID PlayerID of the player you want to give the bounty to.
1618     * @param number Amount of bounty / random prizes to give.
1619     */
1620    public void giveBounty(int playerID, int number)
1621    {
1622        sendUnfilteredPrivateMessage(playerID, "*prize " + number);
1623    }
1624
1625    /**
1626     * Issues the number of random prizes specified to a specific player.  The
1627     * prizes are chosen based on the probability in the arena settings.  This
1628     * method is equivalent to giveBounty, and should not be used because it can
1629     * be confused with specificPrize, which issues one specific prize to a player.
1630     * @param playerName Name of the player.
1631     * @param numPrizes Number of random prizes to issue.
1632     * @see #specificPrize(String, int)
1633     * @see #giveBounty(String, int)
1634     * @deprecated Duplicate functionality of giveBounty.  Often confused with specificPrize
1635     */
1636    @Deprecated
1637    public void prize(String playerName, int numPrizes)
1638    {
1639        sendUnfilteredPrivateMessage(playerName, "*prize " + numPrizes);
1640    }
1641
1642    /**
1643     * Issues the number of random prizes specified to a specific player.  The
1644     * prizes are chosen based on the probability in the arena settings.  This
1645     * method is equivalent to giveBounty, and should not be used because it can
1646     * be confused with specificPrize, which issues one specific prize to a player.
1647     * @param playerID Player ID of the player.
1648     * @param numPrizes Number of random prizes to issue.
1649     * @see #specificPrize(int, int)
1650     * @see #giveBounty(int, int)
1651     * @deprecated Duplicate functionality of giveBounty.  Often confused with specificPrize
1652     */
1653    @Deprecated
1654    public void prize(int playerID, int numPrizes)
1655    {
1656        sendUnfilteredPrivateMessage(playerID, "*prize " + numPrizes);
1657    }
1658
1659    /**
1660     * Warns the player with a moderator warning (red).  The message will be
1661     * appended with MODERATOR WARNING:, and will be signed with the bot's name.
1662     * @param playername The name of the player.
1663     * @param message The message to be sent.
1664     */
1665    public void warnPlayer(String playername, String message)
1666    {
1667        sendUnfilteredPrivateMessage(playername, "*warn " + message);
1668    }
1669
1670    /**
1671     * Warns the player with a moderator warning (red).  The message will be
1672     * appended with MODERATOR WARNING:, and will be signed with the bot's name.
1673     * @param playerID The player to be warned
1674     * @param message The message to be sent
1675     */
1676    public void warnPlayer(int playerID, String message)
1677    {
1678        sendUnfilteredPrivateMessage(playerID, "*warn " + message);
1679    }
1680
1681    /**
1682     * Toggles the WatchDamage attribute for this player, which will send
1683     * detailed information about weapon damage to/from them, and can be picked
1684     * up by handling WatchDamage events.
1685     * @param playerID PlayerID of the player to receive damage info about.
1686     */
1687    public void toggleWatchDamage(int playerID)
1688    {
1689        sendUnfilteredPrivateMessage(playerID, "*watchdamage");
1690    }
1691
1692    /**
1693     * Toggles the WatchDamage attribute for this player, which will send
1694     * detailed information about weapon damage to/from them, and can be picked
1695     * up by handling WatchDamage events.
1696     * @param playerName PlayerName of the player to receive damage info about.
1697     */
1698    public void toggleWatchDamage(String playerName)
1699    {
1700        sendUnfilteredPrivateMessage(playerName, "*watchdamage");
1701    }
1702   
1703    /**
1704     * Toggles the WatchGreen attribute for this player, which will send
1705     * information about the green that has been picked up by the player.
1706     * @param playerID PlayerID of the player to receive green info about.
1707     */
1708    public void toggleWatchGreen(int playerID)
1709    {
1710        sendUnfilteredPrivateMessage(playerID, "*watchgreen");
1711    }
1712
1713    /**
1714     * Toggles the WatchGreen attribute for this player, which will send
1715     * information about the green that has been picked up by the player.
1716     * @param playerName playerName of the player to receive green info about.
1717     */
1718    public void toggleWatchGreen(String playerName)
1719    {
1720        sendUnfilteredPrivateMessage(playerName, "*watchgreen");
1721    }
1722
1723
1724    // ***** ARENA OPERATIONS *****
1725    /*
1726     * Pertaining to anything done that is done to the arena or is arena-specific.
1727     */
1728
1729    /**
1730     * Locks or unlocks the arena, depending on previous lock status.  A locked
1731     * arena can't be entered by anyone in the standard method, but only by *setship.
1732     */
1733    public void toggleLocked()
1734    {
1735        sendUnfilteredPublicMessage("*lock");
1736    }
1737
1738    /**
1739     * Resets the flag game so that all stationary flags become neutral, and
1740     * all grabbable flags are respawned.
1741     */
1742    public void resetFlagGame()
1743    {
1744        sendUnfilteredPublicMessage("*flagreset");
1745    }
1746
1747    /**
1748     * Sets the *timer game timer to a specified time.  0 to turn off.
1749     * @param minutes Time until end of game, in minutes.
1750     */
1751    public void setTimer(int minutes)
1752    {
1753        sendUnfilteredPublicMessage("*timer " + minutes);
1754    }
1755
1756    /**
1757     * Resets the timer on a timed game to its starting value.
1758     * @see #setTimer(int)
1759     */
1760    public void resetTimer()
1761    {
1762        sendUnfilteredPublicMessage("*timereset");
1763    }
1764
1765    /**
1766     * Sets the doors in the arena to the specified door value.  The integer
1767     * provided is a length 8 bit vector.  If you wish to explicitly declare
1768     * the doors in a String, such as "01010111", use setDoors( String ).
1769     * @param doors Value of the doors to be set (bitvector).
1770     */
1771    public void setDoors(int doors)
1772    {
1773        if (doors < -2 || doors > 255)
1774            return;
1775        sendUnfilteredPublicMessage("?set door:doormode:" + doors);
1776    }
1777
1778    /**
1779     * Uses a binary string such as "11010110" to turn specific doors on or off.
1780     * @param eightBinaryDigits Door state as a String
1781     */
1782    public void setDoors(String eightBinaryDigits)
1783    {
1784        try
1785        {
1786            setDoors((int) Short.parseShort(eightBinaryDigits, 2));
1787        }
1788        catch (NumberFormatException e)
1789        {
1790            Tools.printStackTrace(e);
1791        }
1792    }
1793
1794    /**
1795     * Starts or ends 'blue message out,' depending on state (locks public
1796     * messages being sent inside the arena).
1797     * <p>The name of this method is confusing, and is incompatible with non-TW
1798     * standards.  It has been replaced by toggleLockPublicChat.
1799     * @see #toggleLockPublicChat()
1800     */
1801    public void toggleBlueOut()
1802    {
1803        toggleLockPublicChat();
1804    }
1805
1806    /**
1807     * Starts or stops the locking of public (blue) messages in the arena.  When
1808     * this is enabled, players will see their text printed to the chat window,
1809     * but it will not actually be displayed.
1810     * <p>All lock options default to off.  After calling this method to switch
1811     * on, call it to turn off again, or else the arena will keep the lock.
1812     * <p>Note that lock options do not apply to staff on moderate.txt.
1813     */
1814    public void toggleLockPublicChat()
1815    {
1816        sendUnfilteredPublicMessage("*lockpublic");
1817    }
1818
1819    /**
1820     * Starts or stops the locking of private messages being sent by anyone
1821     * in the arena.  (Blocks / and :: msgs)
1822     * <p>All lock options default to off.  After calling this method to switch
1823     * on, call it to turn off again, or else the arena will keep the lock.
1824     * <p>Note that lock options do not apply to staff on moderate.txt.
1825     */
1826    public void toggleLockPrivateChat()
1827    {
1828        sendUnfilteredPublicMessage("*lockprivate");
1829    }
1830
1831    /**
1832     * Toggles between applying chat locks to spectators or all players.  By
1833     * default the locks affect all players.  This should be used in conjunction
1834     * with toggleLockPublicChat() and toggleLockPrivateChat().
1835     * @see #toggleLockPublicChat()
1836     * @see #toggleLockPrivateChat()
1837     */
1838    public void toggleLocksToSpectators()
1839    {
1840        sendUnfilteredPublicMessage("*lockspec");
1841    }
1842
1843    /**
1844     * Sets the power of the thor weapon.  0 is normal, 1 is instant death on contact,
1845     * and >1 is instant death with increasing proximity as 1 -> infinity.
1846     * @param thorAdjust Amount to adjust thor power by (0: norm, 1:instant death, >1:instant death w/ prox)
1847     */
1848    public void setThorAdjust(int thorAdjust)
1849    {
1850        sendUnfilteredPublicMessage("*thor " + thorAdjust);
1851    }
1852
1853    /**
1854     * Uses the *locate command to locate a player inside this server (NOT cross-zone).
1855     * The response is given in an arena message in the form of:
1856     * [player] - [arena]
1857     *
1858     * An advantage of this command in comparison to ?find is that this command can be used as many times as
1859     * necessary without the possibility of getting kicked for ?command spamming (simply because it isn't a - biller - ?command).
1860     *
1861     * @param player The playername to find.
1862     */
1863    public void locatePlayer(String player) {
1864        sendUnfilteredPublicMessage("*locate "+player);
1865    }
1866
1867
1868    // ***** COMPLEX OPERATIONS *****
1869    /*
1870     * Operations made up of other operations to form more complex behaviors.
1871     */
1872
1873    /**
1874     * Creates a set of teams of a particular size randomly. Starts at freq 0
1875     * and continues filling freqs completely until there are no players left.
1876     * @param teamSize The size of team desired.
1877     */
1878    public void createRandomTeams(int teamSize)
1879    {
1880        StringBag plist = new StringBag();
1881        int freq = 0;
1882        String name;
1883
1884        //stick all of the players in randomizer
1885        Iterator<Player> i = m_arenaTracker.getPlayingPlayerIterator();
1886        while(i.hasNext())
1887            plist.add(i.next().getPlayerName());
1888
1889        while(!plist.isEmpty() && freq > -1)
1890        {
1891            for(int x = 0; x < teamSize; x++)
1892            {
1893                name = plist.grabAndRemove();
1894                if(name != null)
1895                    setFreq(name, freq);
1896                else
1897                {
1898                    freq = -2;
1899                    break;
1900                }
1901            }
1902            freq++; //that freq is done, move on to the next
1903        }
1904    }
1905
1906    /**
1907     * Creates a certain number of random teams from non-specced players.  Starts
1908     * with freq 0 and goes up to number specified - 1, in an even distribution.
1909     * @param howMany Number of random teams to create.
1910     */
1911    public void createNumberOfTeams(int howMany)
1912    {
1913        StringBag plist = new StringBag();
1914        int current = 0;
1915        howMany -= 1;
1916        String name;
1917
1918        // stick all of the players in randomizer
1919        Iterator<Player> i = m_arenaTracker.getPlayingPlayerIterator();
1920        while(i.hasNext())
1921            plist.add(i.next().getPlayerName());
1922
1923        // assign players to teams
1924        while(!plist.isEmpty())
1925        {
1926            if(current > howMany)
1927                current = 0;
1928            name = plist.grabAndRemove();
1929            setFreq(name,current);
1930            current++;
1931        }
1932    }
1933
1934    /**
1935     * Warps all the players in the arena to location X, Y.
1936     * @param x X coordinate to warp the players to (1-1024)
1937     * @param y X coordinate to warp the players to (1-1024)
1938     */
1939    public void warpAllToLocation(int x, int y)
1940    {
1941        Iterator<Integer> i = m_arenaTracker.getPlayerIDIterator();
1942        if (i == null)
1943            return;
1944        while (i.hasNext())
1945        {
1946            warpTo(i.next().intValue(), x, y);
1947        }
1948    }
1949
1950    /**
1951     * Warps all the players on a frequency to location X, Y.
1952     * @param freq Frequency to warp
1953     * @param x X coordinate to warp the players to (1-1024)
1954     * @param y Y coordinate to warp the players to (1-1024)
1955     */
1956    public void warpFreqToLocation(int freq, int x, int y)
1957    {
1958        Iterator<Integer> i = m_arenaTracker.getFreqIDIterator(freq);
1959        if (i == null)
1960        {
1961            Tools.printLog("Arena: Freq " + freq + " does not exist.");
1962            return;
1963        }
1964
1965        while (i.hasNext())
1966        {
1967            int next = i.next().intValue();
1968            warpTo(next, x, y);
1969        }
1970    }
1971
1972    /**
1973     * Sends the warp prize to all players that are currently in a ship, creating
1974     * the same effect as if they had just hit the warp key.
1975     */
1976    public void warpAllRandomly()
1977    {
1978        prizeAll(Tools.Prize.WARP);
1979    }
1980
1981    /**
1982     * Issues a *setship to all the players in the arena who are not in spec.
1983     * @param shipType Ship type to switch the players into.
1984     */
1985    public void changeAllShips(int shipType)
1986    {
1987        if (!(shipType >= 1 && shipType <= 8))
1988            return;
1989        for (Iterator<Integer> i = m_arenaTracker.getPlayingIDIterator(); i.hasNext();)
1990        {
1991            setShip(i.next().intValue(), shipType);
1992        }
1993    }
1994
1995    /**
1996     * Changes all the ships on a freq to a particular ship type.
1997     * @param freq The frequency of the players you wish to change
1998     * @param shipType The ship type that you wish to change the players to
1999     */
2000    public void changeAllShipsOnFreq(int freq, int shipType)
2001    {
2002        if (!(shipType >= 1 && shipType <= 8))
2003            return;
2004        Iterator<Integer> i = m_arenaTracker.getFreqIDIterator(freq);
2005        if (i == null)
2006            return;
2007        while (i.hasNext())
2008        {
2009            setShip(i.next().intValue(), shipType);
2010        }
2011    }
2012
2013    /**
2014     * Changes all the players on one freq to another freq.  Great for
2015     * consolidating teams.  (Merge operation.)
2016     * @param initialFreq Frequency of the players you wish to change
2017     * @param destFreq The frequency you wish to change the players to
2018     */
2019    public void setFreqtoFreq(int initialFreq, int destFreq)
2020    {
2021        Iterator<Integer> i = m_arenaTracker.getFreqIDIterator(initialFreq);
2022        if (i == null)
2023            return;
2024        while (i.hasNext())
2025        {
2026            setFreq(i.next().intValue(), destFreq);
2027        }
2028    }
2029
2030    /**
2031     * Changes the frequency of all the playing players in the game to a
2032     * specified frequency.
2033     * @param destFreq Frequency to change the players to.
2034     */
2035    public void setAlltoFreq(int destFreq)
2036    {
2037        Iterator<Integer> i = m_arenaTracker.getPlayingIDIterator();
2038        if (i == null)
2039            return;
2040        while (i.hasNext())
2041        {
2042            setFreq(i.next().intValue(), destFreq);
2043        }
2044    }
2045
2046    /**
2047     * Changes all players in a particular ship to a particular freq. If shipType
2048     * is negative, then every other ship is affected instead. Spectators are not
2049     * affected by this method.
2050     * @param shipType The ship type to set to a freq, can be negative for 'not'
2051     * @param freq The frequency to switch the players to
2052     */
2053    public void changeAllInShipToFreq(int shipType, int freq) {
2054        boolean neg;
2055        if(neg = shipType < 0) {
2056                shipType = -shipType;
2057        }
2058        if(shipType < 1 || shipType > 8) {
2059            return;
2060        }
2061        Iterator<Player> i = m_arenaTracker.getPlayingPlayerIterator();
2062        if(i == null) {
2063            return;
2064        }
2065        while(i.hasNext()) {
2066                Player p = i.next();
2067                int pShip = p.getShipType();
2068                int pFreq = p.getFrequency();
2069                if(neg) {
2070                        if(pShip != shipType && pShip != Tools.Ship.SPECTATOR && pFreq != freq) {
2071                                setFreq(p.getPlayerID(), freq);
2072                        }
2073                } else {
2074                        if(pShip == shipType && pFreq != freq) {
2075                        setFreq(p.getPlayerID(), freq);
2076                        }
2077                }
2078        }
2079    }
2080
2081    /**
2082     * Sends all ships on a specific frequency into spectator mode, without locking
2083     * them in spectator mode.  (Arena should be *lock'd first.)
2084     * @param freq Frequency to spec
2085     */
2086    public void specAllOnFreq(int freq) {
2087        Iterator<Integer> i = m_arenaTracker.getFreqIDIterator(freq);
2088        if (i == null)
2089            return;
2090        while (i.hasNext())
2091        {
2092            specWithoutLock(i.next().intValue());
2093        }
2094    }
2095
2096    /**
2097     * Sends all ships on a specific frequency into spectator mode, without locking
2098     * them in spectator mode, and then sets their freq to the old freq.
2099     * @param freq Frequency to spec
2100     */
2101    public void specFreqAndKeepFreq(int freq) {
2102        Iterator<Player> i = m_arenaTracker.getFreqPlayerIterator(freq);
2103        if (i == null)
2104            return;
2105        while (i.hasNext())
2106        {
2107            int id = (int)i.next().getPlayerID();
2108            specWithoutLock(id);
2109            setFreq(id, freq);
2110        }
2111    }
2112
2113    /**
2114     * Sends all ships into spectator mode, but keeps their freq as what it was prior
2115     * to being spec'd.  Does not lock in spectator mode, so arena should be *lock'd.
2116     * @param freq Frequency to spec
2117     */
2118    public void specAllAndKeepFreqs() {
2119        Iterator<Player> i = m_arenaTracker.getPlayingPlayerIterator();
2120        Player p;
2121        int priorfreq;
2122
2123        if (i == null)
2124            return;
2125        while (i.hasNext()) {
2126            p = i.next();
2127            priorfreq = p.getFrequency();
2128            specWithoutLock(p.getPlayerID());
2129            setFreq(p.getPlayerID(), priorfreq);
2130        }
2131    }
2132
2133
2134    /**
2135     * Checks every player in the game for X number of deaths, and places them
2136     * in spectator mode.  Normally not used (spec module used instead).  Note
2137     * that if you use this method, you still have to monitor PlayerDeath packets
2138     * to check the remaining deaths.  It is not advisable to use checkAndSpec.
2139     * @param deaths Number of deaths that, if over, will result in a speccing.
2140     */
2141    public void checkAndSpec(int deaths)
2142    {
2143
2144        Iterator<Player> i = m_arenaTracker.getPlayerIterator();
2145        while (i.hasNext())
2146        {
2147            Player p = i.next();
2148            if (p.getShipType() != 0 && p.getLosses() >= deaths)
2149            {
2150                spec(p.getPlayerID());
2151                spec(p.getPlayerID());
2152                sendArenaMessage(p.getPlayerName() + " is out with " + p.getWins() + " wins, " + p.getLosses() + " losses.");
2153            }
2154        }
2155    }
2156
2157    /**
2158     * Wipes all data from the arena tracker (player lists, etc)
2159     */
2160    public void clearArenaData(){
2161        m_arenaTracker.clear();
2162    }
2163
2164
2165    // ***** BOT OPERATIONS *****
2166    /*
2167     * Covers operations that involve the bot performing some kind of action
2168     * relating directly to itself.
2169     */
2170
2171    /**
2172     * Causes the bot to join an arena at initial login.  If the bot has logged
2173     * on and is already in an arena, use changeArena( int ) instead.
2174     * <p>If arenaname contains only a number, then it's assumed to be a pub
2175     * that is being joined.
2176     * @param arenaName The name of arena (or number of pub) to join.
2177     * @author DoCk> (modified by FoN)
2178     * @see #changeArena(String)
2179     */
2180    public void joinArena(String arenaName)
2181    {
2182        if( arenaName == null || arenaName.equals("") )
2183            return;
2184
2185        if (Tools.isAllDigits(arenaName))
2186        {
2187            joinArena(Short.parseShort(arenaName));
2188        }
2189        else
2190        {
2191            m_arenaName = arenaName;
2192            try
2193            {
2194                m_packetGenerator.sendArenaLoginPacket((byte) 8, (short) m_botSession.getCoreData().getResolutionX(), (short) m_botSession.getCoreData().getResolutionY(), (short) 0xFFFD, arenaName);
2195            }
2196            catch (Exception e)
2197            {
2198                //don't do anything cause hardcoded res is correct
2199            }
2200
2201            m_packetGenerator.sendSpectatePacket((short) - 1);
2202        }
2203    }
2204
2205    /**
2206     * Causes the bot to join an arena at initial login.  If the bot has logged
2207     * on and is already in an arena, use changeArena( int ) instead.  You can
2208     * also specify the resolution for the bot to enter in at.
2209     * <p>If arenaname contains only a number, then it's assumed to be a pub
2210     * that is being joined.
2211     * @param arenaName The arena string to join.
2212     * @param xResolution The X coordinate resolution for the screen.
2213     * @param yResolution The Y coordinate resolution for the screen.
2214     * @author Kirthi Sugnanam - FoN
2215     * @see #changeArena(String, short, short)
2216     */
2217    public void joinArena(String arenaName, short xResolution, short yResolution) throws Exception
2218    {
2219
2220        if (Tools.isAllDigits(arenaName))
2221        {
2222            joinArena(Short.parseShort(arenaName), xResolution, yResolution);
2223        }
2224        else
2225        {
2226            try
2227            {
2228                m_arenaName = arenaName;
2229                m_packetGenerator.sendArenaLoginPacket((byte) 8, xResolution, yResolution, (short) 0xFFFD, arenaName);
2230                m_packetGenerator.sendSpectatePacket((short) - 1);
2231            }
2232            catch (Exception e)
2233            {
2234                throw new Exception("The resolution isnt an allowed specification: Specified X: " + xResolution + "Specified Y: " + yResolution);
2235            }
2236        }
2237    }
2238
2239    /**
2240     * Causes the bot to join the specified public arena.  Should be used only
2241     * at initial login.
2242     * @param arena The arena number to join.
2243     * @author DoCk> (modified by FoN)
2244     * @see #changeArena(short)
2245     */
2246    public void joinArena(short arena)
2247    {
2248        m_arenaName = "(Public " + arena + ")";
2249        try
2250        {
2251            m_packetGenerator.sendArenaLoginPacket((byte) 8, (short) m_botSession.getCoreData().getResolutionX(), (short) m_botSession.getCoreData().getResolutionY(), arena, "");
2252        }
2253        catch (Exception e)
2254        {
2255            //don't do anything cause hardcoded res is correct
2256        }
2257        m_packetGenerator.sendSpectatePacket((short) - 1);
2258    }
2259
2260    /**
2261     * Causes the bot to join the specified public arena at a given resolution.
2262     * Should be used only at initial login.
2263     * @param arena The arena number to join.
2264     * @param xResolution The X coordinate resolution for the screen.
2265     * @param yResolution The Y coordinate resolution for the screen.
2266     * @author Kirthi Sugnanam - FoN
2267     * @see #changeArena(short, short, short)
2268     */
2269    public void joinArena(short arena, short xResolution, short yResolution) throws Exception
2270    {
2271        try
2272        {
2273            m_arenaName = "(Public " + arena + ")";
2274            m_packetGenerator.sendArenaLoginPacket((byte) 8, xResolution, yResolution, arena, "");
2275            m_packetGenerator.sendSpectatePacket((short) - 1);
2276        }
2277        catch (Exception e)
2278        {
2279            throw new Exception("The resolution isnt an allowed specification: Specified X: " + xResolution + "Specified Y: " + yResolution);
2280        }
2281    }
2282
2283    /**
2284     * Joins the bot to a random public arena.
2285     * @author DoCk> (modified by FoN)
2286     */
2287    public void joinRandomPublicArena()
2288    {
2289        try
2290        {
2291            m_packetGenerator.sendArenaLoginPacket((byte) 8, (short) m_botSession.getCoreData().getResolutionX(), (short) m_botSession.getCoreData().getResolutionY(), (short) 0xFFFF, "");
2292        }
2293        catch (Exception e)
2294        {
2295            //don't do anything because hardcoded res is correct
2296        }
2297        m_packetGenerator.sendSpectatePacket((short) - 1);
2298    }
2299
2300    /**
2301     * This method tells the bot to leave the current arena, and join another
2302     * arena specified by a String.  If the String contains an integer value, the
2303     * bot will go to a public arena of that value.
2304     * @param newArenaName Name or number of the arena to change to.
2305     */
2306    public void changeArena(String newArenaName)
2307    {
2308        m_packetGenerator.sendArenaLeft();
2309        m_arenaTracker.clear();
2310        joinArena(newArenaName);
2311    }
2312
2313    /**
2314     * This is an overloaded change function to allow variations in resolution.
2315     * @param newArenaName Name or number of the arena to change to.
2316     * @param xResolution The X Max for the screen size.
2317     * @param yResolution The Y Max for the screen size.
2318     * @exception Catches the resolution mistake for specifying a bad max X and Y
2319     * @author Kirthi Sugnanam - FoN
2320     */
2321    public void changeArena(String newArenaName, short xResolution, short yResolution) throws Exception
2322    {
2323        m_packetGenerator.sendArenaLeft();
2324        m_arenaTracker.clear();
2325        try
2326        {
2327            joinArena(newArenaName, xResolution, yResolution);
2328        }
2329        catch (Exception e)
2330        {
2331            throw new Exception("The resolution isnt an allowed specification: Specified X: " + xResolution + "Specified Y: " + yResolution);
2332        }
2333    }
2334
2335    /**
2336     * Forces the bot to leave the current arena and join a public arena specified
2337     * by an integer.
2338     * @param arenaNumber The number of the public arena to change to.
2339     */
2340    public void changeArena(short arenaNumber)
2341    {
2342        m_packetGenerator.sendArenaLeft();
2343        m_arenaTracker.clear();
2344        joinArena(arenaNumber);
2345    }
2346
2347    /**
2348     * Forces the bot to leave the current arena and join a public arena specified
2349     * by an integer.  Allows resolution to be specified.
2350     * @param arenaNumber The number of the public arena to change to.
2351     * @param xResolution The X Max for the screen size.
2352     * @param yResolution The Y Max for the screen size.
2353     * @exception catches the resolution mistake for specifying X max and Y
2354     * @author Kirthi Sugnanam - FoN
2355     */
2356    public void changeArena(short arenaNumber, short xResolution, short yResolution) throws Exception
2357    {
2358        m_packetGenerator.sendArenaLeft();
2359        m_arenaTracker.clear();
2360        try
2361        {
2362            joinArena(arenaNumber, xResolution, yResolution);
2363        }
2364        catch (Exception e)
2365        {
2366            throw new Exception ("The resolution isnt an allowed specification: Specified X: " + xResolution + "Specified Y: " + yResolution);
2367        }
2368    }
2369
2370    /**
2371     * Moves the bot to the center of tile (x,y).  512, 512 is the center of the map.
2372     * To make the bot move to the center of the specified tile, +7 is added to the pixel count.
2373     * For more complex operations, get a copy of the Ship object with getShip().
2374     * @param x X value to move the bot to
2375     * @param y Y value to move the bot to
2376     * @see #getShip()
2377     */
2378    public void moveToTile(int x, int y)
2379    {
2380        m_botSession.getShip().move(x * 16 + 7, y * 16 + 7);
2381    }
2382
2383    /**
2384     * Moves the bot to position X, Y in exact map coordinates as opposed to
2385     * tile coordinates.  This means that the center of the arena is (8192, 8192)
2386     * as opposed to the less accurate (512, 512) of moveToTile( X, Y ).
2387     * For more complex operations, get a copy of the Ship object with getShip().
2388     * @param x Position of the bot along the X axis.
2389     * @param y Position of the bot along the Y axis.
2390     * @see #getShip()
2391     */
2392    public void move(int x, int y)
2393    {
2394        m_botSession.getShip().move(x, y);
2395    }
2396
2397    /**
2398     * Moves the bot to position X, Y in exact map coordinates and applies the
2399     * x and y plane velocities as specified.  Center of the arena is (8192, 8192).
2400     * For more complex operations, get a copy of the Ship object with getShip().
2401     * @param x Position of the bot along the X axis.
2402     * @param y Position of the bot along the Y axis.
2403     * @param vx Velocity along the X axis (# pixels per 10 seconds).
2404     * @param vy Velocity along the Y axis (# pixels per 10 seconds).
2405     * @see #getShip()
2406     */
2407    public void move(int x, int y, int vx, int vy)
2408    {
2409        m_botSession.getShip().move(x, y, vx, vy);
2410    }
2411
2412    /**
2413     * Sets the bot to spectate the specified player and surrounding area.
2414     * PlayerPosition events for any players in radar range will be created,
2415     * but any outside will not.  If you send -1, the bot will cease spectating
2416     * anyone, but will still receive position packets from the area.  You
2417     * may also call stopSpectatingPlayer() to do this.
2418     * @param playerID Ident of the player to spectate. (-1 to stop spectating)
2419     * @see #stopSpectatingPlayer()
2420     */
2421    public void spectatePlayer(int playerID)
2422    {
2423        m_packetGenerator.sendSpectatePacket((short) playerID);
2424    }
2425
2426    /**
2427     * Sets the bot to spectate the specified player and surrounding area.
2428     * PlayerPosition events for any players in radar range will be created,
2429     * but any outside will not.
2430     * @param playerName Name of the player to spectate.
2431     */
2432    public void spectatePlayer(String playerName)
2433    {
2434        m_packetGenerator.sendSpectatePacket((short) m_arenaTracker.getPlayerID(playerName));
2435    }
2436
2437    /**
2438     * Sets the bot to spectate the specified player immediately, in front of all
2439     * other packets.  Use this method only when you need extremely fresh position
2440     * data on a specific player and know exactly what you are doing.  (This method
2441     * sacrifices some efficiency for speed.)
2442     * @param playerID Ident of the player to spectate. (-1 to stop spectating)
2443     * @see #stopSpectatingPlayer()
2444     */
2445    public void spectatePlayerImmediately(int playerID)
2446    {
2447        m_packetGenerator.sendImmediateSpectatePacket((short) playerID);
2448    }
2449
2450    /**
2451     * Sets the bot to spectate the specified player immediately, in front of all
2452     * other packets.  Use this method only when you need extremely fresh position
2453     * data on a specific player and know exactly what you are doing.  (This method
2454     * sacrifices some efficiency for speed.)
2455     * @param playerName Name of the player to spectate.
2456     */
2457    public void spectatePlayerImmediately(String playerName)
2458    {
2459        m_packetGenerator.sendImmediateSpectatePacket((short) m_arenaTracker.getPlayerID(playerName));
2460    }
2461
2462    /**
2463     * Ceases spectating the player the bot is currently spectating on, if any.
2464     */
2465    public void stopSpectatingPlayer()
2466    {
2467        m_packetGenerator.sendSpectatePacket((short) -1);
2468    }
2469
2470    /**
2471     * Sent a request for the bot to pick up flag specified by the given ID.
2472     * @param flagID ID of flag to pickup.
2473     */
2474    public void grabFlag(int flagID)
2475    {
2476        m_packetGenerator.sendFlagRequestPacket((short) flagID);
2477    }
2478
2479    /**
2480     * Gets the number of flags a frequency is carrying.
2481     * @param freq - The frequency number.
2482     * @return - The number of flags carried.
2483     */
2484    public int getFlagsOnFreq(int freq){
2485        int flags = 0;
2486        Iterator<Player> i = getFreqPlayerIterator(freq);
2487        while( i.hasNext() ){
2488            Player p = i.next();
2489            flags += p.getFlagsCarried();
2490        }
2491        return flags;
2492    }
2493
2494    public void getBall(){
2495       
2496        m_packetGenerator.sendBallPickupPacket((byte) 0);
2497    }
2498   
2499    /**
2500     * Drops all flags the bot is carrying.
2501     */
2502    public void dropFlags()
2503    {
2504        m_packetGenerator.sendFlagDropPacket();
2505    }
2506
2507    /**
2508     * Sets the bot's personal banner.
2509     * @param _banner A byte array containing the banner data (BMP format, no palette).
2510     */
2511    public void setBanner( byte[] _banner ) {
2512
2513        m_packetGenerator.sendBannerPacket( _banner );
2514    }
2515
2516    /**
2517     * Sends a death packet, causing the bot to die. (Fixed by D1st0rt 3-26-05)
2518     * If you send an invalid playerID, hypothetically nothing should happen.
2519     * @param playerID ID of the player who killed the bot.
2520     * @param bounty Bot's bounty at time of death.
2521     */
2522    public void sendDeath(int playerID, int bounty)
2523    {
2524        m_packetGenerator.sendPlayerDeath(playerID, bounty);
2525    }
2526
2527    /**
2528     * Tells the bot to go parachuting (without the parachute).
2529     */
2530    public void die()
2531    {
2532        m_botSession.disconnect();
2533    }
2534
2535    /**
2536     * Tells the bot to go parachuting (without the parachute), and sends a message giving
2537     * the reason for its death.
2538     */
2539    public void die( String msg )
2540    {
2541        m_botSession.disconnect( msg );
2542    }
2543
2544    /**
2545     * Sets the minimum bounty needed for a player's kills/deaths to be sent
2546     * reliably to the bot.  If it's essential that you receive all player death
2547     * events / packets, it's advisable to set this to 1, or to use the more
2548     * explicit receiveAllPlayerDeaths() method.
2549     * @param minBounty Amount of bounty for kill messages to become reliable.
2550     * @see #receiveAllPlayerDeaths()
2551     */
2552    public void setReliableKills(int minBounty)
2553    {
2554        sendUnfilteredPublicMessage("*relkills " + minBounty);
2555    }
2556
2557    /**
2558     * Sets the bot to receive all death events / packets, regardless of the
2559     * amount of bounty a player has.  Useful if you must account for every death.
2560     */
2561    public void receiveAllPlayerDeaths()
2562    {
2563        sendUnfilteredPublicMessage("*relkills 1");
2564    }
2565
2566    /**
2567     * Sends out a request for an ArenaList packet to be sent back.  It can
2568     * then be received by handling the ArenaList event (if it has been requested
2569     * by using EventRequester).
2570     */
2571    public void requestArenaList()
2572    {
2573        sendUnfilteredPublicMessage("?arena");
2574    }
2575
2576    /**
2577     * Sets up spam protection for the bot, allowing only the specified number of
2578     * messages per minute to be sent to the bot before it begins to ignore them.
2579     * <p>In this implementation of the method, by default protection does not apply
2580     * to staff (who should know what they are doing!).
2581     * @param msgsPerMin Number of messages per minute the bot will allow.
2582     */
2583    public void setMessageLimit(int msgsPerMin )
2584    {
2585        m_botSession.getGamePacketInterpreter().setMessageLimiter(msgsPerMin, this, true);
2586    }
2587
2588    /**
2589     * Sets up spam protection for the bot, allowing only the specified number of
2590     * messages per minute to be sent to the bot before it begins to ignore them.
2591     * <p>You can specify whether or not staff player messages should also be limited.
2592     * @param msgsPerMin Number of messages per minute the bot will allow.
2593     * @param staffExempt True if staff should be exempt from message limiting.
2594     */
2595    public void setMessageLimit(int msgsPerMin, boolean staffExempt )
2596    {
2597        m_botSession.getGamePacketInterpreter().setMessageLimiter(msgsPerMin, this, staffExempt);
2598    }
2599
2600    /**
2601     * Adjusts the packet send delay.  The more packets that are sent out, the greater
2602     * the possibility you'll overflow the 2500 packet/minute limit.  The default
2603     * setting is 75 ms.  If your bot doesn't require a quick response, set it higher.
2604     * If your bot requires a near-real time response, set it lower.  The lower you set
2605     * it the more packets will be sent from the bot.<p>
2606     * A way to safely reduce the delay is to use this method in conjunction with
2607     * setLowPriorityPacketCap(int).  This will allow faster packet sending for
2608     * important packets, while preventing chat messages from being sent too rapidly.
2609     * @param milliseconds Number of milliseconds between each packet.
2610     * @see #setLowPriorityPacketCap(int)
2611     */
2612    public void setPacketSendDelay(int milliseconds)
2613    {
2614        m_botSession.getGamePacketGenerator().setSendDelay(milliseconds);
2615    }
2616
2617    /**
2618     * Adjusts the packet cap for low-priority (chat) packets.  The cap decides how many
2619     * low-priority packets can go out per clustered packet send (packet send delay is
2620     * adjusted with setPacketSendDelay(int)).  Generally you will not need to adjust this
2621     * unless your bot sends out a lot of chat packets.
2622     * @param cap Number of chat packets allowed per clustered send.
2623     * @see #setPacketSendDelay(int)
2624     */
2625    public void setLowPriorityPacketCap(int cap)
2626    {
2627        m_botSession.getGamePacketGenerator().setPacketCap( cap );
2628    }
2629
2630    /**
2631     * Turns automatic player position updating on and off.  By default it is ON,
2632     * and set to change between the players it spectates for packets at the rate
2633     * specified in setup.cfg under the DefaultSpectateTime field (in ms).  Clearly
2634     * the lower the number, the more reliable any position information stored in
2635     * Player will be, and the more frequently and reliably will the bot receive
2636     * PlayerPosition events.  The network load is almost inconsequential,
2637     * particularly with DefaultSpectateTime at values >1000 -- it requires only 3
2638     * bytes sent each specified tick -- but it is recommended to turn this feature
2639     * off by using a 0 value if you will not need reliable information in Player
2640     * classes.  (Name, playerID, ship type, wins and losses do not rely on this.)
2641     * <p>Note that because TWCore is a client emulator (does not operate on the
2642     * server side but logs in as a bot) it only receives position packets from
2643     * the server about players within radar range.  This is why it must switch
2644     * who is being spectated in order to get as many packets as possible.  Fortunately
2645     * this process requires only minimal load.
2646     * <p>If the arena the bot operates in is small, you may want to manually
2647     * adjust its position using the move() methods to most effectively receive packets.
2648     * @param milliseconds - specified time to update player positions at
2649     * 0     : turns tracking off and has the bot spectate its current area (change with move())
2650     * <200  : turns tracking on with 200 ms change rate
2651     * >=200 : turns tracking on with specified rate
2652     */
2653    public void setPlayerPositionUpdating( int milliseconds ) {
2654
2655        int delay = Math.max(0, milliseconds);
2656        Ship ship = getShip();
2657
2658        if(delay <= 0) {
2659                        ship.setSpectatorUpdateTime(0);
2660            stopSpectatingPlayer();
2661            moveToTile(512, 512);
2662                return;
2663        }
2664
2665        delay = Math.max(200, delay);
2666        ship.setSpectatorUpdateTime(delay);
2667    }
2668
2669    /**
2670     * Resets the automatic player position updating system to the default rate in setup.cfg.
2671     * It will stop if this value is 0 or start otherwise.  The load on the network is a fairly trivial
2672     * 3 bytes sent at each change of player that the bot is spectating.
2673     * <p>Note that 200ms is the default floor value.  If you wish to switch faster
2674     * than every 200ms, you must edit the setPlayerPositionUpdating method manually.
2675     */
2676    public void resetReliablePositionUpdating() {
2677        setPlayerPositionUpdating(DefaultSpectateTime);
2678    }
2679
2680    /**
2681     * Stops the automatic player position updating system, causing the bot to
2682     * stop spectating the player it is currently spectating on, and cease
2683     * switching between players to update position packets.  Use this if your
2684     * bot will not be receiving any position packets.
2685     */
2686    public void stopReliablePositionUpdating() {
2687        setPlayerPositionUpdating(0);
2688    }
2689
2690    /**
2691     * Retreives a file from the server.  File arrives in a "FileArrived" packet /
2692     * event, and can be received by handling that event.
2693     * @param fileName Filename of the file requested.
2694     */
2695    public void getServerFile(String fileName)
2696    {
2697        sendUnfilteredPublicMessage("*getfile " + fileName);
2698    }
2699
2700    /**
2701     * Sends a file to the server.  This could be used to back up logs, upload
2702     * alternate maps and configuration files.
2703     * @param fileName Name of the file to send.
2704     */
2705    public void putFile(String fileName)
2706    {
2707        sendUnfilteredPublicMessage("*putfile " + fileName);
2708    }
2709
2710
2711    // ***** LVZ OBJECT OPERATIONS *****
2712    /*
2713     * For use with LVZ objects.  If you're not familiar with the specification:
2714     *
2715     * http://www.kolumbus.fi/sakari.aura/contmapdevguide.html
2716     */
2717
2718    /**
2719     * Issues an LVZ object on command, displaying it for all players.  The ID
2720     * of the object is configured from inside the LVZ.
2721     * <p>For more information on this format, please download the LVZToolkit from
2722     * your favorite Subspace download site (subspacedownloads.com ?), and see
2723     * http://www.kolumbus.fi/sakari.aura/contmapdevguide.html for a solid spec.
2724     * @param objID The ID of the object as denoted in the LVZ.
2725     */
2726    public void showObject(int objID)
2727    {
2728        m_packetGenerator.sendSingleLVZObjectToggle(-1, objID, true);
2729    }
2730
2731    /**
2732     * Issues an LVZ object off command, hiding it for all players.  The ID of
2733     * the object is configured from inside the LVZ.
2734     * <p>For more information on this format, please download the LVZToolkit from
2735     * your favorite Subspace download site (subspacedownloads.com ?), and see
2736     * http://www.kolumbus.fi/sakari.aura/contmapdevguide.html for a solid spec.
2737     * @param objID The ID of the object as denoted in the LVZ.
2738     */
2739    public void hideObject(int objID)
2740    {
2741        m_packetGenerator.sendSingleLVZObjectToggle(-1, objID, false);
2742    }
2743
2744    /**
2745     * Shows the specified LVZ object for a specific player.
2746     * @param playerID ID of player.
2747     * @param objID ID of object.
2748     */
2749    public void showObjectForPlayer(int playerID, int objID)
2750    {
2751        m_packetGenerator.sendSingleLVZObjectToggle(playerID, objID, true);
2752    }
2753
2754    /**
2755     * Hides the specified LVZ object for a specific player.
2756     * @param playerID ID of player.
2757     * @param objID ID of object.
2758     */
2759    public void hideObjectForPlayer(int playerID, int objID)
2760    {
2761        m_packetGenerator.sendSingleLVZObjectToggle(playerID, objID, false);
2762    }
2763
2764    /**
2765     * Shows the specified LVZ object for a specific frequency, via a MANUAL "*objon #
2766     * @param playerID ID of player.
2767     * @param objID ID of object.
2768     */
2769    /*  Doesn't appear to work. -qan
2770    public void showObjectForFrequency(int frequency, int objID)
2771    {
2772        sendUnfilteredTargetTeamMessage(frequency, "*objon " + objID);
2773    }
2774    */
2775
2776    /**
2777     * Hides the specified LVZ object for a specific player, via a MANUAL "*objoff #
2778     * @param playerID ID of player.
2779     * @param objID ID of object.
2780     */
2781    /*  Doesn't appear to work. -qan
2782    public void hideObjectForFrequency(int frequency, int objID)
2783    {
2784        sendUnfilteredTargetTeamMessage(frequency, "*objoff " + objID);
2785    }
2786    */
2787
2788    /**
2789     * Sets objects for a particular frequency via a MANUAL "*objset
2790     * @param playerID ID of player.
2791     * @param String Object list with + for show and - for hide, as in "+1,-4," (must end in comma!)
2792     */
2793    /*  Doesn't appear to work. -qan
2794    public void setObjectsForFrequency(int frequency, String objs)
2795    {
2796        sendUnfilteredTargetTeamMessage(frequency, "*objset " + objs);
2797    }
2798    */
2799
2800    /**
2801     * Setup the specified LVZ object to be shown or hidden to all players.
2802     * @param objID ID of object.
2803     * @param isVisible True if object should be shown; false if hidden
2804     */
2805    public void setupObject( int objID, boolean isVisible )
2806    {
2807        m_packetGenerator.setupLVZObjectToggle(-1, objID, isVisible);
2808    }
2809
2810    /**
2811     * Setup the specified LVZ object to be shown or hidden to a specific
2812     * player.
2813     * @param playerID ID of player to whom you shall send the objects
2814     * @param objID ID of object.
2815     * @param isVisible True if object should be shown; false if hidden
2816     */
2817    public void setupObject( int playerID, int objID, boolean isVisible )
2818    {
2819        m_packetGenerator.setupLVZObjectToggle(playerID, objID, isVisible);
2820    }
2821
2822    /**
2823     * Sends all objects that have been set up to be sent to all players.
2824     */
2825    public void sendSetupObjects() {
2826        m_packetGenerator.sendLVZObjectCluster(-1);
2827    }
2828
2829    /**
2830     * Sends all objects that have been set up to be sent to a specific player.
2831     * @param playerID ID of player.
2832    */
2833    public void sendSetupObjectsForPlayer(int playerID ) {
2834        m_packetGenerator.sendLVZObjectCluster(playerID);
2835    }
2836
2837    /**
2838     * Manually sets multiple objects to either be shown or hidden using the
2839     * syntax of the *objset command.  objString should contain comma-separated
2840     * object IDs marked either with a + or - for show or hide.  Also the string
2841     * MUST terminate with a comma!  For example:
2842     * <p>  "+2,+5,-7,+12,-14,+15,"
2843     * @param objString Comma-separated list of object IDs marked with + for show or - for hide, ending with a comma.
2844     */
2845    public void manuallySetObjects( HashMap <Integer,Boolean>objects ) {
2846        m_packetGenerator.setupMultipleLVZObjectToggles(-1, objects );
2847        m_packetGenerator.sendLVZObjectCluster(-1);
2848    }
2849
2850    /**
2851     * Manually sets multiple objects to either be shown or hidden using a HashMap
2852     * with mappings from Integer objIDs to Boolean visibility.
2853     * @param objects Mapping from Integer objID to Boolean visibility
2854     * @param playerID ID of the player to send to.
2855     */
2856    public void manuallySetObjects( HashMap <Integer,Boolean>objects, int playerID ) {
2857        m_packetGenerator.setupMultipleLVZObjectToggles(playerID, objects );
2858        m_packetGenerator.sendLVZObjectCluster(playerID);
2859    }
2860
2861    /**
2862     * Sets objects using the current objects set under BotAction's copy of Objset.
2863     * In order for this command to work, first get the Objset with getObjectSet(),
2864     * add the objects you would like set all at once, and then run the command.
2865     */
2866    public void setObjects() {
2867        if( m_objectSet.toSet() ) {
2868            m_packetGenerator.setupMultipleLVZObjectToggles(-1, m_objectSet.getObjects() );
2869            m_packetGenerator.sendLVZObjectCluster(-1);
2870        }
2871    }
2872
2873    /**
2874     * Sets objects using the current objects set under BotAction's copy of Objset
2875     * for a specific player, as specified by ID.  In order for this command to
2876     * work, first get the Objset with getObjectSet(), add the objects you would
2877     * like set all at once, and then run the command.
2878     * @param playerID ID of the player to send to.
2879     */
2880    public void setObjects( int playerID ) {
2881        if( m_objectSet.toSet( playerID ) ) {
2882            m_packetGenerator.setupMultipleLVZObjectToggles(playerID, m_objectSet.getObjects( playerID ) );
2883            m_packetGenerator.sendLVZObjectCluster(playerID);
2884        }
2885    }
2886
2887    /**
2888     * Sets objects using the current objects set under BotAction's copy of Objset
2889     * for a specific freq, as specified by given #.  In order for this command to
2890     * work, first get the Objset with getObjectSet(), add the objects you would
2891     * like set all at once, and then run the command.
2892     * @param freq is the freq of players to send to.
2893     */
2894    public void setFreqObjects( int freq ) {
2895        if( m_objectSet.toFreqSet( freq ) ) {
2896            Iterator<Player> freqPlayers = getFreqPlayerIterator( freq );
2897            HashMap<Integer,Boolean> freqObjs = m_objectSet.getFreqObjects( freq );
2898            while (freqPlayers.hasNext())       {
2899                Player current = freqPlayers.next();
2900                if (current != null)    {
2901                        m_packetGenerator.setupMultipleLVZObjectToggles(current.getPlayerID(), freqObjs );
2902                        m_packetGenerator.sendLVZObjectCluster(current.getPlayerID());
2903                }
2904            }
2905        }
2906    }
2907
2908
2909    // ***** SQL DATABASE OPERATIONS *****
2910    /*
2911     * SQL in TWCore is easy [and fun :P]!  If you set up a connection to a
2912     * SQL server inside sql.cfg, in order to run a query, just supply the
2913     * name of the connection and the SQL query you'd like to run.  Then the
2914     * results will be returned to you in a ResultSet.  Search for ResultSet
2915     * inside various bots to see examples of usage -- it's very simple to do,
2916     * and adds a tremendous amount of functionality to a bot.
2917     * <p>
2918     * NOTE: After retrieving the ResultSet of a query, you absolutely <b>MUST</b>
2919     * run BotAction's SQLClose() on the ResultSet to free it from memory, regardless
2920     * of whether or not you use the results of the ResultSet in your code!
2921     */
2922
2923    /**
2924     * Runs a direct SQL Query.  This method does block up the bot from doing anything
2925     * else, so be careful while using it.  Queries should be quick.  Only use it for
2926     * queries where the performance of the bot depends primarily upon the value
2927     * returned by this query.<p>
2928     * <u>NOTE</u>: After retrieving the ResultSet of the query from this method, you <b>MUST</b>
2929     * run BotAction's SQLClose() on the ResultSet to free it from memory!  If
2930     * you do not wish to do it manually, use the SQLQueryAndClose method, which
2931     * closes your ResultSet automatically (useful for INSERT, DELETE, UPDATE, etc).
2932     * @param connectName The connection name as specified in sql.cfg
2933     * @param query The SQL query to be executed
2934     * @throws SQLException SQLException
2935     * @return ResultSet from the SQL Query. (MAY be null)  You must close w/ {@link #SQLClose(ResultSet)} after use.
2936     * @see #SQLQueryAndClose(String, String)
2937     */
2938    public ResultSet SQLQuery(String connectName, String query) throws SQLException
2939    {
2940        return getCoreData().getSQLManager().query(connectName, query);
2941    }
2942
2943    /**
2944     * Runs a direct SQL Query, and then closes its ResultSet.  This is useful for
2945     * UPDATE, INSERT and DELETE queries.<p>
2946     * This method does block up the bot from doing anything else, so be careful
2947     * while using it.  Queries should be quick.  Only use it for queries where the
2948     * performance of the bot depends primarily upon the value returned by this query.
2949     * @param connectName The connection name as specified in sql.cfg
2950     * @param query The SQL query to be executed
2951     * @throws SQLException SQLException
2952     */
2953    public void SQLQueryAndClose(String connectName, String query) throws SQLException
2954    {
2955        SQLClose( getCoreData().getSQLManager().query(connectName, query) );
2956    }
2957
2958    /**
2959     * Runs a regular backround SQL Query, which is placed in a queue and waits
2960     * behind any other background queries ahead of it.  Background queries are
2961     * generally used when getting the data isn't time critical, or when a bot's
2962     * program thread should not be blocked (for example if the query is large).
2963     * <p>Background queries are returned to the bot by handling an SQLResultEvent
2964     * and checking for a specific identifier to determine if it's the right query.
2965     * NOTE: If you retrieve the ResultSet of the background query, you <b>MUST</b>
2966     * run BotAction's SQLClose() on the ResultSet.  If you do not wish to retrieve
2967     * the ResultSet, provide a null identifier and it will be closed automatically.
2968     * @param connectName The connection name as specified in sql.cfg
2969     * @param identifier A unique identifier that describes what the query is.
2970     * This identifier will be found in the SQLResultEvent when it is returned.
2971     * The ID allows you to handle different sorts of background queries differently
2972     * when they come out the other end.  If the identifier is null, the result
2973     * of the query won't be delivered, and the ResultSet will be closed automatically.
2974     * @param query The SQL query to be executed.
2975     */
2976    public void SQLBackgroundQuery(String connectName, String identifier, String query)
2977    {
2978        getCoreData().getSQLManager().queryBackground(connectName, identifier, query, m_botSession.getSubspaceBot());
2979    }
2980
2981    /**
2982     * Runs a high priority background query.  This query will be executed before all
2983     * other background queries.  This is useful for when you know the queue will be
2984     * rather large, and you need a query done very quickly, but still wish to run it
2985     * in the background without blocking the bot's thread.
2986     * NOTE: If you retrieve the ResultSet of the background query, you <b>MUST</b>
2987     * run BotAction's SQLClose() on the ResultSet.  If you do not wish to retrieve
2988     * the ResultSet, provide a null identifier and it will be closed automatically.
2989     * @param connectName The connection name as specified in sql.cfg
2990     * @param identifier A unique identifier that describes what the query is.
2991     * This identifier will be found in the SQLResultEvent when it is returned.
2992     * The ID allows you to handle different sorts of background queries differently
2993     * when they come out the other end.  If the identifier is null, the result
2994     * of the query will not be delivered, and the ResultSet will be closed automatically.
2995     * @param query The SQL query to be executed
2996     */
2997    public void SQLHighPriorityBackgroundQuery(String connectName, String identifier, String query)
2998    {
2999        getCoreData().getSQLManager().queryBackgroundHighPriority(connectName, identifier, query, m_botSession.getSubspaceBot());
3000    }
3001
3002    /**
3003     * Runs an insert query into the specified table, given a set of fields and
3004     * values that correspond to those fields.  This is a helper method that
3005     * forms the query for you.  Experienced users of SQL may wish to form the
3006     * query themselves using the {@link #SQLQuery(String, String)} method.<p>
3007     * NOTE: This method runs SQLClose() automatically for you.
3008     * @param connectName The connection name as specified in sql.cfg
3009     * @param tableName The name of the table you wish to insert the values into.
3010     * @param fields The field names you want to enter data into.
3011     * @param values The corresponding values for the field names.
3012     * @see #SQLQuery(String, String)
3013     */
3014    public void SQLInsertInto(String connectName, String tableName, String[] fields, String[] values)
3015    {
3016        if (fields.length != values.length)
3017        {
3018            Tools.printLog("SQLInsertInfo error: mismatch in the number of " + "fields/values");
3019            return;
3020        }
3021        StringBuffer beginning = new StringBuffer();
3022        beginning.append("INSERT INTO " + tableName + "(");
3023        StringBuffer end = new StringBuffer();
3024        end.append(")VALUES(");
3025        for (int i = 0; i < fields.length; i++)
3026        {
3027            beginning.append(fields[i]);
3028            end.append("\"" + values[i] + "\"");
3029            if (i < fields.length - 1)
3030            {
3031                beginning.append(",");
3032                end.append(",");
3033            }
3034        }
3035
3036        String query = beginning.toString() + end.toString() + ")";
3037        try
3038        {
3039            SQLClose( SQLQuery(connectName, query) );  // Run query & close ResultSet
3040        }
3041        catch (Exception e)
3042        {
3043                Tools.printStackTrace(e);
3044        }
3045    }
3046
3047    /**
3048     * Runs an insert query into the specified table, given a set of fields and
3049     * values that correspond to those fields.  This is a helper method that
3050     * forms the query for you.  Experienced users of SQL may wish to form the
3051     * query themselves using the SQLBackgroundQuery() method.<p>
3052     * NOTE: This method automatically closes the ResultSet for you.
3053     * @param connectName The connection name as specified in sql.cfg.
3054     * @param tableName The name of the table you wish to insert the values into.
3055     * @param fields The field names you want to enter data into.
3056     * @param values The corresponding values for the field names.
3057     * @see #SQLBackgroundQuery(String, String, String)
3058     */
3059    public void SQLBackgroundInsertInto(String connectName, String tableName, String[] fields, String[] values)
3060    {
3061        if (fields.length != values.length)
3062        {
3063            Tools.printLog("SQLInsertInfo error: mismatch in the number of " + "fields/values");
3064            return;
3065        }
3066        StringBuffer beginning = new StringBuffer();
3067        beginning.append("INSERT INTO " + tableName + "(");
3068        StringBuffer end = new StringBuffer();
3069        end.append(")VALUES(");
3070        for (int i = 0; i < fields.length; i++)
3071        {
3072            beginning.append(fields[i]);
3073            end.append("\"" + values[i] + "\"");
3074            if (i < fields.length - 1)
3075            {
3076                beginning.append(",");
3077                end.append(",");
3078            }
3079        }
3080
3081        String query = beginning.toString() + end.toString() + ")";
3082        try
3083        {
3084            SQLBackgroundQuery(connectName, null, query);
3085        }
3086        catch (Exception e)
3087        {
3088                Tools.printStackTrace(e);
3089        }
3090    }
3091
3092    /**
3093     * Closes a ResultSet and the Statement that called it.  After you have made
3094     * a query and are done with its ResultSet, you <b>MUST</b> call this method --
3095     * otherwise it will not be garbage-collected, and will continue to use memory.
3096     * You also must call this method whether you choose to get the returned
3097     * ResultSet from a query or not.  If you don't use the ResultSet, use
3098     * {@link #SQLQueryAndClose(String, String)} instead of {@link #SQLQuery(String, String)};
3099     * this will close the ResultSet automatically.
3100     * @param rs ResultSet you wish to close
3101     */
3102    public void SQLClose( ResultSet rs ) {
3103        if (rs != null) {
3104            Statement smt = null;
3105            try {
3106                smt = rs.getStatement();
3107            } catch (SQLException sqlEx) {} // ignore any errors
3108
3109            try {
3110                rs.close();
3111            } catch (SQLException sqlEx) {} // ignore any errors
3112            rs=null;
3113
3114            if (smt != null) {
3115                try {
3116                    smt.close();
3117                } catch (SQLException sqlEx) {} // ignore any errors
3118                smt=null;
3119            }
3120        }
3121
3122    }
3123
3124    /**
3125     * Creates a PreparedStatement with the specified query using the specified connection.
3126     *
3127     * NOTE: This PreparedStatement MUST be closed when it's destroyed!! (At the end of a method or when the bot disconnects.)
3128     * Use the method closePreparedStatement for this.
3129     * If you don't, the connection in the connectionpool will be left locked and no other bots will be able to use it.
3130     * This will become a connection leak!
3131     * NOTE2: The returned value can be NULL if something went wrong or if there were no more connections available.
3132     * Always check for this!
3133     *
3134     * @param connectionName The connection name as specified in sql.cfg.
3135     * @param uniqueID A unique string that you can identify your bot with. Usually your bot name suffices. <br/>You will get a PreparedStatement on the same Connection when using the same uniqueID.
3136     * @param sqlstatement The (dynamic) SQL INSERT/UPDATE statement for pre-parsing
3137     * @return a PreparedStatement object or null if there was an error
3138     */
3139    public PreparedStatement createPreparedStatement(String connectionName, String uniqueID, String sqlstatement) {
3140        return getCoreData().getSQLManager().createPreparedStatement(connectionName, uniqueID, sqlstatement, false);
3141    }
3142
3143    /**
3144     * Creates a PreparedStatement with the specified query using the specified connection.
3145     *
3146     * NOTE: This PreparedStatement MUST be closed when it's destroyed!! (At the end of a method or when the bot disconnects.)
3147     * Use the method closePreparedStatement for this.
3148     * If you don't, the connection in the connectionpool will be left locked and no other bots will be able to use it.
3149     * This will become a connection leak!
3150     * NOTE2: The returned value can be NULL if something went wrong or if there were no more connections available.
3151     * Always check for this!
3152     *
3153     * @param connectionName The connection name as specified in sql.cfg.
3154     * @param uniqueID A unique string that you can identify your bot with. Usually your bot name suffices. <br/>You will get a PreparedStatement on the same Connection when using the same uniqueID.
3155     * @param sqlstatement The (dynamic) SQL INSERT/UPDATE statement for pre-parsing
3156     * @param retrieveAutoGeneratedKeys whether auto-generated keys should be returned
3157     * @return a PreparedStatement object or null if there was an error
3158     */
3159    public PreparedStatement createPreparedStatement(String connectionName, String uniqueID, String sqlstatement, boolean retrieveAutoGeneratedKeys) {
3160        return getCoreData().getSQLManager().createPreparedStatement(connectionName, uniqueID, sqlstatement, retrieveAutoGeneratedKeys);
3161    }
3162
3163    /**
3164     * Closes the specified PreparedStatement and frees the used Connection in the specified connection pool.
3165     *
3166     * You MUST close this PreparedStatement when it's about to be destroyed! (At the end of a method or when the bot disconnects.)
3167     *
3168     * If you don't, the connection in the connectionpool will be left locked and no other bots will be able to use it.
3169     * This will become a connection leak!
3170     *
3171     * @param connectionName The connection name as specified in sql.cfg.
3172     * @param uniqueID The uniqueID used to create the Prepared Statement
3173     * @param p The PreparedStatement to be closed
3174     */
3175    public void closePreparedStatement(String connectionName, String uniqueID, PreparedStatement p) {
3176        if(p != null) {
3177                Connection conn = null;
3178                try {
3179                        conn = p.getConnection();
3180                } catch(SQLException sqle) {}
3181
3182                try {
3183                        p.close();
3184                } catch(SQLException sqle) {}
3185
3186                if(conn != null) {
3187                        getCoreData().getSQLManager().freeConnection(connectionName, uniqueID, conn);
3188                }
3189        }
3190
3191    }
3192
3193
3194    // ***** INTER-PROCESS COMMUNICATION OPERATIONS *****
3195    /*
3196     * IPC messages are a very simple and easy way to send information from one
3197     * bot to another.  This can be used to coordinate activities between them.
3198     * To use Inter-Process Communications:
3199     *
3200     *  - All bots that will communicate with one another need to be subscribed
3201     *    to a particular channel (something like tuning into the same frequency
3202     *    on a radio).  The channel is identified by a unique String.  Use
3203     *    the ipcSubscribe(String) to do this.
3204     *  - Any bot that wishes to receive messages needs to request and handle
3205     *    InterProcessEvent in their code.  From there the bot can verify the
3206     *    sender, receiver, message itself, and handle how it wishes.
3207     *  - Send messages using ipcSendMessage, or the more generic ipcTransmit.
3208     */
3209
3210    /**
3211     * Constructs a basic IPC message across a given channel.  This is the simplest
3212     * way to transmit a message.  To transmit a more generic object, use the
3213     * ipcTransmit method.  Note that in this method, sender and receiver may be null.
3214     * @param channelName Name of the channel to broadcast the message over.
3215     * @param message Message to send.
3216     * @param recipient Unique name of a specific receiver, if any.  Null for all on channel.
3217     * @param sender Unique name of the 'sender' -- usually name of the bot.  May be null.
3218     * @see #ipcTransmit(String, Object)
3219     */
3220    public void ipcSendMessage( String channelName, String message, String recipient, String sender ) {
3221        IPCMessage msg = new IPCMessage( message, recipient, sender );
3222        getCoreData().getInterProcessCommunicator().broadcast(channelName, m_botSession.getBotName(), m_botSession.getSubspaceBot(), msg);
3223    }
3224
3225    /**
3226     * Transmits a generic object (usually an IPCMessage) to the specified channel
3227     * using inter-process communication.  The object will arrive as an InterProcessEvent,
3228     * which must be requested and handled by any bot that will receive the message.
3229     * If a bot is not yet subscribed to the channel, the bot will automatically be
3230     * subscribed.
3231     * @param channelName Channel name to send the object to.
3232     * @param o Object to be transmitted.
3233     * @see #ipcSendMessage(String, String, String, String)
3234     */
3235    public void ipcTransmit(String channelName, Object o)
3236    {
3237        getCoreData().getInterProcessCommunicator().broadcast(channelName, m_botSession.getBotName(), m_botSession.getSubspaceBot(), o);
3238    }
3239
3240    /**
3241     * Subscribes the current bot to the specified IPC Channel.  Do not attempt to
3242     * use this method in the constructor of a bot.  Use it during or after handling
3243     * the LoggedOn event, or at a later time.
3244     * @param channelName Name of the IPC channel you wish to subscribe to.
3245     */
3246    public void ipcSubscribe(String channelName)
3247    {
3248        getCoreData().getInterProcessCommunicator().subscribe(channelName, m_botSession.getSubspaceBot());
3249    }
3250   
3251    /**
3252     * Subscribes the current bot to the specified Socket Channel.  Do not attempt to
3253     * use this method in the constructor of a bot.  Use it during or after handling
3254     * the LoggedOn event, or at a later time.
3255     * @param channelName Name of the Socket channel you wish to subscribe to.
3256     */
3257    public void socketSubscribe(String channelName)
3258    {
3259        //getCoreData().getSocketCommunicator().subscribe(channelName, m_botSession.getSubspaceBot());
3260    }
3261
3262    /**
3263     * Unsubscribes this bot from the provided channelName.  If the bot is the last
3264     * one on the channel, the channel is destroyed.
3265     * @param channelName Name of the IPC channel you wish to unsubscribe from.
3266     */
3267    public void ipcUnSubscribe(String channelName)
3268    {
3269        getCoreData().getInterProcessCommunicator().unSubscribe(channelName, m_botSession.getSubspaceBot());
3270    }
3271
3272    /**
3273     * Explicitly destroys a channel, regardless of if any bots are subscribed.
3274     * @param channelName The name of the channel to be destroyed.
3275     */
3276    public void ipcDestroyChannel(String channelName)
3277    {
3278        getCoreData().getInterProcessCommunicator().destroy(channelName);
3279    }
3280   
3281    public String[] ipcSubscribedChannels() {
3282        return getCoreData().getInterProcessCommunicator().getSubscribedChannels(m_botSession.getSubspaceBot());
3283    }
3284
3285
3286
3287
3288    // **********************************************************************************
3289    //
3290    //                                   4. GETTERS
3291    //
3292    // **********************************************************************************
3293    /*
3294     * The following methods are labelled getters because their main intent is to
3295     * return data.  They are organized into the following categories:
3296     *
3297     *  - SIMPLE GETTERS: Get bot name, ID #, current arena name, number of players
3298     *    in current arena, server address, server port, current state, & SQL status.
3299     *  - SIMPLE CLASS GETTERS: Gets for the following classes - BotAction, CoreData,
3300     *    OperatorList, EventRequester, BotSettings (for the general setup.cfg and
3301     *    for specific bots), Ship, InterProcessCommunicator, and Objset.
3302     *  - PLAYER/FLAG GETTERS: Get player by ID or name, get player ID, get player by
3303     *    fuzzy (incomplete) name, get player name by fuzzy name, get flag by flag ID.
3304     *  - COMPLEX GETTERS: Get number of players in the arena playing, if a frequency
3305     *    has a particular ship currently on it, total combined scores of all players
3306     *    on a freq, File corresponding to setup.cfg, File to file in /data directory,
3307     *    File to TWCore root directory, and File to file in TWCore root directory.
3308     *  - ITERATORS: Iterators over - all players currently in a ship in the arena,
3309     *    all players on a frequency, all players (spec'd and playing), all players
3310     *    by ID rather than Player objects, and all flags by ID.
3311     */
3312
3313
3314    // ***** SIMPLE GETTERS *****
3315
3316    /**
3317     * @return The login name of the bot as displayed to players.
3318     */
3319    public String getBotName() {
3320        return m_botSession.getBotName();
3321    }
3322
3323    /**
3324     * @return m_botNumber Bot's TWCore ID number, generally only used internally.
3325     */
3326    public int getBotNumber() {
3327        return m_botNumber;
3328    }
3329
3330    /**
3331     * @return Name of the arena the bot is currently in or travelling to.
3332     */
3333    public String getArenaName() {
3334        if (m_arenaName != null)
3335            return m_arenaName;
3336        else
3337            return "Unknown";
3338    }
3339
3340    /**
3341     * @return The number of players currently in the arena (in-game + spectating).
3342     */
3343    public int getArenaSize() {
3344        return m_arenaTracker.size();
3345    }
3346
3347    /**
3348     * @return The number of players currently playing (in-game).
3349     */
3350    public int getNumPlaying() {
3351        return m_arenaTracker.getNumPlaying();
3352    }
3353
3354    /**
3355     * @return The number of players currently spectating.
3356     */
3357    public int getNumSpectating() {
3358        return m_arenaTracker.getNumSpectating();
3359    }
3360
3361    /**
3362     * @return The number of players currently on a frequency (in-game + spectating).
3363     */
3364    public int getFrequencySize( int freq ) {
3365        return m_arenaTracker.getFreqSize(freq);
3366    }
3367
3368    /**
3369     * @return The number of players currently playing on a frequency (only those in-game).
3370     */
3371    public int getPlayingFrequencySize( int freq ) {
3372        return m_arenaTracker.getPlayingFreqSize(freq);
3373    }
3374
3375    /**
3376     * @return The host name of the server the bot is connected to.
3377     */
3378    public String getServerName() {
3379        return getCoreData().getServerName();
3380    }
3381
3382    /**
3383     * @return The port number on the server that the bot is connected to.
3384     */
3385    public int getServerPort() {
3386        return getCoreData().getServerPort();
3387    }
3388
3389    /**
3390     * @return The server time difference in 100ths of a second.
3391     */
3392        public int getServerTimeDifference() {
3393                return m_packetGenerator.getServerTimeDifference();
3394    }
3395
3396        /**
3397         * @return The server time in 100ths of a second.
3398         */
3399    public int getServerTime() {
3400        return (int)(System.currentTimeMillis() / 10) + m_packetGenerator.getServerTimeDifference();
3401    }
3402
3403    /**
3404     * Used to expand the 2-byte timestamp in PlayPosition events to full 4-byte timestamp.
3405     * @return The expanded timstamp in 100ths of a second.
3406     */
3407        public int expandPlayerPositionTimestamp(int timestampLow) {
3408
3409                int timestampHi = getServerTime();// & 0x7FFFFFFF;
3410                int serverTimeLow = timestampHi & 0x0000FFFF;
3411                timestampLow &= 0x0000FFFF;
3412
3413                //outTimestamp &= 0xFFFF0000;
3414
3415                if(serverTimeLow >= timestampLow) {
3416                        if(serverTimeLow - timestampLow > 0x00007FFF) {
3417                                timestampHi += 0x00010000;
3418                        }
3419                } else {
3420                        if(timestampLow - serverTimeLow > 0x00007FFF) {
3421                                timestampHi -= 0x00010000;
3422                        }
3423                }
3424                return (timestampHi & 0xFFFF0000) | timestampLow;
3425        }
3426
3427
3428    /**
3429     * Gets the state of the bot.  This is only important from an internal
3430     * perspective, and unless you plan to add new bot states, can be ignored.
3431     * @return An integer representing the state of the bot as reflected in Session.
3432     */
3433    public int getBotState() {
3434        return m_botSession.getBotState();
3435    }
3436
3437    /**
3438     * True if the SQL connection pools were initialized properly, a general
3439     * indicator that the SQL system appears to be running properly.  The
3440     * reliability of this method is somewhat questionable.
3441     * @return True if the SQL connection pools were initialized properly.
3442     */
3443    public boolean SQLisOperational() {
3444        return getCoreData().getSQLManager().isOperational();
3445    }
3446
3447
3448    // ***** SIMPLE CLASS GETTERS *****
3449
3450    /**
3451     * Return the correct BotAction for the running Thread.  Useful for subclasses
3452     * of bots, so you don't need to pass BotAction down the entire hierarchy.
3453     * @return The BotAction object of the currently running Thread / bot.
3454     */
3455    static public BotAction getBotAction() {
3456        return ((Session)Thread.currentThread()).getBotAction();
3457    }
3458
3459    /**
3460     * @return A reference to the CoreData storage class for the bot core.
3461     */
3462    public CoreData getCoreData()
3463    {
3464        return m_botSession.getCoreData();
3465    }
3466
3467    /**
3468     * Gets the OperatorList object shared between bots that is used to determine
3469     * access levels.
3470     * @return An instance containing methods and information related to access control.
3471     */
3472    public OperatorList getOperatorList() {
3473        return getCoreData().getOperatorList();
3474    }
3475
3476    /**
3477     * Gets the EventRequester object.  This object controls what events are being sent
3478     * to your bot.  EventRequester can turn on or off requested events at any time.
3479     * Use its requestEvent(int) method to request an event.  See source for more info.
3480     * @return The EventRequester object for this bot.
3481     */
3482    public EventRequester getEventRequester()
3483    {
3484        return m_botSession.getEventRequester();
3485    }
3486
3487    /**
3488     * Gets a copy of the general settings object, which stores information found
3489     * in setup.cfg.
3490     * @return An instance of a class that provides the data contained in setup.cfg
3491     */
3492    public BotSettings getGeneralSettings() {
3493        return getCoreData().getGeneralSettings();
3494    }
3495
3496    /**
3497     * Gets a BotSettings object for this bot (from botname.cfg, where botname
3498     * is the main class name of the bot).
3499     * @return A BotSettings object containing data from the bot's .cfg
3500     */
3501    public BotSettings getBotSettings() {
3502        String botName = m_botSession.getBotClass().getName();
3503        if (botName.indexOf(".") != -1 ) {
3504            botName = botName.substring(botName.lastIndexOf(".") + 1);
3505        }
3506        return m_botSession.getCoreData().getBotConfig(botName);
3507    }
3508
3509    /**
3510     * Gets the Ship object for this bot, which allows you to control the bot
3511     * as an in-game ship, including movement, firing, attaching, etc.
3512     * @return This bot's Ship object, which controls the in-game flight of the bot.
3513     */
3514    public Ship getShip() {
3515        return m_botSession.getShip();
3516    }
3517
3518    /**
3519     * Gets a copy of the InterProcessCommunicator for use with sending messages
3520     * between bots.  See the source for BotAction, under MISC OPERATIONS ->
3521     * INTER-PROCESS COMMUNICATOR OPERATIONS, for a guide on using IPC.
3522     * @return The main class of IPC messaging, the InterProcessCommunicator.
3523     * @see #ipcSendMessage(String, String, String, String)
3524     * @see #ipcSubscribe(String)
3525     */
3526    public InterProcessCommunicator getIPC() {
3527        return getCoreData().getInterProcessCommunicator();
3528    }
3529
3530    /**
3531     * Returns the Objset associated with this bot.  Objset can be used to
3532     * maintain a list of LVZ objects to shown or hide without the hassle
3533     * of handling them manually.
3534     */
3535    public Objset getObjectSet() {
3536        return m_objectSet;
3537    }
3538
3539        /**
3540     * Gets the TempSettingsManager associated with this bot. This allows for
3541     * multiple plugins to utilize the functionality without having to maintain
3542     * more than one instance.
3543     * @return a TempSettingsManager object for use
3544     */
3545    public synchronized TempSettingsManager getTSM()
3546    {
3547        if(m_tsm == null)
3548        {
3549                m_tsm = new TempSettingsManager(this);
3550        }
3551
3552        return m_tsm;
3553    }
3554
3555    // ***** PLAYER/FLAG GETTERS *****
3556    /*
3557     * One word of warning: when looking up Players, be sure to check them for
3558     * null before using them.  Even if an event passes you a name or ID, it's
3559     * not a 100% chance you can use that information to find the player, and
3560     * an attempt to do so can from time to time return a null value.
3561     */
3562
3563    /**
3564     * Gets the Player object associated with the PlayerID provided.  The Player
3565     * object describes all the pertinent details about a player in the arena the
3566     * bot is in.  PlayerID is not the same as ?userid or a network adapter's MAC
3567     * address, and is assigned arena-wide, not zone-wide.  As such it is not
3568     * considered an extremely reliable way of tracking players.
3569     * <p>
3570     * <b>!!NOTE!!</b>  It's important to check the returned Player object for a null
3571     * value or catch the resulting NullPointerException if you make reference to the
3572     * object without checking for null.  This is the single most common error
3573     * made by new TWCore botmakers.  Don't trust that the playerID provided from
3574     * an event packet will correspond to an existing player.  It's possible that
3575     * the event will fire simultaneously as the player leaves, resulting in a null
3576     * value for the Player object.
3577     *
3578     * @param playerID The PlayerID of the player you wish to retrieve info for.
3579     * @return If a matching ID is found, returns that Player object; otherwise, ** NULL **.
3580     */
3581    public Player getPlayer(int playerID)
3582    {
3583        return m_arenaTracker.getPlayer(playerID);
3584    }
3585
3586    /**
3587     * Gets the Player object associated with the PlayerID provided.  The Player
3588     * object describes all the pertinent details about a player in the arena the
3589     * bot is in.  PlayerID is not the same as ?userid or a network adapter's MAC
3590     * address, and is assigned arena-wide, not zone-wide.  As such it is not
3591     * considered an extremely reliable way of tracking players.
3592     * <p>
3593     * <b>!!NOTE!!</b>  It's important to check the returned Player object for a null
3594     * value or catch the resulting NullPointerException if you make reference to the
3595     * object without checking for null.  This is the single most common error
3596     * made by new TWCore botmakers.  Don't trust that the playerID provided from
3597     * an event packet will correspond to an existing player.  It's possible that
3598     * the event will fire simultaneously as the player leaves, resulting in a null
3599     * value for the Player object.
3600     *
3601     * @param playerName The name of the player you wish to retreive info for.
3602     * @return If a matching name is found, returns that Player object; otherwise, ** NULL **.
3603     */
3604    public Player getPlayer(String playerName)
3605    {
3606        return m_arenaTracker.getPlayer(playerName);
3607    }
3608
3609    /**
3610     * Looks up a player's name using a given player ID.  If the name can't be
3611     * found based on that ID, null may be returned.
3612     * @param playerID The PlayerID to look up.
3613     * @return The player's name.  ** MAY BE NULL **
3614     */
3615    public String getPlayerName(int playerID)
3616    {
3617        return m_arenaTracker.getPlayerName(playerID);
3618    }
3619
3620    /**
3621     * Looks up a player's name using a playerID.  If the player is not in the
3622     * arena, they will not have an ID associated with their name, and -1 will
3623     * be returned.
3624     * @param playerName The name of the player.
3625     * @return If found, the ID of the player; -1 if not found.
3626     */
3627    public int getPlayerID(String playerName)
3628    {
3629        return m_arenaTracker.getPlayerID(playerName);
3630    }
3631
3632    /**
3633     * Gets the Player whose name most accurately matches the supplied search
3634     * name.  If the name matches exactly, this is returned; else, all names
3635     * are checked to see if they begin with the search term, and of those that
3636     * do, the one that comes latest in the dictionary is returned.  For
3637     * example, if the search name is "Oli", and both the players "Oliver" and
3638     * "Oliver Claushauf" are in the arena, the latter will be returned.  If no
3639     * name in the arena begins with the provided name, null is returned.
3640     * @param playerName The partial name of a player.
3641     * @return A Player with name matching or starting with playerName; null if no match.
3642     */
3643    public Player getFuzzyPlayer(String playerName)
3644    {
3645        String fuzzyResult = getFuzzyPlayerName(playerName);
3646        if (fuzzyResult != null)
3647            return getPlayer(fuzzyResult);
3648         else
3649            return null;
3650    }
3651
3652    /**
3653     * Gets the name of the player that most accurately matches the supplied
3654     * search name.  If the name matches exactly, this is returned; else, all
3655     * names are checked to see if they begin with the search term, and of those
3656     * that do, the one that comes latest in the dictionary is returned.  For
3657     * example, if the search name is "Oli", and both the players "Oliver" and
3658     * "Oliver Claushauf" are in the arena, the latter will be returned.  If no
3659     * name in the arena begins with the provided name, null is returned.
3660     * @param playerName The partial name of a player.
3661     * @return Name of the player matching or starting with playerName; null if no match.
3662     */
3663    public String getFuzzyPlayerName(String playerName)
3664    {
3665        Map<Integer, Player> m_playerMap = m_arenaTracker.getPlayerMap();
3666        Iterator<Player> i = m_playerMap.values().iterator();
3667        String answ, best = null;
3668        synchronized(m_playerMap) {
3669            while (i.hasNext())
3670            {
3671                answ = i.next().getPlayerName();
3672                if (answ.toLowerCase().startsWith(playerName.toLowerCase()))
3673                    if (best == null)
3674                        best = answ;
3675                    else if (best.toLowerCase().compareTo(answ.toLowerCase()) > 0)
3676                        best = answ;
3677
3678                if (answ.equalsIgnoreCase(playerName))
3679                    return answ;
3680            }
3681         }
3682
3683        return best;
3684    }
3685
3686    /**
3687     * Gets the Flag object associated with the FlagID provided.  The Flag object
3688     * describes all the pertinent details about a flag in the arena the bot is in.
3689     * @param flagID The FlagID of the player you wish to retrieve info for.
3690     * @return The corresponding Flag object.
3691     */
3692    public Flag getFlag(int flagID)
3693    {
3694        return m_arenaTracker.getFlag(flagID);
3695    }
3696
3697
3698    // ***** COMPLEX GETTERS *****
3699
3700    /**
3701     * Gets the total number of players currently playing in the arena (does
3702     * NOT include those who are spectating).
3703     * @return Number of arena players currently in a ship.
3704     */
3705    public int getNumPlayers() {
3706        int numPlayers = 0;
3707        Iterator<Player> i = m_arenaTracker.getPlayingPlayerIterator();
3708
3709        while ( i.hasNext() ) {
3710            numPlayers++;
3711            i.next();
3712        }
3713        return numPlayers;
3714    }
3715
3716    /**
3717     * Returns true if a freq in the arena contains a specific type of ship.
3718     * @param freq Frequency to check for the ship.
3719     * @param ship Ship type to look for.
3720     * @return True if the ship does exist in the freq.
3721     */
3722    public boolean freqContainsShip(int freq, int ship)
3723    {
3724        Iterator<Player> i = m_arenaTracker.getPlayerIterator();
3725        while (i.hasNext())
3726        {
3727            Player p = i.next();
3728            if (p.getShipType() == ship && p.getFrequency() == freq)
3729            {
3730                return true;
3731            }
3732        }
3733        return false;
3734    }
3735
3736    /**
3737     * Gets the combined total score for all players on a particular frequency.
3738     * @param freq Frequency of the players to get the score for.
3739     * @return The total combined score for the players on the specified frequency.
3740     */
3741    public int getScoreForFreq(int freq)
3742    {
3743        int result = 0;
3744        Iterator<Player> i = m_arenaTracker.getPlayerIterator(); //if( i == null ) return 0;
3745        while (i.hasNext())
3746        {
3747            Player p = i.next();
3748            if (p.isPlaying() && p.getFrequency() == freq)
3749                result += p.getScore();
3750        }
3751        return result;
3752    }
3753
3754    /**
3755     * Returns the core CFG (setup.cfg) as a File.
3756     * @param filename Temporary filename to use for this File.
3757     * @return A File containing the core configuration (setup.cfg).
3758     */
3759    public File getCoreCfg(String filename)
3760    {
3761        String location = getCoreData().getGeneralSettings().getString("Core Location");
3762        return new File(location + File.separatorChar + "corecfg", filename);
3763    }
3764
3765    /**
3766     * Gets a file from the /data directory, given the filename as a String.
3767     * The filename can also be a path relative to the data directory.
3768     * @param filename Filename or pathname to the file in question.
3769     * @return File object for the specified file.
3770     */
3771    public File getDataFile(String filename)
3772    {
3773        String location = getCoreData().getGeneralSettings().getString("Core Location");
3774        return new File(location + File.separatorChar + "data", filename);
3775    }
3776
3777    /**
3778     * Gets the root directory of TWCore, returning as a File object.  This
3779     * directory is specified in setup.cfg.
3780     * @return File object representing the directory the core is in.
3781     */
3782    public File getCoreDirectory()
3783    {
3784        return new File(getGeneralSettings().getString("Core Location"));
3785    }
3786
3787    /**
3788     * Gets a File in the root directory of TWCore.  May be a pathname relative
3789     * to this directory as well.
3790     * @param filename Filename or pathname of the file or directory.
3791     * @return File representation of the object.
3792     */
3793    public File getCoreDirectoryFile(String filename)
3794    {
3795        return new File(getGeneralSettings().getString("Core Location") + File.separatorChar + filename);
3796    }
3797
3798
3799    // ***** ITERATORS *****
3800    /*
3801     * Iterators return the entire contents of records stored in Arena, and are
3802     * very useful for managing players in the arena in ways not already covered
3803     * by BotAction.  See getPlayingPlayerIterator() for an example of usage.
3804     */
3805
3806    /**
3807     * Returns an iterator of all non-specced Players in the arena.  Example usage:
3808     * <code><pre>
3809     * Iterator&lt;Player&gt; i = m_botAction.getPlayingPlayerIterator();
3810     * while( i.hasNext() ){
3811     *    Player p = i.next();
3812     *    if( p.getPlayerName().equals( "DoCk>" )){
3813     *        m_botAction.sendPrivateMessage( p.getPlayerID(), "Hi DoCk>!" );
3814     *    } else if( p.getFrequency() == 223 && p.getSquadName().equals( "LAME" )){
3815     *        m_botAction.sendPrivateMessage( p.getPlayerID(), "L!" );
3816     *    }
3817     * }
3818     * </pre></code>
3819     * @return An Iterator of all Players who are currently playing in the arena.
3820     */
3821    public Iterator<Player> getPlayingPlayerIterator()
3822    {
3823        return m_arenaTracker.getPlayingPlayerIterator();
3824    }
3825
3826        /**
3827         * Gets a List of playing players (ie. players in ships and not in spec)
3828         * @return A List of players in ships
3829         */
3830        public List<Player> getPlayingPlayers() {
3831                return m_arenaTracker.getPlayingPlayers();
3832        }
3833
3834    /**
3835     * @param freq Frequency to fetch
3836     * @return An iterator of all players on a frequency.  (Should now be working)
3837     */
3838    public Iterator<Integer> getFreqIDIterator(int freq)
3839    {
3840        return m_arenaTracker.getFreqIDIterator(freq);
3841    }
3842
3843    /**
3844     * @param freq Frequency to fetch
3845     * @return An iterator of all players on a frequency.  (Should now be working)
3846     */
3847    public Iterator<Player> getFreqPlayerIterator(int freq)
3848    {
3849        return m_arenaTracker.getFreqPlayerIterator(freq);
3850    }
3851
3852    /**
3853     * @return An Iterator of all Players in the arena, both spec'd and playing.
3854     */
3855    public Iterator<Player> getPlayerIterator()
3856    {
3857        return m_arenaTracker.getPlayerIterator();
3858    }
3859
3860    /**
3861     * @return An Iterator of the IDs of all players in the arena, both spec'd and playing.
3862     */
3863    public Iterator<Integer> getPlayerIDIterator()
3864    {
3865        return m_arenaTracker.getPlayerIDIterator();
3866    }
3867
3868    /**
3869     * @return An Iterator of all Flag IDs in the arena.
3870     */
3871    public Iterator<Integer> getFlagIDIterator()
3872    {
3873        return m_arenaTracker.getFlagIDIterator();
3874    }
3875
3876    /**
3877     * @return An Iterator of all Flag objects in the arena.
3878     */
3879    public Iterator<Flag> getFlagIterator()
3880    {
3881        return m_arenaTracker.getFlagIterator();
3882    }
3883}
Note: See TracBrowser for help on using the browser.