diff --git a/application/core/ColorUtil.php b/application/core/ColorUtil.php new file mode 100644 index 00000000..1ca6f6f0 --- /dev/null +++ b/application/core/ColorUtil.php @@ -0,0 +1,57 @@ + 0.5) { + $red = 2. * (1. - $value); + } + $red = ColorUtil::floatToCode($red); + $green = ColorUtil::floatToCode($green); + return $red . $green . '0'; + } + + /** + * Get hex color representation of the float + * + * @param float $value + * @return string + */ + public static function floatToCode($value) { + $value = floatval($value); + if ($value < 0.) { + $value = 0.; + } + if ($value > 1.) { + $value = 1.; + } + $value *= 15.; + $value = round($value); + if ($value < 10) { + return (string) $value; + } + $codes = array(10 => 'a', 11 => 'b', 12 => 'c', 13 => 'd', 14 => 'e', 15 => 'f'); + return $codes[$value]; + } +} + +?> diff --git a/application/core/FML/Controls/Control.php b/application/core/FML/Controls/Control.php index 14872f75..92c1f5bd 100644 --- a/application/core/FML/Controls/Control.php +++ b/application/core/FML/Controls/Control.php @@ -121,19 +121,37 @@ abstract class Control implements Renderable { * @param real $z * @return \FML\Controls\Control */ - public function setPosition($x = null, $y = null, $z = null) { - if ($x !== null) { - $this->setX($x); - } - if ($y !== null) { - $this->setY($y); - } + public function setPosition($x, $y, $z = null) { + $this->setX($x); + $this->setY($y); if ($z !== null) { $this->setZ($z); } return $this; } + /** + * Set width + * + * @param real $width + * @return \FML\Controls\Control + */ + public function setWidth($width) { + $this->width = $width; + return $this; + } + + /** + * Set height + * + * @param real $height + * @return \FML\Controls\Control + */ + public function setHeight($height) { + $this->height = $height; + return $this; + } + /** * Set size * @@ -141,13 +159,9 @@ abstract class Control implements Renderable { * @param real $height * @return \FML\Controls\Control */ - public function setSize($width = null, $height = null) { - if ($width !== null) { - $this->width = $width; - } - if ($height !== null) { - $this->height = $height; - } + public function setSize($width, $height) { + $this->setWidth($width); + $this->setHeight($height); return $this; } diff --git a/application/core/FML/Controls/Gauge.php b/application/core/FML/Controls/Gauge.php index df32a267..c90119a4 100644 --- a/application/core/FML/Controls/Gauge.php +++ b/application/core/FML/Controls/Gauge.php @@ -2,6 +2,8 @@ namespace FML\Controls; +use FML\Types\Styleable; + /** * Class representing CMlGauge * @@ -13,11 +15,13 @@ class Gauge extends Control implements Styleable { */ protected $ratio = 1.; protected $grading = 1.; + protected $color = ''; protected $rotation = 0.; protected $centered = 0; protected $clan = 0; protected $drawBg = 1; protected $drawBlockBg = 1; + protected $style = ''; /** * Construct a new gauge control @@ -26,70 +30,104 @@ class Gauge extends Control implements Styleable { */ public function __construct($id = null) { parent::__construct($id); - $this->name = 'gauge'; + $this->tagName = 'gauge'; } /** * Set ratio * * @param real $ratio + * @return \FML\Controls\Gauge */ public function setRatio($ratio) { $this->ratio = $ratio; + return $this; } /** * Set grading * * @param real $grading + * @return \FML\Controls\Gauge */ public function setGrading($grading) { $this->grading = $grading; + return $this; + } + + /** + * Set color + * + * @param string $color + * @return \FML\Controls\Gauge + */ + public function setColor($color) { + $this->color = $color; + return $this; } /** * Set rotation * * @param real $rotation + * @return \FML\Controls\Gauge */ public function setRotation($rotation) { $this->rotation = $rotation; + return $this; } /** * Set centered * * @param bool $centered + * @return \FML\Controls\Gauge */ public function setCentered($centered) { $this->centered = ($centered ? 1 : 0); + return $this; } /** * Set clan * * @param int $clan + * @return \FML\Controls\Gauge */ public function setClan($clan) { $this->clan = $clan; + return $this; } /** * Set draw background * * @param bool $drawBg + * @return \FML\Controls\Gauge */ public function setDrawBg($drawBg) { $this->drawBg = ($drawBg ? 1 : 0); + return $this; } /** * Set draw block background * * @param bool $drawBlockBg + * @return \FML\Controls\Gauge */ public function setDrawBlockBg($drawBlockBg) { $this->drawBlockBg = ($drawBlockBg ? 1 : 0); + return $this; + } + + /** + * + * @see \FML\Types\Styleable::setStyle() + */ + public function setStyle($style) { + $this->style = $style; + return $this; } /** @@ -100,11 +138,23 @@ class Gauge extends Control implements Styleable { $xml = parent::render($domDocument); $xml->setAttribute('ratio', $this->ratio); $xml->setAttribute('grading', $this->grading); - $xml->setAttribute('rotation', $this->rotation); - $xml->setAttribute('centered', $this->centered); - $xml->setAttribute('clan', $this->clan); + if ($this->color) { + $xml->setAttribute('color', $this->color); + } + if ($this->rotation) { + $xml->setAttribute('rotation', $this->rotation); + } + if ($this->centered) { + $xml->setAttribute('centered', $this->centered); + } + if ($this->clan) { + $xml->setAttribute('clan', $this->clan); + } $xml->setAttribute('drawbg', $this->drawBg); $xml->setAttribute('drawblockbg', $this->drawBlockBg); + if ($this->style) { + $xml->setAttribute('style', $this->style); + } return $xml; } } diff --git a/application/core/FML/Controls/Label.php b/application/core/FML/Controls/Label.php index 1159a193..4138b774 100644 --- a/application/core/FML/Controls/Label.php +++ b/application/core/FML/Controls/Label.php @@ -20,6 +20,7 @@ class Label extends Control implements Linkable, NewLineable, Scriptable, Stylea protected $text = ''; protected $textPrefix = ''; protected $textEmboss = 0; + protected $translate = 0; protected $maxLines = 0; protected $url = ''; protected $manialink = ''; @@ -75,6 +76,17 @@ class Label extends Control implements Linkable, NewLineable, Scriptable, Stylea return $this; } + /** + * Set translate + * + * @param bool $translate + * @return \FML\Controls\Label + */ + public function setTranslate($translate) { + $this->translate = ($translate ? 1 : 0); + return $this; + } + /** * Set max lines * @@ -191,6 +203,9 @@ class Label extends Control implements Linkable, NewLineable, Scriptable, Stylea if ($this->textEmboss) { $xml->setAttribute('textemboss', $this->textEmboss); } + if ($this->translate) { + $xml->setAttribute('translate', $this->translate); + } if ($this->maxLines) { $xml->setAttribute('maxlines', $this->maxLines); } diff --git a/application/core/ManiaControl.php b/application/core/ManiaControl.php index a7d9f195..9b1e0978 100644 --- a/application/core/ManiaControl.php +++ b/application/core/ManiaControl.php @@ -31,6 +31,7 @@ require_once __DIR__ . '/Settings/SettingManager.php'; require_once __DIR__ . '/GbxDataFetcher/gbxdatafetcher.inc.php'; require_once __DIR__ . '/ManiaExchange/mxinfofetcher.inc.php'; require_once __DIR__ . '/ManiaExchange/mxinfosearcher.inc.php'; +require_once __DIR__ . '/ColorUtil.php'; list($endiantest) = array_values(unpack('L1L', pack('V', 1))); if ($endiantest == 1) { require_once __DIR__ . '/PhpRemote/GbxRemote.inc.php'; diff --git a/application/core/Manialinks/ManialinkUtil.php b/application/core/Manialinks/ManialinkUtil.php index f9baf239..e3f1fa1b 100644 --- a/application/core/Manialinks/ManialinkUtil.php +++ b/application/core/Manialinks/ManialinkUtil.php @@ -9,19 +9,19 @@ require_once __DIR__ . '/../FML/autoload.php'; * * @author steeffeen & kremsy */ -class ManialinkUtil { +abstract class ManialinkUtil { /** * Send the given manialink to players * * @param \IXR_ClientMulticall_Gbx $client * @param string $manialink - * @param array $logins + * @param mixed $logins * @param int $timeout * @param bool $hideOnClick * @return bool */ - public static function sendManialinkPage(\IXR_ClientMulticall_Gbx $client, $manialinkText, array $logins = null, $timeout = 0, + public static function sendManialinkPage(\IXR_ClientMulticall_Gbx $client, $manialinkText, $logins = null, $timeout = 0, $hideOnClick = false) { if (!$client || !$manialinkText) { return false; diff --git a/application/core/Players/PlayerManager.php b/application/core/Players/PlayerManager.php index a39799fe..441e4d60 100644 --- a/application/core/Players/PlayerManager.php +++ b/application/core/Players/PlayerManager.php @@ -138,6 +138,15 @@ class PlayerManager implements CallbackListener { $this->maniaControl->chat->sendChat('$<' . $player->nickname . '$> $ff0has left the game. Played:$fff ' . $played); } + /** + * Get the complete PlayerList + * + * @return array + */ + public function getPlayers() { + return $this->playerList; + } + /** * Get a Player from the PlayerList * diff --git a/application/core/Settings/SettingManager.php b/application/core/Settings/SettingManager.php index 6f1b5e30..441c6bc3 100644 --- a/application/core/Settings/SettingManager.php +++ b/application/core/Settings/SettingManager.php @@ -133,7 +133,7 @@ class SettingManager { return (int) $value; } if ($type === self::TYPE_REAL) { - return (real) $value; + return (float) $value; } if ($type === self::TYPE_BOOL) { return (bool) $value; diff --git a/application/plugins/Karma.php b/application/plugins/Karma.php new file mode 100644 index 00000000..3e46ed7e --- /dev/null +++ b/application/plugins/Karma.php @@ -0,0 +1,416 @@ +maniaControl = $maniaControl; + + $this->name = 'Karma Plugin'; + $this->author = 'steeffeen'; + $this->version = self::VERSION; + $this->description = 'Plugin offering Karma Voting for Maps.'; + + // Init database + $this->initTables(); + + // Init settings + $this->maniaControl->settingManager->initSetting($this, self::SETTING_AVAILABLE_VOTES, '-2,2'); + $this->maniaControl->settingManager->initSetting($this, self::SETTING_WIDGET_TITLE, 'Map-Karma'); + $this->maniaControl->settingManager->initSetting($this, self::SETTING_WIDGET_POSX, 90.); + $this->maniaControl->settingManager->initSetting($this, self::SETTING_WIDGET_POSY, 82.); + $this->maniaControl->settingManager->initSetting($this, self::SETTING_WIDGET_WIDTH, 25.); + $this->maniaControl->settingManager->initSetting($this, self::SETTING_WIDGET_HEIGHT, 13.); + + // Register for callbacks + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_ONINIT, $this, 'handleOnInit'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_BEGINMAP, $this, 'handleBeginMap'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MC_1_SECOND, $this, 'handle1Second'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MP_PLAYERCONNECT, $this, + 'handlePlayerConnect'); + $this->maniaControl->callbackManager->registerCallbackListener(CallbackManager::CB_MP_PLAYERCHAT, $this, 'handlePlayerChat'); + } + + /** + * Handle ManiaControl 1 Second callback + * + * @param array $callback + */ + public function handle1Second(array $callback) { + if (!$this->updateManialink) return; + + // Get players + $players = $this->updateManialink; + if ($players === true) { + $players = $this->maniaControl->playerManager->getPlayers(); + } + $this->updateManialink = false; + + // Get map karma + $map = $this->maniaControl->mapManager->getCurrentMap(); + $karma = $this->getMapKarma($map); + $votes = $this->getMapVotes($map); + + // Build karma manialink + $this->buildManialink(); + + $karmaGauge = $this->manialink->karmaGauge; + if (is_numeric($karma)) { + $karma = floatval($karma); + $karmaGauge->setRatio($karma + 0.15 - $karma * 0.15); + $karmaColor = ColorUtil::floatToStatusColor($karma); + $karmaGauge->setColor($karmaColor . '9'); + } + else { + $karmaGauge->setRatio(0.); + $karmaGauge->setColor('00fb'); + } + + $width = $this->maniaControl->settingManager->getSetting($this, self::SETTING_WIDGET_WIDTH); + $height = $this->maniaControl->settingManager->getSetting($this, self::SETTING_WIDGET_HEIGHT); + + // Loop players + foreach ($players as $login => $player) { + // Get player vote + $vote = $this->getPlayerVote($player, $map); + + // Adjust manialink for vote + $votesFrame = $this->manialink->votesFrame; + $votesFrame->removeChildren(); + + // Send manialink + $manialinkText = $this->manialink->render()->saveXML(); + ManialinkUtil::sendManialinkPage($this->maniaControl->client, $manialinkText, $login); + } + } + + /** + * Handle ManiaControl OnInit callback + * + * @param array $callback + */ + public function handleOnInit(array $callback) { + $this->updateManialink = true; + } + + /** + * Handle BeginMap ManiaControl callback + * + * @param array $callback + */ + public function handleBeginMap(array $callback) { + $this->updateManialink = true; + } + + /** + * Handle PlayerConnect callback + * + * @param array $callback + */ + public function handlePlayerConnect(array $callback) { + $login = $callback[1][0]; + $player = $this->maniaControl->playerManager->getPlayer($login); + if (!$player) { + return; + } + $this->queryManialinkUpdateFor($player); + } + + /** + * Handle PlayerChat callback + * + * @param array $chatCallback + */ + public function handlePlayerChat(array $chatCallback) { + $login = $chatCallback[1][1]; + $player = $this->maniaControl->playerManager->getPlayer($login); + if (!$player) { + return; + } + $message = $chatCallback[1][2]; + if ($chatCallback[1][3]) { + $message = substr($message, 1); + } + $firstChar = substr($message, 0, 1); + if ($firstChar !== '+' && $firstChar !== '-') { + return; + } + $vote = substr_count($message, '+'); + $vote -= substr_count($message, '-'); + $success = $this->handleVote($player, $vote); + if (!$success) { + $this->maniaControl->chat->sendError('Error occured.', $player->login); + return; + } + $this->maniaControl->chat->sendSuccess('Vote updated!', $player->login); + } + + /** + * Handle a vote done by a player + * + * @param Player $player + * @param int $vote + * @return bool + */ + private function handleVote(Player $player, $vote) { + // Check vote + $votesSetting = $this->maniaControl->settingManager->getSetting($this, self::SETTING_AVAILABLE_VOTES); + $votes = explode(',', $votesSetting); + $voteLow = intval($votes[0]); + $voteHigh = $voteLow + 2; + if (isset($votes[1])) { + $voteHigh = intval($votes[1]); + } + if ($vote < $voteLow || $vote > $voteHigh) { + return false; + } + + // Calculate actual voting + $vote -= $voteLow; + $voteHigh -= $voteLow; + $vote /= $voteHigh; + + // Save vote + $map = $this->maniaControl->mapManager->getCurrentMap(); + $success = $this->savePlayerVote($player, $map, $vote); + if (!$success) { + return false; + } + $this->updateManialink = true; + return true; + } + + /** + * Query the player to update the manialink + * + * @param Player $player + */ + private function queryManialinkUpdateFor(Player $player) { + if ($this->updateManialink === true) { + return; + } + if (!is_array($this->updateManialink)) { + $this->updateManialink = array(); + } + $this->updateManialink[$player->login] = $player; + } + + /** + * Create necessary database tables + */ + private function initTables() { + $mysqli = $this->maniaControl->database->mysqli; + $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_KARMA . "` ( + `index` int(11) NOT NULL AUTO_INCREMENT, + `mapIndex` int(11) NOT NULL, + `playerIndex` int(11) NOT NULL, + `vote` float NOT NULL DEFAULT '-1', + `changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`index`), + UNIQUE KEY `player_map_vote` (`mapIndex`, `playerIndex`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Save players map votes' AUTO_INCREMENT=1;"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error, E_USER_ERROR); + } + } + + /** + * Save the vote of the player for the map + * + * @param Player $player + * @param Map $map + * @param float $vote + * @return bool + */ + private function savePlayerVote(Player $player, Map $map, $vote) { + $mysqli = $this->maniaControl->database->mysqli; + $query = "INSERT INTO `" . self::TABLE_KARMA . "` ( + `mapIndex`, + `playerIndex`, + `vote` + ) VALUES ( + {$map->index}, + {$player->index}, + {$vote} + ) ON DUPLICATE KEY UPDATE + `vote` = VALUES(`vote`);"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error); + return false; + } + return $result; + } + + /** + * Get the current vote of the player for the map + * + * @param Player $player + * @param Map $map + * @return int + */ + private function getPlayerVote(Player $player, Map $map) { + $mysqli = $this->maniaControl->database->mysqli; + $query = "SELECT * FROM `" . self::TABLE_KARMA . "` + WHERE `playerIndex` = {$player->index} + AND `mapIndex` = {$map->index} + AND `vote` >= 0;"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error); + return false; + } + if ($result->num_rows <= 0) { + $result->free(); + return false; + } + $item = $result->fetch_object(); + $result->free(); + $vote = $item->vote; + return floatval($vote); + } + + /** + * Get the current karma of the map + * + * @param Map $map + */ + private function getMapKarma(Map $map) { + $mysqli = $this->maniaControl->database->mysqli; + $query = "SELECT AVG(`vote`) AS `karma` FROM `" . self::TABLE_KARMA . "` + WHERE `mapIndex` = {$map->index} + AND `vote` >= 0;"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error); + return false; + } + if ($result->num_rows <= 0) { + $result->free(); + return false; + } + $item = $result->fetch_object(); + $result->free(); + $karma = $item->karma; + if ($karma === null) { + return false; + } + return floatval($karma); + } + + /** + * Get the current votings for the map + * + * @param Map $map + */ + private function getMapVotes(Map $map) { + $mysqli = $this->maniaControl->database->mysqli; + $query = "SELECT `vote`, COUNT(`vote`) AS `count` FROM `" . self::TABLE_KARMA . "` + WHERE `mapIndex` = {$map->index} + AND `vote` >= 0 + GROUP BY `vote`;"; + $result = $mysqli->query($query); + if ($mysqli->error) { + trigger_error($mysqli->error); + return false; + } + $votes = array(); + while ($vote = $result->fetch_object()) { + $votes[$vote->vote] = $vote; + } + $result->free(); + return $votes; + } + + /** + * Build karma voting manialink if necessary + * + * @param bool $forceBuild + */ + private function buildManialink($forceBuild = false) { + if (is_object($this->manialink) && !$forceBuild) return; + + $title = $this->maniaControl->settingManager->getSetting($this, self::SETTING_WIDGET_TITLE, 'Map-Karma'); + $pos_x = $this->maniaControl->settingManager->getSetting($this, self::SETTING_WIDGET_POSX); + $pos_y = $this->maniaControl->settingManager->getSetting($this, self::SETTING_WIDGET_POSY); + $width = $this->maniaControl->settingManager->getSetting($this, self::SETTING_WIDGET_WIDTH); + $height = $this->maniaControl->settingManager->getSetting($this, self::SETTING_WIDGET_HEIGHT); + + $manialink = new ManiaLink(self::MLID_KARMA); + + $frame = new Frame(); + $manialink->add($frame); + $frame->setPosition($pos_x, $pos_y); + + $backgroundQuad = new Quad(); + $frame->add($backgroundQuad); + $backgroundQuad->setY($height * 0.15); + $backgroundQuad->setSize($width, $height); + $backgroundQuad->setStyles('Bgs1InRace', 'BgTitleShadow'); + + $titleLabel = new Label(); + $frame->add($titleLabel); + $titleLabel->setY($height * 0.36); + $titleLabel->setWidth($width * 0.85); + $titleLabel->setStyle('TextTitle1'); + $titleLabel->setTranslate(true); + $titleLabel->setTextSize(1); + $titleLabel->setText($title); + + $karmaGauge = new Gauge(); + $frame->add($karmaGauge); + $karmaGauge->setSize($width * 0.95, $height * 0.92); + $karmaGauge->setDrawBg(false); + $manialink->karmaGauge = $karmaGauge; + + $votesFrame = new Frame(); + $frame->add($votesFrame); + $manialink->votesFrame = $votesFrame; + + $this->manialink = $manialink; + } +} + +?> diff --git a/application/plugins/LocalRecords.php b/application/plugins/LocalRecords.php index 7c4f8fb2..9be9e098 100644 --- a/application/plugins/LocalRecords.php +++ b/application/plugins/LocalRecords.php @@ -171,6 +171,8 @@ class LocalRecordsPlugin extends Plugin implements CallbackListener { $this->updateManialink = true; // Announce record + // TODO: setting für nur-zum-spieler senden + // TODO: setting für nur-besten-x-announcen $newRecord = $this->getLocalRecord($map, $player); if (!$oldRecord || $newRecord->rank < $oldRecord->rank) { $improvement = 'gained the';