Overview

Namespaces

  • Budabot
    • Core
      • Modules
    • User
      • Modules
  • None
  • Tyrence
    • Modules

Classes

  • AccessLevel
  • Budabot\Core\AccessManager
  • Budabot\Core\AdminManager
  • Budabot\Core\AOChat
  • Budabot\Core\AOChatPacket
  • Budabot\Core\AOChatQueue
  • Budabot\Core\AOExtMsg
  • Budabot\Core\AsyncHttp
  • Budabot\Core\AutoInject
  • Budabot\Core\BotRunner
  • Budabot\Core\Budabot
  • Budabot\Core\BuddylistManager
  • Budabot\Core\CacheManager
  • Budabot\Core\CacheResult
  • Budabot\Core\ClassLoader
  • Budabot\Core\ColorSettingHandler
  • Budabot\Core\CommandAlias
  • Budabot\Core\CommandManager
  • Budabot\Core\ConfigFile
  • Budabot\Core\DB
  • Budabot\Core\DBRow
  • Budabot\Core\EventLoop
  • Budabot\Core\EventManager
  • Budabot\Core\GuildChannelCommandReply
  • Budabot\Core\GuildManager
  • Budabot\Core\HelpManager
  • Budabot\Core\Http
  • Budabot\Core\HttpRequest
  • Budabot\Core\LegacyLogger
  • Budabot\Core\LimitsController
  • Budabot\Core\LoggerWrapper
  • Budabot\Core\MMDBParser
  • Budabot\Core\Modules\AdminController
  • Budabot\Core\Modules\AliasController
  • Budabot\Core\Modules\AltInfo
  • Budabot\Core\Modules\AltsController
  • Budabot\Core\Modules\BanController
  • Budabot\Core\Modules\BuddylistController
  • Budabot\Core\Modules\ColorsController
  • Budabot\Core\Modules\CommandlistController
  • Budabot\Core\Modules\CommandSearchController
  • Budabot\Core\Modules\ConfigController
  • Budabot\Core\Modules\EventlistController
  • Budabot\Core\Modules\HelpController
  • Budabot\Core\Modules\LogsController
  • Budabot\Core\Modules\PlayerLookupController
  • Budabot\Core\Modules\ProfileCommandReply
  • Budabot\Core\Modules\ProfileController
  • Budabot\Core\Modules\SettingsController
  • Budabot\Core\Modules\SQLController
  • Budabot\Core\Modules\SystemController
  • Budabot\Core\Modules\UsageController
  • Budabot\Core\Modules\WhitelistController
  • Budabot\Core\NumberSettingHandler
  • Budabot\Core\OptionsSettingHandler
  • Budabot\Core\PlayerHistory
  • Budabot\Core\PlayerHistoryManager
  • Budabot\Core\PlayerManager
  • Budabot\Core\Preferences
  • Budabot\Core\PrivateChannelCommandReply
  • Budabot\Core\PrivateMessageCommandReply
  • Budabot\Core\Registry
  • Budabot\Core\SettingHandler
  • Budabot\Core\SettingManager
  • Budabot\Core\SettingObject
  • Budabot\Core\SocketManager
  • Budabot\Core\SocketNotifier
  • Budabot\Core\SubcommandManager
  • Budabot\Core\Text
  • Budabot\Core\TextSettingHandler
  • Budabot\Core\Timer
  • Budabot\Core\TimerEvent
  • Budabot\Core\TimeSettingHandler
  • Budabot\Core\Util
  • Budabot\Core\xml
  • Budabot\User\Modules\AlienArmorController
  • Budabot\User\Modules\AlienBioController
  • Budabot\User\Modules\AlienMiscController
  • Budabot\User\Modules\AOSpeakController
  • Budabot\User\Modules\AOUController
  • Budabot\User\Modules\AXPController
  • Budabot\User\Modules\BankController
  • Budabot\User\Modules\BosslootController
  • Budabot\User\Modules\BroadcastController
  • Budabot\User\Modules\BuffPerksController
  • Budabot\User\Modules\CacheController
  • Budabot\User\Modules\ChatAssistController
  • Budabot\User\Modules\ChatCheckController
  • Budabot\User\Modules\ChatLeaderController
  • Budabot\User\Modules\ChatRallyController
  • Budabot\User\Modules\ChatSayController
  • Budabot\User\Modules\ChatTopicController
  • Budabot\User\Modules\CityWaveController
  • Budabot\User\Modules\CloakController
  • Budabot\User\Modules\ClusterController
  • Budabot\User\Modules\CountdownController
  • Budabot\User\Modules\DevController
  • Budabot\User\Modules\DingController
  • Budabot\User\Modules\EventsController
  • Budabot\User\Modules\FightController
  • Budabot\User\Modules\FindOrgController
  • Budabot\User\Modules\FindPlayerController
  • Budabot\User\Modules\FunController
  • Budabot\User\Modules\GitController
  • Budabot\User\Modules\GuideController
  • Budabot\User\Modules\GuildController
  • Budabot\User\Modules\HelpbotController
  • Budabot\User\Modules\HtmlDecodeController
  • Budabot\User\Modules\ImplantController
  • Budabot\User\Modules\ImplantDesignerController
  • Budabot\User\Modules\InactiveMemberController
  • Budabot\User\Modules\ItemsController
  • Budabot\User\Modules\KillOnSightController
  • Budabot\User\Modules\LevelController
  • Budabot\User\Modules\LinksController
  • Budabot\User\Modules\LootListsController
  • Budabot\User\Modules\MdbController
  • Budabot\User\Modules\MessageInfoCommandReply
  • Budabot\User\Modules\MockCommandReply
  • Budabot\User\Modules\NanoController
  • Budabot\User\Modules\NewsController
  • Budabot\User\Modules\NotesController
  • Budabot\User\Modules\OnlineController
  • Budabot\User\Modules\OrgHistoryController
  • Budabot\User\Modules\OrglistController
  • Budabot\User\Modules\OrgMembersController
  • Budabot\User\Modules\OSController
  • Budabot\User\Modules\PlayerHistoryController
  • Budabot\User\Modules\PlayfieldController
  • Budabot\User\Modules\PocketbossController
  • Budabot\User\Modules\PremadeImplantController
  • Budabot\User\Modules\PrivateChannelController
  • Budabot\User\Modules\QuoteController
  • Budabot\User\Modules\RaffleController
  • Budabot\User\Modules\RaidController
  • Budabot\User\Modules\RandomController
  • Budabot\User\Modules\RecipeController
  • Budabot\User\Modules\RelayController
  • Budabot\User\Modules\ReputationController
  • Budabot\User\Modules\ResearchController
  • Budabot\User\Modules\RunAsController
  • Budabot\User\Modules\SendTellController
  • Budabot\User\Modules\ShoppingController
  • Budabot\User\Modules\SilenceController
  • Budabot\User\Modules\SkillsController
  • Budabot\User\Modules\SpiritsController
  • Budabot\User\Modules\StopwatchController
  • Budabot\User\Modules\Teamspeak3
  • Budabot\User\Modules\TeamspeakController
  • Budabot\User\Modules\TestController
  • Budabot\User\Modules\TimeController
  • Budabot\User\Modules\TimerController
  • Budabot\User\Modules\TimezoneController
  • Budabot\User\Modules\TowerController
  • Budabot\User\Modules\TrackerController
  • Budabot\User\Modules\TrickleController
  • Budabot\User\Modules\UnixtimeController
  • Budabot\User\Modules\VoteController
  • Budabot\User\Modules\WeatherController
  • Budabot\User\Modules\WhatBuffsController
  • Budabot\User\Modules\WhereisController
  • Budabot\User\Modules\WhoisController
  • Budabot\User\Modules\WhoisOrgController
  • Budabot\User\Modules\WhompahController
  • Command
  • DefaultStatus
  • DefineCommand
  • Description
  • Event
  • HandlesCommand
  • Help
  • Inject
  • Instance
  • Intoptions
  • Matches
  • Options
  • Setting
  • Setup
  • Type
  • Tyrence\Modules\DemoResponseCommandReply
  • Tyrence\Modules\SameChannelResponseController
  • Visibility

Interfaces

  • Budabot\Core\CommandReply

Exceptions

  • Budabot\Core\InvalidHttpRequest
  • Budabot\Core\SQLException
  • Budabot\Core\StopExecutionException

Functions

  • Budabot\Core\isWindows
  • Budabot\Core\Modules\read_input
  • Overview
  • Namespace
  • Class
   1: <?php
   2: 
   3: namespace Budabot\User\Modules;
   4: 
   5: use stdClass;
   6: 
   7: /**
   8:  * @Instance
   9:  *
  10:  * Commands this controller contains:
  11:  *  @DefineCommand(
  12:  *      command     = 'towerstats',
  13:  *      accessLevel = 'all',
  14:  *      description = 'Show how many towers each faction has lost',
  15:  *      help        = 'towerstats.txt'
  16:  *  )
  17:  *  @DefineCommand(
  18:  *      command     = 'attacks',
  19:  *      alias       = 'battles',
  20:  *      accessLevel = 'all',
  21:  *      description = 'Show the last Tower Attack messages',
  22:  *      help        = 'attacks.txt'
  23:  *  )
  24:  *  @DefineCommand(
  25:  *      command     = 'forcescout',
  26:  *      accessLevel = 'guild',
  27:  *      description = 'Add tower info to watch list (bypasses some of the checks)',
  28:  *      help        = 'scout.txt'
  29:  *  )
  30:  *  @DefineCommand(
  31:  *      command     = 'lc',
  32:  *      accessLevel = 'all',
  33:  *      description = 'Show status of towers',
  34:  *      help        = 'lc.txt'
  35:  *  )
  36:  *  @DefineCommand(
  37:  *      command     = 'opentimes',
  38:  *      accessLevel = 'guild',
  39:  *      description = 'Show status of towers',
  40:  *      help        = 'scout.txt'
  41:  *  )
  42:  *  @DefineCommand(
  43:  *      command     = 'penalty',
  44:  *      accessLevel = 'all',
  45:  *      description = 'Show orgs in penalty',
  46:  *      help        = 'penalty.txt'
  47:  *  )
  48:  *  @DefineCommand(
  49:  *      command     = 'remscout',
  50:  *      accessLevel = 'guild',
  51:  *      description = 'Remove tower info from watch list',
  52:  *      help        = 'scout.txt'
  53:  *  )
  54:  *  @DefineCommand(
  55:  *      command     = 'scout',
  56:  *      accessLevel = 'guild',
  57:  *      description = 'Add tower info to watch list',
  58:  *      help        = 'scout.txt'
  59:  *  )
  60:  *  @DefineCommand(
  61:  *      command     = 'victory',
  62:  *      accessLevel = 'all',
  63:  *      description = 'Show the last Tower Battle results',
  64:  *      help        = 'victory.txt'
  65:  *  )
  66:  */
  67: class TowerController {
  68: 
  69:     /**
  70:      * Name of the module.
  71:      * Set automatically by module loader.
  72:      */
  73:     public $moduleName;
  74: 
  75:     /** @Inject */
  76:     public $playfieldController;
  77: 
  78:     /** @Inject */
  79:     public $playerManager;
  80: 
  81:     /** @Inject */
  82:     public $text;
  83: 
  84:     /** @Inject */
  85:     public $settingManager;
  86: 
  87:     /** @Inject */
  88:     public $chatBot;
  89: 
  90:     /** @Inject */
  91:     public $db;
  92: 
  93:     /** @Inject */
  94:     public $util;
  95:     
  96:     /** @Inject */
  97:     public $levelController;
  98: 
  99:     /** @Logger */
 100:     public $logger;
 101: 
 102:     protected $attackListeners = array();
 103: 
 104:     /**
 105:      * @Setting("tower_attack_spam")
 106:      * @Description("Layout types when displaying tower attacks")
 107:      * @Visibility("edit")
 108:      * @Type("options")
 109:      * @Options("off;compact;normal")
 110:      * @Intoptions("0;1;2")
 111:      * @AccessLevel("mod")
 112:      */
 113:     public $defaultTowerAttackSpam = "1";
 114: 
 115:     /**
 116:      * @Setting("tower_page_size")
 117:      * @Description("Number of results to display for victory/attacks")
 118:      * @Visibility("edit")
 119:      * @Type("options")
 120:      * @Options("5;10;15;20;25")
 121:      * @AccessLevel("mod")
 122:      */
 123:     public $defaultTowerPageSize = "15";
 124:     
 125:     /**
 126:      * @Setting("check_close_time_on_scout")
 127:      * @Description("Check that close time is within one hour of last victory on site")
 128:      * @Visibility("edit")
 129:      * @Type("options")
 130:      * @Options("true;false")
 131:      * @Intoptions("1;0")
 132:      * @AccessLevel("mod")
 133:      */
 134:     public $defaultCheckCloseTimeOnScout = "1";
 135:     
 136:     /**
 137:      * @Setting("check_guild_name_on_scout")
 138:      * @Description("Check that guild name has attacked or been attacked before")
 139:      * @Visibility("edit")
 140:      * @Type("options")
 141:      * @Options("true;false")
 142:      * @Intoptions("1;0")
 143:      * @AccessLevel("mod")
 144:      */
 145:     public $defaultCheckGuildNameOnScout = "1";
 146: 
 147:     /**
 148:      * Adds listener callback which will be called when tower attacks occur.
 149:      */
 150:     public function registerAttackListener($callback, $data = null) {
 151:         if (!is_callable($callback)) {
 152:             $this->logger->log('ERROR', 'Given callback is not valid.');
 153:             return;
 154:         }
 155:         $listener = new stdClass();
 156:         $listener->callback = $callback;
 157:         $listener->data = $data;
 158:         $this->attackListeners []= $listener;
 159:     }
 160: 
 161:     /**
 162:      * @Setup
 163:      * This handler is called on bot startup.
 164:      */
 165:     public function setup() {
 166:         $this->db->loadSQLFile($this->moduleName, 'tower_attack');
 167:         $this->db->loadSQLFile($this->moduleName, 'scout_info');
 168:         $this->db->loadSQLFile($this->moduleName, 'tower_site');
 169:     }
 170: 
 171:     /**
 172:      * This command handler shows the last tower attack messages.
 173:      *
 174:      * @HandlesCommand("attacks")
 175:      * @Matches("/^attacks (\d+)$/i")
 176:      * @Matches("/^attacks$/i")
 177:      */
 178:     public function attacksCommand($message, $channel, $sender, $sendto, $args) {
 179:         $this->attacksCommandHandler($args[1], '', '', $sendto);
 180:     }
 181: 
 182:     /**
 183:      * This command handler shows the last tower attack messages by site number
 184:      * and optionally by page.
 185:      *
 186:      * @HandlesCommand("attacks")
 187:      * @Matches("/^attacks (?!org|player)([a-z0-9]+) (\d+) (\d+)$/i")
 188:      * @Matches("/^attacks (?!org|player)([a-z0-9]+) (\d+)$/i")
 189:      */
 190:     public function attacks2Command($message, $channel, $sender, $sendto, $args) {
 191:         $playfield = $this->playfieldController->getPlayfieldByName($args[1]);
 192:         if ($playfield === null) {
 193:             $msg = "Please enter a valid playfield.";
 194:             $sendto->reply($msg);
 195:             return;
 196:         }
 197:     
 198:         $tower_info = $this->getTowerInfo($playfield->id, $args[2]);
 199:         if ($tower_info === null) {
 200:             $msg = "Invalid site number.";
 201:             $sendto->reply($msg);
 202:             return;
 203:         }
 204:     
 205:         $cmd = "$args[1] $args[2] ";
 206:         $search = "WHERE a.`playfield_id` = {$tower_info->playfield_id} AND a.`site_number` = {$tower_info->site_number}";
 207:         $this->attacksCommandHandler($args[3], $search, $cmd, $sendto);
 208:     }
 209: 
 210:     /**
 211:      * This command handler shows the last tower attack messages where given
 212:      * org has been an attacker or defender.
 213:      *
 214:      * @HandlesCommand("attacks")
 215:      * @Matches("/^attacks org (.+) (\d+)$/i")
 216:      * @Matches("/^attacks org (.+)$/i")
 217:      */
 218:     public function attacksOrgCommand($message, $channel, $sender, $sendto, $args) {
 219:         $cmd = "org $args[1] ";
 220:         $value = str_replace("'", "''", $args[1]);
 221:         $search = "WHERE a.`att_guild_name` LIKE '$value' OR a.`def_guild_name` LIKE '$value'";
 222:         $this->attacksCommandHandler($args[2], $search, $cmd, $sendto);
 223:     }
 224: 
 225:     /**
 226:      * This command handler shows the last tower attack messages where given
 227:      * player has been as attacker.
 228:      *
 229:      * @HandlesCommand("attacks")
 230:      * @Matches("/^attacks player (.+) (\d+)$/i")
 231:      * @Matches("/^attacks player (.+)$/i")
 232:      */
 233:     public function attacksPlayerCommand($message, $channel, $sender, $sendto, $args) {
 234:         $cmd = "player $args[1] ";
 235:         $value = str_replace("'", "''", $args[1]);
 236:         $search = "WHERE a.`att_player` LIKE '$value'";
 237:         $this->attacksCommandHandler($args[2], $search, $cmd, $sendto);
 238:     }
 239: 
 240:     /**
 241:      * This command handler shows status of towers.
 242:      *
 243:      * @HandlesCommand("lc")
 244:      * @Matches("/^lc$/i")
 245:      */
 246:     public function lcCommand($message, $channel, $sender, $sendto, $args) {
 247:         $sql = "SELECT * FROM playfields WHERE `id` IN (SELECT DISTINCT `playfield_id` FROM tower_site) ORDER BY `short_name`";
 248:         $data = $this->db->query($sql);
 249: 
 250:         $blob = '';
 251:         forEach ($data as $row) {
 252:             $baseLink = $this->text->makeChatcmd($row->long_name, "/tell <myname> lc $row->short_name");
 253:             $blob .= "$baseLink <highlight>($row->short_name)<end>\n";
 254:         }
 255:         $msg = $this->text->makeBlob('Land Control Index', $blob);
 256:         $sendto->reply($msg);
 257:     }
 258: 
 259:     /**
 260:      * This command handler shows status of towers.
 261:      *
 262:      * @HandlesCommand("lc")
 263:      * @Matches("/^lc ([0-9a-z]+)$/i")
 264:      */
 265:     public function lc2Command($message, $channel, $sender, $sendto, $args) {
 266:         $playfield_name = strtoupper($args[1]);
 267:         $playfield = $this->playfieldController->getPlayfieldByName($playfield_name);
 268:         if ($playfield === null) {
 269:             $msg = "Playfield '$playfield_name' could not be found.";
 270:             $sendto->reply($msg);
 271:             return;
 272:         }
 273: 
 274:         $sql = "SELECT *, t1.playfield_id, t1.site_number FROM tower_site t1
 275:             JOIN playfields p ON (t1.playfield_id = p.id)
 276:             WHERE t1.playfield_id = ?";
 277:     
 278:         $data = $this->db->query($sql, $playfield->id);
 279:         if (count($data)) {
 280:             $blob = '';
 281:             forEach ($data as $row) {
 282:                 $blob .= "<pagebreak>" . $this->formatSiteInfo($row) . "\n\n";
 283:             }
 284: 
 285:             $msg = $this->text->makeBlob("All Bases in $playfield->long_name", $blob);
 286:         } else {
 287:             $msg = "Playfield <highlight>$playfield->long_name<end> does not have any tower sites.";
 288:         }
 289: 
 290:         $sendto->reply($msg);
 291:     }
 292: 
 293:     /**
 294:      * This command handler shows status of towers.
 295:      *
 296:      * @HandlesCommand("lc")
 297:      * @Matches("/^lc ([0-9a-z]+) ([0-9]+)$/i")
 298:      */
 299:     public function lc3Command($message, $channel, $sender, $sendto, $args) {
 300:         $playfield_name = strtoupper($args[1]);
 301:         $playfield = $this->playfieldController->getPlayfieldByName($playfield_name);
 302:         if ($playfield === null) {
 303:             $msg = "Playfield '$playfield_name' could not be found.";
 304:             $sendto->reply($msg);
 305:             return;
 306:         }
 307: 
 308:         $site_number = $args[2];
 309:         $sql = "SELECT *, t1.playfield_id, t1.site_number FROM tower_site t1
 310:             JOIN playfields p ON (t1.playfield_id = p.id)
 311:             WHERE t1.playfield_id = ? AND t1.site_number = ?";
 312: 
 313:         $row = $this->db->queryRow($sql, $playfield->id, $site_number);
 314:         if ($row !== null) {
 315:             $blob = $this->formatSiteInfo($row) . "\n\n";
 316: 
 317:             // show last attacks and victories
 318:             $sql = "
 319:                 SELECT
 320:                     a.*,
 321:                     v.*,
 322:                     COALESCE(v.time, a.time) dt
 323:                 FROM
 324:                     tower_attack_<myname> a
 325:                     LEFT JOIN tower_victory_<myname> v
 326:                         ON v.attack_id = a.id
 327:                 WHERE
 328:                     a.playfield_id = ?
 329:                     AND a.site_number = ?
 330:                 ORDER BY
 331:                     dt DESC
 332:                 LIMIT 10";
 333:             $data = $this->db->query($sql, $playfield->id, $site_number);
 334:             forEach ($data as $row) {
 335:                 if (empty($row->attack_id)) {
 336:                     // attack
 337:                     if (!empty($row->att_guild_name)) {
 338:                         $name = $row->att_guild_name;
 339:                     } else {
 340:                         $name = $row->att_player;
 341:                     }
 342:                     $blob .= "<$row->att_faction>$name<end> attacked <$row->def_faction>$row->def_guild_name<end>\n";
 343:                 } else {
 344:                     // victory
 345:                     $blob .= "<$row->win_faction>$row->win_guild_name<end> won against <$row->lose_faction>$row->lose_guild_name<end>\n";
 346:                 }
 347:             }
 348: 
 349:             $msg = $this->text->makeBlob("$playfield->short_name $site_number", $blob);
 350:         } else {
 351:             $msg = "Invalid site number.";
 352:         }
 353: 
 354:         $sendto->reply($msg);
 355:     }
 356: 
 357:     /**
 358:      * @HandlesCommand("opentimes")
 359:      * @Matches("/^opentimes$/i")
 360:      */
 361:     public function opentimesCommand($message, $channel, $sender, $sendto, $args) {
 362:         $sql = "
 363:             SELECT
 364:                 guild_name,
 365:                 sum(ct_ql) AS total_ql
 366:             FROM
 367:                 scout_info
 368:             GROUP BY
 369:                 guild_name
 370:             ORDER BY
 371:                 guild_name ASC";
 372:         $data = $this->db->query($sql);
 373:         $contractQls = array();
 374:         forEach ($data as $row) {
 375:             $contractQls[$row->guild_name] = $row->total_ql;
 376:         }
 377:     
 378:         $sql = "
 379:             SELECT
 380:                 *
 381:             FROM
 382:                 tower_site t
 383:                 JOIN scout_info s ON (t.playfield_id = s.playfield_id AND s.site_number = t.site_number)
 384:                 JOIN playfields p ON (t.playfield_id = p.id)
 385:             ORDER BY
 386:                 guild_name ASC,
 387:                 ct_ql DESC";
 388:         $data = $this->db->query($sql);
 389:     
 390:         if (count($data) > 0) {
 391:             $blob = '';
 392:             $currentGuildName = '';
 393:             forEach ($data as $row) {
 394:                 if ($row->guild_name != $currentGuildName) {
 395:                     $contractQl = $contractQls[$row->guild_name];
 396:                     $contractQl = ($contractQl * 2);
 397:                     $faction = strtolower($row->faction);
 398:     
 399:                     $blob .= "\n<u><$faction>$row->guild_name<end></u> (Total Contract QL: $contractQl)\n";
 400:                     $currentGuildName = $row->guild_name;
 401:                 }
 402:                 $gas_level = $this->getGasLevel($row->close_time);
 403:                 $gas_change_string = "$gas_level->color $gas_level->gas_level - $gas_level->next_state in " . gmdate('H:i:s', $gas_level->gas_change) . "<end>";
 404:     
 405:                 $site_link = $this->text->makeChatcmd("$row->short_name $row->site_number", "/tell <myname> lc $row->short_name $row->site_number");
 406:                 $open_time = $row->close_time - (3600 * 6);
 407:                 if ($open_time < 0) {
 408:                     $open_time += 86400;
 409:                 }
 410:     
 411:                 $blob .= "$site_link - {$row->min_ql}-{$row->max_ql}, $row->ct_ql CT, $gas_change_string [by $row->scouted_by]\n";
 412:             }
 413:     
 414:             $msg = $this->text->makeBlob("Scouted Bases", $blob);
 415:         } else {
 416:             $msg = "No sites currently scouted.";
 417:         }
 418:         $sendto->reply($msg);
 419:     }
 420: 
 421:     /**
 422:      * This command handler shows orgs in penalty.
 423:      *
 424:      * @HandlesCommand("penalty")
 425:      * @Matches("/^penalty$/i")
 426:      * @Matches("/^penalty ([a-z0-9]+)$/i")
 427:      */
 428:     public function penaltyCommand($message, $channel, $sender, $sendto, $args) {
 429:         if (count($args) == 2) {
 430:             $budatime = $args[1];
 431:         } else {
 432:             $budatime = '2h';  // default to 2 hours
 433:         }
 434:         
 435:         $time = $this->util->parseTime($budatime);
 436:         if ($time < 1) {
 437:             $msg = "You must enter a valid time parameter.";
 438:             $sendto->reply($msg);
 439:             return;
 440:         }
 441: 
 442:         $penaltyTimeString = $this->util->unixtimeToReadable($time, false);
 443:     
 444:         $data = $this->getSitesInPenalty(time() - $time);
 445:     
 446:         if (count($data) > 0) {
 447:             $blob = '';
 448:             $current_faction = '';
 449:             forEach ($data as $row) {
 450:                 if ($current_faction != $row->att_faction) {
 451:                     $blob .= "\n<header2>{$row->att_faction}<end>\n";
 452:                     $current_faction = $row->att_faction;
 453:                 }
 454:                 $timeString = $this->util->unixtimeToReadable(time() - $row->penalty_time, false);
 455:                 $blob .= "<{$row->att_faction}>{$row->att_guild_name}<end> - $timeString ago\n";
 456:             }
 457:             $msg = $this->text->makeBlob("Orgs in penalty ($penaltyTimeString)", $blob);
 458:         } else {
 459:             $msg = "There are no orgs who have attacked or won battles in the past $penaltyTimeString.";
 460:         }
 461:         $sendto->reply($msg);
 462:     }
 463: 
 464:     /**
 465:      * This command handler removes tower info to watch list.
 466:      *
 467:      * @HandlesCommand("remscout")
 468:      * @Matches("/^remscout ([a-z0-9]+) ([0-9]+)$/i")
 469:      */
 470:     public function remscoutCommand($message, $channel, $sender, $sendto, $args) {
 471:         $playfield_name = $args[1];
 472:         $site_number = $args[2];
 473:     
 474:         $playfield = $this->playfieldController->getPlayfieldByName($playfield_name);
 475:         if ($playfield === null) {
 476:             $msg = "Invalid playfield.";
 477:             $sendto->reply($msg);
 478:             return;
 479:         }
 480:     
 481:         $tower_info = $this->getTowerInfo($playfield->id, $site_number);
 482:         if ($tower_info === null) {
 483:             $msg = "Invalid site number.";
 484:             $sendto->reply($msg);
 485:             return;
 486:         }
 487:     
 488:         $num_rows = $this->remScoutSite($playfield->id, $site_number);
 489:     
 490:         if ($num_rows == 0) {
 491:             $msg = "Could not find a scout record for {$playfield->short_name} {$site_number}.";
 492:         } else {
 493:             $msg = "{$playfield->short_name} {$site_number} removed successfully.";
 494:         }
 495:         $sendto->reply($msg);
 496:     }
 497: 
 498:     /**
 499:      * @HandlesCommand("scout")
 500:      * @Matches("/^scout ([a-z0-9]+) ([0-9]+) ([0-9]{1,2}:[0-9]{2}:[0-9]{2}) ([0-9]+) ([a-z]+) (.*)$/i")
 501:      * @Matches("/^scout ([a-z0-9]+) ([0-9]+) (.*)$/i")
 502:      */
 503:     public function scoutCommand($message, $channel, $sender, $sendto, $args) {
 504:         $skip_checks = false;
 505:         
 506:         if (count($args) == 7) {
 507:             $playfield_name = $args[1];
 508:             $site_number = $args[2];
 509:             $closing_time = $args[3];
 510:             $ct_ql = $args[4];
 511:             $faction = $this->getFaction($args[5]);
 512:             $guild_name = $args[6];
 513:         } else {
 514:             $pattern = "@Control Tower - ([^ ]+) Level: (\d+) Danger level: (.+) Alignment: ([^ ]+)  Organization: (.+) Created at UTC: ([^ ]+) ([^ ]+)@si";
 515:             if (preg_match($pattern, $args[3], $arr)) {
 516:                 $playfield_name = $args[1];
 517:                 $site_number = $args[2];
 518:                 $closing_time = $arr[7];
 519:                 $ct_ql = $arr[2];
 520:                 $faction = $this->getFaction($arr[1]);
 521:                 $guild_name = $arr[5];
 522:             } else {
 523:                 // syntax error
 524:                 return false;
 525:             }
 526:         }
 527: 
 528:         $msg = $this->addScoutInfo($sender, $playfield_name, $site_number, $closing_time, $ct_ql, $faction, $guild_name, $skip_checks);
 529:         $sendto->reply($msg);
 530:     }
 531:     
 532:     /**
 533:      * @HandlesCommand("forcescout")
 534:      * @Matches("/^forcescout ([a-z0-9]+) ([0-9]+) ([0-9]{1,2}:[0-9]{2}:[0-9]{2}) ([0-9]+) ([a-z]+) (.*)$/i")
 535:      * @Matches("/^forcescout ([a-z0-9]+) ([0-9]+) (.*)$/i")
 536:      */
 537:     public function forcescoutCommand($message, $channel, $sender, $sendto, $args) {
 538:         $skip_checks = true;
 539: 
 540:         if (count($args) == 7) {
 541:             $playfield_name = $args[1];
 542:             $site_number = $args[2];
 543:             $closing_time = $args[3];
 544:             $ct_ql = $args[4];
 545:             $faction = $this->getFaction($args[5]);
 546:             $guild_name = $args[6];
 547:         } else {
 548:             $pattern = "@Control Tower - ([^ ]+) Level: (\d+) Danger level: (.+) Alignment: ([^ ]+)  Organization: (.+) Created at UTC: ([^ ]+) ([^ ]+)@si";
 549:             if (preg_match($pattern, $args[3], $arr)) {
 550:                 $playfield_name = $args[1];
 551:                 $site_number = $args[2];
 552:                 $closing_time = $arr[7];
 553:                 $ct_ql = $arr[2];
 554:                 $faction = $this->getFaction($arr[1]);
 555:                 $guild_name = $arr[5];
 556:             } else {
 557:                 // syntax error
 558:                 return false;
 559:             }
 560:         }
 561:     
 562:         $msg = $this->addScoutInfo($sender, $playfield_name, $site_number, $closing_time, $ct_ql, $faction, $guild_name, $skip_checks);
 563:         $sendto->reply($msg);
 564:     }
 565:     
 566:     public function addScoutInfo($sender, $playfield_name, $site_number, $closing_time, $ct_ql, $faction, $guild_name, $skip_checks) {
 567:         if ($faction != 'Omni' && $faction != 'Neutral' && $faction != 'Clan') {
 568:             return "Valid values for faction are: 'Omni', 'Neutral', and 'Clan'.";
 569:         }
 570:     
 571:         $playfield = $this->playfieldController->getPlayfieldByName($playfield_name);
 572:         if ($playfield === null) {
 573:             return "Invalid playfield.";
 574:         }
 575:     
 576:         $tower_info = $this->getTowerInfo($playfield->id, $site_number);
 577:         if ($tower_info === null) {
 578:             return "Invalid site number.";
 579:         }
 580:     
 581:         if ($ct_ql < $tower_info->min_ql || $ct_ql > $tower_info->max_ql) {
 582:             return "$playfield->short_name $tower_info->site_number can only accept Control Tower of ql {$tower_info->min_ql}-{$tower_info->max_ql}.";
 583:         }
 584:     
 585:         $closing_time_array = explode(':', $closing_time);
 586:         $closing_time_seconds = $closing_time_array[0] * 3600 + $closing_time_array[1] * 60 + $closing_time_array[2];
 587:     
 588:         if (!$skip_checks && $this->settingManager->get('check_close_time_on_scout') == 1) {
 589:             $last_victory = $this->getLastVictory($tower_info->playfield_id, $tower_info->site_number);
 590:             if ($last_victory !== null) {
 591:                 $victory_time_of_day = $last_attack->time % 86400;
 592:                 if ($victory_time_of_day > $closing_time_seconds) {
 593:                     $victory_time_of_day -= 86400;
 594:                 }
 595:     
 596:                 if ($closing_time_seconds - $victory_time_of_day > 3600) {
 597:                     $check_blob .= "- <green>Closing time<end> The closing time you have specified is more than 1 hour after the site was destroyed.";
 598:                     $check_blob .= " Please verify that you are using the closing time and not the gas change time and that the closing time is correct.\n\n";
 599:                 }
 600:             }
 601:         }
 602:     
 603:         if (!$skip_checks && $this->settingManager->get('check_guild_name_on_scout') == 1) {
 604:             if (!$this->checkGuildName($guild_name)) {
 605:                 $check_blob .= "- <green>Org name<end> The org name you entered has never attacked or been attacked.\n\n";
 606:             }
 607:         }
 608:     
 609:         if ($check_blob) {
 610:             $forceCmd = "forcescout $playfield->short_name $site_number $closing_time $ct_ql $faction $guild_name";
 611:             $forcescoutLink = $this->text->makeChatcmd("<symbol>$forceCmd", "/tell <myname> $forceCmd");
 612:             $check_blob .= "Please correct these errors, or, if you are sure the values you entered are correct, use !forcescout to bypass these checks.\n\n";
 613:             $check_blob .= $forcescoutLink;
 614: 
 615:             return $this->text->makeBlob("Scouting problems for $playfield->short_name $site_number", $check_blob);
 616:         } else {
 617:             $this->addScoutSite($playfield->id, $site_number, $closing_time_seconds, $ct_ql, $faction, $guild_name, $sender);
 618:             return "Scout info for <highlight>$playfield->short_name $site_number<end> has been updated.";
 619:         }
 620:     }
 621: 
 622:     /**
 623:      * @HandlesCommand("towerstats")
 624:      * @Matches("/^towerstats (.+)$/i")
 625:      * @Matches("/^towerstats$/i")
 626:      */
 627:     public function towerStatsCommand($message, $channel, $sender, $sendto, $args) {
 628:         if (count($args) == 2) {
 629:             $budatime = $args[1];
 630:         } else {
 631:             $budatime = "1d";
 632:         }
 633: 
 634:         $time = $this->util->parseTime($budatime);
 635:         if ($time < 1) {
 636:             $msg = "You must enter a valid time parameter.";
 637:             $sendto->reply($msg);
 638:             return;
 639:         }
 640: 
 641:         $timeString = $this->util->unixtimeToReadable($time);
 642: 
 643:         $blob = '';
 644: 
 645:         $sql = "SELECT
 646:                 att_faction,
 647:                 COUNT(att_faction) AS num
 648:             FROM
 649:                 tower_attack_<myname>
 650:             WHERE
 651:                 `time` >= ?
 652:             GROUP BY
 653:                 att_faction
 654:             ORDER BY
 655:                 num DESC";
 656: 
 657:         $data = $this->db->query($sql, time() - $time);
 658:         forEach ($data as $row) {
 659:             $blob .= "<{$row->att_faction}>{$row->att_faction}<end> have attacked <highlight>{$row->num}<end> times.\n";
 660:         }
 661:         if (count($data) > 0) {
 662:             $blob .= "\n";
 663:         }
 664: 
 665:         $sql = "SELECT
 666:                 lose_faction,
 667:                 COUNT(lose_faction) AS num
 668:             FROM
 669:                 tower_victory_<myname>
 670:             WHERE
 671:                 `time` >= ?
 672:             GROUP BY
 673:                 lose_faction
 674:             ORDER BY
 675:                 num DESC";
 676: 
 677:         $data = $this->db->query($sql, time() - $time);
 678:         forEach ($data as $row) {
 679:             $blob .= "<{$row->lose_faction}>{$row->lose_faction}<end> have lost <highlight>{$row->num}<end> tower sites.\n";
 680:         }
 681: 
 682:         if ($blob == '') {
 683:             $msg = "No tower attacks or victories have been recorded.";
 684:         } else {
 685:             $msg = $this->text->makeBlob("Tower Stats for the Last $timeString", $blob);
 686:         }
 687:         $sendto->reply($msg);
 688:     }
 689: 
 690:     /**
 691:      * This command handler shows the last tower battle results.
 692:      *
 693:      * @HandlesCommand("victory")
 694:      * @Matches("/^victory (\d+)$/i")
 695:      * @Matches("/^victory$/i")
 696:      */
 697:     public function victoryCommand($message, $channel, $sender, $sendto, $args) {
 698:         $this->victoryCommandHandler($args[1], $search, "", $sendto);
 699:     }
 700: 
 701:     /**
 702:      * This command handler shows the last tower battle results.
 703:      *
 704:      * @HandlesCommand("victory")
 705:      * @Matches("/^victory (?!org|player)([a-z0-9]+) (\d+) (\d+)$/i")
 706:      * @Matches("/^victory (?!org|player)([a-z0-9]+) (\d+)$/i")
 707:      */
 708:     public function victory2Command($message, $channel, $sender, $sendto, $args) {
 709:         $playfield = $this->playfieldController->getPlayfieldByName($args[1]);
 710:         if ($playfield === null) {
 711:             $msg = "Invalid playfield.";
 712:             $sendto->reply($msg);
 713:             return;
 714:         }
 715:     
 716:         $tower_info = $this->getTowerInfo($playfield->id, $args[2]);
 717:         if ($tower_info === null) {
 718:             $msg = "Invalid site number.";
 719:             $sendto->reply($msg);
 720:             return;
 721:         }
 722:     
 723:         $cmd = "$args[1] $args[2] ";
 724:         $search = "WHERE a.`playfield_id` = {$tower_info->playfield_id} AND a.`site_number` = {$tower_info->site_number}";
 725:         $this->victoryCommandHandler($args[3], $search, $cmd, $sendto);
 726:     }
 727: 
 728:     /**
 729:      * This command handler shows the last tower battle results.
 730:      *
 731:      * @HandlesCommand("victory")
 732:      * @Matches("/^victory org (.+) (\d+)$/i")
 733:      * @Matches("/^victory org (.+)$/i")
 734:      */
 735:     public function victoryOrgCommand($message, $channel, $sender, $sendto, $args) {
 736:         $cmd = "org $args[1] ";
 737:         $value = str_replace("'", "''", $args[1]);
 738:         $search = "WHERE v.`win_guild_name` LIKE '$value' OR v.`lose_guild_name` LIKE '$value'";
 739:         $this->victoryCommandHandler($args[2], $search, $cmd, $sendto);
 740:     }
 741: 
 742:     /**
 743:      * This command handler shows the last tower battle results.
 744:      *
 745:      * @HandlesCommand("victory")
 746:      * @Matches("/^victory player (.+) (\d+)$/i")
 747:      * @Matches("/^victory player (.+)$/i")
 748:      */
 749:     public function victoryPlayerCommand($message, $channel, $sender, $sendto, $args) {
 750:         $cmd = "player $args[1] ";
 751:         $value = str_replace("'", "''", $args[1]);
 752:         $search = "WHERE a.`att_player` LIKE '$value'";
 753:         $this->victoryCommandHandler($args[2], $search, $cmd, $sendto);
 754:     }
 755: 
 756:     /**
 757:      * This event handler record attack messages.
 758:      *
 759:      * @Event("towers")
 760:      * @Description("Record attack messages")
 761:      */
 762:     public function attackMessagesEvent($eventObj) {
 763:         if (preg_match("/^The (Clan|Neutral|Omni) organization (.+) just entered a state of war! (.+) attacked the (Clan|Neutral|Omni) organization (.+)'s tower in (.+) at location \\((\\d+),(\\d+)\\)\\.$/i", $eventObj->message, $arr)) {
 764:             $att_side = ucfirst(strtolower($arr[1]));  // comes across as a string instead of a reference, so convert to title case
 765:             $att_guild = $arr[2];
 766:             $att_player = $arr[3];
 767:             $def_side = ucfirst(strtolower($arr[4]));  // comes across as a string instead of a reference, so convert to title case
 768:             $def_guild = $arr[5];
 769:             $playfield_name = $arr[6];
 770:             $x_coords = $arr[7];
 771:             $y_coords = $arr[8];
 772:         } else if (preg_match("/^(.+) just attacked the (Clan|Neutral|Omni) organization (.+)'s tower in (.+) at location \(([0-9]+), ([0-9]+)\).(.*)$/i", $eventObj->message, $arr)) {
 773:             $att_player = $arr[1];
 774:             $def_side = ucfirst(strtolower($arr[2]));  // comes across as a string instead of a reference, so convert to title case
 775:             $def_guild = $arr[3];
 776:             $playfield_name = $arr[4];
 777:             $x_coords = $arr[5];
 778:             $y_coords = $arr[6];
 779:         } else {
 780:             return;
 781:         }
 782:         
 783:         // regardless of what the player lookup says, we use the information from the
 784:         // attack message where applicable because that will always be most up to date
 785:         $whois = $this->playerManager->getByName($att_player);
 786:         if ($whois === null) {
 787:             $whois = new stdClass;
 788:             $whois->type = 'npc';
 789:             
 790:             // in case it's not a player who causes attack message (pet, mob, etc)
 791:             $whois->name = $att_player;
 792:             $whois->faction = 'Neutral';
 793:         }
 794:         if (isset($att_side)) {
 795:             $whois->faction = $att_side;
 796:         }
 797:         if (isset($att_guild)) {
 798:             $whois->guild = $att_guild;
 799:         }
 800:         
 801:         $playfield = $this->playfieldController->getPlayfieldByName($playfield_name);
 802:         $closest_site = $this->getClosestSite($playfield->id, $x_coords, $y_coords);
 803: 
 804:         $defender = new stdClass();
 805:         $defender->faction   = $def_side;
 806:         $defender->guild     = $def_guild;
 807:         $defender->playfield = $playfield;
 808:         $defender->site      = $closest_site;
 809: 
 810:         forEach ($this->attackListeners as $listener) {
 811:             call_user_func($listener->callback, $whois, $defender, $listener->data);
 812:         }
 813: 
 814:         if ($closest_site === null) {
 815:             $this->logger->log('error', "ERROR! Could not find closest site: ({$playfield_name}) '{$playfield->id}' '{$x_coords}' '{$y_coords}'");
 816:             $more = "[<red>UNKNOWN AREA!<end>]";
 817:         } else {
 818:         
 819:             $this->recordAttack($whois, $def_side, $def_guild, $x_coords, $y_coords, $closest_site);
 820:             $this->logger->log('debug', "Site being attacked: ({$playfield_name}) '{$closest_site->playfield_id}' '{$closest_site->site_number}'");
 821:         
 822:             // Beginning of the 'more' window
 823:             $link = "Attacker: <highlight>";
 824:             if ($whois->firstname) {
 825:                 $link .= $whois->firstname . " ";
 826:             }
 827:         
 828:             $link .= '"' . $att_player . '"';
 829:             if ($whois->lastname)  {
 830:                 $link .= " " . $whois->lastname;
 831:             }
 832:             $link .= "<end>\n";
 833:         
 834:             if ($whois->breed) {
 835:                 $link .= "Breed: <highlight>$whois->breed<end>\n";
 836:             }
 837:             if ($whois->gender) {
 838:                 $link .= "Gender: <highlight>$whois->gender<end>\n";
 839:             }
 840:         
 841:             if ($whois->profession) {
 842:                 $link .= "Profession: <highlight>$whois->profession<end>\n";
 843:             }
 844:             if ($whois->level) {
 845:                 $level_info = $this->levelController->getLevelInfo($whois->level);
 846:                 $link .= "Level: <highlight>{$whois->level}/<green>{$whois->ai_level}<end> ({$level_info->pvpMin}-{$level_info->pvpMax})<end>\n";
 847:             }
 848:         
 849:             $link .= "Alignment: <highlight>$whois->faction<end>\n";
 850:         
 851:             if ($whois->guild) {
 852:                 $link .= "Organization: <highlight>$whois->guild<end>\n";
 853:                 if ($whois->guild_rank) {
 854:                     $link .= "Organization Rank: <highlight>$whois->guild_rank<end>\n";
 855:                 }
 856:             }
 857:         
 858:             $link .= "\n";
 859:         
 860:             $link .= "Defender: <highlight>$def_guild<end>\n";
 861:             $link .= "Alignment: <highlight>$def_side<end>\n\n";
 862:         
 863:             $base_link = $this->text->makeChatcmd("{$playfield->short_name} {$closest_site->site_number}", "/tell <myname> lc {$playfield->short_name} {$closest_site->site_number}");
 864:             $attack_waypoint = $this->text->makeChatcmd("{$x_coords}x{$y_coords}", "/waypoint {$x_coords} {$y_coords} {$playfield->id}");
 865:             $link .= "Playfield: <highlight>{$base_link} ({$closest_site->min_ql}-{$closest_site->max_ql})<end>\n";
 866:             $link .= "Location: <highlight>{$closest_site->site_name} ({$attack_waypoint})<end>\n";
 867:         
 868:             $more = $this->text->makeBlob("{$playfield->short_name} {$closest_site->site_number}", $link, 'Advanced Tower Info');
 869:         }
 870:         
 871:         $targetorg = "<".strtolower($def_side).">".$def_guild."<end>";
 872:         
 873:         // Starting tower message to org/private chat
 874:         $msg .= "";
 875:         
 876:         // tower_attack_spam >= 2 (normal) includes attacker stats
 877:         if ($this->settingManager->get("tower_attack_spam") >= 2) {
 878:         
 879:             if ($whois->profession == "") {
 880:                 $msg .= "<".strtolower($whois->faction).">$att_player<end> (Unknown";
 881:             } else {
 882:                 if (!$whois->guild){
 883:                     $msg .= "<".strtolower($whois->faction).">$att_player<end>";
 884:                 } else {
 885:                     $msg .= "<font color=#AAAAAA>$att_player<end>";
 886:                 }
 887:                 $msg .= " (<font color=#AAAAAA>$whois->level<end>";
 888:                 if ($whois->ai_level) {
 889:                     $msg .= "/<green>$whois->ai_level<end>";
 890:                 }
 891:                 $msg .= ", $whois->breed <font color=#AAAAAA>$whois->profession<end>";
 892:             }
 893:         
 894:             if (!$whois->guild) {
 895:                 $msg .= ")";
 896:             } else if (!$whois->guild_rank) {
 897:                 $msg .= "<".strtolower($whois->faction).">$whois->guild<end>)";
 898:             } else {
 899:                 $msg .= ", $whois->guild_rank of <".strtolower($whois->faction).">$whois->guild<end>)";
 900:             }
 901:         
 902:         } else if ($whois->guild) {
 903:             $msg .= "<".strtolower($whois->faction).">$whois->guild<end>";
 904:         } else {
 905:             $msg .= "<".strtolower($whois->faction).">$att_player<end>";
 906:         }
 907:         
 908:         $msg .= " attacked $targetorg [$more]";
 909:         
 910:         $s = $this->settingManager->get("tower_attack_spam");
 911:         
 912:         if ($s > 0) {
 913:             $this->chatBot->sendGuild($msg, true);
 914:         }
 915:     }
 916: 
 917:     /**
 918:      * This event handler record victory messages.
 919:      *
 920:      * @Event("towers")
 921:      * @Description("Record victory messages")
 922:      */
 923:     public function victoryMessagesEvent($eventObj) {
 924:         if (preg_match("/^The (Clan|Neutral|Omni) organization (.+) attacked the (Clan|Neutral|Omni) (.+) at their base in (.+). The attackers won!!$/i", $eventObj->message, $arr)) {
 925:             $win_faction = $arr[1];
 926:             $win_guild_name = $arr[2];
 927:             $lose_faction = $arr[3];
 928:             $lose_guild_name = $arr[4];
 929:             $playfield_name = $arr[5];
 930:         } else if (preg_match("/^Notum Wars Update: The (clan|neutral|omni) organization (.+) lost their base in (.+).$/i", $eventObj->message, $arr)) {
 931:             $win_faction = '';
 932:             $win_guild_name = '';
 933:             $lose_faction = ucfirst($arr[1]);  // capitalize the faction name to match the other messages
 934:             $lose_guild_name = $arr[2];
 935:             $playfield_name = $arr[3];
 936:         } else {
 937:             return;
 938:         }
 939:         
 940:         $playfield = $this->playfieldController->getPlayfieldByName($playfield_name);
 941:         if ($playfield === null) {
 942:             $this->logger->log('error', "Could not find playfield for name '$playfield_name'");
 943:             return;
 944:         }
 945:         
 946:         $last_attack = $this->getLastAttack($win_faction, $win_guild_name, $lose_faction, $lose_guild_name, $playfield->id);
 947:         if ($last_attack !== null) {
 948:             $this->remScoutSite($last_attack->playfield_id, $last_attack->site_number);
 949:         } else {
 950:             $last_attack = new stdClass;
 951:             $last_attack->att_guild_name = $win_guild_name;
 952:             $last_attack->def_guild_name = $lose_guild_name;
 953:             $last_attack->att_faction = $win_faction;
 954:             $last_attack->def_faction = $lose_faction;
 955:             $last_attack->playfield_id = $playfield->id;
 956:             $last_attack->id = '-1';
 957:         }
 958:         
 959:         $this->recordVictory($last_attack);
 960:     }
 961: 
 962:     protected function attacksCommandHandler($page_label, $search, $cmd, $sendto) {
 963:         if (is_numeric($page_label) == false) {
 964:             $page_label = 1;
 965:         } else if ($page_label < 1) {
 966:             $msg = "You must choose a page number greater than 0";
 967:             $sendto->reply($msg);
 968:             return;
 969:         }
 970: 
 971:         $page_size = $this->settingManager->get('tower_page_size');
 972:         $start_row = ($page_label - 1) * $page_size;
 973: 
 974:         $sql =
 975:             "SELECT
 976:                 *
 977:             FROM
 978:                 tower_attack_<myname> a
 979:                 LEFT JOIN playfields p ON (a.playfield_id = p.id)
 980:                 LEFT JOIN tower_site s ON (a.playfield_id = s.playfield_id AND a.site_number = s.site_number)
 981:             $search
 982:             ORDER BY
 983:                 a.`time` DESC
 984:             LIMIT
 985:                 ?, ?";
 986: 
 987:         $data = $this->db->query($sql, intval($start_row), intval($page_size));
 988:         if (count($data) == 0) {
 989:             $msg = "No tower attacks found.";
 990:         } else {
 991:             $links = array();
 992:             if ($page_label > 1) {
 993:                 $links['Previous Page'] = '/tell <myname> attacks ' . ($page_label - 1);
 994:             }
 995:             $links['Next Page'] = "/tell <myname> attacks {$cmd}" . ($page_label + 1);
 996: 
 997:             $blob = "The last $page_size Tower Attacks (page $page_label)\n\n";
 998:             $blob .= $this->text->makeHeaderLinks($links) . "\n\n";
 999: 
1000:             forEach ($data as $row) {
1001:                 $timeString = $this->util->unixtimeToReadable(time() - $row->time);
1002:                 $blob .= "Time: " . $this->util->date($row->time) . " (<highlight>$timeString<end> ago)\n";
1003:                 if ($row->att_faction == '') {
1004:                     $att_faction = "unknown";
1005:                 } else {
1006:                     $att_faction = strtolower($row->att_faction);
1007:                 }
1008: 
1009:                 if ($row->def_faction == '') {
1010:                     $def_faction = "unknown";
1011:                 } else {
1012:                     $def_faction = strtolower($row->def_faction);
1013:                 }
1014: 
1015:                 if ($row->att_profession == 'Unknown') {
1016:                     $blob .= "Attacker: <{$att_faction}>{$row->att_player}<end> ({$row->att_faction})\n";
1017:                 } else if ($row->att_guild_name == '') {
1018:                     $blob .= "Attacker: <{$att_faction}>{$row->att_player}<end> ({$row->att_level}/<green>{$row->att_ai_level}<end> {$row->att_profession}) ({$row->att_faction})\n";
1019:                 } else {
1020:                     $blob .= "Attacker: {$row->att_player} ({$row->att_level}/<green>{$row->att_ai_level}<end> {$row->att_profession}) <{$att_faction}>{$row->att_guild_name}<end> ({$row->att_faction})\n";
1021:                 }
1022: 
1023:                 $base = $this->text->makeChatcmd("{$row->short_name} {$row->site_number}", "/tell <myname> lc {$row->short_name} {$row->site_number}");
1024:                 $base .= " ({$row->min_ql}-{$row->max_ql})";
1025: 
1026:                 $blob .= "Defender: <{$def_faction}>{$row->def_guild_name}<end> ({$row->def_faction})\n";
1027:                 $blob .= "Site: $base\n\n";
1028:             }
1029:             $msg = $this->text->makeBlob("Tower Attacks", $blob);
1030:         }
1031: 
1032:         $sendto->reply($msg);
1033:     }
1034: 
1035:     protected function victoryCommandHandler($page_label, $search, $cmd, $sendto) {
1036:         if (is_numeric($page_label) == false) {
1037:             $page_label = 1;
1038:         } else if ($page_label < 1) {
1039:             $msg = "You must choose a page number greater than 0";
1040:             $sendto->reply($msg);
1041:             return;
1042:         }
1043: 
1044:         $page_size = $this->settingManager->get('tower_page_size');
1045:         $start_row = ($page_label - 1) * $page_size;
1046: 
1047:         $sql = "
1048:             SELECT
1049:                 *,
1050:                 v.time AS victory_time,
1051:                 a.time AS attack_time
1052:             FROM
1053:                 tower_victory_<myname> v
1054:                 LEFT JOIN tower_attack_<myname> a ON (v.attack_id = a.id)
1055:                 LEFT JOIN playfields p ON (a.playfield_id = p.id)
1056:                 LEFT JOIN tower_site s ON (a.playfield_id = s.playfield_id AND a.site_number = s.site_number)
1057:             {$search}
1058:             ORDER BY
1059:                 `victory_time` DESC
1060:             LIMIT
1061:                 ?, ?";
1062: 
1063:         $data = $this->db->query($sql, intval($start_row), intval($page_size));
1064:         if (count($data) == 0) {
1065:             $msg = "No Tower results found.";
1066:         } else {
1067:             $links = array();
1068:             if ($page_label > 1) {
1069:                 $links['Previous Page'] = '/tell <myname> victory ' . ($page_label - 1);
1070:             }
1071:             $links['Next Page'] = "/tell <myname> victory {$cmd}" . ($page_label + 1);
1072: 
1073:             $blob = "The last $page_size Tower Results (page $page_label)\n\n";
1074:             $blob .= $this->text->makeHeaderLinks($links) . "\n\n";
1075:             forEach ($data as $row) {
1076:                 $timeString = $this->util->unixtimeToReadable(time() - $row->victory_time);
1077:                 $blob .= "Time: " . $this->util->date($row->victory_time) . " (<highlight>$timeString<end> ago)\n";
1078: 
1079:                 if (!$win_side = strtolower($row->win_faction)) {
1080:                     $win_side = "unknown";
1081:                 }
1082:                 if (!$lose_side = strtolower($row->lose_faction)) {
1083:                     $lose_side = "unknown";
1084:                 }
1085: 
1086:                 if ($row->playfield_id != '' && $row->site_number != '') {
1087:                     $base = $this->text->makeChatcmd("{$row->short_name} {$row->site_number}", "/tell <myname> lc {$row->short_name} {$row->site_number}");
1088:                     $base .= " ({$row->min_ql}-{$row->max_ql})";
1089:                 } else {
1090:                     $base = "Unknown";
1091:                 }
1092: 
1093:                 $blob .= "Winner: <{$win_side}>{$row->win_guild_name}<end> (".ucfirst($win_side).")\n";
1094:                 $blob .= "Loser: <{$lose_side}>{$row->lose_guild_name}<end> (".ucfirst($lose_side).")\n";
1095:                 $blob .= "Site: $base\n\n";
1096:             }
1097:             $msg = $this->text->makeBlob("Tower Victories", $blob);
1098:         }
1099: 
1100:         $sendto->reply($msg);
1101:     }
1102: 
1103:     public function getTowerInfo($playfield_id, $site_number) {
1104:         $sql = "
1105:             SELECT
1106:                 *
1107:             FROM
1108:                 tower_site t
1109:             WHERE
1110:                 `playfield_id` = ?
1111:                 AND `site_number` = ?
1112:             LIMIT 1";
1113: 
1114:         return $this->db->queryRow($sql, $playfield_id, $site_number);
1115:     }
1116: 
1117:     protected function findSitesInPlayfield($playfield_id) {
1118:         $sql = "SELECT * FROM tower_site WHERE `playfield_id` = ?";
1119: 
1120:         return $this->db->query($sql, $playfield_id);
1121:     }
1122: 
1123:     protected function getClosestSite($playfield_id, $x_coords, $y_coords) {
1124:         $sql = "
1125:             SELECT
1126:                 *,
1127:                 ((x_distance * x_distance) + (y_distance * y_distance)) radius
1128:             FROM
1129:                 (SELECT
1130:                     playfield_id,
1131:                     site_number,
1132:                     min_ql,
1133:                     max_ql,
1134:                     x_coord,
1135:                     y_coord,
1136:                     site_name,
1137:                     (x_coord - {$x_coords}) as x_distance,
1138:                     (y_coord - {$y_coords}) as y_distance
1139:                 FROM
1140:                     tower_site
1141:                 WHERE
1142:                     playfield_id = ?) t
1143:             ORDER BY
1144:                 radius ASC
1145:             LIMIT 1";
1146: 
1147:         return $this->db->queryRow($sql, $playfield_id);
1148:     }
1149: 
1150:     protected function getLastAttack($att_faction, $att_guild_name, $def_faction, $def_guild_name, $playfield_id) {
1151:         $time = time() - (7 * 3600);
1152: 
1153:         $sql = "
1154:             SELECT
1155:                 *
1156:             FROM
1157:                 tower_attack_<myname>
1158:             WHERE
1159:                 `att_guild_name` = ?
1160:                 AND `att_faction` = ?
1161:                 AND `def_guild_name` = ?
1162:                 AND `def_faction` = ?
1163:                 AND `playfield_id` = ?
1164:                 AND `time` >= ?
1165:             ORDER BY
1166:                 `time` DESC
1167:             LIMIT 1";
1168: 
1169:         return $this->db->queryRow($sql, $att_guild_name, $att_faction, $def_guild_name, $def_faction, $playfield_id, $time);
1170:     }
1171: 
1172:     protected function recordAttack($whois, $def_faction, $def_guild_name, $x_coords, $y_coords, $closest_site) {
1173:         $sql = "
1174:             INSERT INTO tower_attack_<myname> (
1175:                 `time`,
1176:                 `att_guild_name`,
1177:                 `att_faction`,
1178:                 `att_player`,
1179:                 `att_level`,
1180:                 `att_ai_level`,
1181:                 `att_profession`,
1182:                 `def_guild_name`,
1183:                 `def_faction`,
1184:                 `playfield_id`,
1185:                 `site_number`,
1186:                 `x_coords`,
1187:                 `y_coords`
1188:             ) VALUES (
1189:                 ?,
1190:                 ?,
1191:                 ?,
1192:                 ?,
1193:                 ?,
1194:                 ?,
1195:                 ?,
1196:                 ?,
1197:                 ?,
1198:                 ?,
1199:                 ?,
1200:                 ?,
1201:                 ?
1202:             )";
1203: 
1204:         return $this->db->exec($sql, time(), $whois->guild, $whois->faction, $whois->name, $whois->level, $whois->ai_level, $whois->profession,
1205:             $def_guild_name, $def_faction, $closest_site->playfield_id, $closest_site->site_number, $x_coords, $y_coords);
1206:     }
1207: 
1208:     protected function findAllScoutedSites() {
1209:         $sql =
1210:             "SELECT
1211:                 *
1212:             FROM
1213:                 scout_info s
1214:                 JOIN tower_site t
1215:                     ON (s.playfield_id = t.playfield_id AND s.site_number = t.site_number)
1216:                 JOIN playfields p
1217:                     ON (s.playfield_id = p.id)
1218:             ORDER BY
1219:                 guild_name, ct_ql";
1220: 
1221:         return $this->db->query($sql);
1222:     }
1223: 
1224:     protected function getLastVictory($playfield_id, $site_number) {
1225:         $sql = "
1226:             SELECT
1227:                 *
1228:             FROM
1229:                 tower_victory_<myname> v
1230:                 JOIN tower_attack_<myname> a ON (v.attack_id = a.id)
1231:             WHERE
1232:                 a.`playfield_id` = ?
1233:                 AND a.`site_number` >= ?
1234:             ORDER BY
1235:                 v.`time` DESC
1236:             LIMIT 1";
1237: 
1238:         return $this->db->queryRow($sql, $playfield_id, $site_number);
1239:     }
1240: 
1241:     protected function recordVictory($last_attack) {
1242:         $sql = "
1243:             INSERT INTO tower_victory_<myname> (
1244:                 `time`,
1245:                 `win_guild_name`,
1246:                 `win_faction`,
1247:                 `lose_guild_name`,
1248:                 `lose_faction`,
1249:                 `attack_id`
1250:             ) VALUES (
1251:                 ?,
1252:                 ?,
1253:                 ?,
1254:                 ?,
1255:                 ?,
1256:                 ?
1257:             )";
1258: 
1259:         return $this->db->exec($sql, time(), $last_attack->att_guild_name, $last_attack->att_faction, $last_attack->def_guild_name, $last_attack->def_faction, $last_attack->id);
1260:     }
1261: 
1262:     protected function addScoutSite($playfield_id, $site_number, $close_time, $ct_ql, $faction, $guild_name, $scouted_by) {
1263:         $this->db->exec("DELETE FROM scout_info WHERE `playfield_id` = ? AND `site_number` = ?", $playfield_id, $site_number);
1264: 
1265:         $sql = "
1266:             INSERT INTO scout_info (
1267:                 `playfield_id`,
1268:                 `site_number`,
1269:                 `scouted_on`,
1270:                 `scouted_by`,
1271:                 `ct_ql`,
1272:                 `guild_name`,
1273:                 `faction`,
1274:                 `close_time`
1275:             ) VALUES (
1276:                 ?,
1277:                 ?,
1278:                 ?,
1279:                 ?,
1280:                 ?,
1281:                 ?,
1282:                 ?,
1283:                 ?
1284:             )";
1285: 
1286:         $numrows = $this->db->exec($sql, $playfield_id, $site_number, time(), $scouted_by, $ct_ql, $guild_name, $faction, $close_time);
1287: 
1288:         return $numrows;
1289:     }
1290: 
1291:     protected function remScoutSite($playfield_id, $site_number) {
1292:         $sql = "DELETE FROM scout_info WHERE `playfield_id` = ? AND `site_number` = ?";
1293: 
1294:         return $this->db->exec($sql, $playfield_id, $site_number);
1295:     }
1296: 
1297:     protected function checkGuildName($guild_name) {
1298:         $sql = "SELECT * FROM tower_attack_<myname> WHERE `att_guild_name` LIKE ? OR `def_guild_name` LIKE ? LIMIT 1";
1299: 
1300:         $data = $this->db->query($sql, $guild_name, $guild_name);
1301:         if (count($data) === 0) {
1302:             return false;
1303:         } else {
1304:             return true;
1305:         }
1306:     }
1307: 
1308:     protected function getSitesInPenalty($time) {
1309:         $sql = "
1310:             SELECT att_guild_name, att_faction, MAX(IFNULL(t2.time, t1.time)) AS penalty_time
1311:             FROM tower_attack_<myname> t1
1312:                 LEFT JOIN tower_victory_<myname> t2 ON t1.id = t2.attack_id
1313:             WHERE
1314:                 att_guild_name <> ''
1315:                 AND (t2.time IS NULL AND t1.time > ?)
1316:                 OR t2.time > ?
1317:             GROUP BY att_guild_name, att_faction
1318:             ORDER BY att_faction ASC, penalty_time DESC";
1319:         return $this->db->query($sql, $time, $time);
1320:     }
1321:     
1322:     protected function getGasLevel($close_time) {
1323:         $current_time = time() % 86400;
1324: 
1325:         $site = new stdClass();
1326:         $site->current_time = $current_time;
1327:         $site->close_time = $close_time;
1328: 
1329:         if ($close_time < $current_time) {
1330:             $close_time += 86400;
1331:         }
1332: 
1333:         $time_until_close_time = $close_time - $current_time;
1334:         $site->time_until_close_time = $time_until_close_time;
1335: 
1336:         if ($time_until_close_time < 3600 * 1) {
1337:             $site->gas_change = $time_until_close_time;
1338:             $site->gas_level = '5%';
1339:             $site->next_state = 'closes';
1340:             $site->color = "<orange>";
1341:         } else if ($time_until_close_time < 3600 * 6) {
1342:             $site->gas_change = $time_until_close_time;
1343:             $site->gas_level = '25%';
1344:             $site->next_state = 'closes';
1345:             $site->color = "<green>";
1346:         } else {
1347:             $site->gas_change = $time_until_close_time - (3600 * 6);
1348:             $site->gas_level = '75%';
1349:             $site->next_state = 'opens';
1350:             $site->color = "<red>";
1351:         }
1352: 
1353:         return $site;
1354:     }
1355:     
1356:     protected function formatSiteInfo($row) {
1357:         $waypointLink = $this->text->makeChatcmd($row->x_coord . "x" . $row->y_coord, "/waypoint {$row->x_coord} {$row->y_coord} {$row->playfield_id}");
1358:         $attacksLink = $this->text->makeChatcmd("Recent attacks", "/tell <myname> attacks {$row->short_name} {$row->site_number}");
1359:         $victoryLink = $this->text->makeChatcmd("Recent victories", "/tell <myname> victory {$row->short_name} {$row->site_number}");
1360: 
1361:         $blob = "Short name: <highlight>{$row->short_name} {$row->site_number}<end>\n";
1362:         $blob .= "Long name: <highlight>{$row->site_name}, {$row->long_name}<end>\n";
1363:         $blob .= "Level range: <highlight>{$row->min_ql}-{$row->max_ql}<end>\n";
1364:         $blob .= "Center coordinates: $waypointLink\n";
1365:         $blob .= $attacksLink . "\n";
1366:         $blob .= $victoryLink;
1367:         
1368:         return $blob;
1369:     }
1370:     
1371:     public function getFaction($input) {
1372:         $faction = ucfirst(strtolower($input));
1373:         if ($faction == "Neut") {
1374:             $faction = "Neutral";
1375:         }
1376:         return $faction;
1377:     }
1378: }
1379: 
Budabot 4 Docs API documentation generated by ApiGen