1 // koth.cpp : Defines the entry point for the DLL application.
2 
3 #include "bzfsAPI.h"
4 #include <map>
5 #include <cmath>
6 
7 class KOTHMapHandler : public bz_CustomMapObjectHandler
8 {
9 public:
10     virtual bool MapObject ( bz_ApiString object, bz_CustomMapObjectInfo *data );
11 };
12 
13 KOTHMapHandler  kothmaphandler;
14 
15 class KOTHCommands : public bz_CustomSlashCommandHandler
16 {
17 public:
~KOTHCommands()18     virtual ~KOTHCommands() {};
19     virtual bool SlashCommand ( int playerID, bz_ApiString command, bz_ApiString message, bz_APIStringList *param );
20 };
21 
22 KOTHCommands kothcommands;
23 
24 class KOTHHandler : public bz_Plugin
25 {
26 public:
Name()27     virtual const char* Name ()
28     {
29         return "King Of The Hill";
30     }
31     virtual void Init ( const char* config );
32     virtual void Cleanup ();
33 
34     virtual void Event ( bz_EventData *eventData );
35 
36 };
37 
BZ_PLUGIN(KOTHHandler)38 BZ_PLUGIN(KOTHHandler)
39 
40 void KOTHHandler::Init(const char* /*commandLine*/)
41 {
42     MaxWaitTime = 0.5f;
43 
44     bz_registerCustomMapObject("KOTH",&kothmaphandler);
45     Register(bz_ePlayerUpdateEvent);
46     Register(bz_ePlayerPausedEvent);
47     Register(bz_ePlayerPartEvent);
48     Register(bz_ePlayerJoinEvent);
49     Register(bz_ePlayerDieEvent);
50     bz_registerCustomSlashCommand("kothstatus",&kothcommands);
51     bz_registerCustomSlashCommand("kothon",&kothcommands);
52     bz_registerCustomSlashCommand("kothoff",&kothcommands);
53     bz_registerCustomSlashCommand("kothsoundon",&kothcommands);
54     bz_registerCustomSlashCommand("kothsoundoff",&kothcommands);
55     bz_registerCustomSlashCommand("kothtimemult",&kothcommands);
56     bz_registerCustomSlashCommand("kothtimemultmin",&kothcommands);
57     bz_registerCustomSlashCommand("kothtime",&kothcommands);
58     bz_registerCustomSlashCommand("kothautotimeon",&kothcommands);
59     bz_registerCustomSlashCommand("kothautotimeoff",&kothcommands);
60     bz_registerCustomSlashCommand("kingsay",&kothcommands);
61 }
62 
Cleanup(void)63 void KOTHHandler::Cleanup(void)
64 {
65 
66     Flush();
67     bz_removeCustomMapObject("KOTH");
68     bz_removeCustomSlashCommand("kothstatus");
69     bz_removeCustomSlashCommand("kothon");
70     bz_removeCustomSlashCommand("kothoff");
71     bz_removeCustomSlashCommand("kothsoundon");
72     bz_removeCustomSlashCommand("kothsoundoff");
73     bz_removeCustomSlashCommand("kothtimemult");
74     bz_removeCustomSlashCommand("kothtimemultmin");
75     bz_removeCustomSlashCommand("kothtime");
76     bz_removeCustomSlashCommand("kothautotimeon");
77     bz_removeCustomSlashCommand("kothautotimeoff");
78     bz_removeCustomSlashCommand("kingsay");
79 }
80 
81 class KOTH
82 {
83 public:
KOTH()84     KOTH()
85     {
86         id = -1;
87         startTime = 0;
88         team = eNoTeam;
89         callsign = "";
90         teamPlay = false;
91         TTH = 60;
92         adjustedTime = 60;
93         timeMult = 0.03;
94         timeMultMin = 0.50;
95         enabled = true;
96         toldHillOpen = false;
97         onePlayerWarn = false;
98         autoTimeOn = false;
99         TTHminutes = 0;
100         TTHseconds = 30;
101         playerJustWon = -1;
102         soundEnabled = true;
103     }
104     bz_eTeamType team;
105     std::string callsign;
106     double TTH;
107     double adjustedTime;
108     double timeMult;
109     double timeMultMin;
110     double startTime;
111     bool teamPlay;
112     bool enabled;
113     bool toldHillOpen;
114     bool onePlayerWarn;
115     bool autoTimeOn;
116     bool soundEnabled;
117     int TTHminutes;
118     int TTHseconds;
119     int playerJustWon;
120     int id;
121 };
122 
123 KOTH koth;
124 
125 class KOTHZone : public bz_CustomZoneObject
126 {
127 public:
KOTHZone()128     KOTHZone() : bz_CustomZoneObject() {}
129 };
130 
131 KOTHZone kothzone;
132 
MapObject(bz_ApiString object,bz_CustomMapObjectInfo * data)133 bool KOTHMapHandler::MapObject ( bz_ApiString object, bz_CustomMapObjectInfo *data )
134 {
135     if (object != "KOTH" || !data)
136         return false;
137 
138     kothzone.handleDefaultOptions(data);
139 
140     // parse all the chunks
141     for ( unsigned int i = 0; i < data->data.size(); i++ )
142     {
143         std::string line = data->data.get(i).c_str();
144 
145         bz_APIStringList *nubs = bz_newStringList();
146         nubs->tokenize(line.c_str()," ",0,true);
147 
148         if ( nubs->size() > 0)
149         {
150             std::string key = bz_toupper(nubs->get(0).c_str());
151 
152             if ( key == "TEAMPLAY")
153                 koth.teamPlay = true;
154             else if ( key == "NOSOUND")
155                 koth.soundEnabled = false;
156             else if ( key == "AUTOTIME" && nubs->size() == 1 )
157                 koth.autoTimeOn = true;
158             else if ( key == "AUTOTIME" && nubs->size() > 2 )
159             {
160                 double temp1 = (double)atof(nubs->get(1).c_str());
161                 double temp2 = (double)atof(nubs->get(2).c_str());
162                 if (temp1 >= 1 && temp1 <= 99)
163                     koth.timeMult = temp1 / 100;
164                 if (temp2 >= 1 && temp2 <= 99)
165                     koth.timeMultMin = temp2 / 100;
166                 koth.autoTimeOn = true;
167             }
168             else if ( key == "HOLDTIME" && nubs->size() > 1 )
169             {
170                 double temp = (double)atof(nubs->get(1).c_str());
171                 if (temp >= 1 && temp <= 7200)
172                     koth.TTH = temp;
173             }
174         }
175 
176         bz_deleteStringList(nubs);
177     }
178     return true;
179 }
180 
truncate(std::string cllsn)181 std::string truncate(std::string cllsn)
182 {
183     std::string fixed = "";
184 
185     for (int i = 0; i < 16; i++)
186         fixed.push_back(cllsn[i]);
187 
188     fixed.append("~");
189 
190     return fixed;
191 }
192 
getTeamColor(bz_eTeamType testteam)193 const char* getTeamColor(bz_eTeamType testteam)
194 {
195     if (testteam == eRedTeam)
196         return "RED";
197     if (testteam == eGreenTeam)
198         return "GREEN";
199     if (testteam == eBlueTeam)
200         return "BLUE";
201     if (testteam == ePurpleTeam)
202         return "PURPLE";
203     if (testteam == eRogueTeam)
204         return "ROGUE";
205 
206     return " ";
207 }
208 
onePlayer()209 bool onePlayer()
210 {
211     int numPlayers = bz_getTeamCount(eRedTeam) + bz_getTeamCount(eGreenTeam) + bz_getTeamCount(eBlueTeam) + bz_getTeamCount(
212                          ePurpleTeam) + bz_getTeamCount(eRogueTeam);
213 
214     if (numPlayers <= 1)
215     {
216         if (!koth.onePlayerWarn)
217             bz_sendTextMessage (BZ_SERVER, BZ_ALLUSERS, "King of the Hill disabled: less than 2 players.");
218 
219         koth.onePlayerWarn = true;
220         return true;
221     }
222     else
223     {
224         if (koth.onePlayerWarn)
225             bz_sendTextMessage (BZ_SERVER, BZ_ALLUSERS, "King of the Hill enabled: more than 1 player.");
226 
227         koth.onePlayerWarn = false;
228         return false;
229     }
230 }
231 
autoTime()232 void autoTime()
233 {
234     int numPlayers = bz_getTeamCount(eRedTeam) + bz_getTeamCount(eGreenTeam) + bz_getTeamCount(eBlueTeam) + bz_getTeamCount(
235                          ePurpleTeam) + bz_getTeamCount(eRogueTeam);
236 
237     if (!koth.autoTimeOn || numPlayers < 3)
238     {
239         koth.adjustedTime = koth.TTH;
240         return;
241     }
242 
243     double timeDown = ( 1 - ((double)numPlayers - 2) * koth.timeMult);
244 
245     if (timeDown < koth.timeMultMin)
246         timeDown = koth.timeMultMin;
247 
248     koth.adjustedTime = (int)(koth.TTH * timeDown);
249 
250     return;
251 }
252 
ConvertToNum(std::string inmessage,double minNum,double maxNum)253 double ConvertToNum(std::string inmessage, double minNum, double maxNum)
254 {
255 
256     int messagelength = (int)inmessage.length();
257 
258     if (messagelength > 0 && messagelength < 5)
259     {
260         double messagevalue = 0;
261         double tens = 1;
262 
263         for ( int i = (messagelength - 1); i >= 0; i-- )
264         {
265 
266             if (inmessage[i] < '0' || inmessage[i] > '9')  // got something other than a number
267                 return 0;
268 
269             tens *= 10;
270             messagevalue +=  (((double)inmessage[i] - '0') / 10) * tens;
271         }
272 
273         if (messagevalue >= minNum && messagevalue <= maxNum)
274             return messagevalue;
275     }
276 
277     return 0;
278 }
279 
killTeams(bz_eTeamType safeteam,std::string kothcallsign)280 void killTeams(bz_eTeamType safeteam, std::string kothcallsign)
281 {
282     bz_APIIntList *playerList = bz_newIntList();
283     bz_getPlayerIndexList ( playerList );
284 
285     for ( unsigned int i = 0; i < playerList->size(); i++ )
286     {
287 
288         bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));
289 
290         if (player)
291         {
292 
293             if (player->team != safeteam)
294             {
295                 bz_killPlayer(player->playerID, true, BZ_SERVER);
296                 if (koth.soundEnabled)
297                     bz_sendPlayCustomLocalSound(player->playerID,"flag_lost");
298             }
299             else if (koth.soundEnabled)
300                 bz_sendPlayCustomLocalSound(player->playerID,"flag_won");
301         }
302 
303         bz_freePlayerRecord(player);
304     }
305     bz_deleteIntList(playerList);
306 
307     bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) IS KING OF THE HILL!", getTeamColor(safeteam),
308                          kothcallsign.c_str());
309 
310     return;
311 }
312 
killPlayers(int safeid,std::string kothcallsign)313 void killPlayers(int safeid, std::string kothcallsign)
314 {
315     bz_APIIntList *playerList = bz_newIntList();
316     bz_getPlayerIndexList ( playerList );
317 
318     for ( unsigned int i = 0; i < playerList->size(); i++ )
319     {
320 
321         bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));
322 
323         if (player)
324         {
325 
326             if (player->playerID != safeid)
327             {
328                 bz_killPlayer(player->playerID, true, koth.id);
329                 if (koth.soundEnabled)
330                     bz_sendPlayCustomLocalSound(player->playerID,"flag_lost");
331             }
332             else if (koth.soundEnabled)
333                 bz_sendPlayCustomLocalSound(player->playerID,"flag_won");
334         }
335 
336         bz_freePlayerRecord(player);
337     }
338 
339     bz_deleteIntList(playerList);
340 
341     bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s IS KING OF THE HILL!", kothcallsign.c_str());
342 
343     return;
344 }
345 
sendWarnings(const char * teamcolor,std::string playercallsign,double kothstartedtime)346 void sendWarnings(const char* teamcolor, std::string playercallsign, double kothstartedtime)
347 {
348     double TimeElapsed = bz_getCurrentTime() - kothstartedtime;
349     double TimeRemaining = koth.adjustedTime - TimeElapsed;
350     int toTens = int((TimeRemaining + 5) / 10) * 10;
351 
352     if ((TimeRemaining/60) < koth.TTHminutes && koth.adjustedTime > 59)
353     {
354         if (!koth.teamPlay || koth.team == eRogueTeam)
355             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s will be King in %i secs!", playercallsign.c_str(), toTens);
356         else
357             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) will be King in %i secs!", teamcolor, playercallsign.c_str(),
358                                  toTens);
359 
360         koth.TTHminutes--;
361     }
362 
363     if (koth.adjustedTime < koth.TTHseconds)
364     {
365         koth.TTHseconds = koth.TTHseconds - 10;
366         return;
367     }
368 
369     if (TimeRemaining < koth.TTHseconds)
370     {
371         if (!koth.teamPlay || koth.team == eRogueTeam)
372             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s will be King in %i secs!", playercallsign.c_str(), koth.TTHseconds);
373         else
374             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) will be King in %i secs!", teamcolor, playercallsign.c_str(),
375                                  koth.TTHseconds);
376 
377         koth.TTHseconds = koth.TTHseconds - 10;
378     }
379     return;
380 }
381 
initiatekoth(bz_eTeamType plyrteam,bz_ApiString plyrcallsign,int plyrID)382 void initiatekoth(bz_eTeamType plyrteam, bz_ApiString plyrcallsign, int plyrID)
383 {
384     koth.team = plyrteam;
385     koth.callsign = plyrcallsign.c_str();
386 
387     if (koth.callsign.size() > 16)
388     {
389         std::string tofix = truncate(koth.callsign);
390         koth.callsign = tofix;
391     }
392 
393     koth.id = plyrID;
394     koth.startTime = bz_getCurrentTime();
395     koth.TTHminutes = (int)(koth.adjustedTime/60 + 0.5);
396     koth.TTHseconds = 30;
397     koth.toldHillOpen = false;
398     bool multipleof30 = false;
399 
400     if ((int)((koth.adjustedTime / 30) + 0.5) != (double)(koth.adjustedTime / 30))
401         multipleof30 = false;
402     else
403         multipleof30 = true;
404 
405     if (!multipleof30)
406     {
407         if ((!koth.teamPlay || koth.team == eRogueTeam))
408             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s has Hill; will be King in %i secs!", koth.callsign.c_str(),
409                                  (int)koth.adjustedTime);
410         else
411             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) has Hill; will be King in %i secs!", getTeamColor(koth.team),
412                                  koth.callsign.c_str(), (int)koth.adjustedTime);
413     }
414 
415     if (koth.soundEnabled)
416     {
417         bz_APIIntList *playerList = bz_newIntList();
418         bz_getPlayerIndexList ( playerList );
419 
420         for ( unsigned int i = 0; i < playerList->size(); i++ )
421         {
422             bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));
423 
424             if (player)
425             {
426                 if (player->team != koth.team)
427                     bz_sendPlayCustomLocalSound(player->playerID,"flag_alert");
428                 else
429                     bz_sendPlayCustomLocalSound(player->playerID,"teamgrab");
430             }
431 
432             bz_freePlayerRecord(player);
433         }
434         bz_deleteIntList(playerList);
435     }
436 
437     return;
438 }
439 
teamClear(bz_eTeamType teamToCheck)440 bool teamClear(bz_eTeamType teamToCheck)
441 {
442     if (teamToCheck == eRogueTeam || teamToCheck == eNoTeam || !koth.teamPlay)
443         return true;
444 
445     bz_APIIntList *playerList = bz_newIntList();
446     bz_getPlayerIndexList ( playerList );
447 
448     bool isOut = true;
449 
450     for ( unsigned int i = 0; i < playerList->size(); i++ )
451     {
452 
453         bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));
454 
455         if (player)
456         {
457             if (player->team == teamToCheck && kothzone.pointInZone(player->lastKnownState.pos) && player->spawned)
458                 isOut = false;
459         }
460 
461         bz_freePlayerRecord(player);
462     }
463 
464     bz_deleteIntList(playerList);
465 
466     return isOut;
467 }
468 
KOTHPlayerPaused(bz_EventData * eventData)469 void KOTHPlayerPaused ( bz_EventData *eventData )
470 {
471     if (eventData->eventType != bz_ePlayerPausedEvent || !koth.enabled)
472         return;
473 
474     bz_PlayerPausedEventData_V1 *PauseData = (bz_PlayerPausedEventData_V1*)eventData;
475     bz_BasePlayerRecord *player = bz_getPlayerByIndex(PauseData->playerID);
476 
477     if (player)
478     {
479         if (kothzone.pointInZone(player->lastKnownState.pos))
480         {
481             bz_killPlayer (PauseData->playerID, true, BZ_SERVER);
482             bz_sendTextMessage (BZ_SERVER, PauseData->playerID, "Cannot pause while on the Hill.");
483         }
484     }
485     bz_freePlayerRecord(player);
486 
487     return;
488 }
489 
KOTHPlayerJoined(bz_EventData * eventData)490 void KOTHPlayerJoined ( bz_EventData *eventData )
491 {
492     if (eventData->eventType != bz_ePlayerJoinEvent || !koth.enabled)
493         return;
494 
495     autoTime();
496 
497     return;
498 }
499 
KOTHPlayerLeft(bz_EventData * eventData)500 void KOTHPlayerLeft ( bz_EventData *eventData )
501 {
502     if (eventData->eventType != bz_ePlayerPartEvent || !koth.enabled)
503         return;
504 
505     autoTime();
506 
507     bz_PlayerJoinPartEventData_V1 *partData = (bz_PlayerJoinPartEventData_V1*)eventData;
508 
509     if (partData->playerID == koth.id)
510     {
511         koth.id = -1;
512         koth.team = eNoTeam;
513     }
514 
515     return;
516 }
517 
KOTHPlayerDied(bz_EventData * eventData)518 void KOTHPlayerDied( bz_EventData *eventData )
519 {
520     if (eventData->eventType != bz_ePlayerDieEvent || !koth.enabled)
521         return;
522 
523     bz_PlayerDieEventData_V1 *dieData = (bz_PlayerDieEventData_V1*)eventData;
524 
525     if (dieData->playerID == koth.id)
526     {
527         koth.id = -1;
528         koth.team = eNoTeam;
529     }
530 
531     return;
532 }
533 
KOTHEventHandler(bz_EventData * eventData)534 inline void KOTHEventHandler( bz_EventData *eventData )
535 {
536     if (!koth.enabled) // King of the Hill disabled - we can leave
537         return;
538 
539     if (onePlayer()) // Not enough players - we can leave
540         return;
541 
542     float pos[3] = {0};
543 
544     int playerID = -1;
545 
546     switch (eventData->eventType)
547     {
548     case bz_ePlayerUpdateEvent:
549         pos[0] = ((bz_PlayerUpdateEventData_V1*)eventData)->state.pos[0];
550         pos[1] = ((bz_PlayerUpdateEventData_V1*)eventData)->state.pos[1];
551         pos[2] = ((bz_PlayerUpdateEventData_V1*)eventData)->state.pos[2];
552         playerID = ((bz_PlayerUpdateEventData_V1*)eventData)->playerID;
553         break;
554 
555     case bz_eShotFiredEvent:
556         pos[0] = ((bz_ShotFiredEventData_V1*)eventData)->pos[0];
557         pos[1] = ((bz_ShotFiredEventData_V1*)eventData)->pos[1];
558         pos[2] = ((bz_ShotFiredEventData_V1*)eventData)->pos[2];
559         playerID = ((bz_ShotFiredEventData_V1*)eventData)->playerID;
560         break;
561 
562     default:
563         return;
564     }
565 
566     if (!koth.toldHillOpen && koth.id == -1) // Hill is open - inform players
567     {
568         bz_sendTextMessage (BZ_SERVER, BZ_ALLUSERS, "Hill is not controlled - take it!");
569         koth.toldHillOpen = true;
570     }
571 
572     if (kothzone.pointInZone(pos)) // player is on Hill
573     {
574         bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerID);
575 
576         if (player)
577         {
578             if (player->playerID != koth.playerJustWon && player->spawned)
579             {
580                 if ((koth.id == -1 && player->team != koth.team) || (koth.id == -1 && teamClear(koth.team)))
581                     initiatekoth(player->team, player->callsign, player->playerID);
582 
583                 double timeStanding = bz_getCurrentTime() - koth.startTime;
584 
585                 if (timeStanding >= koth.adjustedTime && koth.id != -1) // time's up - kill 'em
586                 {
587                     if (koth.teamPlay && koth.team != eRogueTeam)
588                         killTeams(koth.team, koth.callsign);
589                     else
590                         killPlayers(koth.id, koth.callsign);
591 
592                     if (!koth.teamPlay || koth.team == eRogueTeam)
593                         bz_sendTextMessage (BZ_SERVER, koth.id, "You are King of the Hill!  You must leave hill to retake it.");
594                     else
595                         bz_sendTextMessage (BZ_SERVER, koth.team, "Your team is King of the Hill!  Entire team must leave hill to retake it.");
596 
597                     koth.playerJustWon = koth.id;
598 
599                     koth.id = -1;
600 
601                     return;
602                 }
603                 if (koth.id != -1)
604                     sendWarnings(getTeamColor(koth.team), koth.callsign, koth.startTime);
605             }
606         }
607 
608         bz_freePlayerRecord(player);
609     }
610     else // player is off Hill
611     {
612         if (playerID == koth.playerJustWon)
613             koth.playerJustWon = -1;
614 
615         if (playerID == koth.id)
616         {
617             koth.id = -1;
618             koth.team = eNoTeam;
619         }
620     }
621 }
622 
Event(bz_EventData * eventData)623 void KOTHHandler::Event( bz_EventData *eventData )
624 {
625     if (eventData->eventType == bz_ePlayerUpdateEvent)
626         KOTHEventHandler(eventData);
627     else if (eventData->eventType == bz_eShotFiredEvent)
628         KOTHEventHandler(eventData);
629     else if (eventData->eventType == bz_ePlayerDieEvent)
630         KOTHPlayerDied(eventData);
631     else if (eventData->eventType == bz_ePlayerJoinEvent)
632         KOTHPlayerJoined(eventData);
633     else if (eventData->eventType == bz_ePlayerPartEvent)
634         KOTHPlayerLeft(eventData);
635     else if (eventData->eventType == bz_ePlayerPausedEvent)
636         KOTHPlayerPaused(eventData);
637 
638 }
639 
640 
SlashCommand(int playerID,bz_ApiString _command,bz_ApiString _message,bz_APIStringList *)641 bool KOTHCommands::SlashCommand ( int playerID, bz_ApiString _command, bz_ApiString _message,
642                                   bz_APIStringList * /*_param*/ )
643 {
644     std::string command = _command.c_str();
645     std::string message = _message.c_str();
646     const char* kingmessage = _message.c_str();
647 
648     if ( command == "kingsay" )
649     {
650         if (koth.id != -1)
651             bz_sendTextMessage (playerID, koth.id, kingmessage);
652         else
653             bz_sendTextMessage(BZ_SERVER,playerID,"There is no one attempting to be king right now.");
654 
655         return true;
656     }
657 
658     bz_BasePlayerRecord *fromPlayer = bz_getPlayerByIndex(playerID);
659 
660     if (fromPlayer)
661     {
662         if ( !fromPlayer->admin )
663         {
664             bz_sendTextMessage(BZ_SERVER,playerID,"You must be admin to use the koth commands.");
665             bz_freePlayerRecord(fromPlayer);
666             return true;
667         }
668 
669         bz_freePlayerRecord(fromPlayer);
670     }
671 
672     if ( command == "kothon")
673     {
674         koth.enabled = true;
675         bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "King of the Hill is enabled.");
676         return true;
677     }
678 
679     if ( command == "kothoff")
680     {
681         koth.enabled = false;
682         bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "King of the Hill is disabled.");
683         return true;
684     }
685 
686     if ( command == "kothsoundon")
687     {
688         koth.soundEnabled = true;
689         bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "King of the Hill sounds are enabled.");
690         return true;
691     }
692 
693     if ( command == "kothsoundoff")
694     {
695         koth.soundEnabled = false;
696         bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "King of the Hill sounds are disabled.");
697         return true;
698     }
699 
700     if ( command == "kothtimemult")
701     {
702         double inputvalue = ConvertToNum(message, 1, 99);
703 
704         if (inputvalue > 0)
705         {
706             koth.timeMult = (inputvalue/100);
707             bz_sendTextMessagef (BZ_SERVER, playerID, "Auto time multiplier set to %i percent.", (int)(koth.timeMult*100 + 0.5));
708         }
709         else
710             bz_sendTextMessagef (BZ_SERVER, playerID, "Auto time multiplier must be between 1 and 99 percent.",
711                                  (int)(koth.timeMult*100 + 0.5));
712 
713         autoTime();
714 
715         return true;
716     }
717 
718     if ( command == "kothtimemultmin")
719     {
720         double inputvalue = ConvertToNum(message, 1, 99);
721 
722         if (inputvalue > 0)
723         {
724             koth.timeMultMin = (inputvalue/100);
725             bz_sendTextMessagef (BZ_SERVER, playerID, "Auto time multiplier minimum set to %i percent.",
726                                  (int)(koth.timeMultMin*100 + 0.5));
727         }
728         else
729             bz_sendTextMessagef (BZ_SERVER, playerID, "Auto time multiplier minimum must be between 1 and 99 percent.");
730 
731         autoTime();
732 
733         return true;
734     }
735 
736     if ( command == "kothstatus")
737     {
738         if (koth.enabled)
739             bz_sendTextMessagef (BZ_SERVER, playerID, "King of the Hill is currently enabled.");
740 
741         if (!koth.enabled)
742             bz_sendTextMessagef (BZ_SERVER, playerID, "King of the Hill is currently disabled.");
743 
744         if (koth.soundEnabled)
745             bz_sendTextMessagef (BZ_SERVER, playerID, "King of the Hill sounds are currently enabled.");
746 
747         if (!koth.soundEnabled)
748             bz_sendTextMessagef (BZ_SERVER, playerID, "King of the Hill sounds are currently disabled.");
749 
750         if (koth.autoTimeOn)
751             bz_sendTextMessagef (BZ_SERVER, playerID, "Automatic time adjustment is currently enabled.");
752 
753         if (!koth.autoTimeOn)
754             bz_sendTextMessagef (BZ_SERVER, playerID, "Automatic time adjustment is currently disabled.");
755 
756         bz_sendTextMessagef (BZ_SERVER, playerID, "Time multiplier = %i percent.", (int)(koth.timeMult*100 + 0.5));
757 
758         bz_sendTextMessagef (BZ_SERVER, playerID, "Time multiplier minimum = %i percent.", (int)(koth.timeMultMin*100 + 0.5));
759 
760         int AdjTime = (int)(koth.adjustedTime + 0.5);
761         bz_sendTextMessagef (BZ_SERVER, playerID, "King of the Hill hold time is currently set to: %i seconds", AdjTime);
762         return true;
763     }
764 
765     // explicit time command handler:
766 
767     if ( command == "kothtime" )
768     {
769         double inputvalue = ConvertToNum(message, 1, 7200);
770 
771         if (inputvalue > 0 )
772         {
773             koth.TTH = inputvalue;
774             autoTime();
775             int AdjTime = (int)(inputvalue + 0.5);
776             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "King of the Hill hold time has been set to %i seconds.", AdjTime);
777         }
778         else
779             bz_sendTextMessagef (BZ_SERVER, playerID, "King of the Hill hold time invalid: must be between 1 and 7200 seconds.");
780 
781         autoTime();
782 
783         return true;
784     }
785 
786     if ( command == "kothautotimeon")
787     {
788         koth.autoTimeOn = true;
789         autoTime();
790         bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "King of the Hill automatic time adjustment on.");
791         return true;
792     }
793 
794     if ( command == "kothautotimeoff")
795     {
796         koth.autoTimeOn = false;
797         koth.adjustedTime = koth.TTH;
798         autoTime();
799         bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "King of the Hill automatic time adjustment off.");
800         return true;
801     }
802 
803     return false;
804 }
805 // Local Variables: ***
806 // mode: C++ ***
807 // tab-width: 4 ***
808 // c-basic-offset: 4 ***
809 // indent-tabs-mode: nil ***
810 // End: ***
811 // ex: shiftwidth=4 tabstop=4
812