From 3cb32783abafa8e952bb48ca942aaa87b98f4a40 Mon Sep 17 00:00:00 2001
From: Krzysztof Sikorski Caern utracony z braku obrońców. Caern $msg!$1', $txt);
+ $txt = preg_replace('@\[sub\](.+)\[/sub\]@uU', '$1', $txt);
+ $txt = preg_replace('@\[sup\](.+)\[/sup\]@uU', '$1', $txt);
+ }
+ return $txt;
+ }
+
+
+ //returns value from array, or defaults if it doesn't exist
+ public static function getArrayValue(array $a, $name, $default = null)
+ {
+ return isset($a[$name]) ? $a[$name] : $default;
+ }
+
+
+ //implodes whitespace, optionally preserving newlines
+ public static function normalizeString($string, $preserveNewlines = false)
+ {
+ $string = str_replace(array("\r\n","\r"), "\n", $string); //unix newlines
+ if($preserveNewlines)
+ $string = preg_replace('/[^\S\n]+/', ' ', $string);
+ else $string = preg_replace('/\s+/', ' ', $string);
+ $string = trim($string);
+ return $string;
+ }
+
+
+ //generates a password hash
+ public static function passwordHash($salt, $text)
+ {
+ return sha1($salt . $text);
+ }
+
+
+ //returns random salt for password hashing
+ public static function passwordSalt()
+ {
+ $c0 = ord('0');
+ $cA = ord('a');
+ $cZ = ord('z');
+ $max = 10+$cZ-$cA;
+ $salt = '';
+ for($i = 0; $i < 8; ++$i)
+ {
+ $x = mt_rand(0, $max);
+ if($x < 10)
+ $salt .= chr($c0 + $x);
+ else $salt .= chr($cA + $x - 10);
+ }
+ return $salt;
+ }
+
+
+ //redirects to selected url
+ public static function redirect($url)
+ {
+ session_write_close(); //just in case
+ header('Content-Type: text/html; charset=UTF-8');
+ header(sprintf('Location: %s', $url), true, 303); //"See Other" status
+ printf('';
+ $summary[] = '
';
+ return implode('', $summary) . (string) $logger;
+ }
+}
diff --git a/2013/daemon/lib/daemon/combat.php b/2013/daemon/lib/daemon/combat.php
new file mode 100644
index 0000000..a1134a9
--- /dev/null
+++ b/2013/daemon/lib/daemon/combat.php
@@ -0,0 +1,164 @@
+attachLogger($logger);
+//$combat->addUnit('a', $unit1, true);
+//$combat->addUnit('b', $unit2, false);
+//$combat->execute();
+//$combatLog = (string) $logger;
+class Daemon_Combat
+{
+ public $units = array();
+ public $sideA = array();
+ public $sideB = array();
+ public $round = 0;
+ public $roundLimit = 120;
+ public $tickLimit = 1;
+ private $_logger = null;
+
+
+ public function addUnit($unitId, Daemon_Combat_Unit $unit, $attacker)
+ {
+ $unit->attachLogger($this->_logger);
+ $unit->_id = $unitId;
+ $unit->_ticks = 0;
+ $unit->_initiative = mt_rand(0, 32 * $unit->speed);
+ $unit->_target = null;
+ $unit->_threat = array();
+ $unit->_dmgDealt = 0;
+ $unit->_dmgTaken = 0;
+ $this->units[$unitId] = $unit;
+ $unit->_attacker = (bool) $attacker;
+ if($unit->_attacker)
+ {
+ $this->sideA[$unitId] = $unit;
+ $unit->_allies = &$this->sideA;
+ $unit->_enemies = &$this->sideB;
+ }
+ else
+ {
+ $this->sideB[$unitId] = $unit;
+ $unit->_allies = &$this->sideB;
+ $unit->_enemies = &$this->sideA;
+ }
+ }
+
+
+ public function attachLogger(Daemon_Combat_Log $logger)
+ {
+ $this->_logger = $logger;
+ foreach($this->units as $unit)
+ $unit->attachLogger($logger);
+ }
+
+
+ protected function callbackActive($unit)
+ {
+ return $unit->_ticks > $this->tickLimit;
+ }
+
+
+ protected function callbackSpeedSum($prev, $unit)
+ {
+ return $prev + $unit->speed;
+ }
+
+
+ protected function callbackTickCompare($unit1, $unit2)
+ {
+ if($unit1->_ticks == $unit2->_ticks)
+ return $unit1->_initiative - $unit2->_initiative;
+ else return ($unit1->_ticks < $unit2->_ticks) ? -1 : +1;
+ }
+
+
+ protected function callbackTickInc($unit, $key)
+ {
+ if($unit->health > 0)
+ $unit->_ticks += $unit->speed;
+ else $unit->_ticks = null;
+ }
+
+
+ protected function debug($round, array $units)
+ {
+ if($this->_logger)
+ {
+ $result = array("Segment $round");
+ foreach($units as $unit)
+ $result[] = (string) $unit;
+ $this->_logger->add($result);
+ }
+ }
+
+
+ public function execute($noCleanup = false)
+ {
+ if(!$this->units)
+ return;
+ $unitCount = count($this->units);
+ if ($this->_logger)
+ $this->_logger->groupCombat = ($unitCount > 2);
+ $round = 0;
+ $roundLimit = 100 + 10 * $unitCount;
+ $speedSum = array_reduce($this->units, array($this, 'callbackSpeedSum'), 0);
+ $this->tickLimit = 1 + ceil(2 * $speedSum / count($this->units));
+ while($round < $roundLimit)
+ {
+ $victory = false;
+ array_walk($this->units, array($this, 'callbackTickInc'));
+ while($actor = $this->getActiveUnit())
+ {
+ ++$round;
+ $actor->_ticks -= $this->tickLimit;
+ $actor->executeRound($round);
+ //victory check, sides should be updated by units
+ $victory = (!count($this->sideA) || !count($this->sideB));
+ if($victory)
+ break;
+ }
+ if($victory)
+ break;
+ }
+ //after-combat regen & fixes
+ if (!$noCleanup)
+ {
+ foreach($this->units as $unit)
+ {
+ if ($unit->health < 1)
+ {
+ $unit->health = 0;
+ }
+ elseif (($unit->regen > 0) && ($unit->health < $unit->health_max))
+ {
+ $unit->health = $unit->health_max;
+ if($this->_logger)
+ $this->_logger->add("$unit->name regeneruje pełnię zdrowia.';
+ $summary[] = ' ';
+ $summary[] = 'Strona Postać ';
+ $summary[] = 'Wykonane ataki Otrzymane ataki ';
+ $summary[] = '';
+ $summary[] = ' ';
+ foreach($units as $factionId => $faction)
+ {
+ foreach($faction as $unit)
+ {
+ $attackerTxt = $unit->_attacker ? 'atak' : 'obrona';
+ $dmgTotal = $unit->_dmgDealt + $unit->_dmgTaken;
+ $health = max(0, ceil($unit->health_max - $unit->_dmgTaken));
+ $healthPercent = $unit->health_max ? round(100 * $health / $unit->health_max, 2) : 0;
+ $avgDealt = $unit->_cntDealt ? $unit->_dmgDealt / $unit->_cntDealt : null;
+ $avgTaken = $unit->_cntTaken ? $unit->_dmgTaken / $unit->_cntTaken : null;
+ $summary[] = 'Imię Frakcja Zdrowie ';
+ $summary[] = 'Ataki Obrażenia Średnia ';
+ $summary[] = 'Ataki Obrażenia Średnia ';
+ $summary[] = '';
+ $summary[] = sprintf(' ';
+ }
+ }
+ $summary[] = '%s %s %s %.2f%% ',
+ $attackerTxt, $unit->name, $unit->faction_id, $healthPercent);
+ $summary[] = sprintf('%d %.3f %.3f ',
+ $unit->_cntDealt, $unit->_dmgDealt, $avgDealt);
+ $summary[] = sprintf('%d %.3f %.3f ',
+ $unit->_cntTaken, $unit->_dmgTaken, $avgTaken);
+ $summary[] = '
");
+ }
+ else
+ {
+ $unit->health = Daemon_Math::round($unit->health);
+ }
+ }
+ }
+ }
+
+
+ protected function getActiveUnit()
+ {
+ $active = array_filter($this->units, array($this, 'callbackActive'));
+ uasort($active, array($this, 'callbackTickCompare'));
+ return array_pop($active);
+ }
+
+
+ public function reset()
+ {
+ $this->units = array();
+ $this->sideA = array();
+ $this->sideB = array();
+ }
+}
diff --git a/2013/daemon/lib/daemon/combat/log.php b/2013/daemon/lib/daemon/combat/log.php
new file mode 100644
index 0000000..0c7708d
--- /dev/null
+++ b/2013/daemon/lib/daemon/combat/log.php
@@ -0,0 +1,117 @@
+clear();
+ }
+
+
+ public function __toString()
+ {
+ return $this->buffer;
+ }
+
+
+ public function add($text)
+ {
+ $this->buffer .= "$text\n";
+ }
+
+
+ public function clear()
+ {
+ $this->buffer = '';
+ }
+
+
+ public static function escape($str)
+ {
+ return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
+ }
+
+
+ public function txtAttackHit($name, $dmg, $magical, $critical, $poison, $shockDmg, $stun, $vampRegen)
+ {
+ $txt = $critical ? 'Trafienie krytyczne! ' : '';
+ $txt .= sprintf('%s zadaje %.2f obrażeń %s', self::escape($name),
+ $dmg, $magical ? 'magicznych' : 'fizycznych');
+ if($poison)
+ $txt .= ', zatruwając cel';
+ if($stun)
+ $txt .= ', ogłuszając cel';
+ if($vampRegen)
+ $txt .= sprintf(', regenerując %.2f HP', $vampRegen);
+ if($shockDmg)
+ $txt .= sprintf('. Otrzymuje %.2f obrażeń od porażenia', $shockDmg);
+ $txt .= '.
';
+ $this->add($txt);
+ }
+
+
+ public function txtAttackMiss($name, $magical)
+ {
+ if($magical)
+ $this->add('Cel odbił zaklęcie.
');
+ else $this->add(sprintf('%s chybił.
', self::escape($name)));
+ }
+
+
+ public function txtDeath($name, $flawlessName = null)
+ {
+ $txt = sprintf('%s umiera.
', $name);
+ if($flawlessName)
+ {
+ $atxt = array('%s ziewa.', '%s śmieje się szyderczo.');
+ $txt .= sprintf($atxt[array_rand($atxt)], self::escape($flawlessName)).'
';
+ }
+ $this->add($txt);
+ }
+
+
+ public function txtDemon($regen)
+ {
+ $this->add(sprintf('Demon w zbroi wchłonął zaklęcie. Cel regeneruje %.2f obrażeń.
', $regen));
+ }
+
+
+ public function txtPoison($name, $dmg)
+ {
+ $this->add(sprintf('%s otrzymuje %.2f obrażeń od trucizny.
', self::escape($name), $dmg));
+ }
+
+
+ public function txtRegen($name, $regen)
+ {
+ $this->add(sprintf('%s regeneruje %.2f obrażeń.
', self::escape($name), $regen));
+ }
+
+
+ public function txtRoundFooter()
+ {
+ $this->add('
', $round));
+ }
+
+
+ public function txtTargetHeader($actorName, $targetName)
+ {
+ if($targetName)
+ {
+ if ($this->groupCombat)
+ $this->add(sprintf('%s wybiera cel: %s.
',
+ self::escape($actorName), self::escape($targetName)));
+ }
+ else $this->add(sprintf('%s nie ma już przeciwników.
', self::escape($actorName)));
+ }
+}
diff --git a/2013/daemon/lib/daemon/combat/unit.php b/2013/daemon/lib/daemon/combat/unit.php
new file mode 100644
index 0000000..37eddc2
--- /dev/null
+++ b/2013/daemon/lib/daemon/combat/unit.php
@@ -0,0 +1,261 @@
+_id, name: $this->name, ticks: $this->_ticks, health: $this->health/$this->health_max]";
+ }
+
+
+ public function attachLogger(Daemon_Combat_Log $logger = null)
+ {
+ $this->_logger = $logger;
+ }
+
+
+ protected function calculateAttack(array $params)
+ {
+ //attack count - check for swarm
+ if(self::SP_SWARM == $params['sp_type'])
+ $attackCount = ceil($params['count'] * $this->health / $this->health_max);
+ else $attackCount = $params['count'];
+ //prepare variables
+ $magical = ('m' == $params['type']);
+ if($magical)
+ {
+ $targetDef = $this->_target->mdef;
+ $targetRes = $this->_target->mres;
+ $armor = 0;
+ }
+ else
+ {
+ $targetDef = $this->_target->pdef;
+ $targetRes = $this->_target->pres;
+ $armor = $this->_target->armor;
+ if(self::SP_ETHER == $params['sp_type'])
+ $armor *= 1 - $params['sp_param']/100;
+ }
+ //calculate basic data
+ $toHit = 100 * $params['atk'] / ($params['atk'] + $targetDef);
+ $baseDmg = $params['str'] * $params['str'] / ($params['str'] + $targetRes);
+ //faction effects
+ if($this->faction_id && $this->_target->faction_id && ($this->faction_id != $this->_target->faction_id))
+ {
+ if(self::SP_FACTION == $params['sp_type'])
+ $baseDmg *= 1 + $params['sp_param']/100;
+ if(self::SP_FACTION == $this->_target->armor_sp_type)
+ $baseDmg /= 1 + $this->_target->armor_sp_param/100;
+ }
+ //execute attacks
+ for($i=0; $i < $attackCount; $i++)
+ {
+ $d100 = mt_rand(0,99);
+ //check for demon
+ $demon = $magical && (self::SP_DEMON == $this->_target->armor_sp_type);
+ if($demon && mt_rand(0,99) < $this->_target->armor_sp_param)
+ {
+ $regen = min($this->_target->health_max - $this->_target->health, $baseDmg * $this->_target->armor_sp_param / 100);
+ $this->_target->health += $regen;
+ if($this->_logger)
+ $this->_logger->txtDemon($regen);
+ }
+ //check for hit
+ elseif($d100 < $toHit)
+ {
+ //calculate damage
+ $critical = $d100>45;
+ if($critical)
+ {
+ $dmgMult = 2;
+ if(self::SP_BLOODY == $params['sp_type'])
+ $dmgMult += $params['sp_param']/100;
+ }
+ else $dmgMult = 1 + mt_rand(0,127)/256;
+ $dmg = max(0, $baseDmg * $dmgMult - $armor);
+ $this->_target->health -= $dmg;
+ //update statistics
+ $this->_target->_flawless = false;
+ $this->_target->_cntTaken += 1;
+ $this->_target->_dmgTaken += $dmg;
+ $this->_cntDealt += 1;
+ $this->_dmgDealt += $dmg;
+ //check for poison
+ if(self::SP_POISON == $params['sp_type'])
+ {
+ $poison = $dmg>0 ? $params['sp_param'] : 0;
+ if(self::SP_ANTIPOISON == $this->_target->armor_sp_type)
+ $poison *= 1 - $this->_target->armor_sp_param / 100;
+ $this->_target->_poison += $poison;
+ }
+ else $poison = 0;
+ //check for shock
+ if(self::SP_SHOCK == $this->_target->armor_sp_type)
+ {
+ $shockDmg = $dmg * $this->_target->armor_sp_param / 100;
+ $this->health -= $shockDmg;
+ }
+ else $shockDmg = 0;
+ //check for stun
+ $stun = $critical && (self::SP_STUN == $params['sp_type']);
+ if($stun)
+ $this->_target->_ticks = 0;
+ //check for vampirism
+ if(self::SP_VAMPIRE == $params['sp_type'])
+ {
+ $vampRegen = min($this->health_max - $this->health, $dmg * $params['sp_param']/100);
+ if(self::SP_ANTIVAMP == $this->_target->armor_sp_type)
+ $vampRegen *= 1 - $this->_target->armor_sp_param / 100;
+ $this->health += $vampRegen;
+ }
+ else $vampRegen = 0;
+ //print info
+ if($this->_logger)
+ {
+ $this->_logger->txtAttackHit($this->name, $dmg, $magical, $critical,
+ $poison, $shockDmg, $stun, $vampRegen);
+ }
+ }
+ else
+ {
+ if($this->_logger)
+ $this->_logger->txtAttackMiss($this->name, $magical);
+ }
+ }
+ }
+
+
+ protected function executeAttacks()
+ {
+ if(!$this->_target)
+ return;
+ $this->_target->_threat[$this->_id] = $this;
+ if($this->count1 && $this->type1)
+ {
+ $attackParams = array(
+ 'str' => $this->str1,
+ 'atk' => $this->atk1,
+ 'type' => $this->type1,
+ 'count' => $this->count1,
+ 'sp_type' => $this->sp1_type,
+ 'sp_param' => $this->sp1_param,
+ );
+ $this->calculateAttack($attackParams);
+ }
+ if($this->count2 && $this->type2)
+ {
+ $attackParams = array(
+ 'str' => $this->str2,
+ 'atk' => $this->atk2,
+ 'type' => $this->type2,
+ 'count' => $this->count2,
+ 'sp_type' => $this->sp2_type,
+ 'sp_param' => $this->sp2_param,
+ );
+ $this->calculateAttack($attackParams);
+ }
+ //check for target death
+ if($this->_target->health <= 0)
+ {
+ $this->_target->_ticks = null;
+ unset($this->_threat[$this->_target->_id]);
+ unset($this->_enemies[$this->_target->_id]);
+ if($this->_logger)
+ $this->_logger->txtDeath($this->_target->name, $this->_flawless ? $this->name : null);
+ }
+ }
+
+
+ public function executeRound($round)
+ {
+ if($this->_logger)
+ $this->_logger->txtRoundHeader($round);
+ $this->regen();
+ $this->pickTarget();
+ if($this->_target)
+ $this->executeAttacks();
+ $this->poison();
+ if($this->_logger)
+ $this->_logger->txtRoundFooter();
+ }
+
+
+ protected function pickTarget()
+ {
+ //clear old target
+ $this->_target = null;
+ //try current threat
+ while($this->_threat && !$this->_target)
+ {
+ $targetId = array_rand($this->_threat);
+ $this->_target = $this->_threat[$targetId];
+ if($this->_target->health <= 0) //killed by someone else
+ {
+ $this->_target = null;
+ unset($this->_threat[$targetId]);
+ }
+ }
+ //no threat, try other enemies
+ if($this->_enemies && !$this->_target)
+ {
+ $targetId = array_rand($this->_enemies);
+ $this->_target = $this->_enemies[$targetId];
+ }
+ //print message
+ if($this->_logger)
+ {
+ if($this->_target)
+ $this->_logger->txtTargetHeader($this->name, $this->_target->name);
+ else $this->_logger->txtTargetHeader($this->name, null);
+ }
+ }
+
+
+ protected function poison()
+ {
+ if($this->_poison)
+ {
+ $dmg = $this->_poison * $this->_poison / ($this->_poison + $this->pres);
+ $this->health -= $dmg;
+ if($this->_logger)
+ $this->_logger->txtPoison($this->name, $dmg);
+ if($this->health <= 0)
+ {
+ unset($this->_allies[$this->_id]);
+ if($this->_logger)
+ $this->_logger->txtDeath($this->name, false);
+ }
+ }
+ }
+
+
+ protected function regen()
+ {
+ if($this->regen && $this->health < $this->health_max)
+ {
+ $delta = min($this->health_max - $this->health, $this->regen);
+ $this->health += $delta;
+ if($this->_logger)
+ $this->_logger->txtRegen($this->name, $delta);
+ }
+ }
+}
diff --git a/2013/daemon/lib/daemon/config.php b/2013/daemon/lib/daemon/config.php
new file mode 100644
index 0000000..0243963
--- /dev/null
+++ b/2013/daemon/lib/daemon/config.php
@@ -0,0 +1,56 @@
+applicationRoot = (string) $applicationRoot;
+ $hostname = mb_strtolower(getenv('SERVER_NAME'));
+ if(!$hostname)
+ $hostname = '_cron';
+ $fileName = $hostname . '.php';
+ $this->loadFile($this->getFilePath('cfg', $fileName));
+ }
+
+
+ //loads config from file
+ public function loadFile($path)
+ {
+ if(is_readable($path))
+ {
+ $data = (array) include $path;
+ foreach($data as $key=>$value)
+ $this->$key = $value;
+ }
+ }
+
+
+ //implodes parameters into relative path and prepends it with root
+ public function getFilePath(/*...*/)
+ {
+ $aPath = array_filter(func_get_args());
+ array_unshift($aPath, $this->applicationRoot);
+ return implode(DIRECTORY_SEPARATOR, $aPath);
+ }
+
+
+ //generates URL from relative path
+ public function getUrl($path)
+ {
+ return $path ? "$this->applicationUrl$path" : $this->applicationUrl;
+ }
+}
diff --git a/2013/daemon/lib/daemon/controller.php b/2013/daemon/lib/daemon/controller.php
new file mode 100644
index 0000000..2a95ec6
--- /dev/null
+++ b/2013/daemon/lib/daemon/controller.php
@@ -0,0 +1,158 @@
+cfg = $cfg;
+ session_name($this->cfg->sessionName);
+ session_cache_limiter(null);
+ session_start();
+ $this->dbClient = Daemon::createDbClient($this->cfg);
+ $this->dbCfg = new Daemon_DbConfig($this->dbClient);
+ if(!$this->disablePlayer)
+ {
+ $this->player = new Daemon_DbObject_Player($this->dbClient);
+ $this->activeCharacter = $this->player->getActiveCharacter();
+ $this->activeCharacter->updateLastAction();
+ $this->characterData = $this->activeCharacter->getCharacterData();
+ $this->location = $this->characterData->getLocation();
+ }
+ $this->view = new Daemon_View($this->cfg);
+ }
+
+
+ //checks last action's timestamp
+ final private function checkActionTimestamp()
+ {
+ $lastAction = isset($_SESSION['ts']) ? $_SESSION['ts'] : 0.0;
+ $_SESSION['ts'] = microtime(true);
+ return (bool) ($_SESSION['ts'] >= $lastAction + $this->cfg->tsDelta);
+ }
+
+
+ final public function execute()
+ {
+ //prepare controller
+ $this->prepareModel();
+ $this->validatePlayer();
+ //check last action's timestamp
+ if($_POST && !$this->checkActionTimestamp())
+ {
+ Daemon_MsgQueue::add('Operacja anulowana: za duża częstość.');
+ $_POST = array();
+ }
+ //execute commands
+ $cmdExecuted = (bool) $this->runCommands();
+ //display page
+ $this->prepareView();
+ if($this->pageSubtitleUseQuery)
+ {
+ if($qs = getenv('QUERY_STRING'))
+ $this->pageSubtitleDetails = urldecode($qs);
+ }
+ $this->view->setPageTitle($this->pageSubtitle, $this->pageSubtitleDetails, $cmdExecuted);
+ if(!$this->disablePlayer)
+ {
+ $this->view->setGameHeader($this->player->getPlayerId(),
+ $this->activeCharacter, $this->characterData, $this->location);
+ $this->view->setPageSkin($this->player->skin);
+ }
+ else $this->view->setPageSkin(null);
+ if(!$this->disableMessages)
+ $messages = Daemon_MsgQueue::getAll();
+ else $messages = array();
+ if($this->dbCfg->globalMessage)
+ $messages[] = $this->dbCfg->globalMessage;
+ $this->view->setMessages($messages);
+ $this->view->display($this->pageTemplatePath, $this->pageOutputMode);
+ }
+
+
+ //page-specific
+ protected function prepareModel()
+ {
+ }
+
+
+ //page-specific
+ protected function prepareView()
+ {
+ }
+
+
+ //page-specific
+ protected function runCommands()
+ {
+ return false;
+ }
+
+
+ final private function validatePlayer()
+ {
+ if($this->disablePlayer)
+ return;
+ if($this->requireAuthentication && !$this->player->getPlayerId())
+ {
+ Daemon_MsgQueue::add('Strona dostępna tylko dla zalogowanych użytkowników.');
+ Daemon::redirect($this->cfg->getUrl(null));
+ exit;
+ }
+ if($this->requireActiveChar && !$this->player->getCharacterId())
+ {
+ Daemon_MsgQueue::add('Musisz najpierw wybrać aktywną postać.');
+ Daemon::redirect($this->cfg->getUrl('account'));
+ exit;
+ }
+ if($this->requiredRole && !$this->player->hasRole($this->requiredRole))
+ {
+ Daemon_MsgQueue::add('Nie masz uprawnień do korzystania z tej funkcji.');
+ Daemon::redirect($this->cfg->getUrl('account'));
+ exit;
+ }
+ if($this->requireLocation && !$this->location->location_id)
+ {
+ Daemon::redirect($this->cfg->getUrl('respawn'));
+ exit;
+ }
+ if($this->requireNoEvents && $this->characterData->getLocationEvent())
+ {
+ Daemon::redirect($this->cfg->getUrl('map'));
+ exit;
+ }
+ if($this->requireFactionMatch && $this->location->faction_id && $this->characterData->faction_id
+ && ($this->location->faction_id != $this->characterData->faction_id))
+ {
+ Daemon_MsgQueue::add('Odejdź! Nie przyjmujemy takich jak ty!');
+ Daemon::redirect($this->cfg->getUrl('map'));
+ exit;
+ }
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbclient.php b/2013/daemon/lib/daemon/dbclient.php
new file mode 100644
index 0000000..70d49b5
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbclient.php
@@ -0,0 +1,148 @@
+dbh = $dbHandle;
+ }
+
+
+ //reads maximum length of columns of selected table
+ public function getColumnMaxLength($table, $column)
+ {
+ $sql = 'SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS
+ WHERE TABLE_SCHEMA = SCHEMA() AND TABLE_NAME = :table AND COLUMN_NAME = :column';
+ $params = array('table' => $table, 'column' => $column);
+ return $this->selectColumn($sql, $params);
+ }
+
+
+ //returns internal PDO handle
+ public function getDbHandle()
+ {
+ return $this->dbh;
+ }
+
+
+ //internal exception handler
+ private function exceptionHandler(PDOException $e, $duplicateMsg = null)
+ {
+ //prepare params
+ $sqlstate = $e->getCode();
+ $dbMessage = $e->getMessage();
+ //check error type
+ if('23000' == $sqlstate && false !== stripos($dbMessage, 'duplicate'))
+ {
+ $message = $duplicateMsg ? $duplicateMsg : 'Wybrany obiekt już istnieje.';
+ Daemon_MsgQueue::add($message);
+ }
+ else throw $e;
+ }
+
+
+ //executes a query, returns the statement resource
+ public function execute($sql, array $params = array(), $duplicateMsg = null)
+ {
+ try
+ {
+ $sth = $this->dbh->prepare($sql);
+ foreach((array)$params as $name => $value)
+ $sth->bindValue(':'.$name, $value, self::paramType($value));
+ $sth->execute();
+ return $sth;
+ }
+ catch(PDOException $e)
+ {
+ $this->exceptionHandler($e, $duplicateMsg);
+ return null;
+ }
+ }
+
+
+ //returns ID of last inserted row
+ public function lastInsertId()
+ {
+ return $this->dbh->lastInsertId();
+ }
+
+
+ //returns appriopriate PDO::PARAM_X constant
+ public static function paramType($value)
+ {
+ if(is_null($value))
+ return PDO::PARAM_NULL;
+ elseif(is_int($value))
+ return PDO::PARAM_INT;
+ else return PDO::PARAM_STR;
+ }
+
+
+ //executes a generic non-select query
+ public function query($sql, array $params = array(), $duplicateMsg = null)
+ {
+ $sth = $this->execute($sql, $params, $duplicateMsg);
+ return !is_null($sth);
+ }
+
+
+ //quotes value for safe use in query
+ public function quote($value)
+ {
+ return $this->dbh->quote($value, self::paramType($value));
+ }
+
+
+ //select multiple rows from table
+ public function selectAll($sql, array $params = array())
+ {
+ $sth = $this->execute($sql, $params);
+ if(is_null($sth))
+ return null;
+ return $sth->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+
+ //select single column from table
+ public function selectColumn($sql, array $params = array())
+ {
+ $sth = $this->execute($sql, $params);
+ if(is_null($sth))
+ return null;
+ return $sth->fetchAll(PDO::FETCH_COLUMN, 0);
+ }
+
+
+ //select single row from table (as array)
+ public function selectRow($sql, array $params = array())
+ {
+ $sth = $this->execute($sql, $params);
+ if(is_null($sth))
+ return null;
+ return $sth->fetch(PDO::FETCH_ASSOC);
+ }
+
+
+ //select single row from table (as object)
+ public function selectObject($sql, array $params = array(), $className = 'stdClass')
+ {
+ $sth = $this->execute($sql, $params);
+ if(is_null($sth))
+ return null;
+ return $sth->fetchObject($className);
+ }
+
+
+ //select single value from table
+ public function selectValue($sql, array $params = array())
+ {
+ $sth = $this->execute($sql, $params);
+ if(is_null($sth))
+ return null;
+ return $sth->fetchColumn(0);
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbconfig.php b/2013/daemon/lib/daemon/dbconfig.php
new file mode 100644
index 0000000..aaf4e63
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbconfig.php
@@ -0,0 +1,87 @@
+dbClient = $dbClient;
+ }
+
+
+ public function __get($name)
+ {
+ return $this->get($name);
+ }
+
+
+ public function __set($name, $value)
+ {
+ return $this->set($name, $value);
+ }
+
+
+ public function get($name)
+ {
+ if(!isset($this->data[$name]))
+ {
+ $sql = "SELECT value FROM parameters WHERE name=:name";
+ $params = array('name' => $name);
+ $this->data[$name] = $this->dbClient->selectValue($sql, $params);
+ }
+ return $this->data[$name];
+ }
+
+
+ public function set($name, $value)
+ {
+ if (empty($value))
+ $value = '';
+ $sql = "INSERT INTO parameters(name, value) VALUES (:name, :value) ON DUPLICATE KEY UPDATE value=:value";
+ $params = array('name' => $name, 'value' => $value);
+ $this->dbClient->query($sql, $params);
+ $this->data[$name] = $value;
+ }
+
+
+ public function getGeneratorWeights($type)
+ {
+ $keys = array(
+ 'pstr_p', 'pstr_c', 'patk_p', 'patk_c', 'pdef_p', 'pdef_c', 'pres_p', 'pres_c',
+ 'mstr_p', 'mstr_c', 'matk_p', 'matk_c', 'mdef_p', 'mdef_c', 'mres_p', 'mres_c',
+ 'armor', 'speed', 'regen', 'special_param');
+ return $this->getGeneratorOptions("w_$type", $keys);
+ }
+
+
+ private function getGeneratorOptions($key, array $keys)
+ {
+ $result = json_decode($this->get("generator_$key"), true);
+ if (!is_array($result))
+ $result = array();
+ foreach ($keys as $key)
+ {
+ if (empty($result[$key]))
+ $result[$key] = 0;
+ }
+ return $result;
+ }
+
+
+ public function setGeneratorWeights($type, array $options)
+ {
+ $this->setGeneratorOptions("w_$type", $options);
+ }
+
+
+ private function setGeneratorOptions($key, array $options)
+ {
+ foreach ($options as &$val)
+ $val = floatval(str_replace(',', '.', $val));
+ $this->set("generator_$key", json_encode($options));
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject.php b/2013/daemon/lib/daemon/dbobject.php
new file mode 100644
index 0000000..42d34c3
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject.php
@@ -0,0 +1,108 @@
+import($params);
+ }
+
+
+ public function attachDbClient(Daemon_DbClient $dbClient = null)
+ {
+ $this->_dbClient = $dbClient;
+ }
+
+
+ //deletes object data from database
+ public function delete()
+ {
+ $cond = array();
+ $params = array();
+ foreach($this->_index as $col)
+ {
+ $cond[] = "$col=:$col";
+ $params[$col] = $this->$col;
+ }
+ $cond = implode(' AND ', $cond);
+ if(!$cond)
+ throw new RuntimeException('Index not specified.');
+ $sql = "DELETE FROM $this->_tableName WHERE $cond";
+ $this->_dbClient->query($sql, $params);
+ }
+
+
+ //retrieves object data from database
+ public function get(array $params, $ignoreDuplicates = false)
+ {
+ if(!$params)
+ throw new RuntimeException('Params not specified.');
+ $cond = array();
+ foreach(array_keys($params) as $key)
+ $cond[] = "$key=:$key";
+ $cond = implode(' AND ', $cond);
+ $sql = "SELECT * FROM $this->_tableName WHERE $cond ORDER BY RAND() LIMIT 2";
+ $data = $this->_dbClient->selectAll($sql, $params);
+ if(is_array($data) && isset($data[0]))
+ {
+ if(!$ignoreDuplicates && (count($data) > 1))
+ throw new RuntimeException('Multiple rows found.');
+ foreach($data[0] as $key => $val)
+ $this->$key = $val;
+ }
+ return true;
+ }
+
+
+ //copies params into object data
+ public function import($params)
+ {
+ $keys = array_keys(get_object_vars($this));
+ foreach($keys as $key)
+ {
+ if(isset($params[$key]) && ($key[0] != '_'))
+ $this->$key = $params[$key];
+ }
+ $this->validate();
+ }
+
+
+ //stores object data in the database
+ public function put()
+ {
+ $this->validate();
+ $cols = array();
+ $vals = array();
+ $mods = array();
+ $params = array();
+ foreach($this as $col => $val)
+ {
+ if($col[0] != '_')
+ {
+ $cols[] = $col;
+ $vals[] = ":$col";
+ if(!in_array($col, $this->_index))
+ $mods[] = "$col=:$col";
+ $params[$col] = $val;
+ }
+ }
+ $cols = implode(', ', $cols);
+ $vals = implode(', ', $vals);
+ $mods = implode(', ', $mods);
+ if($mods)
+ $sql = "INSERT INTO $this->_tableName ($cols) VALUES ($vals) ON DUPLICATE KEY UPDATE $mods";
+ else $sql = "REPLACE INTO $this->_tableName ($cols) VALUES ($vals)";
+ $this->_dbClient->query($sql, $params);
+ }
+
+
+ //checks object data
+ public function validate() {}
+}
diff --git a/2013/daemon/lib/daemon/dbobject/character.php b/2013/daemon/lib/daemon/dbobject/character.php
new file mode 100644
index 0000000..ffe541b
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/character.php
@@ -0,0 +1,165 @@
+_player = $player;
+ }
+
+
+ //checks character's inbox for new messages
+ public function checkMail()
+ {
+ if(!$this->character_id)
+ return false;
+ $sql = "SELECT COUNT(1) FROM mail WHERE message_id > COALESCE(:lastMailId, 0) AND recipient_id = :charId";
+ $params = array('lastMailId' => $this->last_mail_id, 'charId' => $this->character_id);
+ return $this->_dbClient->selectValue($sql, $params);
+ }
+
+
+ //try to create a new clan (id or name may be already taken)
+ public function createClan($clanId, $name)
+ {
+ $clanId = Daemon::normalizeString($clanId, false);
+ $name = Daemon::normalizeString($name, false);
+ $validId = $this->validateClanId($clanId);
+ $validName = $this->validateClanId($name);
+ if($validId && $validName)
+ {
+ $sql = "INSERT INTO clans(clan_id, name, leader_id) VALUES (:id, :name, :leaderId)";
+ $params = array('id' => $clanId, 'name' => $name, 'leaderId' => $this->character_id);
+ if($this->_dbClient->query($sql, $params, 'Wybrany ID lub nazwa klanu są już zajęte.'))
+ {
+ $this->clan_id = $clanId;
+ $this->put();
+ }
+ }
+ }
+
+
+ //returns DbObject_CharacterData instance
+ public function getCharacterData()
+ {
+ if(!$this->_characterData)
+ {
+ $this->_characterData = new Daemon_DbObject_CharacterData();
+ $this->_characterData->attachDbClient($this->_dbClient);
+ if($this->character_id)
+ $this->_characterData->get(array('character_id' => $this->character_id));
+ $this->_characterData->_characterName = $this->name;
+ $this->_characterData->_gender = $this->gender;
+ }
+ return $this->_characterData;
+ }
+
+
+ //returns a channel=>writeAccess array of channels allowed for character
+ public function getForumChannels()
+ {
+ $cdata = $this->getCharacterData();
+ $channels = array('public' => array('name' => 'publiczne', 'writable' => false));
+ if($this->character_id)
+ {
+ $channels['public']['writable'] = $this->_player->hasRole('chat');
+ }
+ if($cdata->faction_id)
+ {
+ $channelId = 'f/'.$cdata->faction_id;
+ $channels[$channelId] = array('name' => 'frakcyjne', 'writable' => $this->_player->hasRole('chat'));
+ }
+ if($this->clan_id)
+ {
+ $channelId = 'c/'.$this->clan_id;
+ $channels[$channelId] = array('name' => 'klanowe', 'writable' => $this->_player->hasRole('chat'));
+ }
+ return $channels;
+ }
+
+
+ //reads a list of invitations
+ public function getInvitations()
+ {
+ $sql = "SELECT i.clan_id, c.name AS clan_name, i.description
+ FROM clan_invitations i JOIN clans c USING(clan_id)
+ WHERE i.character_id=:id";
+ $params = array('id' => $this->character_id);
+ return $this->_dbClient->selectAll($sql, $params);
+ }
+
+
+ //sends invitation to specified clan
+ public function inviteClan($clanId, $description, Daemon_Forum $forum)
+ {
+ if(!$description)
+ $description = null;
+ $sql = "SELECT leader_id FROM clans WHERE clan_id=:id";
+ $leaderId = $this->_dbClient->selectValue($sql, array('id' => $clanId));
+ if($leaderId)
+ {
+ $sql = "INSERT IGNORE INTO clan_invitations(clan_id, character_id, description)
+ VALUES (:clanId, :charId, :description) ON DUPLICATE KEY UPDATE description=:description";
+ $params = array('clanId' => $clanId, 'charId' => $this->character_id,
+ 'description' => $description);
+ $this->_dbClient->query($sql, $params);
+ $msg = "Postać $this->name pragnie dołączyć do klanu.";
+ $forum->addMailById(null, $leaderId, $msg);
+ }
+ else Daemon_MsgQueue::add('Wybrany klan nie istnieje lub nie ma przywódcy.');
+ }
+
+
+ public function updateLastAction()
+ {
+ if($this->character_id)
+ {
+ $sql = "UPDATE characters SET last_action = NOW() WHERE character_id=:id";
+ $this->_dbClient->query($sql, array('id' => $this->character_id));
+ }
+ }
+
+
+ //checks clan id validity
+ private function validateClanId($input)
+ {
+ $maxLength = $this->_dbClient->getColumnMaxLength('clans', 'clan_id');
+ if(!$input)
+ Daemon_MsgQueue::add('Musisz podać ID klanu.');
+ elseif(iconv_strlen($input) > $maxLength)
+ Daemon_MsgQueue::add('Wybrany ID jest za długi.');
+ else return true;
+ return false;
+ }
+
+
+ //checks clan name validity
+ private function validateClanName($input)
+ {
+ $maxLength = $this->_dbClient->getColumnMaxLength('clans', 'name');
+ if(!$input)
+ Daemon_MsgQueue::add('Musisz podać nazwę klanu.');
+ elseif(iconv_strlen($input) > $maxLength)
+ Daemon_MsgQueue::add('Wybrana nazwa jest za długa.');
+ else return true;
+ return false;
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject/characterdata.php b/2013/daemon/lib/daemon/dbobject/characterdata.php
new file mode 100644
index 0000000..0471327
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/characterdata.php
@@ -0,0 +1,609 @@
+checkTurnCosts())
+ return null;
+ $combat = new Daemon_Duel();
+ $combat->attachCharacterData($this);
+ $combat->attachDbClient($this->_dbClient);
+ $combat->execute($view, $locationType, $targetId);
+ return $combat->getCombatLog();
+ }
+
+
+ //checks of character can attack selected target
+ public function canAttack(array $target, $rolloverId, $withMessages)
+ {
+ //check Id
+ if($this->character_id == $target['character_id'])
+ {
+ if($withMessages)
+ Daemon_MsgQueue::add('Samobójstwo?');
+ return false;
+ }
+ //check location
+ if($this->location_id != $target['location_id'])
+ {
+ if($withMessages)
+ Daemon_MsgQueue::add('Cel opuścił już tą lokację.');
+ return false;
+ }
+ //check levels
+ if(4 * $this->xp_used > 5 * $target['xp_used'])
+ {
+ if($withMessages)
+ Daemon_MsgQueue::add('Cel jest za słaby.');
+ return false;
+ }
+ if(5 * $this->xp_used < 4 * $target['xp_used'])
+ {
+ if($withMessages)
+ Daemon_MsgQueue::add('Cel jest za silny.');
+ return false;
+ }
+ //check previous attacks
+ $cond = "attacker_id=:attackerId AND defender_id=:defenderId";
+ $params = array('attackerId' => $this->character_id, 'defenderId' => $target['character_id']);
+ if($rolloverId)
+ {
+ $cond .= " AND rollover_id=:rolloverId";
+ $params['rolloverId'] = $rolloverId;
+ }
+ $sql = "SELECT COUNT(duel_id) FROM duels WHERE $cond";
+ if($this->_dbClient->selectValue($sql, $params))
+ {
+ if($withMessages)
+ Daemon_MsgQueue::add('Cel już był atakowany przez ciebie w tym przeliczeniu.');
+ return false;
+ }
+ return true;
+ }
+
+
+ //updates turns, gold & mana; returns check result
+ public function checkTurnCosts($costGold = 0, $costMana = 0)
+ {
+ if($this->turns < 1)
+ {
+ Daemon_MsgQueue::add('Nie masz dostępnych tur.');
+ return false;
+ }
+ if($this->gold_purse < $costGold)
+ {
+ Daemon_MsgQueue::add('Masz za mało zlota.');
+ return false;
+ }
+ if($this->mana < $costMana)
+ {
+ Daemon_MsgQueue::add('Masz za mało zlota.');
+ return false;
+ }
+ $this->turns -= 1;
+ $this->gold_purse -= $costGold;
+ $this->mana -= $costMana;
+ return true;
+ }
+
+
+ //checks of target can be attacked, returns combat type (null for error)
+ public function getCombatType(array $target, $locationType)
+ {
+ //check location type
+ if('normal' != $locationType)
+ return 'arena';
+ //check factions
+ if($this->faction_id && ($this->faction_id == $target['faction_id']))
+ return 'arena';
+ //no restrictions, normal fight
+ return 'normal';
+ }
+
+
+ //returns Daemon_DbObject_CombatUnit instance
+ public function getCombatUnit($full = false)
+ {
+ if(!$this->_combatUnit)
+ {
+ $this->_combatUnit = $full ? new Daemon_Combat_Unit() : new Daemon_DbObject_CombatUnit();
+ $this->_combatUnit->attachDbClient($this->_dbClient);
+ if($this->character_id)
+ {
+ if(!$this->combat_unit_id)
+ $this->combat_unit_id = "character-$this->character_id";
+ $this->_combatUnit->get(array('combat_unit_id' => $this->combat_unit_id));
+ }
+ $this->_combatUnit->combat_unit_id = $this->combat_unit_id;
+ $this->_combatUnit->name = $this->_characterName;
+ $this->_combatUnit->faction_id = $this->faction_id;
+ $this->_combatUnit->health = $this->health;
+ $this->_combatUnit->health_max = $this->health_max;
+ }
+ return $this->_combatUnit;
+ }
+
+
+ //calculates "real" level from level & health
+ public function getEffectiveLevel($addFaction)
+ {
+ $factionMult = 0.1; //magic number!
+ $level = $this->level;
+ if($addFaction)
+ $level *= 1 + $factionMult * $this->rank_id;
+ if($this->health_max)
+ $level = round($level * $this->health / $this->health_max);
+ return $level;
+ }
+
+
+ //checks mission history for last mission's data
+ public function getLastMission($maxProgress)
+ {
+ $sql = "SELECT m.*, s.name AS service_name
+ FROM character_missions m LEFT JOIN services s USING(service_id)
+ WHERE character_id=:id AND progress<=:maxProgress
+ ORDER BY rollover_id DESC LIMIT 1";
+ $params = array('id' => $this->character_id, 'maxProgress' => $maxProgress);
+ if($row = $this->_dbClient->selectRow($sql, $params))
+ {
+ $row['_target'] = $this->getMissionTarget($row['type'], $row['params']);
+ $row['_name'] = $this->getMissionName($row['type'], $row['_target']);
+ $row['_statusName'] = Daemon_Dictionary::$missionProgress[$row['progress']];
+ return $row;
+ }
+ else return null;
+ }
+
+
+ private function getMissionName($type, $name)
+ {
+ switch($type)
+ {
+ case'monster':
+ return "pokonaj potwora: $name";
+ case'item':
+ return "przynieś przedmiot: $name";
+ default:
+ return null;
+ }
+ }
+
+
+ private function getMissionTarget($type, $params)
+ {
+ switch($type)
+ {
+ case'monster':
+ $sql = "SELECT name FROM monsters WHERE monster_id=:id";
+ return $this->_dbClient->selectValue($sql, array('id' => $params));
+ case'item':
+ $sql = "SELECT name FROM items WHERE item_id=:id";
+ return $this->_dbClient->selectValue($sql, array('id' => $params));
+ default:
+ return null;
+ }
+ }
+
+
+ //returns a DbObject_Location instance representing current location
+ public function getLocation()
+ {
+ $object = new Daemon_DbObject_Location();
+ $object->attachDbClient($this->_dbClient);
+ $object->attachCharacterData($this);
+ if($this->character_id)
+ $object->get(array('location_id' => $this->location_id));
+ return $object;
+ }
+
+
+ public function getLocationEvent()
+ {
+ return json_decode($this->location_event, true);
+ }
+
+
+ //fetches a list of possible respawn locations
+ public function getRespawns($defaultRespawn)
+ {
+ $sql = "SELECT location_id, name FROM locations WHERE location_id = :defaultRespawn
+ OR location_id IN (
+ SELECT r.respawn_id FROM regions r
+ JOIN character_regions cr ON cr.region_id=r.region_id AND cr.character_id=:characterId
+ )
+ OR location_id IN (
+ SELECT l.location_id FROM character_regions cr
+ JOIN locations l ON cr.region_id=l.region_id AND cr.character_id=:characterId
+ WHERE l.type='caern' AND l.faction_id=:factionId
+ )";
+ $params = array('characterId' => $this->character_id, 'defaultRespawn' => $defaultRespawn,
+ 'factionId' => $this->faction_id);
+ if($data = $this->_dbClient->selectAll($sql, $params))
+ {
+ $result = array();
+ foreach($data as $row)
+ $result[$row['location_id']] = $row['name'];
+ return $result;
+ }
+ else return array();
+ }
+
+
+ public function getSpells()
+ {
+ $sql = "SELECT spell_id, name FROM spells ORDER BY name";
+ $data = $this->_dbClient->selectAll($sql);
+ foreach($data as &$row)
+ {
+ $col = "sp_$row[spell_id]";
+ $row['_cost'] = isset($this->$col) ? $this->$col : null;
+ $row['_cast'] = ($row['_cost'] <= $this->mana);
+ }
+ return $data;
+ }
+
+
+ //increases selected attribute if there is enough xp
+ public function improveAttribute($key)
+ {
+ $col = "a_$key";
+ if(isset($this->$col))
+ {
+ $cost = $this->$col;
+ if($cost <= $this->xp_free)
+ {
+ $this->$col += 1;
+ $this->xp_used += $cost;
+ $this->xp_free -= $cost;
+ $this->resetCombatStats();
+ $this->put();
+ }
+ else Daemon_MsgQueue::add('Nie masz dość doświadczenia.');
+ }
+ else Daemon_MsgQueue::add('Wybrana cecha nie istnieje.');
+ }
+
+
+ //increases faction reputation & rank
+ public function improveReputation($factionId, $delta)
+ {
+ //join faction if needed
+ if(!$this->faction_id)
+ {
+ $sql = "SELECT name FROM factions WHERE faction_id=:id";
+ $factionName = $this->_dbClient->selectValue($sql, array('id' => $factionId));
+ $this->faction_id = $factionId;
+ Daemon_MsgQueue::add("Dołączasz do frakcji: $factionName.");
+ }
+ //raise reputation
+ $this->faction_points += $delta;
+ //check for new rank
+ $sql = "SELECT r.rank_id, r.title_id, t.name_$this->_gender AS title_name
+ FROM faction_ranks r LEFT JOIN titles t USING(title_id)
+ WHERE faction_id=:id
+ AND rank_id = (SELECT MAX(rank_id) FROM faction_ranks WHERE faction_id=:id AND min_points <= :points)";
+ $params = array('id' => $factionId, 'points' => $this->faction_points);
+ $newRank = $this->_dbClient->selectRow($sql, $params);
+ if($newRank && ($newRank['rank_id'] > (int) $this->rank_id))
+ {
+ $this->rank_id = $newRank['rank_id'];
+ if($newRank['title_id'])
+ Daemon_MsgQueue::add("Zdobywasz nową rangę: $newRank[title_name] (poziom $newRank[rank_id]).");
+ else Daemon_MsgQueue::add("Zdobywasz nową rangę (poziom $newRank[rank_id]).");
+ }
+ else Daemon_MsgQueue::add("Rośnie twoja reputacja we frakcji.");
+ }
+
+
+ //increases selected skill if there is enough xp
+ public function improveSkill($key)
+ {
+ $col = "s_$key";
+ if(isset($this->$col))
+ {
+ $cost = $this->$col;
+ if($cost > 0)
+ {
+ if($cost <= $this->xp_free)
+ {
+ $this->$col += 1;
+ $this->xp_used += $cost;
+ $this->xp_free -= $cost;
+ $this->resetCombatStats();
+ $this->put();
+ }
+ else Daemon_MsgQueue::add('Nie masz dość doświadczenia.');
+ }
+ else Daemon_MsgQueue::add('Nie znasz jeszcze tej umiejętności.');
+ }
+ else Daemon_MsgQueue::add('Wybrana umiejętność nie istnieje.');
+ }
+
+
+ //updates combat stats with equipment bonuses
+ private function loadEquipmentBonuses()
+ {
+ //prepare variables
+ $unit = $this->getCombatUnit();
+ $inventory = new Daemon_Inventory($this->_dbClient, $this);
+ $zweihander = false;
+ $attackBonusKeys = array(
+ 'pstr_p', 'pstr_c', 'patk_p', 'patk_c',
+ 'mstr_p', 'mstr_c', 'matk_p', 'matk_c',
+ );
+ $modWpn1 = array_fill_keys($attackBonusKeys, 0);
+ $modWpn2 = array_fill_keys($attackBonusKeys, 0);
+ $armorBonusKeys = array(
+ 'pdef_p', 'pdef_c', 'pres_p', 'pres_c',
+ 'mdef_p', 'mdef_c', 'mres_p', 'mres_c',
+ 'armor', 'speed', 'regen',
+ );
+ $modArmor = array_fill_keys($armorBonusKeys, 0);
+ //reset hands & armor
+ $unit->type1 = 'p';
+ $unit->count1 = 1;
+ $unit->sp1_type = null;
+ $unit->sp1_param = null;
+ $unit->type2 = 'p';
+ $unit->count2 = 1;
+ $unit->sp2_type = null;
+ $unit->sp2_param = null;
+ $unit->armor_sp_type = null;
+ $unit->armor_sp_param = null;
+ //find equipped items, calculate equipment bonuses
+ $sql = "SELECT item_id, equipped FROM inventory WHERE character_id=:id AND equipped IS NOT NULL";
+ $params = array('id' => $this->character_id);
+ foreach($this->_dbClient->selectAll($sql, $params) AS $row)
+ {
+ $item = new Daemon_DbObject_Item();
+ $item->attachDbClient($this->_dbClient);
+ $item->get(array('item_id' => $row['item_id']));
+ switch($row['equipped'])
+ {
+ case'hand_a':
+ $zweihander = ('weapon2h' == $item->type);
+ $unit->count1 = 1;
+ $unit->type1 = $item->damage_type;
+ $unit->sp1_type = $item->special_type;
+ $unit->sp1_param = $item->special_param;
+ foreach($attackBonusKeys as $name)
+ $modWpn1[$name] += $item->$name;
+ break;
+ case'hand_b':
+ $unit->count2 = 1;
+ $unit->type2 = $item->damage_type;
+ $unit->sp2_type = $item->special_type;
+ $unit->sp2_param = $item->special_param;
+ foreach($attackBonusKeys as $name)
+ $modWpn2[$name] += $item->$name;
+ break;
+ case'armor':
+ $unit->armor_sp_type = $item->special_type;
+ $unit->armor_sp_param = $item->special_param;
+ //nobreak
+ default:
+ foreach($attackBonusKeys as $name)
+ {
+ $modWpn1[$name] += $item->$name;
+ $modWpn2[$name] += $item->$name;
+ }
+ break;
+ }
+ foreach($armorBonusKeys as $name)
+ $modArmor[$name] += $item->$name;
+ }
+ $unit->count1 = 1;
+ $unit->count2 = $zweihander ? 0 : 1;
+ //first hand
+ $pstrSkill = $this->s_pstr;
+ $mstrSkill = $this->s_mstr;
+ if($zweihander)
+ {
+ $pstrSkill *= 1.7;
+ $mstrSkill *= 1.7;
+ }
+ if('m' != $unit->type1)
+ {
+ $unit->atk1 = Daemon_Math::combatStat($this->a_dex, $modWpn1['patk_p'], $modWpn1['patk_c'], $this->s_patk);
+ $unit->str1 = Daemon_Math::combatStat($this->a_str, $modWpn1['pstr_p'], $modWpn1['pstr_c'], $pstrSkill);
+ }
+ else
+ {
+ $unit->atk1 = Daemon_Math::combatStat($this->a_pwr, $modWpn1['matk_p'], $modWpn1['matk_c'], $this->s_matk);
+ $unit->str1 = Daemon_Math::combatStat($this->a_pwr, $modWpn1['mstr_p'], $modWpn1['mstr_c'], $mstrSkill);
+ }
+ //second hand
+ if('m' != $unit->type2)
+ {
+ $unit->atk2 = Daemon_Math::combatStat($this->a_dex, $modWpn2['patk_p'], $modWpn2['patk_c'], $this->s_patk);
+ $unit->str2 = Daemon_Math::combatStat($this->a_str, $modWpn2['pstr_p'], $modWpn2['pstr_c'], $this->s_pstr);
+ }
+ else
+ {
+ $unit->atk2 = Daemon_Math::combatStat($this->a_pwr, $modWpn2['matk_p'], $modWpn2['matk_c'], $this->s_matk);
+ $unit->str2 = Daemon_Math::combatStat($this->a_pwr, $modWpn2['mstr_p'], $modWpn2['mstr_c'], $this->s_mstr);
+ }
+ //physical defense
+ $unit->pdef = Daemon_Math::combatStat($this->a_dex, $modArmor['pdef_p'], $modArmor['pdef_c'], $this->s_pdef);
+ $unit->pres = Daemon_Math::combatStat($this->a_vit, $modArmor['pres_p'], $modArmor['pres_c'], $this->s_pres);
+ //magical defense
+ $unit->mdef = Daemon_Math::combatStat($this->a_wil, $modArmor['mdef_p'], $modArmor['mdef_c'], $this->s_mdef);
+ $unit->mres = Daemon_Math::combatStat($this->a_wil, $modArmor['mres_p'], $modArmor['mres_c'], $this->s_mres);
+ //armor & speed
+ $unit->speed = Daemon_Math::combatStat($this->a_dex, $modArmor['speed'], 0, 0);
+ $unit->armor = $modArmor['armor'];
+ //regen
+ $unit->regen = Daemon_Math::combatRegen($this->a_vit, $modArmor['regen'] + $this->s_preg);
+ $unit->put();
+ }
+
+
+ //try to subtract the cost from character's gold, cancel the operation if the cost is too big
+ public function payGold($cost, $bankEnabled)
+ {
+ $charGold = $this->gold_purse;
+ if($bankEnabled)
+ $charGold += $this->gold_bank;
+ if($charGold < $cost)
+ {
+ Daemon_MsgQueue::add("Wymagane $cost zł - nie masz tyle złota.");
+ return false;
+ }
+ $deltaPurse = min($cost, $this->gold_purse);
+ $this->gold_purse -= $deltaPurse;
+ $this->gold_bank -= $cost - $deltaPurse;
+ $this->put();
+ return true;
+ }
+
+
+ //regenerates one-turn-worth of health & mana
+ public function regen($resting)
+ {
+ $this->mana += $this->mana_regen;
+ if($resting)
+ {
+ $this->health += $this->a_vit;
+ $this->mana += ceil($this->a_pwr / 4);
+ }
+ if($this->health > $this->health_max)
+ $this->health = $this->health_max;
+ if($this->mana > $this->mana_max)
+ $this->mana = $this->mana_max;
+ }
+
+
+ //calculates combat stats based on attributes, skills and equipment
+ public function resetCombatStats()
+ {
+ //health
+ $old = $this->health_max;
+ $this->health_max = 3*$this->a_str + 7*$this->a_vit;
+ $this->health += $this->health_max - $old;
+ //mana
+ $old = $this->mana_max;
+ $this->mana_max = 3*$this->a_pwr + 2*$this->a_wil;
+ $this->mana += $this->mana_max - $old;
+ $this->mana_regen = Daemon_Math::manaRegen($this->a_wil, $this->s_mreg);
+ //used xp
+ $this->xp_used = 0;
+ foreach(array_keys(Daemon_Dictionary::$characterAttributes) as $key)
+ {
+ $col = "a_$key";
+ $this->xp_used += $this->$col * ($this->$col + 1) / 2;
+ }
+ foreach(array_keys(Daemon_Dictionary::$characterSkills) as $key)
+ {
+ $col = "s_$key";
+ $this->xp_used += $this->$col * ($this->$col + 1) / 2;
+ }
+ //update combat stats
+ $this->loadEquipmentBonuses();
+ }
+
+
+ //respawns in selected location
+ public function respawn($locationId, $defaultRespawn)
+ {
+ $respawns = $this->getRespawns($defaultRespawn);
+ if(!isset($respawns[$locationId]))
+ {
+ Daemon_MsgQueue::add('Wybrana lokacja nie jest dostępna.');
+ return false;
+ }
+ $this->location_id = $locationId;
+ $this->health = $this->health_max;
+ $this->mana = 4 * $this->mana_regen;
+ $this->resetCombatStats();
+ $this->put();
+ return true;
+ }
+
+
+ //executes selected event, returns event log
+ public function runEvent(Daemon_View $view)
+ {
+ $event = $this->getLocationEvent();
+ //monster attack
+ if(isset($event['monsterId']))
+ {
+ $combat = new Daemon_MonsterCombat();
+ $combat->attachCharacterData($this);
+ $combat->attachDbClient($this->_dbClient);
+ $combat->execute($view, $event['monsterId']);
+ //return log
+ return $combat->getCombatLog();
+ }
+ //special event
+ if(isset($event['eventId'], $event['params']))
+ {
+ $handler = new Daemon_Event();
+ $handler->attachCharacterData($this);
+ $handler->attachDbClient($this->_dbClient);
+ $handler->execute($view, $event['eventId'], $event['params']);
+ return $handler->getEventLog();
+ }
+ //no event
+ return null;
+ }
+
+
+ //updates character's health, location etc to represent death
+ public function setDeath($clearEquipment)
+ {
+ $this->deaths += 1;
+ $this->health = 0;
+ $this->mana = 0;
+ $this->xp_free = 0;
+ $this->location_id = null;
+ if($clearEquipment)
+ {
+ $this->gold_purse = 0;
+ $sql = "DELETE FROM inventory WHERE character_id=:id
+ AND status='inventory' AND NOT FIND_IN_SET('bound', flags)";
+ $params = array('id' => $this->character_id);
+ $this->_dbClient->query($sql, $params);
+ }
+ $this->resetCombatStats();
+ }
+
+
+ public function setLocationEvent(array $data)
+ {
+ $event = array();
+ //monster attack
+ if(isset($data['monsterId']))
+ $event['monsterId'] = $data['monsterId'];
+ //special event
+ if(isset($data['eventId'], $data['params']))
+ {
+ $event['eventId'] = $data['eventId'];
+ $event['params'] = $data['params'];
+ }
+ $this->location_event = json_encode($event);
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject/clan.php b/2013/daemon/lib/daemon/dbobject/clan.php
new file mode 100644
index 0000000..c0dab12
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/clan.php
@@ -0,0 +1,91 @@
+ $this->clan_id, 'charId' => $characterId);
+ if($this->_dbClient->selectValue($sql, $params))
+ {
+ $sql = "UPDATE characters SET clan_id=:clanId WHERE character_id=:charId";
+ $params = array('clanId' => $this->clan_id, 'charId' => $characterId);
+ $this->_dbClient->query($sql, $params);
+ $sql = "DELETE FROM clan_invitations WHERE character_id=:id";
+ $this->_dbClient->query($sql, array('id' => $characterId));
+ $msg = "Podanie do klanu $this->name zostało zaakceptowane.";
+ $forum->addMailById(null, $characterId, $msg);
+ }
+ else Daemon_MsgQueue::add('Wybrane zaproszenie nie istnieje.');
+ }
+
+
+ //deletes clan and updates its members
+ public function delete()
+ {
+ //TODO
+ $params = array('id' => $this->clan_id);
+ $sql = "DELETE FROM clans WHERE clan_id=:id";
+ $this->_dbClient->query($sql, $params);
+ $sql = "DELETE FROM clan_invitations WHERE clan_id=:id";
+ $this->_dbClient->query($sql, $params);
+ $sql = "UPDATE characters SET clan_id=NULL WHERE clan_id=:id";
+ $this->_dbClient->query($sql, $params);
+ }
+
+
+ //reads a list of invitations
+ public function getInvitations()
+ {
+ $sql = "SELECT i.*, c.name AS character_name, cd.level, cd.xp_used,
+ cd.faction_id, COALESCE(cd.rank_id, 0) AS rank_id,
+ date_format(c.date_created, '%Y-%m-%d') AS date_created
+ FROM clan_invitations i JOIN characters c USING(character_id) JOIN character_data cd USING(character_id)
+ WHERE i.clan_id=:id";
+ return $this->_dbClient->selectAll($sql, array('id' => $this->clan_id));
+ }
+
+
+ //fetches leader's name
+ public function getLeaderName()
+ {
+ $sql = "SELECT name FROM characters WHERE character_id=:id";
+ return $this->_dbClient->selectValue($sql, array('id' => $this->leader_id));
+ }
+
+
+ //reads a list of members
+ public function getMembers()
+ {
+ $sql = "SELECT cd.character_id, c.name, cd.level, cd.xp_used,
+ cd.faction_id, COALESCE(cd.rank_id, 0) AS rank_id,
+ date_format(c.date_created, '%Y-%m-%d') AS date_created
+ FROM characters c JOIN character_data cd USING(character_id) WHERE c.clan_id=:id";
+ $result = $this->_dbClient->selectAll($sql, array('id' => $this->clan_id));
+ foreach($result as &$row)
+ $row['_isLeader'] = ($row['character_id'] == $this->leader_id);
+ return $result;
+ }
+
+
+ //removes member from clan
+ public function kickMember($characterId, Daemon_Forum $forum)
+ {
+ if($characterId != $this->leader_id)
+ {
+ $sql = "UPDATE characters SET clan_id = NULL WHERE character_id=:id";
+ $this->_dbClient->query($sql, array('id' => $characterId));
+ $forum->addMailById(null, $characterId, 'Wyrzucono cię z klanu.');
+ }
+ else Daemon_MsgQueue::add('Przywódca nie może odejść, jedynie rozwiązać klan.');
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject/combatunit.php b/2013/daemon/lib/daemon/dbobject/combatunit.php
new file mode 100644
index 0000000..4497dde
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/combatunit.php
@@ -0,0 +1,63 @@
+type1]))
+ $this->type1 = null;
+ if (!isset($attackTypes[$this->type2]))
+ $this->type2 = null;
+ if (!isset($attackSpecials[$this->sp1_type]))
+ $this->sp1_type = null;
+ if (!isset($attackSpecials[$this->sp2_type]))
+ $this->sp2_type = null;
+ if (!isset($armorSpecials[$this->armor_sp_type]))
+ $this->armor_sp_type = null;
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject/event.php b/2013/daemon/lib/daemon/dbobject/event.php
new file mode 100644
index 0000000..9465441
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/event.php
@@ -0,0 +1,11 @@
+type]))
+ {
+ if ($this->damage_type && isset($itemDamageTypes[$this->damage_type]))
+ {
+ $typeName = sprintf('broń %s (obrażenia %s)',
+ $itemWeaponTypes[$this->type], $itemDamageTypes[$this->damage_type]);
+ }
+ else
+ $typeName = 'tarcza';
+ if(isset($combatAttackSpecials[$this->special_type]))
+ $specialName = sprintf('%s (%s)', $combatAttackSpecials[$this->special_type], $this->special_param);
+ }
+ elseif (!empty($itemArmorTypes[$this->type]))
+ {
+ $typeName = $itemArmorTypes[$this->type];
+ if(isset($combatArmorSpecials[$this->special_type]))
+ $specialName = sprintf('%s (%s)', $combatArmorSpecials[$this->special_type], $this->special_param);
+ }
+ else //usables
+ {
+ $typeName = 'niezakładalny';
+ }
+ //stats
+ $stats = array();
+ if($specialName)
+ $stats[] = $specialName;
+ if('item' != $this->type)
+ {
+ if($this->pstr_p || $this->pstr_c)
+ $stats[] = $this->getStatDescription('pstr', $this->pstr_p, $this->pstr_c);
+ if($this->patk_p || $this->patk_c)
+ $stats[] = $this->getStatDescription('patk', $this->patk_p, $this->patk_c);
+ if($this->pdef_p || $this->pdef_c)
+ $stats[] = $this->getStatDescription('pdef', $this->pdef_p, $this->pdef_c);
+ if($this->pres_p || $this->pres_c)
+ $stats[] = $this->getStatDescription('pres', $this->pres_p, $this->pres_c);
+ if($this->mstr_p || $this->mstr_c)
+ $stats[] = $this->getStatDescription('mstr', $this->mstr_p, $this->mstr_c);
+ if($this->matk_p || $this->matk_c)
+ $stats[] = $this->getStatDescription('matk', $this->matk_p, $this->matk_c);
+ if($this->mdef_p || $this->mdef_c)
+ $stats[] = $this->getStatDescription('mdef', $this->mdef_p, $this->mdef_c);
+ if($this->mres_p || $this->mres_c)
+ $stats[] = $this->getStatDescription('mres', $this->mres_p, $this->mres_c);
+ if($this->armor)
+ $stats[] = sprintf('pancerz%+d', $this->armor);
+ if($this->speed)
+ $stats[] = sprintf('szybkość%+d%%', $this->speed);
+ if($this->regen)
+ $stats[] = sprintf('regen%+d', $this->regen);
+ }
+ //final concatenation
+ if($stats)
+ $desc = sprintf('%s; %s', $typeName, implode(', ', $stats));
+ else $desc = $typeName;
+ return $desc;
+ }
+
+
+ //returns a list of matching equipment slots
+ public function getSlots()
+ {
+ if ($this->type == 'weapon1h')
+ return array('hand_a', 'hand_b');
+ elseif ($this->type == 'weapon2h')
+ return array('hand_a');
+ elseif ($this->type == 'accesory')
+ return array('accesory_a', 'accesory_b');
+ elseif ($this->type != 'item')
+ return array($this->type);
+ else return array();
+ }
+
+
+ private function getStatDescription($name, $value_p = 0, $value_c = 0)
+ {
+ $result = $name;
+ if($value_p)
+ $result .= sprintf('%+d%%', $value_p);
+ if($value_c)
+ $result .= sprintf('%+d', $value_c);
+ return $result;
+ }
+
+
+ public function updateSuggestedValue(Daemon_DbConfig $dbCfg)
+ {
+ if ($this->type == 'item')
+ return;
+ $this->suggested_value = 0.0;
+ $baseValue = $dbCfg->generatorBaseValue;
+ $weights = $dbCfg->getGeneratorWeights($this->type);
+ foreach ($weights as $key => $val)
+ {
+ if (isset($this->$key))
+ $this->suggested_value += $this->$key * $val;
+ }
+ $this->suggested_value *= $baseValue;
+ }
+
+
+ public function validate()
+ {
+ if ($this->type == 'weapon1h' || $this->type == 'weapon2h')
+ {
+ $specials = Daemon_Dictionary::$combatAttackSpecials;
+ if(!isset($specials[$this->special_type]))
+ {
+ $this->special_type = null;
+ $this->special_param = null;
+ }
+ }
+ elseif ($this->type != 'item')
+ {
+ $this->damage_type = null;
+ $specials = Daemon_Dictionary::$combatArmorSpecials;
+ if(($this->type != 'armor') || !isset($specials[$this->special_type]))
+ {
+ $this->special_type = null;
+ $this->special_param = null;
+ }
+ }
+ else //usables
+ {
+ $this->damage_type = null;
+ }
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject/itemtemplate.php b/2013/daemon/lib/daemon/dbobject/itemtemplate.php
new file mode 100644
index 0000000..4439aa7
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/itemtemplate.php
@@ -0,0 +1,20 @@
+_characterData->checkTurnCosts())
+ return false;
+ //update character data
+ $this->_characterData->regen(false);
+ //check for events
+ $this->checkEvents(2);
+ //save character data
+ $this->_characterData->put();
+ return true;
+ }
+
+
+ //spends one turn on resting
+ public function actionRest()
+ {
+ //check turn costs
+ if(!$this->_characterData->checkTurnCosts())
+ return false;
+ //update character data
+ $this->_characterData->regen(true);
+ //check for events
+ $this->checkEvents(1);
+ //save character data
+ $this->_characterData->put();
+ return true;
+ }
+
+
+ //spends one turn on training
+ public function actionTrain()
+ {
+ //check turn costs
+ if(!$this->_characterData->checkTurnCosts())
+ return false;
+ //update character data
+ $this->_characterData->regen(false);
+ $this->_characterData->xp_free += 1;
+ //check for events
+ $this->checkEvents(1);
+ //save character data
+ $this->_characterData->put();
+ return true;
+ }
+
+
+ //moves character to specified location
+ public function actionTravel($destinationId)
+ {
+ //read path data
+ $sql = "SELECT * FROM location_paths WHERE location_id=:id AND destination_id=:destId";
+ $params = array('id' => $this->location_id, 'destId' => $destinationId);
+ $path = $this->_dbClient->selectRow($sql, $params);
+ if(!$path)
+ {
+ Daemon_MsgQueue::add('Wybrana ścieżka jest niedostępna.');
+ return false;
+ }
+ //check turn costs
+ if(!$this->_characterData->checkTurnCosts($path['cost_gold'], $path['cost_mana']))
+ return false;
+ //update character data
+ $this->_characterData->regen(false);
+ $this->_characterData->location_id = $destinationId;
+ //load destination
+ $this->get(array('location_id' => $destinationId));
+ if($this->region_id)
+ {
+ $sql = "INSERT IGNORE INTO character_regions (character_id, region_id) VALUES (:charId, :regionId)";
+ $params = array('charId' => $this->_characterData->character_id, 'regionId' => $this->region_id);
+ $this->_dbClient->query($sql, $params);
+ }
+ //check for events
+ $this->checkEvents(1);
+ //save character data
+ $this->_characterData->put();
+ return true;
+ }
+
+
+ public function attachCharacterData(Daemon_DbObject_CharacterData $characterData)
+ {
+ $this->_characterData = $characterData;
+ }
+
+
+ //checks for events in a location
+ private function checkEvents($chanceMult)
+ {
+ $event = false;
+ $eventParams = array();
+ //check if there was any event
+ if($this->chance2)
+ {
+ $d256 = mt_rand(0, 255);
+ $failChance = 256 * (1 - $this->chance1 / $this->chance2);
+ $chance = $chanceMult ? (256 - $failChance / $chanceMult) : 0;
+ $event = ($d256 < $chance);
+ }
+ //check event type
+ if($event)
+ {
+ //read monster data
+ $sql = "SELECT * FROM location_monsters WHERE location_id=:id";
+ $monsters = $this->_dbClient->selectAll($sql, array('id' => $this->location_id));
+ //read special events
+ $sql = "SELECT * FROM location_events WHERE location_id=:id";
+ $events = $this->_dbClient->selectAll($sql, array('id' => $this->location_id));
+ //find event data
+ $chanceSum = 0;
+ foreach($monsters as $row)
+ $chanceSum += $row['chance'];
+ foreach($events as $row)
+ $chanceSum += $row['chance'];
+ $d256 = mt_rand(0, 255);
+ foreach($monsters as $row)
+ {
+ $chance = 256 * $row['chance'] / $chanceSum;
+ if($d256 < $chance)
+ {
+ $eventParams = array('monsterId' => $row['monster_id']);
+ break;
+ }
+ $d256 -= $chance;
+ }
+ foreach($events as $row)
+ {
+ $chance = 256 * $row['chance'] / $chanceSum;
+ if($d256 < $chance)
+ {
+ $eventParams = array('eventId' => $row['event_id'], 'params' => $row['params']);
+ break;
+ }
+ $d256 -= $chance;
+ }
+ }
+ //store event
+ if(!$eventParams)
+ Daemon_MsgQueue::add('Brak zdarzeń w tej turze.');
+ $this->_characterData->setLocationEvent($eventParams);
+ }
+
+
+ //counts characters present in a location
+ public function getCharacterCount($limit)
+ {
+ $sql = "SELECT COUNT(1) FROM character_data WHERE location_id=:id LIMIT :limit";
+ $params = array('id' => $this->location_id, 'limit' => $limit);
+ return $this->_dbClient->selectValue($sql, $params);
+ }
+
+
+ //gets a list of characters present in a location
+ public function getCharacters(Daemon_DbObject_CharacterData $char, $halfLimit = null)
+ {
+ if(empty($this->location_id))
+ return array();
+ $sql = "SELECT MAX(rollover_id) FROM rollovers";
+ $rolloverId = $this->_dbClient->selectValue($sql);
+ $charId = $this->_characterData->character_id;
+ $cols = "c.character_id, c.name, cp.location_id, cp.level, cp.xp_used,
+ c.clan_id, cp.faction_id, COALESCE(cp.rank_id, 0) AS rank_id, f.name AS faction_name";
+ $tables = "character_data cp
+ JOIN characters c USING(character_id)
+ LEFT JOIN factions f USING(faction_id)";
+ $params = array('id' => $this->location_id, 'charId' => $charId);
+ if($halfLimit)
+ {
+ $params['xp'] = $char->xp_used;
+ $params['halfLimit'] = $halfLimit;
+ $sql = "(
+ SELECT $cols FROM $tables
+ WHERE cp.location_id=:id AND cp.character_id!=:charId AND cp.xp_used >= :xp
+ ORDER BY xp_used ASC LIMIT $halfLimit
+ ) UNION (
+ SELECT $cols FROM $tables
+ WHERE cp.location_id=:id AND cp.character_id!=:charId AND cp.xp_used <= :xp
+ ORDER BY xp_used DESC LIMIT $halfLimit
+ ) ORDER BY xp_used DESC, name ASC";
+ }
+ else
+ {
+ $sql = "SELECT $cols FROM $tables
+ WHERE cp.location_id=:id AND cp.character_id!=:charId
+ ORDER BY cp.xp_used DESC, c.name ASC";
+ }
+ $data = $this->_dbClient->selectAll($sql, $params);
+ foreach($data as &$row)
+ {
+ $row['_canAttack'] = $char->canAttack($row, $rolloverId, false);
+ $row['_sparring'] = ('normal' != $char->getCombatType($row, $this->type));
+ }
+ return $data;
+ }
+
+
+ //gets the name of a faction owning location/caern
+ public function getFactionName()
+ {
+ if(empty($this->faction_id))
+ return null;
+ $sql = "SELECT name FROM factions WHERE faction_id=:id";
+ return $this->_dbClient->selectValue($sql, array('id' => $this->faction_id));
+ }
+
+
+ //get a full list of maps
+ public function getMaps()
+ {
+ $sql = "SELECT * FROM maps WHERE url IS NOT NULL AND url != '' ORDER BY sort, name";
+ return $this->_dbClient->selectAll($sql, array());
+ }
+
+
+ //get a list of paths starting in current location
+ public function getPaths()
+ {
+ if(empty($this->location_id))
+ return array();
+ $sql = "SELECT p.*, IF(p.name IS NULL OR p.name='', l.name, p.name) AS path_name,
+ (p.cost_gold<=:gold AND p.cost_mana<=:mana) AS _enabled
+ FROM location_paths p JOIN locations l ON p.destination_id=l.location_id
+ WHERE p.location_id=:id ORDER BY l.name, l.location_id";
+ $params = array('id' => $this->location_id,
+ 'gold' => $this->_characterData->gold_purse,
+ 'mana' => $this->_characterData->mana,
+ );
+ return $this->_dbClient->selectAll($sql, $params);
+ }
+
+
+ public function getPictureUrl()
+ {
+ if ($this->picture_url)
+ return $this->picture_url;
+ $sql = "SELECT picture_url FROM regions WHERE region_id=:id";
+ return $this->_dbClient->selectValue($sql, array('id' => $this->region_id));
+ }
+
+
+ //get name of the location's region
+ public function getRegionName()
+ {
+ if(empty($this->region_id))
+ return null;
+ $sql = "SELECT name FROM regions WHERE region_id=:id";
+ return $this->_dbClient->selectValue($sql, array('id' => $this->region_id));
+ }
+
+
+ //get a list of available services
+ public function getServices()
+ {
+ if(empty($this->location_id))
+ return array();
+ $sql = "SELECT s.*, (
+ (s.faction_id IS NULL OR :factionId IS NULL OR s.faction_id = :factionId)
+ AND (s.rank_id IS NULL OR s.rank_id <= :rankId)
+ ) AS _enabled
+ FROM location_services l JOIN services s USING(service_id)
+ WHERE l.location_id=:id ORDER BY s.name";
+ $params = array('id' => $this->location_id,
+ 'factionId' => $this->_characterData->faction_id,
+ 'rankId' => $this->_characterData->rank_id,
+ );
+ return $this->_dbClient->selectAll($sql, $params);
+ }
+
+
+ //checks object data
+ public function validate()
+ {
+ $this->chance1 = max(0, (int) $this->chance1);
+ $this->chance2 = max(1, (int) $this->chance2);
+ if($this->type != 'boss')
+ {
+ $this->boss_id = null;
+ $this->boss_status = null;
+ if ($this->type != 'caern')
+ $this->faction_id = null;
+ }
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject/monster.php b/2013/daemon/lib/daemon/dbobject/monster.php
new file mode 100644
index 0000000..6a81f69
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/monster.php
@@ -0,0 +1,49 @@
+_combatUnit)
+ {
+ $this->_combatUnit = $full ? new Daemon_Combat_Unit() : new Daemon_DbObject_CombatUnit();
+ $this->_combatUnit->attachDbClient($this->_dbClient);
+ if($this->combat_unit_id)
+ $this->_combatUnit->get(array('combat_unit_id' => $this->combat_unit_id));
+ $this->_combatUnit->name = $this->name;
+ $this->_combatUnit->faction_id = null;
+ }
+ return $this->_combatUnit;
+ }
+
+
+ public function validate()
+ {
+ $this->class = max(0, (int) $this->class);
+ $this->level = max(0, (int) $this->level);
+ $this->gold = max(0, (int) $this->gold);
+ $this->chance1 = max(0, (int) $this->chance1);
+ $this->chance2 = max(1, (int) $this->chance2);
+ //set class
+ foreach (Daemon_Dictionary::$monsterClassLevels as $class => $level)
+ {
+ if ($this->level > $level)
+ $this->class = $class;
+ }
+ }
+}
diff --git a/2013/daemon/lib/daemon/dbobject/player.php b/2013/daemon/lib/daemon/dbobject/player.php
new file mode 100644
index 0000000..c60300c
--- /dev/null
+++ b/2013/daemon/lib/daemon/dbobject/player.php
@@ -0,0 +1,387 @@
+attachDbClient($dbClient);
+ $this->checkSession();
+ if($id = $this->getPlayerId())
+ parent::get(array('player_id' => $id));
+ }
+
+
+ //creates a new character
+ public function addCharacter($name, $gender, $turnDelta, $turnLimit)
+ {
+ if(!$this->getPlayerId())
+ return false;
+ $maxLength = $this->_dbClient->getColumnMaxLength('characters', 'name');
+ $name = Daemon::normalizeString($name, false);
+ $gender = Daemon::normalizeString($gender, false);
+ $validName = $this->validateName($name, $maxLength);
+ $validGender = $this->validateGender($gender);
+ if($validName && $validGender)
+ {
+ $sql = "INSERT INTO characters (player_id, name, gender, last_action) VALUES (:playerId, :name, :gender, now())";
+ $params = array(
+ 'playerId' => $this->getPlayerId(),
+ 'name' => $name, 'gender' => $gender,
+ );
+ $this->_dbClient->query($sql, $params, 'Wybrane imię jest już zajęte.');
+ if($id = $this->_dbClient->lastInsertId())
+ {
+ $turns = $this->getStartingTurns($turnDelta, $turnLimit);
+ $sql = "INSERT INTO character_data(character_id, turns) VALUES (:id, :turns)";
+ $this->_dbClient->query($sql, array('id' => $id, 'turns' => $turns));
+ $sql = "INSERT INTO character_statistics(character_id) VALUES (:id)";
+ $this->_dbClient->query($sql, array('id' => $id));
+ }
+ }
+ }
+
+
+ //checks and stores authentication data (logs in)
+ public function authenticate($login, $password)
+ {
+ $this->get(array('login' => $login));
+ if($this->player_id)
+ {
+ //check password
+ list($salt, $hash) = explode(':', $this->password.':');
+ if($hash == Daemon::passwordHash($salt, $password))
+ {
+ session_regenerate_id(true);
+ $_SESSION[self::VARNAME_PLAYER_ID] = (int) $this->player_id;
+ $_SESSION[self::VARNAME_PLAYER_ADDR] = getenv('REMOTE_ADDR');
+ $_SESSION[self::VARNAME_TIMESTAMP] = time();
+ $this->last_login = $this->_dbClient->selectValue("SELECT NOW()");
+ $this->put();
+ }
+ else $this->player_id = null;
+ }
+ if(!$this->player_id)
+ {
+ Daemon_MsgQueue::add('Nieprawidłowy login lub hasło.');
+ $this->unauthenticate();
+ }
+ }
+
+
+ //compares request data with stored auth data
+ private function checkSession()
+ {
+ $prevAddr = $currentAddr = getenv('REMOTE_ADDR');
+ $prevTime = $currentTime = time();
+ if(isset($_SESSION[self::VARNAME_PLAYER_ADDR]))
+ $prevAddr = $_SESSION[self::VARNAME_PLAYER_ADDR];
+ if(isset($_SESSION[self::VARNAME_TIMESTAMP]))
+ $prevTime = $_SESSION[self::VARNAME_TIMESTAMP];
+ $validAddr = ($currentAddr == $prevAddr);
+ $validTime = ($currentTime < $prevTime + self::SESSION_TIMEOUT);
+ if($validAddr && $validTime)
+ $_SESSION[self::VARNAME_TIMESTAMP] = $currentTime;
+ else $this->unauthenticate();
+ }
+
+
+ //resets or deletes selected character
+ public function deleteCharacter($characterId, $reset, $turnDelta, $turnLimit)
+ {
+ if(!$this->getPlayerId())
+ return false;
+ $sql = "SELECT character_id FROM characters WHERE character_id=:id AND player_id=:playerId";
+ $params = array('id' => $characterId, 'playerId' => $this->getPlayerId());
+ if($id = $this->_dbClient->selectValue($sql, $params))
+ {
+ $params = array('id' => $characterId);
+ $tables = array('character_data', 'character_missions', 'character_regions',
+ 'character_statistics', 'character_titles', 'inventory');
+ foreach($tables as $table)
+ {
+ $sql = "DELETE FROM $table WHERE character_id=:id";
+ if ($table == 'character_titles')
+ $sql .= " AND title_id NOT IN (SELECT title_id FROM titles WHERE type='special')";
+ $this->_dbClient->query($sql, $params);
+ }
+ if ($reset)
+ {
+ $turns = $this->getStartingTurns($turnDelta, $turnLimit);
+ $sql = "INSERT INTO character_data(character_id, turns) VALUES (:id, :turns)";
+ $this->_dbClient->query($sql, array('id' => $id, 'turns' => $turns));
+ $sql = "INSERT INTO character_statistics(character_id) VALUES (:id)";
+ $this->_dbClient->query($sql, array('id' => $id));
+ }
+ else
+ {
+ $sql = "DELETE FROM characters WHERE character_id=:id";
+ $this->_dbClient->query($sql, $params);
+ }
+ }
+ }
+
+
+ //returns basic data of active character
+ public function getActiveCharacter()
+ {
+ $char = new Daemon_DbObject_Character;
+ $char->attachDbClient($this->_dbClient);
+ if($id = $this->getCharacterId())
+ $char->get(array('character_id' => $id));
+ $this->setCharacterId($char->character_id);
+ $char->attachPlayer($this);
+ return $char;
+ }
+
+
+ //returns active character's ID
+ public function getCharacterId()
+ {
+ if(isset($_SESSION[self::VARNAME_CHARACTER_ID]))
+ return $_SESSION[self::VARNAME_CHARACTER_ID];
+ else return null;
+ }
+
+
+ //returns a list of player's characters
+ public function getCharacters()
+ {
+ $sql = "SELECT c.character_id, c.name,
+ cp.level, cp.turns, cp.health, cp.health_max, l.name AS location_name
+ FROM characters c
+ LEFT JOIN character_data cp USING(character_id)
+ LEFT JOIN locations l USING(location_id)
+ WHERE player_id = :playerId";
+ $params = array('playerId' => $this->getPlayerId());
+ $result = array();
+ foreach((array) $this->_dbClient->selectAll($sql, $params) as $row)
+ $result[$row['character_id']] = $row;
+ return $result;
+ }
+
+
+ //returns authenticated player's ID
+ public function getPlayerId()
+ {
+ if(isset($_SESSION[self::VARNAME_PLAYER_ID]))
+ $this->player_id = $_SESSION[self::VARNAME_PLAYER_ID];
+ else $this->player_id = null;
+ return $this->player_id;
+ }
+
+
+ //returns a list of player's access roles
+ public function getRoles()
+ {
+ if(!$id = $this->getPlayerId())
+ return array();
+ if(!is_array($this->roles))
+ {
+ $sql = "SELECT roles FROM players WHERE player_id=:playerId";
+ $params = array('playerId' => $this->getPlayerId());
+ $this->roles = explode(',', (string) $this->_dbClient->selectValue($sql, $params));
+ }
+ return $this->roles;
+ }
+
+
+ protected function getStartingTurns($turnDelta, $turnLimit)
+ {
+ $sql = "SELECT COUNT(rollover_id) FROM rollovers";
+ $n = (int) $this->_dbClient->selectValue($sql);
+ return min($turnLimit, $turnDelta * (1 + $n));
+ }
+
+
+ //checks if player has selected access role
+ public function hasRole($name)
+ {
+ return in_array($name, $this->getRoles());
+ }
+
+
+ //stores a new password and sends mail with reset key
+ public function preparePasswordReset($login, $email, $password, $passwordCopy)
+ {
+ if (!$this->validatePassword($password, $passwordCopy))
+ return false;
+ if (!$login || !$email)
+ {
+ Daemon_MsgQueue::add('Musisz podać login oraz email.');
+ return false;
+ }
+ //validate login+email
+ $sql = "SELECT player_id FROM players WHERE login=:login AND email=:email";
+ $params = array('login' => $login, 'email' => $email);
+ $playerId = $this->_dbClient->selectValue($sql, $params);
+ if (!$playerId)
+ {
+ Daemon_MsgQueue::add('Nieprawidłowy login lub hasło.');
+ return false;
+ }
+ //store password
+ $key = sha1(Daemon::passwordSalt() . $login . $email);
+ $salt = Daemon::passwordSalt();
+ $passwordSql = sprintf('%s:%s', $salt, Daemon::passwordHash($salt, $password));
+ $sql = "UPDATE players SET reset_key = :key, reset_password = :password,
+ reset_until = now() + INTERVAL 1 WEEK WHERE player_id = :id";
+ $params = array('id' => $playerId, 'key' => $key, 'password' => $passwordSql);
+ $ok = $this->_dbClient->query($sql, $params);
+ if (!$ok)
+ {
+ Daemon_MsgQueue::add('Nie udało się zapisać nowego hasła.');
+ return false;
+ }
+ //send mail
+ $url = sprintf('%sreset-password?key=%s', $GLOBALS['cfg']->applicationUrl, $key);
+ $subject = "Daemon 2: reset hasla";
+ $message = "Aby zresetowac haslo przejdz pod adres:\n$url\n";
+ $from = $GLOBALS['cfg']->applicationMail;
+ $headers = "From: $from\r\nReply-To: $from";
+ $ok = mail($email, $subject, $message, $headers);
+ if ($ok)
+ $msg = 'Na podany email wysłana została wiadomość z kluczem resetującym hasło.';
+ else
+ $msg = 'Niestety mailer nie działa, reset hasła jest chwilowo niemozliwy.';
+ Daemon_MsgQueue::add($msg);
+ return $ok;
+ }
+
+
+ //creates a new player
+ public function register($login, $password, $passwordCopy)
+ {
+ $maxLength = $this->_dbClient->getColumnMaxLength('players', 'login');
+ $validLogin = $this->validateLogin($login, $maxLength);
+ $validPassword = $this->validatePassword($password, $passwordCopy);
+ if($validLogin && $validPassword)
+ {
+ $salt = Daemon::passwordSalt();
+ $passwordSql = sprintf('%s:%s', $salt, Daemon::passwordHash($salt, $password));
+ $sql = "INSERT INTO players (login, password, roles) VALUES (:login, :password, 'chat')";
+ $params = array('login' => $login, 'password' => $passwordSql);
+ $ok = $this->_dbClient->query($sql, $params, 'Wybrany login jest już zajęty.');
+ Daemon_MsgQueue::add(sprintf('Rejestracja zakończona %s.', $ok ? 'powodzeniem' : 'niepowodzeniem'));
+ return $ok;
+ }
+ return false;
+ }
+
+
+ //resets password based on a hash key
+ public function resetPassword($key)
+ {
+ $sql = "SELECT player_id FROM players WHERE reset_key = :key AND reset_until >= current_date";
+ $params = array('key' => $key);
+ $playerId = $this->_dbClient->selectValue($sql, $params);
+ if ($playerId)
+ {
+ $sql = "UPDATE players SET password = reset_password, reset_password = null,
+ reset_key = null, reset_until = null WHERE player_id = :id";
+ $params = array('id' => $playerId);
+ $ok = $this->_dbClient->query($sql, $params);
+ Daemon_MsgQueue::add(sprintf('Zmiana hasła zakończona %s.', $ok ? 'powodzeniem' : 'niepowodzeniem'));
+ }
+ else
+ Daemon_MsgQueue::add('Podany kod jest nieprawidłowy lub nieaktualny.');
+ }
+
+
+ //stores selected character's ID
+ public function setCharacterId($id)
+ {
+ $_SESSION[self::VARNAME_CHARACTER_ID] = (int) $id;
+ }
+
+
+ //updates password
+ public function setPassword($password, $passwordCopy)
+ {
+ if(!$password && !$passwordCopy)
+ return;
+ if($this->validatePassword($password, $passwordCopy))
+ {
+ $salt = Daemon::passwordSalt();
+ $this->password = sprintf('%s:%s', $salt, Daemon::passwordHash($salt, $password));
+ $this->put();
+ Daemon_MsgQueue::add('Hasło zostało zmienione.');
+ }
+ }
+
+
+ //deletes stored authentication data (logs out)
+ public function unauthenticate()
+ {
+ unset($_SESSION[self::VARNAME_CHARACTER_ID]);
+ unset($_SESSION[self::VARNAME_PLAYER_ID]);
+ unset($_SESSION[self::VARNAME_PLAYER_ADDR]);
+ unset($_SESSION[self::VARNAME_TIMESTAMP]);
+ }
+
+
+ //checks login validity
+ private function validateLogin($input, $maxLength)
+ {
+ if(!$input)
+ Daemon_MsgQueue::add('Musisz podać login.');
+ elseif(iconv_strlen($input) > $maxLength)
+ Daemon_MsgQueue::add('Wybrany login jest za długi.');
+ else return true;
+ return false;
+ }
+
+
+ //checks gender validity
+ private function validateGender($input)
+ {
+ if(!in_array($input, array_keys(Daemon_Dictionary::$genders)))
+ Daemon_MsgQueue::add('Wybrana płeć nie jest dostępna.');
+ else return true;
+ return false;
+ }
+
+
+ //checks name validity
+ private function validateName($input, $maxLength)
+ {
+ if(!$input)
+ Daemon_MsgQueue::add('Musisz podać imię.');
+ elseif(iconv_strlen($input) > $maxLength)
+ Daemon_MsgQueue::add('Wybrane imię jest za długie.');
+ else return true;
+ return false;
+ }
+
+
+ //checks password validity
+ private function validatePassword($input, $inputCopy)
+ {
+ if(!$input)
+ Daemon_MsgQueue::add('Musisz podać hasło.');
+ elseif($input != $inputCopy)
+ Daemon_MsgQueue::add('Źle powtórzone hasło.');
+ else return true;
+ return false;
+ }
+}
diff --git a/2013/daemon/lib/daemon/dictionary.php b/2013/daemon/lib/daemon/dictionary.php
new file mode 100644
index 0000000..597b7a8
--- /dev/null
+++ b/2013/daemon/lib/daemon/dictionary.php
@@ -0,0 +1,85 @@
+ 'ukryty', 'active' => 'aktywny', 'defeated' => 'pokonany');
+
+ public static $characterAttributes = array(
+ 'str' => 'Siła', 'dex' => 'Zręczność', 'vit' => 'Wytrzymałość', 'pwr' => 'Moc', 'wil' => 'Siła Woli');
+
+ public static $characterSkills = array(
+ 'pstr' => 'Silny Cios', 'patk' => 'Przycelowanie',
+ 'pdef' => 'Unik', 'pres' => 'Twardziel', 'preg' => 'Regeneracja',
+ 'mstr' => 'Koncentracja', 'matk' => 'Magia Bojowa',
+ 'mdef' => 'Kontrzaklęcie', 'mres' => 'Antymagia', 'mreg' => 'Medytacja');
+
+ public static $combatAttackTypes = array('p' => 'fizyczny', 'm' => 'magiczny');
+
+ public static $combatAttackSpecials = array(
+ Daemon_DbObject_CombatUnit::SP_POISON => 'trucizna',
+ Daemon_DbObject_CombatUnit::SP_VAMPIRE => 'wampiryzm',
+ Daemon_DbObject_CombatUnit::SP_ETHER => 'eteryczny',
+ Daemon_DbObject_CombatUnit::SP_BLOODY => 'krwawy',
+ Daemon_DbObject_CombatUnit::SP_STUN => 'ogłuszenie',
+ Daemon_DbObject_CombatUnit::SP_FACTION => 'nienawiść',
+ Daemon_DbObject_CombatUnit::SP_SWARM => 'stado',
+ );
+
+ public static $combatArmorSpecials = array(
+ Daemon_DbObject_CombatUnit::SP_DEMON => 'demon',
+ Daemon_DbObject_CombatUnit::SP_ANTIPOISON => 'odporność na trucizny',
+ Daemon_DbObject_CombatUnit::SP_ANTIVAMP => 'odporność na wampiryzm',
+ Daemon_DbObject_CombatUnit::SP_SHOCK => 'porażenie',
+ Daemon_DbObject_CombatUnit::SP_FACTION => 'fanatyzm',
+ );
+
+ public static $equipmentButtons = array('use' => 'użyj', 'equip' => 'załóż', 'unequip' => 'zdejmij');
+
+ public static $equipmentFlags = array('bound' => 'przypisany', 'identified' => 'zidentyfikowany');
+
+ public static $equipmentGroups = array(
+ 'weapon1h' => 'BROŃ 1R i TARCZE', 'weapon2h' => 'BROŃ 2R',
+ 'armor' => 'PANCERZE', 'helmet' => 'HEŁMY', 'gloves' => 'RĘKAWICE',
+ 'boots' => 'BUTY', 'pendant' => 'NASZYJNIKI', 'accesory' => 'DODATKI',
+ 'item' => 'INNE PRZEDMIOTY');
+
+ public static $equipmentSlots = array(
+ 'hand_a' => 'główna ręka', 'hand_b' => 'druga ręka',
+ 'armor' => 'pancerz', 'helmet' => 'hełm', 'gloves' => 'rękawice', 'boots' => 'buty',
+ 'pendant' => 'naszyjnik', 'accesory_a' => 'dodatek A', 'accesory_b' => 'dodatek B');
+
+ public static $genders = array('f' => 'kobieta', 'm' => 'mężczyzna', 'n' => 'nieokreślona');
+
+ public static $generatorItemTypes = array(
+ 'weapon1h' => 'broń 1R', 'weapon2h' => 'broń 2R',
+ 'armor' => 'pancerz', 'helmet' => 'hełm', 'gloves' => 'rękawice',
+ 'boots' => 'buty', 'pendant' => 'naszyjnik', 'accesory' => 'dodatek');
+
+ public static $itemTypes = array(
+ 'weapon1h' => 'broń 1R', 'weapon2h' =>'broń 2R',
+ 'armor' => 'pancerz', 'helmet' => 'hełm', 'gloves' => 'rękawice',
+ 'boots' => 'buty', 'pendant' => 'naszyjnik', 'accesory' => 'dodatek',
+ 'item' => 'niezakładalny');
+
+ public static $itemDamageTypes = array('' => 'brak', 'p' => 'fizyczne', 'm' => 'magiczne');
+
+ public static $itemWeaponTypes = array('weapon1h' => 'jednoręczna', 'weapon2h' =>'dwuręczna');
+
+ public static $itemArmorTypes = array(
+ 'armor' => 'pancerz', 'helmet' => 'hełm', 'gloves' => 'rękawice',
+ 'boots' => 'buty', 'pendant' => 'naszyjnik', 'accesory' => 'dodatek');
+
+ public static $locationTypes = array('normal' => 'zwykła', 'arena'=>'arena', 'caern'=>'caern', 'boss' => 'boss');
+
+ public static $missionProgress = array(
+ 'active' => 'aktywna', 'completed' => 'ukończona', 'rewarded' => 'nagrodzona');
+
+ public static $monsterClasses = array(1 => 'słaby', 2 => 'średni', 3 => 'silny', 4 => 'epicki');
+
+ public static $monsterClassLevels = array(1 => 0, 2 => 20, 3 => 45, 4 => 70);
+
+ public static $serviceTypes = array(
+ 'bank'=>'bank', 'healer' => 'uzdrowiciel', 'shop' => 'sklep', 'temple'=>'świątynia');
+
+ public static $skinDirUrls = array('Ciemny' => 'static/dark', 'Jasny' => 'static/light');
+}
diff --git a/2013/daemon/lib/daemon/duel.php b/2013/daemon/lib/daemon/duel.php
new file mode 100644
index 0000000..52c6e72
--- /dev/null
+++ b/2013/daemon/lib/daemon/duel.php
@@ -0,0 +1,182 @@
+characterData = $characterData;
+ }
+
+
+ public function attachDbClient(Daemon_DbClient $dbClient)
+ {
+ $this->dbClient = $dbClient;
+ }
+
+
+ public function getCombatLog()
+ {
+ return $this->combatLog;
+ }
+
+
+ //returns target character's object
+ private function getTarget($targetId)
+ {
+ $params = array('character_id' => $targetId);
+ $cdata = new Daemon_DbObject_CharacterData();
+ $cdata->attachDbClient($this->dbClient);
+ $cdata->get($params);
+ $sql = "SELECT name FROM characters WHERE character_id=:character_id";
+ $cdata->_characterName = $this->dbClient->selectValue($sql, $params);
+ return $cdata;
+ }
+
+
+ public function execute(Daemon_View $view, $locationType, $targetId)
+ {
+ $levelMultiplier = 1.2; //magic number!
+ $attacker = $this->characterData;
+ $defender = $this->getTarget($targetId);
+ $winner = null;
+ $winnerName = null;
+ $winnerXp = 0;
+ $winnerLevel = null;
+ $loserName = null;
+ $loserLevel = 0;
+ $sql = "SELECT MAX(rollover_id) FROM rollovers";
+ $rolloverId = $this->dbClient->selectValue($sql);
+ //check if combat is possibile
+ if(!$attacker->canAttack((array) $defender, $rolloverId, true))
+ return null;
+ //check combat type
+ $combatType = $attacker->getCombatType((array) $defender, $locationType);
+ if(!$combatType)
+ {
+ Daemon_MsgQueue::add('Nie możesz zaatakować tej postaci.');
+ return false;
+ }
+ $arena = ('arena' == $combatType);
+ //pre-calculate xp rewards (depends on health, which changes in combat)
+ $factionBonus = ($attacker->faction_id && $defender->faction_id
+ && ($attacker->faction_id != $defender->faction_id));
+ $attackerLevel = $attacker->getEffectiveLevel($factionBonus);
+ $defenderLevel = $defender->getEffectiveLevel($factionBonus);
+ //execute combat
+ $winner = $this->runCombat($defender, $combatType);
+ //update winner
+ if(self::WINNER_ACTOR == $winner)
+ {
+ $winnerChar = $attacker;
+ $loserChar = $defender;
+ $winnerXp = Daemon_Math::round($levelMultiplier * $defenderLevel);
+ }
+ elseif(self::WINNER_TARGET == $winner)
+ {
+ $winnerChar = $defender;
+ $loserChar = $attacker;
+ $winnerXp = Daemon_Math::round($levelMultiplier * $attackerLevel);
+ }
+ else
+ {
+ $winnerChar = null;
+ $loserChar = null;
+ }
+ if($winnerChar && $loserChar)
+ {
+ $winnerName = $winnerChar->_characterName;
+ $loserName = $loserChar->_characterName;
+ $winnerChar->xp_free += $winnerXp;
+ $sql = "UPDATE character_statistics SET duel_wins=duel_wins+1 WHERE character_id=:id";
+ $this->dbClient->query($sql, array('id' => $winnerChar->character_id));
+ $sql = "UPDATE character_statistics SET duel_losses=duel_losses+1 WHERE character_id=:id";
+ $this->dbClient->query($sql, array('id' => $loserChar->character_id));
+ }
+ //save characters
+ $attacker->put();
+ $defender->put();
+ //log duel
+ $sql = "INSERT INTO duels(rollover_id, attacker_id, defender_id, type, winner, combat_log)
+ VALUES (:rolloverId, :attackerId, :defenderId, :type, :winner, :combatLog)";
+ $params = array('rolloverId' => $rolloverId,
+ 'attackerId' => $attacker->character_id, 'defenderId' => $defender->character_id,
+ 'type' => $combatType, 'winner' => $winner, 'combatLog' => $this->combatLog);
+ $this->dbClient->query($sql, $params);
+ //generate report
+ $view->arena = $arena;
+ $view->combatLog = $this->combatLog;
+ $view->attackerName = $attacker->_characterName;
+ $view->defenderName = $defender->_characterName;
+ $view->winner = $winner;
+ $view->winnerName = $winnerName;
+ $view->winnerXp = $winnerXp;
+ $view->winnerLevel = $winnerLevel;
+ $view->loserName = $loserName;
+ ob_start();
+ $view->display('duelcombat.xml');
+ $this->combatLog = ob_get_clean();
+ return true;
+ }
+
+
+ //executes combat, returns winner flag
+ private function runCombat(Daemon_DbObject_CharacterData $target, $combatType)
+ {
+ $combat = new Daemon_Combat();
+ $logger = new Daemon_Combat_Log();
+ $combat->attachLogger($logger);
+ //prepare units
+ $attackerUnit = $this->characterData->getCombatUnit(true);
+ $defenderUnit = $target->getCombatUnit(true);
+ if('arena' == $combatType)
+ {
+ $attackerUnit->health = $attackerUnit->health_max;
+ $defenderUnit->health = $defenderUnit->health_max;
+ }
+ //execute combat
+ $combat->addUnit('a', $attackerUnit, true);
+ $combat->addUnit('b', $defenderUnit, false);
+ $combat->execute();
+ $this->combatLog = (string) $logger;
+ //check deaths
+ $deathA = ($attackerUnit->health < 1);
+ $deathB = ($defenderUnit->health < 1);
+ //update characters health, gold & equipment, return winner
+ if('arena' != $combatType)
+ {
+ $attackerUnit->put();
+ $this->characterData->health = floor($attackerUnit->health);
+ $defenderUnit->put();
+ $target->health = floor($defenderUnit->health);
+ }
+ if($deathA && $deathB)
+ {
+ if('arena' != $combatType)
+ {
+ $this->characterData->setDeath(true);
+ $target->setDeath(true, null);
+ }
+ return null;//double KO
+ }
+ elseif($deathA && !$deathB)
+ {
+ if('arena' != $combatType)
+ $this->characterData->setDeath(true);
+ return self::WINNER_TARGET;
+ }
+ elseif(!$deathA && $deathB)
+ {
+ if('arena' != $combatType)
+ $target->setDeath(true);
+ return self::WINNER_ACTOR;
+ }
+ else return null;//draw
+ }
+}
diff --git a/2013/daemon/lib/daemon/email.php b/2013/daemon/lib/daemon/email.php
new file mode 100644
index 0000000..b59569f
--- /dev/null
+++ b/2013/daemon/lib/daemon/email.php
@@ -0,0 +1,28 @@
+from = sprintf('no-reply@%s', getenv('SERVER_NAME'));
+ }
+
+
+ public function send()
+ {
+ $headers = implode("\r\n", array(
+ sprintf('From: %s', $this->from),
+ 'Content-Type:text/plain;charset=UTF-8',
+ ));
+ echo "mail($this->to, $this->subject, $this->message, $headers);";
+ exit;
+ }
+}
diff --git a/2013/daemon/lib/daemon/errorhandler.php b/2013/daemon/lib/daemon/errorhandler.php
new file mode 100644
index 0000000..2454d31
--- /dev/null
+++ b/2013/daemon/lib/daemon/errorhandler.php
@@ -0,0 +1,47 @@
+getMessage(), $ex->getCode(), $ex->getFile(), $ex->getLine(), $ex->getTraceAsString());
+ $path = sprintf('%s/exception.%s.log', self::$logDir, date('Ymd.His'));
+ self::logError($path, $data);
+ }
+
+
+ public static function logError($path, $data)
+ {
+ $stored = @file_put_contents($path, $data);
+ if(!headers_sent())
+ header('Content-Type:text/html;charset=UTF-8');
+ echo'
Kod gry zrobił Aaarghhh!
i przestał działać. Albo zdechła baza danych. Albo cokolwiek.
Na szczęście nie zawiodło automatyczne logowanie błędów,' + .' więc nie musisz nic robić poza szturchnięciem admina że jego badziew znow nie działa ;)
'; + } + else + { + echo 'Na dokładkę zawiodło też automatyczne logowanie błędów,' + .' więc to do Ciebie należy ciężkie zadanie powiadomienia o nim admina.' + .' Pamiętaj żeby podać wszystkie możliwe informacje o błędzie,' + .' ułatwi to lub w ogóle umożliwi jego wytropienie.
'; + } + exit; + } +} diff --git a/2013/daemon/lib/daemon/event.php b/2013/daemon/lib/daemon/event.php new file mode 100644 index 0000000..43a2547 --- /dev/null +++ b/2013/daemon/lib/daemon/event.php @@ -0,0 +1,50 @@ +characterData = $characterData; + } + + + public function attachDbClient(Daemon_DbClient $dbClient) + { + $this->dbClient = $dbClient; + } + + + public function getEventLog() + { + return $this->eventLog; + } + + + public function execute(Daemon_View $view, $eventId, $params) + { + //fetch event info + $sql = "SELECT name, handle FROM events WHERE event_id=:id"; + $event = $this->dbClient->selectRow($sql, array('id' => $eventId)); + if(!$event) + $event = array('name' => '???', 'handle' => null); + //check if event is implemented + $className = "Daemon_Event_$event[handle]"; + if(class_exists($className, true) && is_subclass_of($className, 'Daemon_EventInterface')) + { + //valid event, execute it (may update character) + $handler = new $className($this->dbClient, $this->characterData, $view); + $this->eventLog = $handler->execute($params); + } + else + { + //no event, update character + Daemon_MsgQueue::add("Nieznane zdarzenie: $event[name]"); + $this->characterData->setLocationEvent(array()); + $this->characterData->put(); + } + } +} diff --git a/2013/daemon/lib/daemon/event/deadend.php b/2013/daemon/lib/daemon/event/deadend.php new file mode 100644 index 0000000..8264846 --- /dev/null +++ b/2013/daemon/lib/daemon/event/deadend.php @@ -0,0 +1,14 @@ +clearEvent(); + ob_start(); + $this->view->display('event/deadend.xml'); + return ob_get_clean(); + } +} diff --git a/2013/daemon/lib/daemon/event/help.php b/2013/daemon/lib/daemon/event/help.php new file mode 100644 index 0000000..f2af808 --- /dev/null +++ b/2013/daemon/lib/daemon/event/help.php @@ -0,0 +1,12 @@ +clearEvent(); + return 'Aaarghhh!'; + } +} diff --git a/2013/daemon/lib/daemon/event/intro.php b/2013/daemon/lib/daemon/event/intro.php new file mode 100644 index 0000000..46aaf21 --- /dev/null +++ b/2013/daemon/lib/daemon/event/intro.php @@ -0,0 +1,31 @@ +clearEvent(); + //check title + $sql = "SELECT 1 FROM character_titles WHERE character_id=:id AND title_id='cultist'"; + $hasTitle = (bool) $this->dbClient->selectValue($sql, array('id' => $this->characterData->character_id)); + if(!$hasTitle) + { + //run combat + $combat = new Daemon_MonsterCombat(); + $combat->attachCharacterData($this->characterData); + $combat->attachDbClient($this->dbClient); + $combat->execute($this->view, 'cultist', true); + $combatLog = $combat->getCombatLog(); + unset($combat); + } + else $combatLog = null; + //display log + ob_start(); + $this->view->combatLog = $combatLog; + $this->view->hasTitle = $hasTitle; + $this->view->display('event/intro.xml'); + return ob_get_clean(); + } +} diff --git a/2013/daemon/lib/daemon/eventinterface.php b/2013/daemon/lib/daemon/eventinterface.php new file mode 100644 index 0000000..9a9df3f --- /dev/null +++ b/2013/daemon/lib/daemon/eventinterface.php @@ -0,0 +1,27 @@ +dbClient = $dbClient; + $this->characterData = $characterData; + $this->view = $view; + } + + + final protected function clearEvent() + { + $this->characterData->setLocationEvent(array()); + $this->characterData->put(); + } + + + abstract public function execute($params); +} diff --git a/2013/daemon/lib/daemon/forum.php b/2013/daemon/lib/daemon/forum.php new file mode 100644 index 0000000..c77d52e --- /dev/null +++ b/2013/daemon/lib/daemon/forum.php @@ -0,0 +1,119 @@ +dbClient = $dbClient; + } + + + //adds new mail message + public function addChat($senderId, $channelId, $content) + { + $content = Daemon::normalizeString($content, true); + if($content) + { + $sql = "INSERT INTO chat (sender_id, channel_id, content) VALUES (:senderId, :channelId, :content)"; + $params = array('senderId' => $senderId, 'channelId' => $channelId, 'content' => $content); + $this->dbClient->query($sql, $params); + } + else Daemon_MsgQueue::add('Podaj treść wiadomości.'); + } + + + //adds new mail message + public function addMail($senderId, $recipientName, $content) + { + if($recipientId = $this->getCharacterIdByName($recipientName)) + $this->addMailById($senderId, $recipientId, $content); + else Daemon_MsgQueue::add('Wybrany adresat nie istnieje.'); + } + + + //adds new mail message + public function addMailById($senderId, $recipientId, $content) + { + $content = Daemon::normalizeString($content, true); + if($content) + { + $sql = "INSERT INTO mail (sender_id, recipient_id, content) VALUES (:senderId, :recipientId, :content)"; + $params = array('senderId' => $senderId, 'recipientId' => $recipientId, 'content' => $content); + $this->dbClient->query($sql, $params); + } + else Daemon_MsgQueue::add('Podaj treść wiadomości.'); + } + + + //updates message with "reply" link + private static function callbackReplyLink(&$row, $key, $characterId) + { + if($row['sender_id'] && ($row['sender_id'] != $characterId)) + $row['replyUrl'] = sprintf('?to=%s', urlencode($row['sender_name'])); + } + + + //finds character Id by its name + private function getCharacterIdByName($name) + { + $sql = "SELECT character_id FROM characters WHERE name = :name"; + return $this->dbClient->selectValue($sql, array('name' => Daemon::normalizeString($name))); + } + + + //fetches messages from selected channel + public function getChat($limit, $from, $channelId) + { + $params = array('limit' => $limit + 1, 'channelId' => $channelId); + $cond = 'channel_id = :channelId'; + if($from) + { + $cond .= ' AND message_id <= :from'; + $params['from'] = (int) $from; + } + $sql = "SELECT m.*, s.name AS sender_name + FROM chat m LEFT JOIN characters s ON s.character_id = m.sender_id + WHERE $cond ORDER BY message_id DESC LIMIT :limit"; + $list = $this->dbClient->selectAll($sql, $params); + if(count($list) > $limit) + { + $next = array_pop($list); + $next = $next['message_id']; + } + else $next = null; + return array('list' => $list, 'next' => $next); + } + + + //fetches character's mailbox + public function getMail($limit, $from, $characterId) + { + $params = array('limit' => $limit + 1, 'cid1' => $characterId, 'cid2' => $characterId); + $cond = '(sender_id = :cid1 OR recipient_id = :cid2)'; + if($from) + { + $cond .= ' AND message_id <= :from'; + $params['from'] = (int) $from; + } + $sql = "SELECT m.*, s.name AS sender_name, r.name AS recipient_name + FROM mail m + LEFT JOIN characters s ON s.character_id = m.sender_id + LEFT JOIN characters r ON r.character_id = m.recipient_id + WHERE $cond ORDER BY message_id DESC LIMIT :limit"; + + $list = $this->dbClient->selectAll($sql, $params); + if(count($list) > $limit) + { + $next = array_pop($list); + $next = $next['message_id']; + } + else $next = null; + if($list) + array_walk($list, array(get_class($this), 'callbackReplyLink'), $characterId); + return array('list' => $list, 'next' => $next); + } +} diff --git a/2013/daemon/lib/daemon/inventory.php b/2013/daemon/lib/daemon/inventory.php new file mode 100644 index 0000000..b169bfb --- /dev/null +++ b/2013/daemon/lib/daemon/inventory.php @@ -0,0 +1,154 @@ +dbClient = $dbClient; + $this->characterData = $characterData; + } + + + //equips selected item + public function equip($inventoryId, $slot) + { + //check item + $sql = "SELECT item_id FROM inventory WHERE inventory_id=:id AND character_id=:charId"; + $params = array('id' => $inventoryId, 'charId' => $this->characterData->character_id); + $id = $this->dbClient->selectValue($sql, $params); + $item = new Daemon_DbObject_Item(); + $item->attachDbClient($this->dbClient); + $item->get(array('item_id' => $id)); + $slots = $item->getSlots(); + //check slot + if(!in_array($slot, $slots)) + { + Daemon_MsgQueue::add('Nie możesz załozyć tego przedmiotu.'); + return false; + } + //check for 2h weapon equipped + $sql = "SELECT 'weapon2h'=i.type FROM inventory inv JOIN items i USING(item_id) + WHERE character_id=:charId AND equipped='hand_a'"; + $params = array('charId' => $this->characterData->character_id); + $zweihander = (bool) $this->dbClient->selectValue($sql, $params); + //equip item, unequip previous item(s) + if( ('weapon2h' == $item->type) || (('weapon1h' == $item->type) && $zweihander) )//2h equipping or equipped + $unequipSlots = "'hand_a','hand_b'"; + else $unequipSlots = "'$slot'"; + $sql = "UPDATE inventory SET equipped = IF(inventory_id=:id, :slot, null) + WHERE character_id=:charId AND (inventory_id=:id OR equipped IN ($unequipSlots))"; + $params = array('id' => $inventoryId, 'charId' => $this->characterData->character_id, 'slot' => $slot); + $this->dbClient->query($sql, $params); + } + + + //creates equipment array based on items list + public function getEquipment(array $items) + { + $result = array(); + foreach(Daemon_Dictionary::$equipmentSlots as $slot => $name) + $result[$slot] = array('slotName' => $name, 'inventoryId' => null, 'flags' => null, 'item' => null); + foreach($items as $row) + { + if(isset($result[$row['equipped']])) + { + $result[$row['equipped']]['inventoryId'] = $row['inventory_id']; + $result[$row['equipped']]['flags'] = $row['flags']; + $result[$row['equipped']]['item'] = $row['item']; + } + } + return $result; + } + + + //fetches a detailed list of character's items + public function getItems($status, $withoutEquipment = false) + { + $cond = "inv.character_id=:characterId"; + if($status) + $cond .= " AND inv.status=:status"; + if($withoutEquipment) + $cond .= " AND equipped IS NULL"; + $sql = "SELECT inv.inventory_id, inv.item_id, inv.status, inv.flags, inv.equipped + FROM inventory inv JOIN items i USING(item_id) + WHERE $cond ORDER BY i.type, i.name, inv.inventory_id"; + $params = array('characterId' => $this->characterData->character_id); + if($status) + $params['status'] = $status; + if($data = $this->dbClient->selectAll($sql, $params)) + { + $result = array(); + foreach($data as $row) + { + $row['_equipped'] = false; + if($row['flags']) + $row['flags'] = array_fill_keys(explode(',', $row['flags']), true); + else $row['flags'] = array(); + $row['item'] = new Daemon_DbObject_Item(); + $row['item']->attachDbClient($this->dbClient); + $row['item']->get(array('item_id' => $row['item_id'])); + $result[$row['inventory_id']] = $row; + } + return $result; + } + else return array(); + } + + + //groups items by status (equipped/inventory/storage) + public function groupItemsByStatus(array $items) + { + $result = array(); + foreach($items as $id => $row) + { + $status = $row['status']; + if(('inventory' == $status) && $row['equipped']) + $status = 'equipment'; + $result[$status]['items'][$id] = $row; + } + $groupNames = array('equipment' => 'Ekwipunek', 'inventory' => 'Plecak', 'storage' => 'Schowek'); + foreach(array_keys($result) as $key) + { + if(isset($groupNames[$key])) + $result[$key]['name'] = $groupNames[$key]; + else unset($groupNames[$key]); + } + return $result; + } + + + //groups items by type + public function groupItemsByType(array $items) + { + $result = array(); + $groupNames = Daemon_Dictionary::$equipmentGroups; + foreach ($groupNames as $key => $name) + $result[$key] = array('name' => $name, 'items' => array()); + foreach($items as $id => $row) + { + $type = $row['item']->type; + $result[$type]['items'][$id] = $row; + } + foreach(array_keys($result) as $key) + { + if (empty($result[$key]['items'])) + unset($result[$key]); + elseif (empty($result[$key]['name'])) + $result[$key]['name'] = '???'; + } + return $result; + } + + + //unequips selected item + public function unequip($inventoryId) + { + $sql = "UPDATE inventory SET equipped=null WHERE inventory_id=:id AND character_id=:charId"; + $params = array('id' => $inventoryId, 'charId' => $this->characterData->character_id); + $this->dbClient->query($sql, $params); + } +} diff --git a/2013/daemon/lib/daemon/item.php b/2013/daemon/lib/daemon/item.php new file mode 100644 index 0000000..bcea5dc --- /dev/null +++ b/2013/daemon/lib/daemon/item.php @@ -0,0 +1,54 @@ +characterData = $characterData; + } + + + public function attachDbClient(Daemon_DbClient $dbClient) + { + $this->dbClient = $dbClient; + } + + + public function getUsageLog() + { + return $this->usageLog; + } + + + public function execute(Daemon_View $view, $inventoryId) + { + //fetch item data + $sql = "SELECT s.handle, i.special_param AS params + FROM inventory inv JOIN items i USING(item_id) + LEFT JOIN item_specials s ON s.special_id = i.special_type + WHERE inv.character_id=:charId AND inv.inventory_id=:id AND i.type='item'"; + $params = array('charId' => $this->characterData->character_id, 'id' => $inventoryId); + $item = $this->dbClient->selectRow($sql, $params); + if(!$item) + $item = array('handle' => null, 'params' => null); + //check if handler is implemented + $className = "Daemon_Item_$item[handle]"; + if(class_exists($className, true) && is_subclass_of($className, 'Daemon_ItemInterface')) + { + //valid event, execute it (may update character) + $handler = new $className($this->dbClient, $this->characterData, $view); + $this->usageLog = $handler->execute($inventoryId, $item['params']); + return true; + } + else + { + //no effect + Daemon_MsgQueue::add('Nie masz pojęcia do czego to może służyć.'); + return false; + } + } +} diff --git a/2013/daemon/lib/daemon/item/givespell.php b/2013/daemon/lib/daemon/item/givespell.php new file mode 100644 index 0000000..d9997be --- /dev/null +++ b/2013/daemon/lib/daemon/item/givespell.php @@ -0,0 +1,33 @@ +characterData; + //get spell data + $sql = "SELECT * FROM spells WHERE spell_id=:id"; + $spell = $this->dbClient->selectRow($sql, array('id' => $spellId)); + if(!$spell) + return 'Takie zaklęcie nie istnieje.'; + //give spell to character + $colName = "sp_$spellId"; + if(($char->$colName > 0) && ($char->$colName <= $spell['min_cost'])) + return "Zaklęcie $spell[name] znasz już na poziomie mistrzowskim."; + if($char->$colName < 1) + { + $msg = "Poznajesz nowe zaklęcie: $spell[name]."; + $char->$colName = $spell['max_cost']; + } + elseif($char->$colName > $spell['min_cost']) + { + $msg = "Lepiej poznajesz zaklęcie $spell[name] - potrzebujesz mniej many by je rzucić."; + $char->$colName -= round(($spell['max_cost'] - $spell['min_cost']) / ($spell['max_level'] - 1)); + } + $char->put(); + $this->deleteItem($inventoryId); + return $msg; + } +} diff --git a/2013/daemon/lib/daemon/item/heal.php b/2013/daemon/lib/daemon/item/heal.php new file mode 100644 index 0000000..5b6ae6d --- /dev/null +++ b/2013/daemon/lib/daemon/item/heal.php @@ -0,0 +1,32 @@ +characterData; + $params = explode(',', $params); + $deltaHealth = isset($params[0]) ? (int) $params[0] : 0; + $deltaMana = isset($params[1]) ? (int) $params[1] : 0; + if($deltaHealth) + { + $oldValue = $char->health; + $char->health = min($char->health_max, $char->health + $deltaHealth); + $deltaHealth = $char->health - $oldValue; + $msg[] = "zdrowie: +$deltaHealth"; + } + if($deltaMana) + { + $oldValue = $char->mana; + $char->mana = min($char->mana_max, $char->mana + $deltaMana); + $deltaMana = $char->mana - $oldValue; + $msg[] = "mana: +$deltaMana"; + } + $char->put(); + $this->deleteItem($inventoryId); + return implode(', ', $msg); + } +} diff --git a/2013/daemon/lib/daemon/item/teleport.php b/2013/daemon/lib/daemon/item/teleport.php new file mode 100644 index 0000000..71d6434 --- /dev/null +++ b/2013/daemon/lib/daemon/item/teleport.php @@ -0,0 +1,15 @@ +characterData->location_id = $locationId; + $this->characterData->put(); + $this->deleteItem($inventoryId); + return "Nagle znajdujesz się zupełnie gdzieindziej..."; + } +} + diff --git a/2013/daemon/lib/daemon/iteminterface.php b/2013/daemon/lib/daemon/iteminterface.php new file mode 100644 index 0000000..c15acf0 --- /dev/null +++ b/2013/daemon/lib/daemon/iteminterface.php @@ -0,0 +1,28 @@ +dbClient = $dbClient; + $this->characterData = $characterData; + $this->view = $view; + } + + + final protected function deleteItem($inventoryId) + { + $sql = "DELETE FROM inventory WHERE character_id=:charId AND inventory_id=:id"; + $params = array('charId' => $this->characterData->character_id, 'id' => $inventoryId); + $this->dbClient->query($sql, $params); + } + + + abstract public function execute($inventoryId, $params); +} diff --git a/2013/daemon/lib/daemon/math.php b/2013/daemon/lib/daemon/math.php new file mode 100644 index 0000000..7ce63b1 --- /dev/null +++ b/2013/daemon/lib/daemon/math.php @@ -0,0 +1,57 @@ +characterData = $characterData; + } + + + public function attachDbClient(Daemon_DbClient $dbClient) + { + $this->dbClient = $dbClient; + } + + + public function getCombatLog() + { + return $this->combatLog; + } + + + public function execute(Daemon_View $view, $monsterId, $fromEvent = false) + { + $char = $this->characterData; + $winner = null; + $winnerXp = 0; + $winnerGold = null; + $winnerLevel = null; + $winnerDrop = null; + $winnerMission = null; + //load monster + $monster = new Daemon_DbObject_Monster(); + $monster->attachDbClient($this->dbClient); + $monster->get(array('monster_id' => $monsterId)); + //execute combat + $winner = $this->runCombat($monster); + //check for winner, modify character + if(self::WINNER_ACTOR == $winner) + { + //experience + $winnerXp = $monster->level; + $this->characterData->xp_free += $winnerXp; + //gold + $winnerGold = $monster->gold; + $this->characterData->gold_purse += $winnerGold; + //level + if($monster->level > $this->characterData->level) + { + $winnerLevel = $monster->level; + $this->characterData->level = $winnerLevel; + } + else $winnerLevel = null; + //statistics + if(($monster->class >=1) && ($monster->class<=4)) + { + $colname = "kills_mob{$monster->class}"; + $sql = "UPDATE character_statistics SET $colname=$colname+1 WHERE character_id=:id"; + $this->dbClient->query($sql, array('id' => $this->characterData->character_id)); + } + //drop + $winnerDrop = $this->rollDrop($monster); + //title + if($monster->title_id) + { + $sql = "INSERT IGNORE INTO character_titles(character_id, title_id) VALUES(:charId, :titleId)"; + $params = array( + 'charId' => $this->characterData->character_id, + 'titleId' => $monster->title_id); + $this->dbClient->query($sql, $params); + } + //mission + $mission = $this->characterData->getLastMission('active'); + if(('monster' == $mission['type']) && ($monster->monster_id == $mission['params'])) + { + $sql = "UPDATE character_missions SET progress='completed' + WHERE character_id=:cid AND rollover_id=:rid"; + $params = array('cid' => $this->characterData->character_id, 'rid' => $mission['rollover_id']); + $this->dbClient->query($sql, $params); + $winnerMission = true; + } + } + elseif(self::WINNER_MONSTER == $winner) + $this->characterData->setDeath(true); + //update character + $this->characterData->setLocationEvent(array()); + $this->characterData->put(); + //generate report + $view->combatLog = $this->combatLog; + $view->fromEvent = $fromEvent; + $view->monsterName = $monster->name; + $view->winner = $winner; + $view->winnerXp = $winnerXp; + $view->winnerGold = $winnerGold; + $view->winnerLevel = $winnerLevel; + $view->winnerDrop = $winnerDrop; + $view->winnerMission = $winnerMission; + ob_start(); + $view->display('monstercombat.xml'); + $this->combatLog = ob_get_clean(); + } + + + //rolls for monster drops, return array of item names + private function rollDrop(Daemon_DbObject_Monster $monster) + { + $itemId = null; + $itemName = null; + //check if there was any drop + if($monster->chance2) + { + $d256 = mt_rand(0, 255); + $chance = 256 * $monster->chance1 / $monster->chance2; + $itemId = ($d256 < $chance); + } + if($itemId) + { + $itemId = null; + //read drops + $sql = "SELECT item_id, chance, name FROM monster_drops JOIN items USING(item_id) WHERE monster_id=:id"; + $drops = $this->dbClient->selectAll($sql, array('id' => $monster->monster_id)); + //roll drop + $chanceSum = 0; + foreach($drops as $row) + $chanceSum += $row['chance']; + $d256 = mt_rand(0, 255); + foreach($drops as $row) + { + $chance = 256 * $row['chance'] / $chanceSum; + if($d256 < $chance) + { + $itemId = $row['item_id']; + $itemName = $row['name']; + break; + } + $d256 -= $chance; + } + //give drop + if($itemId) + { + $sql = "INSERT INTO inventory(character_id, item_id) VALUES (:charId, :itemId)"; + $params = array('charId' => $this->characterData->character_id, 'itemId' => $itemId); + $this->dbClient->query($sql, $params); + } + } + return $itemName; + } + + + //executes combat, returns winner flag + private function runCombat(Daemon_DbObject_Monster $monster) + { + $combat = new Daemon_Combat(); + $logger = new Daemon_Combat_Log(); + $combat->attachLogger($logger); + //prepare units + $characterUnit = $this->characterData->getCombatUnit(true); + $monsterUnit = $monster->getCombatUnit(true); + $monsterUnit->health = $monsterUnit->health_max; + //add units + $combat->addUnit('a', $characterUnit, true); + $combat->addUnit('b', $monsterUnit, false); + //execute combat + $combat->execute(); + $this->combatLog = (string) $logger; + //update character + $characterUnit->put(); + $this->characterData->health = floor($characterUnit->health); + //check winner + if($this->characterData->health < 1) + return self::WINNER_MONSTER; + if($monsterUnit->health < 1) + return self::WINNER_ACTOR; + return null; + } +} diff --git a/2013/daemon/lib/daemon/msgqueue.php b/2013/daemon/lib/daemon/msgqueue.php new file mode 100644 index 0000000..f9043bf --- /dev/null +++ b/2013/daemon/lib/daemon/msgqueue.php @@ -0,0 +1,27 @@ +dbClient = $dbClient; + } + + + protected function callbackFormatDates(&$row) + { + $row['published'] = date(DATE_ATOM, $row['published_ts']); + $row['updated'] = date(DATE_ATOM, $row['updated_ts']); + } + + + //deletes entry by ID + public function deleteEntry($id) + { + $sql = "DELETE FROM newsfeed WHERE id=:id"; + $this->dbClient->query($sql, array('id' => $id)); + } + + + //generates an ID for a new entry + public function generateId($domain, $title) + { + $suffix = preg_replace('/\W+/', '-', $title); + return sprintf('tag:%s,%s:%s', $domain, date('Y-m-d'), $suffix); + } + + + //fetches last entry's update time + public function getLastUpdated() + { + $sql = "SELECT UNIX_TIMESTAMP(MAX(updated)) FROM newsfeed"; + return date(DATE_ATOM, (int) $this->dbClient->selectValue($sql, array())); + } + + + //fetches a list of last entries + public function getEntries($limit, $format = false) + { + $params = array(); + $sql = "SELECT *, UNIX_TIMESTAMP(published) AS published_ts, UNIX_TIMESTAMP(updated) AS updated_ts + FROM newsfeed ORDER BY published DESC"; + if($limit) + { + $params = array('limit' => $limit); + $sql .= " LIMIT :limit"; + } + $data = $this->dbClient->selectAll($sql, $params); + if($format && $data) + array_walk($data, array($this, 'callbackFormatDates')); + return $data; + } + + + //creates or updates a feed entry + public function updateEntry($id, $title, $author, $content) + { + $sql = "INSERT INTO newsfeed (id, published, title, author, content) + VALUES (:id, NOW(), :title, :author, :content) + ON DUPLICATE KEY UPDATE updated=NOW(), title=:title, author=:author, content=:content"; + $params = array('id' => $id, 'title' => $title, 'author' => $author, 'content' => $content); + $this->dbClient->query($sql, $params); + } +} diff --git a/2013/daemon/lib/daemon/scyzoryk.php b/2013/daemon/lib/daemon/scyzoryk.php new file mode 100644 index 0000000..d3db7ed --- /dev/null +++ b/2013/daemon/lib/daemon/scyzoryk.php @@ -0,0 +1,73 @@ +dbClient = $dbClient; + } + + + public function deleteRows($tableName, $indexCol, array $ids) + { + $sql = "DELETE FROM $tableName WHERE $indexCol=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('id' => $id)); + } + + + public function selectRow($className, $id, $id2 = null) + { + $tableName = constant("$className::TABLE_NAME"); + $indexCol = constant("$className::INDEX_COL"); + $indexCol2 = constant("$className::INDEX_COL2"); + if(!$tableName || !$indexCol) + throw new InvalidArgumentException('Unsupported class name!'); + $cond = array("$indexCol=:id"); + $params = array('id' => $id); + if($indexCol2 && $id2) + { + $cond[] = "$indexCol2=:id2"; + $params['id2'] = $id2; + } + $cond = implode(' AND ', $cond); + $sql = "SELECT * FROM $tableName WHERE $cond"; + return $this->dbClient->selectObject($sql, $params, $className); + } + + + public function updateRow(Daemon_Scyzoryk_DbRow $row) + { + $row->validate(); + $className = get_class($row); + $tableName = constant("$className::TABLE_NAME"); + $indexCol = constant("$className::INDEX_COL"); + $indexCol2 = constant("$className::INDEX_COL2"); + if(!$tableName || !$indexCol) + throw new InvalidArgumentException('This table must be edited manually!'); + $cols = array(); + $vals = array(); + $mods = array(); + $params = array(); + $ignore = array($indexCol, $indexCol2); + foreach($row as $col => $val) + { + $cols[] = $col; + $vals[] = ":$col"; + if(!in_array($col, $ignore)) + $mods[] = "$col=:$col"; + $params[$col] = $val; + } + $cols = implode(', ', $cols); + $vals = implode(', ', $vals); + $mods = implode(', ', $mods); + if($mods) + $sql = "INSERT INTO $tableName ($cols) VALUES ($vals) ON DUPLICATE KEY UPDATE $mods"; + else $sql = "REPLACE INTO $tableName ($cols) VALUES ($vals)"; + $this->dbClient->query($sql, $params); + } +} diff --git a/2013/daemon/lib/daemon/scyzoryk/browser.php b/2013/daemon/lib/daemon/scyzoryk/browser.php new file mode 100644 index 0000000..5ec4cfd --- /dev/null +++ b/2013/daemon/lib/daemon/scyzoryk/browser.php @@ -0,0 +1,235 @@ + $id, 'name' => $name); + return $this->dbClient->selectAll($sql, $params); + } + + + public function getCombatUnits(Daemon_Scyzoryk_Filter $filter = null) + { + $cond = array(); + $params = array(); + if($filter instanceof Daemon_Scyzoryk_Filter) + { + if($filter->id) + { + $cond[] = "combat_unit_id LIKE CONCAT('%', :id, '%')"; + $params['id'] = $filter->id; + } + if($filter->name) + { + $cond[] = "name LIKE CONCAT('%', :name, '%')"; + $params['name'] = $filter->name; + } + } + $cond = $cond ? 'WHERE '.implode(' AND ', $cond) : null; + $sql = "SELECT *, (combat_unit_id LIKE 'character-%') AS _character + FROM combat_units $cond ORDER BY _character, combat_unit_id"; + return $this->dbClient->selectAll($sql, $params); + } + + + public function getFactionRanks($factionId) + { + $sql = "SELECT * FROM faction_ranks r LEFT JOIN titles USING(title_id) WHERE r.faction_id=:id ORDER BY rank_id"; + return $this->dbClient->selectAll($sql, array('id' => $factionId)); + } + + + public function getFactions() + { + $sql = "SELECT * FROM factions ORDER BY faction_id"; + return $this->dbClient->selectAll($sql, array()); + } + + + public function getItems(Daemon_Scyzoryk_Filter $filter) + { + $cond = array(); + $params = array(); + if($filter->id) + { + $cond[] = "item_id LIKE CONCAT('%', :id, '%')"; + $params['id'] = $filter->id; + } + if($filter->name) + { + $cond[] = "name LIKE CONCAT('%', :name, '%')"; + $params['name'] = $filter->name; + } + if($filter->type) + { + $cond[] = 'type = :type'; + $params['type'] = $filter->type; + } + $cond = $cond ? 'WHERE '.implode(' AND ', $cond) : null; + $sql = "SELECT * FROM items $cond + ORDER BY type, damage_type, suggested_value, value, item_id"; + return $this->dbClient->selectAll($sql, $params); + } + + + public function getItemTemplates() + { + $sql = "SELECT * FROM item_templates ORDER BY id"; + return $this->dbClient->selectAll($sql, array()); + } + + + public function getLocationEvents($locationId) + { + $sql = "SELECT * FROM location_events + WHERE location_id=:id ORDER BY event_id"; + return $this->dbClient->selectAll($sql, array('id' => $locationId)); + } + + + public function getLocationMonsters($locationId) + { + $sql = "SELECT l.*, m.name AS monster_name + FROM location_monsters l + LEFT JOIN monsters m USING(monster_id) + WHERE l.location_id=:id ORDER BY l.monster_id"; + return $this->dbClient->selectAll($sql, array('id' => $locationId)); + } + + + public function getLocationPaths($locationId) + { + $sql = "SELECT p.*, loc.name AS destination_name + FROM location_paths p + LEFT JOIN locations loc ON p.destination_id = loc.location_id + WHERE p.location_id=:id ORDER BY p.destination_id"; + return $this->dbClient->selectAll($sql, array('id' => $locationId)); + } + + + public function getLocationServices($locationId) + { + $sql = "SELECT l.*, s.name AS service_name, s.type AS service_type + FROM location_services l + LEFT JOIN services s USING(service_id) + WHERE l.location_id=:id ORDER BY s.service_id"; + return $this->dbClient->selectAll($sql, array('id' => $locationId)); + } + + + public function getLocations(Daemon_Scyzoryk_Filter $filter) + { + $cond = array(); + $params = array(); + if($filter->id) + { + $cond[] = "l.location_id LIKE CONCAT('%', :id, '%')"; + $params['id'] = $filter->id; + } + if($filter->name) + { + $cond[] = "l.name LIKE CONCAT('%', :name, '%')"; + $params['name'] = $filter->name; + } + if($filter->region_id) + { + $cond[] = 'l.region_id = :region_id'; + $params['region_id'] = $filter->region_id; + } + $cond = $cond ? 'WHERE '.implode(' AND ', $cond) : null; + $sql = "SELECT l.*, r.name AS region_name, + ( SELECT GROUP_CONCAT(lp.destination_id SEPARATOR ',') + FROM location_paths lp WHERE lp.location_id=l.location_id ) AS paths, + ( SELECT GROUP_CONCAT(lm.monster_id SEPARATOR ',') + FROM location_monsters lm WHERE lm.location_id=l.location_id ) AS monsters + FROM locations l LEFT JOIN regions r USING(region_id) $cond ORDER BY l.region_id, l.location_id"; + $data = $this->dbClient->selectAll($sql, $params); + foreach ($data as &$row) + { + $row['paths'] = explode(',', $row['paths']); + $row['monsters'] = explode(',', $row['monsters']); + } + return $data; + } + + + public function getMaps() + { + $sql = "SELECT * FROM maps ORDER BY sort, map_id"; + return $this->dbClient->selectAll($sql, array()); + } + + + public function getMonsterDrops($monsterId) + { + $sql = "SELECT m.*, i.name FROM monster_drops m LEFT JOIN items i USING(item_id) WHERE m.monster_id=:id"; + return $this->dbClient->selectAll($sql, array('id' => $monsterId)); + } + + + public function getMonsters(Daemon_Scyzoryk_Filter $filter) + { + $cond = array(); + $params = array(); + if($filter->id) + { + $cond[] = "m.monster_id LIKE CONCAT('%', :id, '%')"; + $params['id'] = $filter->id; + } + if($filter->name) + { + $cond[] = "m.name LIKE CONCAT('%', :name, '%')"; + $params['name'] = $filter->name; + } + if($filter->class) + { + $cond[] = "m.class = :class"; + $params['class'] = $filter->class; + } + $cond = $cond ? 'WHERE '.implode(' AND ', $cond) : null; + $sql = "SELECT m.*, ( SELECT GROUP_CONCAT(md.item_id SEPARATOR ', ') + FROM monster_drops md WHERE md.monster_id=m.monster_id ) AS drops + FROM monsters m $cond ORDER BY m.level, m.monster_id"; + $data = $this->dbClient->selectAll($sql, $params); + foreach ($data as &$row) + $row['drops'] = explode(',', $row['drops']); + return $data; + } + + + public function getRegions() + { + $sql = "SELECT r.*, l.name AS respawn_name + FROM regions r LEFT JOIN locations l ON l.location_id = r.respawn_id + ORDER BY r.region_id"; + return $this->dbClient->selectAll($sql, array()); + } + + + public function getServiceItems($serviceId) + { + $sql = "SELECT s.*, i.name, s.type='drop' AS _drop + FROM service_items s LEFT JOIN items i USING(item_id) + WHERE s.service_id=:id ORDER BY type, item_id"; + return $this->dbClient->selectAll($sql, array('id' => $serviceId)); + } + + + public function getServices() + { + $sql = "SELECT * FROM services ORDER BY service_id"; + return $this->dbClient->selectAll($sql, array()); + } + + + public function getTitles() + { + $sql = "SELECT * FROM titles ORDER BY title_id"; + return $this->dbClient->selectAll($sql, array()); + } +} diff --git a/2013/daemon/lib/daemon/scyzoryk/controller.php b/2013/daemon/lib/daemon/scyzoryk/controller.php new file mode 100644 index 0000000..0baccb7 --- /dev/null +++ b/2013/daemon/lib/daemon/scyzoryk/controller.php @@ -0,0 +1,81 @@ +cfg = $cfg; + session_name($this->cfg->sessionName); + session_cache_limiter(null); + session_start(); + $this->dbClient = Daemon::createDbClient($this->cfg); + $this->browser = new Daemon_Scyzoryk_Browser($this->dbClient); + $this->editor = new Daemon_Scyzoryk_Editor($this->dbClient); + $this->view = new Daemon_View($this->cfg); + $this->editId = isset($_GET['id']) ? $_GET['id'] : null; + $this->editId2 = isset($_GET['id2']) ? $_GET['id2'] : null; + } + + + //checks last action's timestamp + final private function checkActionTimestamp() + { + $lastAction = isset($_SESSION['ts']) ? $_SESSION['ts'] : 0.0; + $_SESSION['ts'] = microtime(true); + return (bool) ($_SESSION['ts'] >= $lastAction + $this->cfg->tsDelta); + } + + + final public function execute() + { + //prepare controller + $this->prepareModel(); + //check last action's timestamp + if($_POST && !$this->checkActionTimestamp()) + { + Daemon_MsgQueue::add('Operacja anulowana: za duża częstość.'); + $_POST = array(); + } + //execute commands + $cmdExecuted = (bool) $this->runCommands(); + //display page + $this->prepareView(); + $this->view->setPageTitle($this->pageSubtitle, $this->pageSubtitleDetails, $cmdExecuted); + $this->view->setMessages(Daemon_MsgQueue::getAll()); + $this->view->display($this->pageTemplatePath, Daemon_View::MODE_HTML); + } + + + //page-specific + protected function prepareModel() + { + } + + + //page-specific + protected function prepareView() + { + } + + + //page-specific + protected function runCommands() + { + return false; + } +} diff --git a/2013/daemon/lib/daemon/scyzoryk/dbrow.php b/2013/daemon/lib/daemon/scyzoryk/dbrow.php new file mode 100644 index 0000000..f271ff9 --- /dev/null +++ b/2013/daemon/lib/daemon/scyzoryk/dbrow.php @@ -0,0 +1,151 @@ + $value) + if(property_exists($this, $name)) + $this->$name = Daemon::normalizeString($value, true); + $this->validate(); + } + public function validate() {} +} + + +class Daemon_Scyzoryk_DbRowFaction extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'factions'; + const INDEX_COL = 'faction_id'; + public $faction_id; + public $name; +} + + +class Daemon_Scyzoryk_DbRowFactionRank extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'faction_ranks'; + const INDEX_COL = 'faction_id'; + const INDEX_COL2 = 'rank_id'; + public $faction_id; + public $rank_id; + public $min_points = 1; + public $title_id = null; +} + + +class Daemon_Scyzoryk_DbRowLocationEvent extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'location_events'; + const INDEX_COL = 'location_id'; + const INDEX_COL2 = 'event_id'; + public $location_id; + public $event_id; + public $chance = 1; + public $params = ''; +} + + +class Daemon_Scyzoryk_DbRowLocationMonster extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'location_monsters'; + const INDEX_COL = 'location_id'; + const INDEX_COL2 = 'monster_id'; + public $location_id; + public $monster_id; + public $chance = 1; +} + + +class Daemon_Scyzoryk_DbRowLocationPath extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'location_paths'; + const INDEX_COL = 'location_id'; + const INDEX_COL2 = 'destination_id'; + public $location_id; + public $destination_id; + public $name = null; + public $cost_gold = 0; + public $cost_mana = 0; +} + + +class Daemon_Scyzoryk_DbRowLocationService extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'location_services'; + const INDEX_COL = 'location_id'; + const INDEX_COL2 = 'service_id'; + public $location_id; + public $service_id; +} + + +class Daemon_Scyzoryk_DbRowMap extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'maps'; + const INDEX_COL = 'map_id'; + public $map_id; + public $name; + public $url = ''; +} + + +class Daemon_Scyzoryk_DbRowMonsterDrop extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'monster_drops'; + const INDEX_COL = 'monster_id'; + const INDEX_COL2 = 'item_id'; + public $monster_id; + public $item_id; + public $chance = 1; +} + + +class Daemon_Scyzoryk_DbRowRegion extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'regions'; + const INDEX_COL = 'region_id'; + public $region_id; + public $name; + public $respawn_id = null; + public $picture_url = null; +} + + +class Daemon_Scyzoryk_DbRowService extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'services'; + const INDEX_COL = 'service_id'; + public $service_id; + public $name; + public $type = 'npc'; + public $faction_id; + public $rank_id; + public $description = null; +} + + +class Daemon_Scyzoryk_DbRowServiceItem extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'service_items'; + const INDEX_COL = 'service_id'; + const INDEX_COL2 = 'item_id'; + public $service_id; + public $item_id; + public $type = 'normal'; + public $quantity = null; +} + + +class Daemon_Scyzoryk_DbRowTitle extends Daemon_Scyzoryk_DbRow +{ + const TABLE_NAME = 'titles'; + const INDEX_COL = 'title_id'; + public $title_id; + public $name_f = ''; + public $name_m = ''; + public $name_n = ''; +} diff --git a/2013/daemon/lib/daemon/scyzoryk/editor.php b/2013/daemon/lib/daemon/scyzoryk/editor.php new file mode 100644 index 0000000..1b5adba --- /dev/null +++ b/2013/daemon/lib/daemon/scyzoryk/editor.php @@ -0,0 +1,139 @@ +deleteRows('combat_units', 'combat_unit_id', $ids); + } + + + public function deleteFactionRanks($factionId, array $ids) + { + $sql = "DELETE FROM faction_ranks WHERE faction_id=:factionId AND rank_id=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('factionId' => $factionId, 'id' => $id)); + } + + + public function deleteFactions(array $ids) + { + $this->deleteRows('factions', 'faction_id', $ids); + $this->deleteRows('faction_ranks', 'faction_id', $ids); + } + + + public function deleteItems(array $ids) + { + $this->deleteRows('items', 'item_id', $ids); + $this->deleteRows('service_items', 'item_id', $ids); + $this->deleteRows('monster_drops', 'item_id', $ids); + } + + + public function deleteItemTemplates(array $ids) + { + $this->deleteRows('item_templates', 'id', $ids); + } + + + public function deleteLocationEvents($locationId, array $ids) + { + $sql = "DELETE FROM location_events WHERE location_id=:locationId AND event_id=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('locationId' => $locationId, 'id' => $id)); + } + + + public function deleteLocationMonsters($locationId, array $ids) + { + $sql = "DELETE FROM location_monsters WHERE location_id=:locationId AND monster_id=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('locationId' => $locationId, 'id' => $id)); + } + + + public function deleteLocationPaths($srcId, array $dst, array $rev = array()) + { + $sql = "DELETE FROM location_paths WHERE location_id=:src AND destination_id=:dst"; + foreach($dst as $key => $id) + { + $this->dbClient->query($sql, array('src' => $srcId, 'dst' => $id)); + if(!empty($rev[$key])) + $this->dbClient->query($sql, array('src' => $id, 'dst' => $srcId)); + } + } + + + public function deleteLocationServices($locationId, array $ids) + { + $sql = "DELETE FROM location_services WHERE location_id=:locationId AND service_id=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('locationId' => $locationId, 'id' => $id)); + } + + + public function deleteLocations(array $ids) + { + $this->deleteRows('locations', 'location_id', $ids); + $this->deleteRows('location_events', 'location_id', $ids); + $this->deleteRows('location_monsters', 'location_id', $ids); + $this->deleteRows('location_paths', 'location_id', $ids); + $this->deleteRows('location_paths', 'destination_id', $ids); + $this->deleteRows('location_services', 'location_id', $ids); + } + + + public function deleteMaps(array $ids) + { + $this->deleteRows('maps', 'map_id', $ids); + } + + + public function deleteMonsterDrops($monsterId, array $ids) + { + $sql = "DELETE FROM monster_drops WHERE monster_id=:monsterId AND item_id=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('monsterId' => $monsterId, 'id' => $id)); + } + + + public function deleteMonsters(array $ids) + { + $this->deleteRows('monsters', 'monster_id', $ids); + $this->deleteRows('monster_drops', 'monster_id', $ids); + $this->deleteRows('location_monsters', 'monster_id', $ids); + } + + + public function deleteRegions(array $ids) + { + $this->deleteRows('regions', 'region_id', $ids); + $sql = "UPDATE locations SET region_id = null WHERE region_id=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('id' => $id)); + } + + + public function deleteServiceItems($serviceId, array $ids) + { + $sql = "DELETE FROM service_items WHERE service_id=:serviceId AND item_id=:id"; + foreach($ids as $id) + $this->dbClient->query($sql, array('serviceId' => $serviceId, 'id' => $id)); + } + + + public function deleteServices(array $ids) + { + $this->deleteRows('services', 'service_id', $ids); + $this->deleteRows('service_items', 'service_id', $ids); + } + + + public function deleteTitles(array $ids) + { + $this->deleteRows('titles', 'title_id', $ids); + } +} diff --git a/2013/daemon/lib/daemon/scyzoryk/filter.php b/2013/daemon/lib/daemon/scyzoryk/filter.php new file mode 100644 index 0000000..3dafddf --- /dev/null +++ b/2013/daemon/lib/daemon/scyzoryk/filter.php @@ -0,0 +1,43 @@ +cols = array_merge(array('id', 'name'), (array) $extraCols); + if(!isset($_SESSION[self::SESSION_VARNAME][$name])) + $_SESSION[self::SESSION_VARNAME][$name] = array(); + if(!$noSession) + $this->data = & $_SESSION[self::SESSION_VARNAME][$name]; + else $this->data = array(); + } + + + public function __get($name) + { + return isset($this->data[$name]) ? $this->data[$name] : null; + } + + + public function __isset($name) + { + return in_array($name, $this->cols) ? true : isset($this->data[$name]); + } + + + public function __set($name, $value) + { + $this->data[$name] = $value; + } + + + public function __unset($name) + { + unset($this->data[$name]); + } +} diff --git a/2013/daemon/lib/daemon/service.php b/2013/daemon/lib/daemon/service.php new file mode 100644 index 0000000..0ea66a7 --- /dev/null +++ b/2013/daemon/lib/daemon/service.php @@ -0,0 +1,47 @@ +dbClient = $dbClient; + $this->characterData = $characterData; + $this->view = $view; + $this->serviceData = $serviceData; + $this->bankEnabled = $bankEnabled; + $this->templeEnabled = $templeEnabled; + } + + + final protected function clearEvent() + { + $this->characterData->setLocationEvent(array()); + $this->characterData->put(); + } + + + final public function getEventLog() + { + return $this->eventLog; + } + + + final public function isCommand() + { + return $this->isCommand; + } + + + abstract public function execute($params); +} diff --git a/2013/daemon/lib/daemon/service/bank.php b/2013/daemon/lib/daemon/service/bank.php new file mode 100644 index 0000000..c78a826 --- /dev/null +++ b/2013/daemon/lib/daemon/service/bank.php @@ -0,0 +1,95 @@ +dbClient, $this->characterData); + //run commands + $this->isCommand = $this->runCommands($params); + //generate output + $items = $inventory->getItems('inventory', true); + $storage = $inventory->getItems('storage'); + $storageLimit = $this->getStorageLimit(); + $this->view->goldBank = $this->characterData->gold_bank; + $this->view->goldPurse = $this->characterData->gold_purse; + $this->view->inventory = $items; + $this->view->storage = $storage; + $this->view->storageLimit = $storageLimit; + $this->view->storageFull = (count($storage) >= $storageLimit); + ob_start(); + $this->view->display('service/bank.xml'); + $this->eventLog = ob_get_clean(); + } + + + private function runCommands($params) + { + //gold operations + if(isset($params['getGold'], $params['putGold'])) + { + $this->goldOperations($params['getGold'], $params['putGold']); + return true; + } + //item operations + if(isset($params['getItem'])) + { + $this->getItem($params['getItem']); + return true; + } + if(isset($params['putItem'])) + { + $this->putItem($params['putItem']); + return true; + } + return false; + } + + + //fetches item from storage + private function getItem($inventoryId) + { + $sql = "UPDATE inventory SET status='inventory' WHERE inventory_id=:id AND character_id=:charId"; + $params = array('id' => $inventoryId, 'charId' => $this->characterData->character_id); + $this->dbClient->query($sql, $params); + } + + + private function getStorageLimit() + { + return self::STORAGE_LIMIT + max(0, floor(log($this->characterData->level))); + } + + + //puts item into storage + private function putItem($inventoryId) + { + $sql = "SELECT COUNT(1) FROM inventory WHERE character_id=:charId AND status='storage'"; + $n = $this->dbClient->selectValue($sql, array('charId' => $this->characterData->character_id)); + if($n < $this->getStorageLimit()) + { + $sql = "UPDATE inventory SET status='storage' WHERE inventory_id=:id AND character_id=:charId AND equipped IS NULL"; + $params = array('id' => $inventoryId, 'charId' => $this->characterData->character_id); + $this->dbClient->query($sql, $params); + } + else Daemon_MsgQueue::add('Twój schowek jest pełny.'); + } + + + private function goldOperations($getGold, $putGold) + { + //get gold + $delta = max(0, min((int) $getGold, $this->characterData->gold_bank)); + $this->characterData->gold_purse += $delta; + $this->characterData->gold_bank -= $delta; + //put gold + $delta = max(0, min((int) $putGold, $this->characterData->gold_purse)); + $this->characterData->gold_bank += $delta; + $this->characterData->gold_purse -= $delta; + //store mods + $this->characterData->put(); + } +} diff --git a/2013/daemon/lib/daemon/service/healer.php b/2013/daemon/lib/daemon/service/healer.php new file mode 100644 index 0000000..38ad744 --- /dev/null +++ b/2013/daemon/lib/daemon/service/healer.php @@ -0,0 +1,86 @@ +setParams(); + //run commands + $this->isCommand = $this->runCommands($params); + //generate output + ob_start(); + $this->view->characterData = $this->characterData; + $this->view->bankEnabled = $this->bankEnabled; + $this->view->deltaHealthMin = $this->deltaHealthMin; + $this->view->deltaHealthMax = $this->deltaHealthMax; + $this->view->deltaManaMin = $this->deltaManaMin; + $this->view->deltaManaMax = $this->deltaManaMax; + $this->view->display('service/healer.xml'); + $this->eventLog = ob_get_clean(); + } + + + private function runCommands($params) + { + if(isset($params['heal'])) + { + $this->heal($params['heal'], $this->bankEnabled); + return true; + } + if(isset($params['rest'])) + { + $this->rest(); + return true; + } + return false; + } + + + //heals a random amount of health and mana + public function heal($gold, $bankEnabled) + { + if (!$this->characterData->payGold($gold, $bankEnabled)) + return false; + $this->characterData->health += mt_rand($gold * $this->deltaHealthMin, $gold * $this->deltaHealthMax); + $this->characterData->mana += mt_rand($gold * $this->deltaManaMin, $gold * $this->deltaManaMax); + if($this->characterData->health > $this->characterData->health_max) + $this->characterData->health = $this->characterData->health_max; + if($this->characterData->mana > $this->characterData->mana_max) + $this->characterData->mana = $this->characterData->mana_max; + $this->characterData->put(); + return true; + } + + + public function setParams() + { + $dbCfg = new Daemon_DbConfig($this->dbClient); + $params = $dbCfg->healer; + if(is_scalar($params)) + $params = explode(',', $params); + if(isset($params[0], $params[1], $params[2], $params[3])) + { + $this->deltaHealthMin = (int) $params[0]; + $this->deltaHealthMax = (int) $params[1]; + $this->deltaManaMin = (int) $params[2]; + $this->deltaManaMax = (int) $params[3]; + } + } + + + //rests one turn without events + public function rest() + { + if(!$this->characterData->checkTurnCosts()) + return false; + $this->characterData->regen(true); + $this->characterData->put(); + return true; + } +} diff --git a/2013/daemon/lib/daemon/service/shop.php b/2013/daemon/lib/daemon/service/shop.php new file mode 100644 index 0000000..9f351a3 --- /dev/null +++ b/2013/daemon/lib/daemon/service/shop.php @@ -0,0 +1,202 @@ +dbClient, $this->characterData); + //run commands + $this->isCommand = $this->runCommands($params); + //generate output + $equipment = array(); + $inventory = array(); + $items = $inventoryObj->getItems('inventory'); + foreach($items as $key => $row) + { + $row['_price'] = ceil($row['item']->value / 2); + if($row['equipped']) + $equipment[$key] = $row; + else $inventory[$key] = $row; + } + $storage = $this->bankEnabled ? $inventoryObj->getItems('storage') : array(); + foreach($storage as &$row) + $row['_price'] = ceil($row['item']->value / 2); + $this->view->shopName = $this->serviceData['name']; + $this->view->shopDescription = $this->serviceData['description']; + $this->view->goldBank = $this->characterData->gold_bank; + $this->view->goldPurse = $this->characterData->gold_purse; + $this->view->bankEnabled = $this->bankEnabled; + $this->view->equipment = $equipment; + $this->view->inventory = $inventory; + $this->view->storage = $storage; + $this->view->hasItems = ($equipment || $inventory || $storage); + $this->view->shopItems = $this->getItems(); + ob_start(); + $this->view->display('service/shop.xml'); + $this->eventLog = ob_get_clean(); + } + + + private function runCommands($params) + { + //sell item + if(isset($params['sell'])) + { + $this->buyFromCharacter($params['sell']); + return true; + } + //buy item(s) + if(isset($params['buy'], $params['amount'])) + { + $this->sellToCharacter($params['buy'], $params['amount'], !empty($params['bind'])); + return true; + } + return false; + } + + + //buys item from character + public function buyFromCharacter(array $ids) + { + foreach($ids as $inventoryId) + { + $cond = "inv.inventory_id=:id AND character_id=:charId"; + $params = array('id' => $inventoryId, 'charId' => $this->characterData->character_id); + if(!$this->bankEnabled) + $cond .= " AND inv.status!='storage'"; + $sql = "SELECT i.item_id, i.name, i.value, inv.equipped + FROM inventory inv JOIN items i USING(item_id) WHERE $cond"; + if($item = $this->dbClient->selectRow($sql, $params)) + { + $sql = "DELETE FROM inventory WHERE inventory_id=:id"; + $this->dbClient->query($sql, array('id' => $inventoryId)); + $buyPrice = ceil($item['value'] / 2); + if($this->bankEnabled) + $this->characterData->gold_bank += $buyPrice; + else $this->characterData->gold_purse += $buyPrice; + $this->characterData->put(); + //add to shop offer + $sql = "INSERT INTO service_items (service_id, item_id, type, quantity) + VALUES (:serviceId, :itemId, 'drop', 1) ON DUPLICATE KEY UPDATE quantity = quantity + 1"; + $params = array('serviceId' => $this->serviceData['service_id'], 'itemId' => $item['item_id']); + $this->dbClient->query($sql, $params); + Daemon_MsgQueue::add("Sprzedajesz $item[name] za $buyPrice zł."); + //update character stats if item was equipped + if($item['equipped']) + { + $this->characterData->resetCombatStats(); + $this->characterData->put(); + } + } + else Daemon_MsgQueue::add('Wybrany przedmiot nie istnieje.'); + } + } + + + //fetches shop's offer, grouped by item type + public function getItems() + { + $result = array(); + $sql = "SELECT s.item_id, s.type, s.quantity + FROM service_items s JOIN items i USING(item_id) + WHERE service_id=:id ORDER BY i.type ASC, s.type ASC, i.damage_type ASC, i.name ASC"; + $params = array('id' => $this->serviceData['service_id']); + if($data = $this->dbClient->selectAll($sql, $params)) + { + $characterGold = $this->characterData->gold_purse; + if($this->bankEnabled) + $characterGold += $this->characterData->gold_bank; + foreach($data as $row) + { + $item = new Daemon_DbObject_Item(); + $item->attachDbClient($this->dbClient); + $item->get(array('item_id' => $row['item_id'])); + $type = $item->type; + $item->_price = $this->sellPrice($item->value, $row['quantity']); + $item->_drop = ('normal' != $row['type']); + $item->_quantity = $row['quantity']; + $item->_canBuy = ($item->_price <= $characterGold); + $item->_soldOff = ($item->_drop && ($item->_quantity < 1)); + if(!empty($this->_flags['temple'])) + $item->_canBind = ($item->value + $item->_price <= $characterGold); + else $item->_canBind = false; + $result[$type]['items'][$row['item_id']] = $item; + } + } + $groupNames = Daemon_Dictionary::$equipmentGroups; + foreach(array_keys($result) as $key) + { + if(isset($groupNames[$key])) + $result[$key]['name'] = $groupNames[$key]; + else unset($groupNames[$key]); + } + return $result; + } + + + //calculates item price + public function sellPrice($value, $quantity) + { + if($quantity > 0) + { + $mult = 1 + 4 * exp(-$quantity / 64); + return ceil($value * $mult); + } + else return $value; + } + + + //sells item to character + public function sellToCharacter($itemId, $amount, $bind) + { + $amount = max(1, $amount); + if(!$this->templeEnabled) + $bind = false; + //fetch item data + $sql = "SELECT s.*, i.name, i.value FROM service_items s JOIN items i USING(item_id) + WHERE service_id=:serviceId AND item_id=:itemId"; + $params = array('serviceId' => $this->serviceData['service_id'], 'itemId' => $itemId); + $item = $this->dbClient->selectRow($sql, $params); + //check availability + if(!$item) + { + Daemon_MsgQueue::add('Wybrany przedmiot nie jest dostępny.'); + return false; + } + if(('normal' != $item['type']) && ($amount > $item['quantity'])) + { + Daemon_MsgQueue::add('Nie ma tyle towaru w ofercie.'); + return false; + } + //calculate total cost + $totalCost = 0; + for($i = 0; $i < $amount; ++$i) + $totalCost += $this->sellPrice($item['value'], $item['quantity'] - $i); + if($bind) + $totalCost += $amount * $item['value']; + //check character gold + if(!$this->characterData->payGold($totalCost, $this->bankEnabled)) + return false; + //update service + if('normal' != $item['type']) + { + $sql = "UPDATE service_items SET quantity = quantity - :amount + WHERE service_id=:serviceId AND item_id=:itemId"; + $params = array('serviceId' => $this->serviceData['service_id'], 'itemId' => $itemId, 'amount' => $amount); + $this->dbClient->query($sql, $params); + } + //update character + $sql = "INSERT INTO inventory(character_id, item_id, flags) VALUES (:charId, :itemId, :flags)"; + $params = array('charId' => $this->characterData->character_id, 'itemId' => $itemId); + $params['flags'] = $bind ? 'bound,identified' : 'identified'; + for($i = 0; $i < $amount; ++$i) + $this->dbClient->query($sql, $params); + //show message + if($amount > 1) + Daemon_MsgQueue::add("Kupujesz {$amount}x $item[name] za łączną kwotę $totalCost zł."); + else Daemon_MsgQueue::add("Kupujesz $item[name] za $totalCost zł."); + return true; + } +} diff --git a/2013/daemon/lib/daemon/service/temple.php b/2013/daemon/lib/daemon/service/temple.php new file mode 100644 index 0000000..34b10e0 --- /dev/null +++ b/2013/daemon/lib/daemon/service/temple.php @@ -0,0 +1,333 @@ +setParams(); + //run commands + $this->isCommand = $this->runCommands($params); + //generate output + ob_start(); + $this->view->characterData = $this->characterData; + $this->view->bankEnabled = $this->bankEnabled; + $this->view->itemsToBind = $this->getItems(false, true); + $this->view->itemsToOffer = $this->getItems(true, false); + $this->view->lastMission = $this->characterData->getLastMission('completed'); + $this->view->display('service/temple.xml'); + $this->eventLog = ob_get_clean(); + } + + + private function runCommands($params) + { + //bind item + if(isset($params['bind'])) + { + $this->bindItem($params['bind']); + return true; + } + //pray at altar + if(isset($params['pray'])) + { + $inventoryId = isset($params['offer']) ? $params['offer'] : null; + $this->pray($params['pray'], $inventoryId); + return true; + } + //give up mission + if(isset($params['giveUp'])) + { + $this->removeMission(); + return true; + } + //check mission + $this->checkMission(); + return false; + } + + + public function bindItem($inventoryId) + { + //get item data + $sql = "SELECT i.name, i.value FROM inventory inv JOIN items i USING(item_id) + WHERE inventory_id=:id AND character_id=:charId AND NOT FIND_IN_SET('bound', inv.flags)"; + $params = array('id' => $inventoryId, 'charId' => $this->characterData->character_id); + $item = $this->dbClient->selectRow($sql, $params); + if(!$item) + { + Daemon_MsgQueue::add('Wybrany przedmiot nie istnieje albo już jest przypisany.'); + return false; + } + //check character gold + if(!$this->characterData->payGold($item['value'], $this->bankEnabled)) + return false; + //bind item + $sql = "UPDATE inventory SET flags = CONCAT(',bound,', flags) WHERE inventory_id=:id"; + $params = array('id' => $inventoryId); + $this->dbClient->query($sql, $params); + } + + + //check if the mission is completed + public function checkMission() + { + $inventoryId = null; + $char = $this->characterData; + $mission = $char->getLastMission('completed'); + //check conditions + if(!$mission) + return false; + if('completed' != $mission['progress']) + return false; + if($this->serviceData['service_id'] != $mission['service_id']) + return false; + //give xp & gold + $rewardXp = ceil(sqrt($char->xp_used)); + $rewardGold = mt_rand(10 * $rewardXp, 15 * $rewardXp); + $char->xp_free += $rewardXp; + if($this->bankEnabled) + $char->gold_bank += $rewardGold; + else $char->gold_purse += $rewardGold; + $message = "Misja wykonana. Doświadczenie +$rewardXp, złoto +$rewardGold."; + //give skill + $skillNames = Daemon_Dictionary::$characterSkills; + foreach(array_keys($skillNames) as $key) + { + $name = "s_$key"; + if($char->$name) + unset($skillNames[$key]); + } + if(isset($skillNames['preg']) && ($char->xp_used >= $this->xpForRegen)) + $skill = 'preg'; + else //random other skill + { + unset($skillNames['preg']); + $skill = array_rand($skillNames); + } + if($skill) + { + $skillName = $skillNames[$skill]; + $message .= " Poznajesz nową umiejętność: $skillName."; + $name = "s_$skill"; + $char->$name = 1; + } + //join faction or raise reputation + if($this->serviceData['faction_id']) + $char->improveReputation($this->serviceData['faction_id'], mt_rand(4,6)); + //save character & mission + $char->put(); + $sql = "UPDATE character_statistics SET missions=missions+1 WHERE character_id=:id"; + $this->dbClient->query($sql, array('id' => $char->character_id)); + $sql = "UPDATE character_missions SET progress='rewarded' + WHERE character_id=:cid AND rollover_id=:rid"; + $params = array('cid' => $char->character_id, 'rid' => $mission['rollover_id']); + $this->dbClient->query($sql, $params); + Daemon_MsgQueue::add($message); + } + + + //returns a list of unbound items + public function getItems($withoutEquipped = false, $withoutBound = false) + { + $result = array(); + $cond = "character_id=:id"; + if($withoutEquipped) + $cond .= " AND inv.equipped IS NULL"; + if($withoutBound) + $cond .= " AND NOT FIND_IN_SET('bound', inv.flags)"; + if(!$this->bankEnabled) + $cond .= " AND status!='storage'"; + $sql = "SELECT inv.*, i.name, i.value FROM inventory inv JOIN items i USING(item_id) + WHERE $cond ORDER BY i.name, inv.inventory_id"; + $params = array('id' => $this->characterData->character_id); + foreach($this->dbClient->selectAll($sql, $params) as $row) + { + $flags = $row['flags'] + ? array_fill_keys(explode(',', $row['flags']), true) + : array(); + $status = $row['status']; + if(('inventory' == $status) && $row['equipped']) + $status = 'equipment'; + $result[$status][$row['inventory_id']] = array( + 'name' => $row['name'], 'value' => $row['value'], 'flags' => $flags); + } + return $result; + } + + + //gives mission if its allowed + private function giveMission() + { + $char = $this->characterData; + $lastMission = $char->getLastMission('rewarded'); + //check character's xp + if($char->xp_used < $this->xpForMission) + { + switch($char->_gender) + { + case 'm': $weak = 'słaby'; break; + case 'k': $weak = 'słaba'; break; + default: $weak = 'słabe'; + } + Daemon_MsgQueue::add( + "Słyszysz głos z nieba:" + ." \"Twa gorliwość jest godna pochwały, lecz jesteś jeszcze za $weak" + ." - wróć gdy osiągniesz $this->xpForMission doświadczenia.\"" + ); + return false; + } + //check last mission + $sql = "SELECT MAX(rollover_id) FROM rollovers"; + $lastRollover = $this->dbClient->selectValue($sql); + if (empty($lastRollover)) + { + Daemon_MsgQueue::add( + 'Słyszysz głos z nieba:' + .' "Twa gorliwość jest godna pochwały, lecz jeszcze za wcześnie na misje. Wróć później."' + ); + return false; + } + if($lastRollover && $lastMission && ($lastRollover <= $lastMission['rollover_id'])) + { + switch($char->_gender) + { + case 'm': $x = 'wykonałeś'; break; + case 'k': $x = 'wykonałaś'; break; + default: $x = 'wykonałeś'; + } + Daemon_MsgQueue::add( + "Słyszysz głos z nieba:" + ." \"Twa gorliwość jest godna pochwały, lecz $x już dzisiaj misję. Wróć później." + ); + return false; + } + //get character's region + $sql = "SELECT region_id FROM locations WHERE location_id=:id"; + $regionId = $this->dbClient->selectValue($sql, array('id' => $char->location_id)); + //find monster near character's level & location + $cols = "m.monster_id, m.name"; + $tables = "monsters m JOIN location_monsters lm USING(monster_id) JOIN locations l USING(location_id)"; + $params = array('regionId' => $regionId, 'level' => $char->level); + $sql = "SELECT $cols FROM $tables + WHERE l.region_id=:regionId AND m.level>=:level AND lm.chance > 0 + ORDER BY level ASC, RAND() LIMIT 1"; + $monster = $this->dbClient->selectRow($sql, $params); + if(!$monster) + { + $sql = "SELECT $cols FROM $tables + WHERE l.region_id=:regionId AND m.level<:level AND lm.chance > 0 + ORDER BY level DESC, RAND() LIMIT 1"; + $monster = $this->dbClient->selectRow($sql, $params); + } + if(!$monster) + return false; + //get random drop from that monster + $sql = "SELECT i.item_id, i.name, md.chance*RAND() AS ord + FROM items i JOIN monster_drops md USING(item_id) + WHERE md.monster_id=:id AND md.chance > 0 ORDER BY ord LIMIT 1"; + $params = array('id' => $monster['monster_id']); + $item = $this->dbClient->selectRow($sql, $params); + //choose mission type + if($item && (mt_rand(0,255) < 128)) + { + $missionType = 'item'; + $missionParams = $item['item_id']; + $message = "Przynieś mi przedmiot - $item[name] - a twój wysiłek zostanie nagrodzony."; + } + else + { + $missionType = 'monster'; + $missionParams = $monster['monster_id']; + $message = "Pokonaj potwora - $monster[name] - a twój wysiłek zostanie nagrodzony."; + } + $sql = "INSERT INTO character_missions(character_id, rollover_id, service_id, type, params) + VALUES (:cid, :rid, :sid, :type, :params)"; + $params = array('cid' => $char->character_id, 'rid' => $lastRollover, + 'sid' => $this->serviceData['service_id'], 'type' => $missionType, 'params' => $missionParams); + $this->dbClient->query($sql, $params); + Daemon_MsgQueue::add("Słyszysz głos z nieba: \"$message\""); + return true; + } + + + //pray at altar, offering gold + public function pray($gold, $inventoryId) + { + $char = $this->characterData; + $mission = $char->getLastMission('active'); + $gold = ceil(abs($gold)); + //check character gold + if(!$char->payGold($gold, $this->bankEnabled)) + return false; + //check item for value and mission + $cond = "inv.character_id=:charId AND inv.inventory_id=:inventoryId"; + if(!$this->bankEnabled) + $cond .= " AND inv.status!='storage'"; + $sql = "SELECT inv.item_id, i.value FROM inventory inv JOIN items i USING(item_id) WHERE $cond"; + $params = array('charId' => $char->character_id, 'inventoryId' => $inventoryId); + $item = $this->dbClient->selectRow($sql, $params); + if($item) + { + $gold += $item['value']; + //check for mission target + if(($this->serviceData['service_id'] == $mission['service_id']) && ('item' == $mission['type']) + && ($item['item_id'] == $mission['params']) && ('completed' != $mission['progress'])) + { + $mission['progress'] = 1; + $sql = "UPDATE character_missions SET progress='completed' + WHERE character_id=:cid AND rollover_id=:rid"; + $params = array('cid' => $char->character_id, 'rid' => $mission['rollover_id']); + $this->dbClient->query($sql, $params); + } + //remove item + $sql = "DELETE FROM inventory WHERE inventory_id=:id"; + $this->dbClient->query($sql, array('id' => $inventoryId)); + $char->resetCombatStats(); + } + //check for reaction + if(!$mission['progress']) + { + $wealth = $gold + $char->gold_purse + $char->gold_bank; + if($wealth) + $chance = round(2560 * $gold / $wealth); + else $chance = 0; + $d256 = mt_rand(0, 255); + if($chance <= $d256) + { + Daemon_msgQueue::add('Twa ofiara nie wywołała żadnej reakcji.'); + return false; + } + } + //heal character & give mission + $char->health = $char->health_max; + $char->mana = $char->mana_max; + Daemon_MsgQueue::add('Stwórca przyjął ofiarę. Twoje rany zostały uleczone a siły odnowione.'); + if($mission['progress']) + $this->checkMission(); + else $this->giveMission(); + //save character + $this->characterData->put(); + return true; + } + + + //removes mission + public function removeMission() + { + $sql = "DELETE FROM character_missions WHERE character_id=:id AND progress < 'completed'"; + $this->dbClient->query($sql, array('id' => $this->characterData->character_id)); + } + + + public function setParams() + { + $dbCfg = new Daemon_DbConfig($this->dbClient); + $this->xpForMission = max(0, (int) $dbCfg->templeXpForMission); + $this->xpForRegen = max(0, (int) $dbCfg->templeXpForRegen); + } +} diff --git a/2013/daemon/lib/daemon/spell.php b/2013/daemon/lib/daemon/spell.php new file mode 100644 index 0000000..56c14fb --- /dev/null +++ b/2013/daemon/lib/daemon/spell.php @@ -0,0 +1,63 @@ +characterData = $characterData; + } + + + public function attachDbClient(Daemon_DbClient $dbClient) + { + $this->dbClient = $dbClient; + } + + + public function getUsageLog() + { + return $this->usageLog; + } + + + public function execute(Daemon_View $view, $spellId) + { + //fetch spell info + $sql = "SELECT name, handle FROM spells WHERE spell_id=:id"; + $spell = $this->dbClient->selectRow($sql, array('id' => $spellId)); + if(!$spell) + $spell = array('name' => '???', 'handle' => null); + //get spell's cost + $cost = null; + foreach($this->characterData->getSpells() as $row) + { + if($row['spell_id'] == $spellId) + $cost = $row['_cost']; + } + if(!$cost) + { + Daemon_MsgQueue::add('Nie znasz tego zaklęcia.'); + return false; + } + //check if handler is implemented + $className = "Daemon_Spell_$spell[handle]"; + if(class_exists($className, true) && is_subclass_of($className, 'Daemon_SpellInterface')) + { + //valid spell, execute it + $handler = new $className($this->dbClient, $this->characterData, $view); + $this->usageLog = $handler->execute($spellId, $cost); + $this->characterData->put(); + return true; + } + else + { + //no effect + Daemon_MsgQueue::add("Nieznany efekt: $spell[name]"); + return false; + } + } +} diff --git a/2013/daemon/lib/daemon/spell/identify.php b/2013/daemon/lib/daemon/spell/identify.php new file mode 100644 index 0000000..c680ac9 --- /dev/null +++ b/2013/daemon/lib/daemon/spell/identify.php @@ -0,0 +1,15 @@ +updateCharacterMana($cost)) + return null; + $sql = "UPDATE inventory SET flags=CONCAT(flags, ',identified') WHERE character_id=:id AND status!='storage'"; + $this->dbClient->query($sql, array('id' => $this->characterData->character_id)); + Daemon_MsgQueue::add('Zawartość plecaka została zidentyfikowana.'); + return null; + } +} diff --git a/2013/daemon/lib/daemon/spell/scancharacter.php b/2013/daemon/lib/daemon/spell/scancharacter.php new file mode 100644 index 0000000..89665fc --- /dev/null +++ b/2013/daemon/lib/daemon/spell/scancharacter.php @@ -0,0 +1,69 @@ +updateCharacterMana($cost)) + return null; + $result['char'] = $this->getCharacterByName($target); + if($result['char']->character_id) + { + $result['cdata'] = $result['char']->getCharacterData(); + if($result['cdata']->level) + $chance = $this->characterData->level / ($this->characterData->level + $result['cdata']->level); + else $chance = 0; + if(mt_rand(0, 255) < 256 * $chance) + { + $result['equipment'] = $this->getEquipmentByCharacterId($result['char']->character_id); + $result['locationName'] = $this->getLocationNameById($result['cdata']->location_id); + } + else + { + Daemon_MsgQueue::add('Nie udało się rzucić zaklęcia!'); + $result = null; + } + } + else + { + Daemon_MsgQueue::add('Dziwne... zaklęcie niczego nie wskazuje...'); + $result = null; + } + } + $this->view->spellId = $spellId; + $this->view->cost = $cost; + $this->view->target = $target; + $this->view->result = $result; + ob_start(); + $this->view->display('spell/scancharacter.xml'); + return ob_get_clean(); + } + + + private function getCharacterByName($name) + { + $char = new Daemon_DbObject_Character(); + $char->attachDbClient($this->dbClient); + $char->get(array('name' => $name), true); + return $char; + } + + + private function getEquipmentByCharacterId($charId) + { + $sql = "SELECT i.name FROM inventory inv JOIN items i USING(item_id) + WHERE inv.character_id=:id AND inv.equipped IS NOT NULL ORDER BY i.name"; + return $this->dbClient->selectColumn($sql, array('id' => $charId)); + } + + + private function getLocationNameById($locationId) + { + $sql = "SELECT name FROM locations WHERE location_id=:id"; + return $this->dbClient->selectValue($sql, array('id' => $locationId)); + } +} diff --git a/2013/daemon/lib/daemon/spell/scanitem.php b/2013/daemon/lib/daemon/spell/scanitem.php new file mode 100644 index 0000000..19356b8 --- /dev/null +++ b/2013/daemon/lib/daemon/spell/scanitem.php @@ -0,0 +1,65 @@ +updateCharacterMana($cost)) + return null; + $item = $this->getItemByName($target); + if($item->item_id) + { + $result['item'] = $item; + $result['typeName'] = Daemon::getArrayValue(Daemon_Dictionary::$itemTypes, $item->type); + $result['damageType'] = Daemon::getArrayValue(Daemon_Dictionary::$itemDamageTypes, $item->damage_type); + $result['shops'] = $this->getShopsByItemId($result['item']->item_id); + $result['monsters'] = $this->getMonstersByItemId($result['item']->item_id); + } + else + { + Daemon_MsgQueue::add('Dziwne... zaklęcie niczego nie wskazuje...'); + $result = null; + } + } + $this->view->spellId = $spellId; + $this->view->cost = $cost; + $this->view->target = $target; + $this->view->result = $result; + ob_start(); + $this->view->display('spell/scanitem.xml'); + return ob_get_clean(); + } + + + private function getItemByName($name) + { + $item = new Daemon_DbObject_Item(); + $item->attachDbClient($this->dbClient); + $item->get(array('name' => $name), true); + return $item; + } + + + private function getMonstersByItemId($itemId) + { + $sql = "SELECT m.name, m.level FROM monster_drops md JOIN monsters m USING(monster_id) + WHERE md.item_id=:id ORDER BY m.name"; + $data = $this->dbClient->selectAll($sql, array('id' => $itemId)); + $result = array(); + foreach($data as $row) + $result[] = sprintf('%s (poziom %d)', $row['name'], $row['level']); + return $result; + } + + + private function getShopsByItemId($itemId) + { + $sql = "SELECT s.name FROM service_items si JOIN services s USING(service_id) + WHERE si.item_id=:id ORDER BY s.name"; + return $this->dbClient->selectColumn($sql, array('id' => $itemId)); + } +} diff --git a/2013/daemon/lib/daemon/spell/scanmonster.php b/2013/daemon/lib/daemon/spell/scanmonster.php new file mode 100644 index 0000000..d8fef0b --- /dev/null +++ b/2013/daemon/lib/daemon/spell/scanmonster.php @@ -0,0 +1,93 @@ +updateCharacterMana($cost)) + return null; + $result['monster'] = $this->getMonsterByName($target); + if($result['monster']->monster_id) + { + if($result['monster']->level) + $chance = $this->characterData->level / ($this->characterData->level + $result['monster']->level); + else $chance = 0; + if(mt_rand(0, 255) < 256 * $chance) + { + $result['title'] = $this->getTitleById($result['monster']->title_id); + $result['items'] = $this->getItemsByMonsterId($result['monster']->monster_id); + $result['locations'] = $this->getLocationsByMonsterId($result['monster']->monster_id); + $result['className'] = Daemon_Dictionary::$monsterClasses[$result['monster']->class]; + //prepare combat unit + $unit = (array) $result['monster']->getCombatUnit(false); + $attackTypes = Daemon_Dictionary::$combatAttackTypes; + $attackSpecials = Daemon_Dictionary::$combatAttackSpecials; + $armorSpecials = Daemon_Dictionary::$combatArmorSpecials; + $unit['type1_name'] = $unit['type1'] ? $attackTypes[$unit['type1']] : null; + $unit['type2_name'] = $unit['type2'] ? $attackTypes[$unit['type2']] : null; + $unit['sp1_name'] = $unit['sp1_type'] ? $attackSpecials[$unit['sp1_type']] : null; + $unit['sp2_name'] = $unit['sp2_type'] ? $attackSpecials[$unit['sp2_type']] : null; + $unit['armor_sp_name'] = $unit['armor_sp_type'] ? $armorSpecials[$unit['armor_sp_type']] : null; + $result['unit'] = $unit; + } + else + { + Daemon_MsgQueue::add('Nie udało się rzucić zaklęcia!'); + $result = null; + } + } + else + { + Daemon_MsgQueue::add('Dziwne... zaklęcie niczego nie wskazuje...'); + $result = null; + } + } + $this->view->spellId = $spellId; + $this->view->cost = $cost; + $this->view->target = $target; + $this->view->result = $result; + ob_start(); + $this->view->display('spell/scanmonster.xml'); + return ob_get_clean(); + } + + + private function getItemsByMonsterId($monsterId) + { + $sql = "SELECT i.name, md.chance FROM monster_drops md JOIN items i USING(item_id) + WHERE md.monster_id=:id ORDER BY i.name"; + $data = $this->dbClient->selectAll($sql, array('id' => $monsterId)); + $result = array(); + foreach($data as $row) + $result[] = sprintf('%s (częstość %d)', $row['name'], $row['chance']); + return $result; + } + + + private function getLocationsByMonsterId($monsterId) + { + $sql = "SELECT l.name FROM location_monsters lm JOIN locations l USING(location_id) + WHERE lm.monster_id=:id ORDER BY l.name"; + return $this->dbClient->selectColumn($sql, array('id' => $monsterId)); + } + + + private function getMonsterByName($name) + { + $monster = new Daemon_DbObject_Monster(); + $monster->attachDbClient($this->dbClient); + $monster->get(array('name' => $name), true); + return $monster; + } + + + private function getTitleById($titleId) + { + $sql = "SELECT name_f, name_m, name_n FROM titles WHERE title_id=:id"; + return $this->dbClient->selectRow($sql, array('id' => $titleId)); + } +} diff --git a/2013/daemon/lib/daemon/spell/scout.php b/2013/daemon/lib/daemon/spell/scout.php new file mode 100644 index 0000000..05a8237 --- /dev/null +++ b/2013/daemon/lib/daemon/spell/scout.php @@ -0,0 +1,56 @@ +updateCharacterMana($cost)) + return null; + $location = $this->getLocationById($this->characterData->location_id); + if(!$location->location_id) + { + Daemon_MsgQueue::add('Nie da się badać Otchłani!'); + return null; + } + $locations = array(0 => $location); + $pathIds = $this->getPathsByLocationId($this->characterData->location_id); + foreach($pathIds as $id) + $locations[] = $this->getLocationById($id); + ob_start(); + $this->view->locations = $locations; + $this->view->display('spell/scout.xml'); + return ob_get_clean(); + } + + + private function getLocationById($locationId) + { + $location = new Daemon_DbObject_Location; + $location->attachDbClient($this->dbClient); + $location->get(array('location_id' => $locationId)); + $location->_monsters = array(); + $location->_events = array(); + if($location->location_id) + { + $params = array('id' => $location->location_id); + $sql = "SELECT m.name, m.level, lm.chance FROM location_monsters lm JOIN monsters m USING(monster_id) + WHERE lm.location_id=:id ORDER BY m.name"; + foreach($this->dbClient->selectAll($sql, $params) as $row) + $location->_monsters[] = sprintf('%s (poziom %d, częstość %d)', $row['name'], $row['level'], $row['chance']); + $sql = "SELECT e.name, le.chance FROM location_events le JOIN events e USING(event_id) + WHERE le.location_id=:id ORDER BY e.name"; + foreach($this->dbClient->selectAll($sql, $params) as $row) + $location->_events[] = sprintf('%s (specjalne, częstość %d)', $row['name'], $row['chance']); + } + return $location; + } + + + private function getPathsByLocationId($locationId) + { + $sql = "SELECT destination_id FROM location_paths p + JOIN locations l ON l.location_id = p.destination_id + WHERE p.location_id=:id ORDER BY l.name, p.destination_id"; + return $this->dbClient->selectColumn($sql, array('id' => $locationId)); + } +} diff --git a/2013/daemon/lib/daemon/spellinterface.php b/2013/daemon/lib/daemon/spellinterface.php new file mode 100644 index 0000000..da5bb72 --- /dev/null +++ b/2013/daemon/lib/daemon/spellinterface.php @@ -0,0 +1,32 @@ +dbClient = $dbClient; + $this->characterData = $characterData; + $this->view = $view; + } + + + abstract public function execute($spellId, $cost); + + + final public function updateCharacterMana($cost) + { + if($this->characterData->mana < $cost) + { + Daemon_MsgQueue::add("Koszt $cost - nie masz tyle many."); + return false; + } + $this->characterData->mana -= $cost; + return true; + } +} diff --git a/2013/daemon/lib/daemon/statistics.php b/2013/daemon/lib/daemon/statistics.php new file mode 100644 index 0000000..3377445 --- /dev/null +++ b/2013/daemon/lib/daemon/statistics.php @@ -0,0 +1,233 @@ + array('key' => 'name', 'sort' => 'name ASC', 'from' => 'name >= :from'), + 'lvl' => array('key' => 'level', 'sort' => 'level DESC', 'from' => 'level <= :from'), + 'xp' => array('key' => 'xp_used', 'sort' => 'xp_used DESC', 'from' => 'xp_used <= :from'), + 'fac' => array('key' => 'faction_id', 'sort' => 'faction_id ASC', 'from' => 'faction_id >= :from'), + 'clan' => array('key' => 'clan_id', 'sort' => 'clan_id ASC', 'from' => 'clan_id >= :from'), + 'date' => array('key' => 'date_created', 'sort' => 'date_created ASC', 'from' => 'date_created >= :from'), + 'last' => array('key' => 'last_action', 'sort' => 'last_action DESC', 'from' => 'last_action >= :from'), + 'win' => array('key' => 'duel_wins', 'sort' => 'duel_wins DESC', 'from' => 'duel_wins <= :from'), + 'los' => array('key' => 'duel_losses', 'sort' => 'duel_losses DESC', 'from' => 'duel_losses <= :from'), + ); + + + public function __construct(Daemon_DbClient $dbClient) + { + $this->dbClient = $dbClient; + } + + + public function getBattleById($battleId) + { + $sql = "SELECT b.combat_log, b.type, l.name AS location_name + FROM battles b + LEFT JOIN locations l USING(location_id) + WHERE b.battle_id = :battleId"; + $params = array('battleId' => $battleId); + return $this->dbClient->selectRow($sql, $params); + } + + + public function getBattles($limit, $from) + { + $params = array('limit' => $limit + 1); + $cond = ''; + if($from) + { + $cond = 'WHERE battle_id <= :from'; + $params['from'] = (int) $from; + } + $sql = "SELECT b.battle_id, b.rollover_id, b.location_id, b.type, l.name AS location_name, + IF(b.combat_log IS NULL OR b.combat_log = '', 0, 1) AS log_exists + FROM battles b + LEFT JOIN locations l USING(location_id) + $cond ORDER BY battle_id DESC LIMIT :limit"; + return $this->getItemList($sql, $params, $limit, 'battle_id'); + } + + + //fetches character data by ID + public function getCharacterById($characterId) + { + $params = array('characterId' => $characterId); + $sql = "SELECT c.player_id, p.name AS player_name, c.show_player, + c.name, c.gender, c.clan_id, cp.level, cp.xp_used, + c.avatar_url, c.quote, c.description, date_format(c.date_created, '%Y-%m-%d') AS date_created, + cl.name AS clan_name, f.name AS faction_name, COALESCE(cp.rank_id, 0) AS rank_id, + CASE c.gender WHEN 'f' THEN frt.name_f WHEN 'm' THEN frt.name_m ELSE frt.name_n END AS rank_name + FROM characters c + LEFT JOIN players p USING(player_id) + LEFT JOIN character_data cp USING(character_id) + LEFT JOIN factions f USING(faction_id) + LEFT JOIN faction_ranks fr USING(faction_id, rank_id) + LEFT JOIN titles frt USING(title_id) + LEFT JOIN clans cl USING(clan_id) + WHERE character_id = :characterId"; + if($character = $this->dbClient->selectRow($sql, $params)) + { + $sql = "SELECT CASE c.gender WHEN 'f' THEN t.name_f WHEN 'm' THEN t.name_m WHEN 'n' THEN t.name_n END AS title + FROM character_titles ct + JOIN characters c ON c.character_id=ct.character_id + JOIN titles t ON ct.title_id=t.title_id + WHERE ct.character_id = :characterId ORDER BY title ASC"; + if($titles = $this->dbClient->selectColumn($sql, $params)) + $character['titles'] = implode(', ', $titles); + else $character['titles'] = null; + $sql = "SELECT * FROM character_statistics WHERE character_id = :characterId"; + $character['statistics'] = $this->dbClient->selectRow($sql, $params); + } + return $character; + } + + + //fetches a list of characters + public function getCharacters($limit, $from, $order, $clanId) + { + if(!isset($this->charactersOrderTypes[$order])) + $order = 'xp'; + $orderParams = $this->charactersOrderTypes[$order]; + + $from = explode(',', $from, 2); + if(count($from)<2) + $from = array(); + + $params = array('limit' => $limit + 1); + $where = array("$orderParams[key] IS NOT NULL", "c.character_id!=392"); + if($clanId) + { + $params['clanId'] = $clanId; + $where[] = 'clan_id=:clanId'; + } + if($from) + { + $params['from'] = $from[1]; + $params['characterId'] = $from[0]; + $where[] = "($orderParams[from] AND character_id>= :characterId)"; + } + if($where) + $where = 'WHERE ' . implode(' AND ', $where); + else $where = ''; + $orderClause = $orderParams['sort']; + $sql = "SELECT c.character_id, c.name, c.clan_id, cp.level, cp.xp_used, + cp.faction_id, COALESCE(cp.rank_id, 0) AS rank_id, + date_format(c.date_created, '%Y-%m-%d') AS date_created, + date_format(c.last_action, '%Y-%m-%d') AS last_action, + cs.duel_wins, cs.duel_losses, cs.kills_mob1, cs.kills_mob2, cs.kills_mob3, cs.kills_mob4 + FROM characters c + LEFT JOIN character_data cp USING(character_id) + LEFT JOIN character_statistics cs USING(character_id) + $where ORDER BY $orderClause, character_id ASC LIMIT :limit"; + $list = $this->dbClient->selectAll($sql, $params); + if(count($list) > $limit) + { + $next = array_pop($list); + $next = sprintf('%s,%s', $next['character_id'], $next[$orderParams['key']]); + } + else $next = null; + return array('list' => $list, 'next' => $next); + } + + + //fetches clan data by ID + public function getClanById($clanId) + { + $params = array('clanId' => $clanId); + $sql = "SELECT cl.*, c.name AS leader_name, + date_format(cl.date_created, '%Y-%m-%d') AS date_created + FROM clans cl LEFT JOIN characters c ON c.character_id=leader_id + WHERE cl.clan_id = :clanId"; + if($clan = $this->dbClient->selectRow($sql, $params)) + { + $sql = "SELECT COUNT(1) FROM characters WHERE clan_id = :clanId"; + $clan['members'] = $this->dbClient->selectColumn($sql, $params); + } + return $clan; + } + + + //fetches a list of clans + public function getClans($limit, $from) + { + $params = array('limit' => $limit + 1); + $cond = ''; + if($from) + { + $cond = 'WHERE cl.clan_id >= :from'; + $params['from'] = $from; + } + $sql = "SELECT cl.*, c.name AS leader_name, n.members, + date_format(cl.date_created, '%Y-%m-%d') AS date_created + FROM clans cl LEFT JOIN characters c ON c.character_id=leader_id + LEFT JOIN ( + SELECT clan_id, COUNT(1) AS members FROM characters WHERE clan_id IS NOT NULL GROUP BY clan_id + ) AS n ON n.clan_id=cl.clan_id + $cond ORDER BY cl.clan_id ASC LIMIT :limit"; + return $this->getItemList($sql, $params, $limit, 'clan_id'); + } + + + //fetches combat log by duelId + public function getDuelById($characterId, $duelId) + { + $sql = "SELECT d.combat_log, ca.name AS attacker_name, cb.name AS defender_name + FROM duels d + LEFT JOIN characters ca ON ca.character_id=d.attacker_id + LEFT JOIN characters cb ON cb.character_id=d.defender_id + WHERE d.duel_id = :duelId AND (d.attacker_id=:characterId OR d.defender_id=:characterId)"; + $params = array('characterId' => $characterId, 'duelId' => $duelId); + return $this->dbClient->selectRow($sql, $params); + } + + + //fetches a list of duels, optionally filtered by character + public function getDuels($limit, $from, $characterId, $viewerId) + { + $params = array('limit' => $limit + 1, 'vid1' => $viewerId, 'vid2' => $viewerId); + $where = array(); + if($characterId) + { + $params['characterId'] = $characterId; + $where[] = '(attacker_id=:characterId OR defender_id=:characterId)'; + } + if($from) + { + $params['from'] = (int) $from; + $where[] = 'duel_id <= :from'; + } + if($where) + $where = 'WHERE ' . implode(' AND ', $where); + else $where = ''; + $sql = "SELECT d.duel_id, + date_format(d.date_added, '%Y-%m-%d %H:%i:%s') AS date_added, + d.rollover_id, d.attacker_id, d.defender_id, d.type, d.winner, + ( + d.combat_log IS NOT NULL AND d.combat_log != '' + AND (d.attacker_id = :vid1 OR d.defender_id = :vid2) + ) AS log_exists, + ca.name AS attacker_name, cb.name AS defender_name + FROM duels d + LEFT JOIN characters ca ON ca.character_id=d.attacker_id + LEFT JOIN characters cb ON cb.character_id=d.defender_id + $where ORDER BY d.duel_id DESC LIMIT :limit"; + return $this->getItemList($sql, $params, $limit, 'duel_id'); + } + + + private function getItemList($sql, array $params, $limit, $indexCol) + { + $list = $this->dbClient->selectAll($sql, $params); + if(count($list) > $limit) + { + $next = array_pop($list); + $next = $next[$indexCol]; + } + else $next = null; + return array('list' => $list, 'next' => $next); + } +} diff --git a/2013/daemon/lib/daemon/view.php b/2013/daemon/lib/daemon/view.php new file mode 100644 index 0000000..856e3af --- /dev/null +++ b/2013/daemon/lib/daemon/view.php @@ -0,0 +1,133 @@ + /u', ">\n", $str); + } +} + + +class Daemon_View +{ + public $subtitle; + public $subtitleDetails; + public $titlePrefix; + private $applicationTitle; //for setPageTitle() + private $filter; + private $phptal; + const MODE_HTML = PHPTAL::HTML5; + const MODE_ATOM = PHPTAL::XML; + const PAGING_MAX_ITEMS = 10; + + + public function __construct(Daemon_Config $cfg) + { + $this->applicationTitle = $cfg->applicationTitle; + $this->phptal = new PHPTAL(); + $this->phptal->setTemplateRepository(array($cfg->getFilePath('tpl'))); + $this->phptal->setPhpCodeDestination($cfg->getFilePath('tmp')); + $this->filter = new Daemon_View_Filter(); + if($cfg->minifyHtml) + $this->phptal->setPostFilter($this->filter); + } + + + public function __set($name, $value) + { + $this->phptal->$name = $value; + } + + + //displays page content + public function display($templateName, $outputMode = self::MODE_HTML) + { + if($outputMode != self::MODE_HTML) + { + $contentType = 'Content-Type:application/atom+xml;charset=UTF-8'; + $this->phptal->setOutputMode(PHPTAL::XML); + } + else + { + $contentType = 'Content-Type:text/html;charset=UTF-8'; + $this->phptal->setOutputMode(PHPTAL::HTML5); + } + $this->phptal->setTemplate($templateName); + header($contentType); + echo $this->phptal->execute(); + } + + + //generates channel menu for chat page + public function getChatMenu($channels, $channelId = null) + { + $menu = array(); + foreach($channels as $key => $row) + $menu[] = array('name' => $row['name'], 'url' => ($key != $channelId) ? "?v=$key" : null); + return $menu; + } + + + //generates menu for statistics pages + public function getStatisticsMenu($type = null) + { + $menu = array( + 'status' => array('name' => 'Status', 'url' => 'stats'), + 'characters' => array('name' => 'Postacie', 'url' => 'stats-characters'), + 'clans' => array('name' => 'Klany', 'url' => 'stats-clans'), + 'duels' => array('name' => 'Pojedynki', 'url' => 'stats-duels'), + 'battles' => array('name' => 'Bitwy', 'url' => 'stats-battles'), + ); + if(isset($menu[$type])) + $menu[$type]['url'] = null; + return $menu; + } + + + public function setGameHeader($playerId, $activeCharacter = null, $characterData = null, $location = null) + { + $this->__set('playerId', $playerId); + $this->__set('activeCharacter', $activeCharacter); + $this->__set('characterData', $characterData); + $this->__set('location', $location); + } + + + public function setMessages($messages) + { + $this->__set('pageMessages', $messages); + } + + + public function setPageSkin($skinId) + { + $skinDirUrls = Daemon_Dictionary::$skinDirUrls; + if (isset($skinDirUrls[$skinId])) + $url = $skinDirUrls[$skinId]; + else + $url = array_shift($skinDirUrls); + $this->__set('pageSkinName', $skinId); + $this->__set('pageSkinUrl', $url); + } + + + public function setPageTitle($subtitle = null, $details = null, $isCommand = false) + { + $title = $this->applicationTitle; + if($subtitle) + { + if($details) + $title = sprintf('%s: %s - %s', $subtitle, $details, $title); + else $title = sprintf('%s - %s', $subtitle, $title); + } + if($isCommand) + $title = sprintf('[cmd] %s', $title); + $this->__set('pageTitle', $title); + } +} diff --git a/2013/daemon/lib/jsmin.php b/2013/daemon/lib/jsmin.php new file mode 100644 index 0000000..2f3bcc7 --- /dev/null +++ b/2013/daemon/lib/jsmin.php @@ -0,0 +1,291 @@ + + * @copyright 2002 Douglas CrockfordWybrana strona jeszcze nie istnieje.
+ diff --git a/2013/daemon/public/register.php b/2013/daemon/public/register.php new file mode 100644 index 0000000..d6ef0e8 --- /dev/null +++ b/2013/daemon/public/register.php @@ -0,0 +1,40 @@ +registerEnabled = (bool) $this->dbCfg->registerEnabled; + } + + + public function prepareView() + { + $this->view->registerEnabled = $this->registerEnabled; + } + + + protected function runCommands() + { + if(!$this->registerEnabled) + Daemon_MsgQueue::add('Rejestracja wyłączona.'); + elseif(isset($_POST['login'], $_POST['pass'], $_POST['pass2'])) + { + $this->player->register($_POST['login'], $_POST['pass'], $_POST['pass2']); + return true; + } + return false; + } +} + + +$ctrl = new Daemon_Controller_Register($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/reset-password.php b/2013/daemon/public/reset-password.php new file mode 100644 index 0000000..948fd43 --- /dev/null +++ b/2013/daemon/public/reset-password.php @@ -0,0 +1,38 @@ +registerEnabled = (bool) $this->dbCfg->registerEnabled; + } + + + protected function runCommands() + { + if(isset($_POST['login'], $_POST['email'], $_POST['pass'], $_POST['pass2'])) + { + $this->player->preparePasswordReset($_POST['login'], $_POST['email'], $_POST['pass'], $_POST['pass2']); + return true; + } + elseif (isset($_GET['key'])) + { + $this->player->resetPassword($_GET['key']); + return true; + } + return false; + } + +} + + +$ctrl = new Daemon_Controller_Page($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/respawn.php b/2013/daemon/public/respawn.php new file mode 100644 index 0000000..064212f --- /dev/null +++ b/2013/daemon/public/respawn.php @@ -0,0 +1,59 @@ +characterData->xp_used)) + { + Daemon_MsgQueue::add('Pamiętaj by wydać startowe doświadczenie.'); + Daemon::redirect($this->cfg->getUrl('character')); + exit; + } + $this->defaultRespawn = $this->dbCfg->defaultRespawn; + $this->gender = $this->characterData->_gender; + if(empty($this->location->location_id)) + $this->respawns = $this->characterData->getRespawns($this->defaultRespawn); + else + { + Daemon_MsgQueue::add('Już posiadasz powłokę cielesną.'); + Daemon::redirect($this->cfg->getUrl('map')); + exit; + } + } + + + public function prepareView() + { + $this->view->respawns = $this->respawns; + $this->view->firstOne = empty($this->characterData->deaths); + $this->view->rolloversEnabled = (bool) $this->dbCfg->rolloversEnabled; + } + + + protected function runCommands() + { + if(isset($_POST['respawn'])) + { + $this->characterData->respawn($_POST['respawn'], $this->defaultRespawn); + Daemon::redirect($this->cfg->getUrl('map')); + exit; + } + return false; + } +} + + +$ctrl = new Daemon_Controller_Respawn($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/robots.txt b/2013/daemon/public/robots.txt new file mode 100644 index 0000000..5d856c8 --- /dev/null +++ b/2013/daemon/public/robots.txt @@ -0,0 +1,2 @@ +User-Agent: * +Allow: * diff --git a/2013/daemon/public/rules.php b/2013/daemon/public/rules.php new file mode 100644 index 0000000..f542c4c --- /dev/null +++ b/2013/daemon/public/rules.php @@ -0,0 +1,21 @@ +view->lastModified = date(DATE_RFC1123, filemtime($this->cfg->getFilePath('tpl', 'rules.xml'))); + } +} + + +$ctrl = new Daemon_Controller_Rules($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/arena.php b/2013/daemon/public/scyzoryk/arena.php new file mode 100644 index 0000000..63e802d --- /dev/null +++ b/2013/daemon/public/scyzoryk/arena.php @@ -0,0 +1,126 @@ +selectMode = isset($_POST['mode']) ? $_POST['mode'] : null; + $this->combatCount = isset($_POST['n']) ? min(500, max(100, (int) $_POST['n'])) : 100; + $this->unitIdA = isset($_POST['unitA']) ? $_POST['unitA'] : null; + $this->unitIdB = isset($_POST['unitB']) ? $_POST['unitB'] : null; + } + + + public function prepareView() + { + $this->view->selectMode = $this->selectMode; + $this->view->combatCount = $this->combatCount; + $this->view->unitIdA = $this->unitIdA; + $this->view->unitIdB = $this->unitIdB; + $this->view->winsA = round(100 * $this->winsA / $this->combatCount, 2); + $this->view->winsB = round(100 * $this->winsB / $this->combatCount, 2); + $this->view->draws = round(100 * $this->draws / $this->combatCount, 2); + $this->view->doubleKOs = round(100 * $this->doubleKOs / $this->combatCount, 2); + $this->view->healthSumA = round(100 * $this->healthSumA / $this->combatCount, 2); + $this->view->healthSumB = round(100 * $this->healthSumB / $this->combatCount, 2); + $this->view->healthMinA = round(100 * $this->healthMinA, 2); + $this->view->healthMinB = round(100 * $this->healthMinB, 2); + $this->view->healthMaxA = round(100 * $this->healthMaxA, 2); + $this->view->healthMaxB = round(100 * $this->healthMaxB, 2); + + $filter = new Daemon_Scyzoryk_Filter('combat-units'); + $filter->noChars = false; + $unitsC = $unitsM = array(); + foreach ($this->browser->getCombatUnits($filter) as $row) + { + if ($row['_character']) + $unitsC[] = $row; + else + $unitsM[] = $row; + } + $this->view->unitsC = $unitsC; + $this->view->unitsM = $unitsM; + } + + + public function runCommands() + { + if(isset($_POST['attack'])) + { + if(!$this->unitIdA || !$this->unitIdB) + { + Daemon_MsgQueue::add('Wybierz obie jednostki.'); + return true; + } + $this->winsA = 0; + $this->winsB = 0; + $this->draws = 0; + $this->doubleKOs = 0; + $this->healthSumA = 0; + $this->healthSumB = 0; + $unitA = new Daemon_Combat_Unit(); + $unitA->attachDbClient($this->dbClient); + $unitA->get(array('combat_unit_id' => $this->unitIdA)); + $unitB = new Daemon_Combat_Unit(); + $unitB->attachDbClient($this->dbClient); + $unitB->get(array('combat_unit_id' => $this->unitIdB)); + $this->healthMinA = 1.0; + $this->healthMinB = 1.0; + $this->healthMaxA = 0.0; + $this->healthMaxB = 0.0; + for ($i = 0; $i < $this->combatCount; ++$i) + { + $unitA->health = $unitA->health_max; + $unitB->health = $unitB->health_max; + $combat = new Daemon_Combat(); + $combat->addUnit('a', $unitA, true); + $combat->addUnit('b', $unitB, false); + $combat->execute(true); + $deathA = ($unitA->health < 1); + $deathB = ($unitB->health < 1); + if ($deathA && $deathB) + $this->doubleKOs += 1; + elseif ($deathA) + $this->winsB += 1; + elseif ($deathB) + $this->winsA += 1; + else + $this->draws += 1; + $relHealthA = max(0.0, $unitA->health / $unitA->health_max); + $relHealthB = max(0.0, $unitB->health / $unitB->health_max); + $this->healthSumA += $relHealthA; + $this->healthSumB += max(0.0, $relHealthB); + $this->healthMinA = min($this->healthMinA, $relHealthA); + $this->healthMinB = min($this->healthMinB, $relHealthB); + $this->healthMaxA = max($this->healthMaxA, $relHealthA); + $this->healthMaxB = max($this->healthMaxB, $relHealthB); + } + return true; + } + } +} + + +$ctrl = new Daemon_Scyzoryk_Controller_Page($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/character-edit.php b/2013/daemon/public/scyzoryk/character-edit.php new file mode 100644 index 0000000..35380bd --- /dev/null +++ b/2013/daemon/public/scyzoryk/character-edit.php @@ -0,0 +1,98 @@ +character = new Daemon_DbObject_Character(); + $this->character->attachDbClient($this->dbClient); + $this->character->get(array('character_id' => $this->editId)); + if (empty($this->character->character_id)) + { + Daemon_MsgQueue::add('Wybrana postać nie istnieje.'); + Daemon::redirect($this->cfg->getUrl('scyzoryk/characters')); + exit; + } + $this->characterData = $this->character->getCharacterData(); + } + + + protected function prepareView() + { + $this->pageSubtitleDetails = $this->character ? $this->character->name : null; + $this->view->character = $this->character; + $this->view->characterData = $this->characterData; + $this->view->genders = Daemon_Dictionary::$genders; + } + + + protected function runCommands() + { + if(is_null($this->character)) + return false; + if(isset($_POST['save'])) + { + $this->character->name = Daemon::getArrayValue($_POST, 'name'); + $this->character->gender = Daemon::getArrayValue($_POST, 'gender'); + $this->character->last_action = Daemon::getArrayValue($_POST, 'last_action'); + $this->character->clan_id = Daemon::getArrayValue($_POST, 'clan_id'); + $this->character->avatar_url = Daemon::getArrayValue($_POST, 'avatar_url'); + $this->character->quote = Daemon::getArrayValue($_POST, 'quote'); + $this->character->description = Daemon::getArrayValue($_POST, 'description'); + $this->character->put(); + return true; + } + if(isset($_POST['saveData'])) + { + $this->characterData->location_id = Daemon::getArrayValue($_POST, 'location_id'); + $this->characterData->faction_id = Daemon::getArrayValue($_POST, 'faction_id'); + $this->characterData->faction_points = (int) Daemon::getArrayValue($_POST, 'faction_points'); + $this->characterData->rank_id = (int) Daemon::getArrayValue($_POST, 'rank_id'); + $this->characterData->turns = (int) Daemon::getArrayValue($_POST, 'turns'); + $this->characterData->gold_purse = (int) Daemon::getArrayValue($_POST, 'gold_purse'); + $this->characterData->gold_bank = (int) Daemon::getArrayValue($_POST, 'gold_bank'); + $this->characterData->level = (int) Daemon::getArrayValue($_POST, 'level'); + $this->characterData->xp_free = (int) Daemon::getArrayValue($_POST, 'xp_free'); + $this->characterData->health = (int) Daemon::getArrayValue($_POST, 'health'); + $this->characterData->health_max = (int) Daemon::getArrayValue($_POST, 'health_max'); + $this->characterData->mana = (int) Daemon::getArrayValue($_POST, 'mana'); + $this->characterData->mana_max = (int) Daemon::getArrayValue($_POST, 'mana_max'); + $this->characterData->a_str = (int) Daemon::getArrayValue($_POST, 'a_str'); + $this->characterData->a_dex = (int) Daemon::getArrayValue($_POST, 'a_dex'); + $this->characterData->a_vit = (int) Daemon::getArrayValue($_POST, 'a_vit'); + $this->characterData->a_pwr = (int) Daemon::getArrayValue($_POST, 'a_pwr'); + $this->characterData->a_wil = (int) Daemon::getArrayValue($_POST, 'a_wil'); + $this->characterData->s_pstr = (int) Daemon::getArrayValue($_POST, 's_pstr'); + $this->characterData->s_patk = (int) Daemon::getArrayValue($_POST, 's_patk'); + $this->characterData->s_pdef = (int) Daemon::getArrayValue($_POST, 's_pdef'); + $this->characterData->s_pres = (int) Daemon::getArrayValue($_POST, 's_pres'); + $this->characterData->s_preg = (int) Daemon::getArrayValue($_POST, 's_preg'); + $this->characterData->s_mstr = (int) Daemon::getArrayValue($_POST, 's_mstr'); + $this->characterData->s_matk = (int) Daemon::getArrayValue($_POST, 's_matk'); + $this->characterData->s_mdef = (int) Daemon::getArrayValue($_POST, 's_mdef'); + $this->characterData->s_mres = (int) Daemon::getArrayValue($_POST, 's_mres'); + $this->characterData->s_mreg = (int) Daemon::getArrayValue($_POST, 's_mreg'); + $this->characterData->sp_scout = (int) Daemon::getArrayValue($_POST, 'sp_scout'); + $this->characterData->sp_identify = (int) Daemon::getArrayValue($_POST, 'sp_identify'); + $this->characterData->sp_vchar = (int) Daemon::getArrayValue($_POST, 'sp_vchar'); + $this->characterData->sp_vmonster = (int) Daemon::getArrayValue($_POST, 'sp_vmonster'); + $this->characterData->sp_vitem = (int) Daemon::getArrayValue($_POST, 'sp_vitem'); + $this->characterData->put(); + return true; + } + return false; + } +} + + +$ctrl = new Daemon_Scyzoryk_Controller_Page($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/characters.php b/2013/daemon/public/scyzoryk/characters.php new file mode 100644 index 0000000..263a68e --- /dev/null +++ b/2013/daemon/public/scyzoryk/characters.php @@ -0,0 +1,30 @@ +dbClient->selectAll($sql); +/* + foreach ($data as &$row) + $row['characters'] = explode("\n", $row['characters']); +*/ + $this->view->rows = $data; + } +} + + +$ctrl = new Daemon_Scyzoryk_Controller_Page($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/combat-unit-edit.php b/2013/daemon/public/scyzoryk/combat-unit-edit.php new file mode 100644 index 0000000..7028005 --- /dev/null +++ b/2013/daemon/public/scyzoryk/combat-unit-edit.php @@ -0,0 +1,51 @@ +unit = new Daemon_DbObject_CombatUnit(); + $this->unit->attachDbClient($this->dbClient); + if($this->editId) + $this->unit->get(array('combat_unit_id' => $this->editId)); + } + + + protected function prepareView() + { + $this->pageSubtitleDetails = $this->unit ? $this->unit->name : null; + $this->view->unit = $this->unit; + $this->view->attackTypes = Daemon_Dictionary::$combatAttackTypes; + $this->view->attackSpecials = Daemon_Dictionary::$combatAttackSpecials; + $this->view->armorSpecials = Daemon_Dictionary::$combatArmorSpecials; + } + + + protected function runCommands() + { + if(isset($_POST['combat_unit_id'])) + { + if(!$_POST['combat_unit_id']) + { + Daemon_MsgQueue::add('Uzupełnij ID.'); + return true; + } + $this->unit->import($_POST); + $this->unit->put(); + return true; + } + return false; + } +} + + +$ctrl = new Daemon_Controller_Page($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/combat-units.php b/2013/daemon/public/scyzoryk/combat-units.php new file mode 100644 index 0000000..fe93a07 --- /dev/null +++ b/2013/daemon/public/scyzoryk/combat-units.php @@ -0,0 +1,61 @@ +filter = new Daemon_Scyzoryk_Filter('combat-units'); + } + + + protected function prepareView() + { + $this->view->filter = $this->filter; + $units = array(); + foreach ($this->browser->getCombatUnits($this->filter) as $row) + if (!$row['_character']) + $units[] = $row; + $this->view->units = $units; + } + + + protected function runCommands() + { + //add new row + if(isset($_POST['newId'], $_POST['newName']) && $_POST['newId']) + { + $object = new Daemon_DbObject_CombatUnit(); + $object->attachDbClient($this->dbClient); + $object->combat_unit_id = $_POST['newId']; + $object->name = $_POST['newName']; + $object->put(); + return true; + } + //delete rows + if(isset($_POST['del'])) + { + $this->editor->deleteCombatUnits($_POST['del']); + return true; + } + //set filter + if(isset($_POST['filter']) && is_array($_POST['filter'])) + { + foreach($_POST['filter'] as $name => $value) + $this->filter->$name = $value; + return true; + } + return false; + } +} + + +$ctrl = new Daemon_Controller_Page($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/config-generator.php b/2013/daemon/public/scyzoryk/config-generator.php new file mode 100644 index 0000000..6757934 --- /dev/null +++ b/2013/daemon/public/scyzoryk/config-generator.php @@ -0,0 +1,51 @@ +dbCfg = new Daemon_DbConfig($this->dbClient); + $this->itemTypes = Daemon_Dictionary::$generatorItemTypes; + } + + + protected function prepareView() + { + $this->view->generatorBaseValue = $this->dbCfg->generatorBaseValue; + $generatorOptions = array(); + foreach ($this->itemTypes as $type => $name) + { + $generatorOptions[$type] = array( + 'name' => $name, + 'weights' => $this->dbCfg->getGeneratorWeights("$type") + ); + } + $this->view->generatorOptions = $generatorOptions; + } + + + protected function runCommands() + { + if(isset($_POST['baseValue'], $_POST['weights']) && is_array($_POST['weights'])) + { + $this->dbCfg->generatorBaseValue = max(1, (int) $_POST['baseValue']); + foreach (array_keys($this->itemTypes) as $type) + $this->dbCfg->setGeneratorWeights($type, $_POST['weights'][$type]); + return true; + } + return false; + } +} + + +$ctrl = new Daemon_Scyzoryk_Controller_Config($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/config.php b/2013/daemon/public/scyzoryk/config.php new file mode 100644 index 0000000..35a91a3 --- /dev/null +++ b/2013/daemon/public/scyzoryk/config.php @@ -0,0 +1,49 @@ +dbCfg = new Daemon_DbConfig($this->dbClient); + } + + + protected function prepareView() + { + $this->view->cfg = $this->dbCfg; + //healer + $healer = explode(',', $this->dbCfg->healer); + if(!isset($healer[0], $healer[1], $healer[2], $healer[3])) + $healer = array(null, null, null, null); + $this->view->healer = $healer; + } + + + protected function runCommands() + { + if(isset($_POST['cfg']) && is_array($_POST['cfg'])) + { + $cfg = $_POST['cfg']; + if(isset($_POST['healer']) && is_array($_POST['healer'])) + $cfg['healer'] = implode(',', $_POST['healer']); + else $cfg['healer'] = '1,1,1,1'; + ksort($cfg); + foreach($cfg as $name => $value) + $this->dbCfg->$name = $value; + return true; + } + return false; + } +} + + +$ctrl = new Daemon_Scyzoryk_Controller_Config($cfg); +$ctrl->execute(); diff --git a/2013/daemon/public/scyzoryk/cron-ui.php b/2013/daemon/public/scyzoryk/cron-ui.php new file mode 100644 index 0000000..38029bf --- /dev/null +++ b/2013/daemon/public/scyzoryk/cron-ui.php @@ -0,0 +1,20 @@ + + + + +1O)Q>0)i%uhnjy$&k+ zQ7%7oI~#9bSx>cDB6rJ;OV6 zUVKc))*o@#{AHcKj83_LeMmPx&I~%^#JCyK;STx}(kL0iq?NEeN?r`c JcUX&faLLEK&qBrm_S zW~t)73-PTt=B<(M{&i{XeC%hpUwWSRb|C)1QrO@_>oYWVk9TbSpT?YAHn+nnTVbv~ zM=mwsqBx8@tD2~QZfk+qJL4+E%XPu7s*YDc@dh2uQNy6Twqd}JCgk$YJu&+Xb8lxh z*p8+427;;F>s1ZwzjUR4#+aNo3wb$N-{uCMm#ag}vDr)tS3pynRqYy71h@&@`VjS~ zt?d2s`DT>9j^nBn`FQyX*)JU3(8b{muce6;uf-_VkhL3}iVtP9E?)VL^yI0s|1lvV z_0GpoUbi>jzfGX0%DD)l^e@bPf4lLYZeVVR&3gyWT2?Hsvk9Md1q5SSL;dlhgNEPt zm& 0 _orh5O8@{Are!fSA~!@JXGqeQGIkYXg0`J$L7|N>4r`flrotqDKB5;X2{& zT?5>Kw(E0<3M}Y$#Fu49vt#3XCwepgL{?4DCSx0OE$*?k%2#x*v@k&jRTJa=f@U{V zh<^tRkHrpz>2`l)kMbrpXFZDm$(kkkc~d(Ij(vh`8~!;XSiA!i)|8?b?$R})df3C> z%J z)IjZYLsvFd$q|AAjL@k_}2CjQ}6KfFWI?6W @Mm!GPaE3 >8+!9;aHFOvB&GgIEJx8$3sQJJ5o4HR$OMPIw)J1y?JJ_UT|qdzXTCa zF2)k(*RzlqHxufW)1um%hJ6sty4U dMl~=5t#RntxIS%%@w0$6Jx8y)3TEmxu72pk?X$SOc zU&-Bgl;hLpn|mfS=`8f8_@JG`cgFEU^H{4y4b@`k(*# WP60R>U5Zj zx0=d^;>#q9HcS6`WmcopXtX4yvetMEv<3Dl>|BQP=0*oJ?>v!uoBS=c;m;zL6!AJa z>&!znmu0X$@vr+wF8HO^Kx$)cPb!Yl^o_W?i%TB=@27_I o0R4W#-syQKYfW?PzA|J%UTcMPZb+A2o>&`72Jq=nX3=?>vP-!5{QwBs=&Cgon% ztg6u#QQ{{zp-T|KGHExGTiYNl=+j)rlN$M~2PVrAhugD`JfEw+Xru?By+5wM^!F9` zFx$XF^i`18TQ-&06V)*4g>2Fj?f&*v+6Xu#-~!IlR}v&s|0{r80j{5&;0N4fJ)sj` z NaCRsk&)P%iX7Ck2KExLU%)R1H$clr9?WE>Vmkx zlTD*lsg^}pJUKO@TwCRDJGp B*NY`Ae$<8`g?*QsbkOE|1vJ? zP=hd%5n{4oh<}PfSx?$JKWVCVy;`)O4>BQQ3*(rS*Nu%?nuP>EHTC$Bb}akIBMNWA zNPrNMC7TC1{0_yv^f@e)lv89=rt-OMGlvw#Z?am5%>)801C!ejjMHpSTr*v$%nO>T zKofKWhJT9OE=OI6a0_OjEWdg1q6~tYXC??Gy07zHT6{8vF@0ZKLzl}cuw~McVy}MS z?3if1-^EO1($lah_%cYRhAYKBFWL$-K|;dsKIi1)LoMm{E~r#X7Pj+P)yQ$bZos>- ze`@8*4bOPNou+{1)3XGpvQSE|4UJ~m5Hcg3GIvKnAAkQj91%fU`xn1$_8m%t1PEao z-5lq@AN?Dqy>h5TAGPQkq~!+Z7`RcOVD0T4(uF0s>FKpHFVnA21AM7c0oiwJRIeBy zyOA>YW*|2lN@#hUJuQ)!x!MHer%`(pgZL5{t`G9;RosV+J0KnQoC=aYuxjC}Bl6uX zG8pGUc|xsL8JQ}5EsnvlD@0v*;(yS&>U*s4)>)hE^SFwHF6QKDKeD1q#`kh#wj%)% zUw(6S>NX#fcr@$wH=n+-c(68nJ}x1vi;8cOit+odY#bXiS7)9TNW(!MHac#FQBi|a zr W`v6NcLU z;G(TEW#KW&YW+#2&_vJA_=aQJY0AK*$@>Ad@%?k{T%J}RlNs#?97WAFk-bzFR037T zqm?G((}D5XPQCV9&U2o_yZj2Q$5pkKjD-0+)6aC6N~*;j1PUV?%r~sJ@c(@MR)aEa z0+BBu#j0q5c2ieAO{WO4EV*Zq9u;FF*EQ+A{o0th+Uw``$l~=h9xXqUMI#7bJXBJ% z*Wzy2qEWv)$&r;=wgRklw20S1B|fKQhcyfi2`zj26Sbap)_ FK1 zG! gcl47xju24RT^&4eX2Y&NIS2&)TDu>gWw^k6TA# zUfH9y5~(YV=;Ls@BDy@awGHEh-T7F56U)5-=QoQ9ZW4TsqrL=QO%K&qB8N9BZA&pd z EDO_8Se8xEoNBTi1=l+|T&iEvlxsCw%VD z<13VA^g-NMPq^N#>U)D@_f|Dw2V50>04F);GfI_ViG
yGaT+ncP9j*VNqqVTcHVtD@%&|eOzY#v;H5@uJD1$~ zWb6WGyV~p;IJzXi`9^rsdB%gy41sV2+b(bqR{UT{smUx0d{C@q7W;H*M |I`Mo zIfVb*e1EhUO4AEI!s0)7S)&HwUKx+&bT|DS?UVz?{M1OuWzZGhL`dNO9$|3ErrgrN zZ+?`)Dt#n7dhe@`{H=vP&TrMxq)9|9ZXjYWYR%!bNflzI4*ssFGDzAl_u0cL_pyCk zEZ2?TQDoEb)+MyLfIwJfIo&B2F$#6kI>VY4alBtaEIh6~8t{6wmi|NEy!wS{A`Gq{ zsKrc_NtK A%*pHA^F?){%Dl-c(AS|yT9$v3Ni5`xw|c2rC0TCu<^0S zw(Vb;T&FmOrDFKGZrP<~?p?<^&BeH*g;4DmnsCQ5YSXk?`TW0VueY0+vBW+DNLj{@ z{ISV~2q!5E6bK?-E)L1;cFoTe`f>T>sj4vz8iwZ661F=vZ_H~;V`;Sv2TvNlOR`s3 zLyo-i*f6$oI-nF2e;n@_voD;-I>|jI;?aNPCR&{QeDVDOr(_$;;Ee{E-pw$}0x4fo zW2P;F&um;$9~H6YzDrKLN)~o&Z+VuVAIU+dXa6m;c{+yNTrqBl+&5#NYe2GbcKc3u z2&Lm|?Ebcm@X+u?#F$9q@ITKb$4RUmQxIuM=(~DD>31i2ey+Jt)hX?c9%W;`ZGS@9 zC(kA%3VQ{77yP4)1G965Xn)-m-x=%sHud4S8QF%MeuZIs&9(dwmJIxJrO8`4vX|*> z8l09&e}uK-(|#6PgNO+nD({HWd?+^l=`r%Bc~NV7tG?!&T*>+p(#SVL{T^mlK&c$t z>lzWs&L-S%YT`xgyf}|^L3#vGZfM*_74dM&*@e)CLwPE*O5cts_cgimd=*j=9RgK* z270i9Q;}*c(5c;1uYY%*%ySv&X?tLl{aL)_{s4zjp_e?Zy9y3A)TRqPQpV1MTFp8X zl$yE88FtBRwk?GEgvgmMf8Mo-WvjgcO1_@``8Fe1q2HT=Qu$DQ*_t}EsUxj_N2&^E zOC1R_IeVs>WidUeF<)P7kTK)7o-BrD;!rROStOD&RN9J`2lzv}8 o2cVw6(e%VM4rlTSP{VbuS7R2;j!jI=gaR62Ckhnx zFSWg5jgMhD(4UVDw@Syx(04ie?=GgROTsdvBt`e`d1m97AU_40u7K 2}p}{Nr_6=&{BiM3^25c RUd^okwzF)jw13U()X{rHmaBu(`4-Ig?4xm@{hS>rD z+S&jC002Pz&>wKW3{U|O;Nj!r;}JXv0s;a;qDRC;51=F?BY8wcNli^fNkv6N&&)tW z`;?A~>Iuh_rz|XNY-}_PoZOtO+{~ (--68}GNaPjaTNCJo-v|5h=ICywC_yoj+gv9tn4@Upf z0tl!GsX2s|h-ko;@wA*G@SyyjDPlThNJ1@_n2Ny%1U YO_UMsiHA&E)9 zADl7a0B{KYuTXe6xDTNKL=Q@SDgZ7nE)E_(E-v2x$L4>bsHh1zgb8Vszz<=R>A5WF z?y-PJ4+gkYcvJud!1O(!kiPFkVY?!;Xcd&XwJ77Tnh>}K-^5mtu%#DGf=nM97`PJN zUF7yRA6jr*kO+ELs#cq zhxUf%qmp0)oQk04?-7=f;GOw{5f60& zvlv-fet`}6o5f6N<3W$dzgNu-NcbmC6dNpwas*=pb*1v
{XRLEYRi&j75Rh5`f z$YMzIxK>Ofn+C){xZSl{+`YeGHM+7=w{AK%W%RYj+D9;!i5Dxn{x-AZU@n7a4ot_x zXkfw@w={^=Tm|KX*vBd3nt=c-0J_B=YMWjDijL8`cpwv&7M-_-!Ho87(*38nsn^0l z+JBj=n4UVRX*1H+m0YVbXQtVWOue4Lau0)6>*<43OP_-I(Ia*ZD=(l6TUxoKnyKgJ zr?z{5ZiDU~H&B#Nn$?kSOY!IGrV+Uh(TH<^UJ`Djj?S+-@0okR=f!b^dQ^o7031`N zcDGbm-BopR%URgak6FA3DbmXlGn#MuTkQXOm7XoJF|o~>6jNo6s_UHVzj~fcBmC3r z8b0Eg&`fD c#@QcdNM8@ZCm;eIQ&==^4 zUw>@RRWXo5wcu&taU$N;4E)MXIl>}lRJ}MUJ^=-hzuJ#`095JU%TOVEx2mwj?7bD3 z;MIQVKZj%n453p*@W}@dL)N3Uo6`Ohu4?5$tPU(}B&mYi`lMebVaJRvLFatysjG0D z=21Q#tviPnH-`Y2={KSME2W+l$FN<}!-Ws6%||RC5^4ZIeizRb$2PI5>dCpwO=)OP ztyY2#DLCjrHhjmV3_t=vR2ref6T}*Og_DPz*z~0TK%r95%-O4v(M_V?Q42-(;E*B? z@wHJh*Bm^L*QOhC96heC)YGiQ+-G3?zx-ymeU*=R)4mHTwJn*|yL&jj&m)@Sc+p-p z$1hMO0885B_h(qx62Nqhl%>!_<;a%TCxREeBv_(IHfppr5aX|{`)<7$eboAgF!e2( zs|Ur!NNq)n*}@fx1Pyjvavk-z%fxTp;>g|9>AciNzYt*-(DGkqnA07yaX)-r7UFKC z>k`Temc5ERi#m52g-dl~D~ 1x<@`@v=1F*92rFFe$*PKrD>1d;;oWu~^ASo`+aG$(zBD}^( zn0V(jw XK`u+O^zXE7A<>cZ?%M%lpwRHZYUAsQiq^>*lYxe zzNdXvKy;NE*M`qbPFjOlhe^p);qwVLE}O{%GF^fa_>V;e8M`tUY9w%)iffTNX~j-< zYifmyn+;R_j`Asx!5=sCT8?zaxo+gCGY6ltGK2`gmyn?~l?C&i#>6=?=Aw{x)AX+$ zwl7Sp9bq-+qmH4VUa2_I<~7d`m`Z}O_xr0apI$shbM&px+W9&%Ltyi>8;t{oezxba zK=*rq{eB{=tL`47$D8My@!u5$hAv}C&zrO#Gq#zzkXVu7us9Ik4m18S%D@?CqxTI2 zmfZuKa}sX2mQ_rVC+-lhE}rLwn+Y_D_W(l+@#xH_NDa7bc3kb}D+6JbZ{*p~m@kzu zxD-OqAo(5;wdGq275W(50la3jF1+0rPgrS;pj8JSdlxM Hjfe~dg%%p0Si3bI}+)}=|+&;vb&WXAk3YnBr<68>qkg6HTm^86xTkFe5Ql% zCG>X4PV-DqJ#p?2$4ae0YO}wu#M&4T7%Q8n3`x9rsgh|;qQ6DRiGiZ}HeG*-J6b6R zk7%9Mth0I!Jt^z+jk?S@^{(cRL-kbl<-VhV61U3iaD*su?wfj8!89PV8W(f-0PTUQ zp^l#6B$F#M#(+AoTJ|)OR_un>D~Ln-c*Rlj0$M%|^kSYy7zWEUa0HMtR#YnDs9iAl zCEWwoD0LbRhd)h)Wd^rRG( #sjsXwM?vj@ZualuHAoKak5Qc2V@Cn9byoi_!JAQqUQvDW z5`GdnlsJhu`v}{JK+iMbu$#1=UPXT13=MqWa%ny}r)jmhLZe@MODTyl{OAgZTME55 zQVf04Q3TnRjCqz1Y2dKbl;R31+I?}Z`6Cl#0h$(W=$reTKbrXXBJ*)Ed5NT0noD7h z*25XUk(cEmTDir{kRS@KD0P+!Hv@yd !09ckAnq7M*4gW8)DhwvOCThJEh&{v9R#hZWFIv791Pic@+b z1>+xj*k9VcGh06nCFkA8(B-6>t{mZWix4lnIm0@O(I;AtwmG!IeMT-7pFobzZzf8k zz86Nb=vh+~jzuQ~SJxCsd)j$DT_hmXQc7H+DUG|aY>*JNL2$~XQpaB=_NZV<%5pW3 zL)ikSVt@bJ@a=BYJB_6O?!}BNb!m&?nkYC%223J6D7At|R+J{bJ>hNM{O?$7{0oX^ z_dvIWkllZlPA36T5xvKf%yFJHqZ<=jnQE*d;5o@Ivn_R49&!0t@B>_8*;fhl+J0BD z`%KwYwyeJFWzDg~HT}TiX_z&8 VkVB`y&{x`U9n{g=cK9<*_YW;_-5j z#GjGK^1UTYeq|v8DgJ4=fwi_t$>*)&`07MHpHXHsn*4h=KXC`8 xBq-`dpdT{8%IFzH-4P`q%_Tf5B@B>fFoXaQw9;V1JvSrd&M#1N*24Qgkg z^ngWQ-^l%!E*rU`0r~I3julHl8EjwE+G4WK^hjguYsi?|W9k_`EqlB;JlukYVD)n( zG+_5{|C?motkV+b_jzLHS!Z}>MWbyjbc4g2$7!bI2dPl`WufZjC_jN(gjc0LH}g84 zd~4JBbfZx@lfDjz-Eab3o!jTc<~a^^E;<`^d(OJ!OOb g*e74*Qd&*_T>zZOB z_K-rF(|lP4PwK~aWZ#n-UeG5u4ron?Y&_9~zVh2_-(o#8I*yvQ$bKak(~!hXKtW16 zaS!lN{D_D5gwASrDwPh=qJej=>D*Y4ttg3KQ;&waB&FM?5_7TNnAe0j9LVkggoZ$% z*pDL<3ZllvILoF#ri*r0zQ>yeFX1cxmn6uXRAAuWFRu6Pk`Cl |2KX3n~buv P`4B4~ck<(?f&sc3@n+FI;uZqGrN0sTs&e*Aw*pshSz{e*5J1;3)b#2= zz*)O6G&(_eXkO*~ohkLvA1mp7$~%w7R4T|T>$}j$ELt76K%Xk12~!WTQMb&buaSzA z!r6mg>3nA|8-}J*gmhVCH4%bN!`*9zQFzDwQa2zI+PQ6>yRb<^4i^1B3NqbCH-kU* zSBbP@eRnTaBN2GfH5-|HyoZ7lM12`mmy*)ZQNFEvK$++}LVX^bdHT SvoutFrBmDya%f4zmB>#|uuCnzYj8N@A^-lM6dn`nb+KYzvw zHbw*RcK})NyrNy`=#KKu-YQKQ VblE=}s zEUh8%_JYEshc1Zu*=6o#i%Nc#haYVQ(Hs>qgGAc#~0|8rXyJdz-}U#{j` zZ9}2GjKZ6aVsYk)*q7Dm9OORQWtLA~igp*qd~*cXM`){4p`A*#$&)I15sAhF`FlE3 zC5uz~D7yNxwed$Ik&Uy&%87N`I MjLMJiyRuSH}Mo% zd}Q!`tVB7av#_E$(k{^8HkZIM>iTqlX4v67>ab{jTvQ%*z|ERZ6kyb$z65P^4*Nl5 zjn3Qrd{pgI(&m(v1O|VBwXLpDDl}4mvfq ()jMi|RU$0d+X%^Es fYD=< -4G}`sXLEIfxp#`wG8MmHbkc1BOmR zzO7$VdeFgq_$x9qmx2$tgFMs#j7|Zz1s`g*(zVrWcs|P1gak?@I4n(HRLSL ?F6u^q9j&oH@)v{3*4ggQs zXV_~0JNrL#^x_?8_n *eu);$d#q(+y+26eYlZxMUyXcFwH zCFZNqTqA9{0GYg79;?7LSh&{4X;zQ9BAK{R#kjH#`FdfdYKFG(HBvdp-y(u{T;uXR zbd`13E|!rBtD@lyz>oS(7i;RlQZ_}>EYTpfcj(-)FIQCj!QurqcpjK6lDJ!#E4%y_ zHHCDyuY{+%jkYd;vuEy7tyFKyW-00K0d$meDKP_GEO!5l?*UikCk5$sqIYHOj>;#_ zf8tfHAKyq`h~zmWPamg1^QMn|WZXpfUevewtaivxT^r6B(XuUNaHNG&DrU_~U~R13 zr}9JhMYKh6Z^WoGE03jDkc|_vJ$irUS2l>nm1~zwXZ&A+0%R>8S5rY7rwMV*K|v7z z>q-`H!)IbF@Qwb_7LymMR;I@3voc7-+LM?DqH?2E<8$3;%T=ne_#&hta@3X7v3it@ zC}9wFa#9US`rI5jinRH5kU>*=SQ(G_PkBBh`WXM$WW|SsT?dCgPoWzf%CFC5GWzN7 zoXn*)*-xB%_Si2Yd&Ql@Y|6Cacp8-x5ZIFzt9ZIyX9k4rT`{M5;$Cwgv+k<#O8%zW zoXp1`0f8UJ94CI%Y^Y~_#`349O%28JU6V!R0?nklHhcs`i 7dt8j3t zvR3}k`dBmxH6=e4Id+lyy2PKq4Mr4)8|bPIsiAzOR~=Nopenhf8S@wRTtKc{EM! DgEl?wd=g|iz_L9TOm|SHESF2h z#F=5EMap4FlFYhmj#?$fn^+d7y0d1F@nH={0r~vvdFTGWnM|dZ;SoPnBKf44VI-nf z1KsdR;0>RKfN6&&(>UU@HB9p*SC)~L2QS4kq9v(?y~QOW>(Gu;O7`E{)lHfF54WiD z*;}?0k@IE{en_ B9>l#51Eejcru9DzEROan}ysmT?@70TM0WI+I3 zH@q#RfzJw0qi&w;V&BSYt_bw0a;L;$Q`VSf5vXOIH`=VgJyY&44-bqc@5kVInXN)h zmMas#2KUt?BNUQ*8jk7Av`OM-ND@JB`b^RsmAs8{fA%sp+QiM#R(ry+N-UXD`q5E# z{EE(oqaF!~+7RIVM_SFLhCFz6=TLda>5QWXx%rDns-``B6xDW)EXlg_*ne+ifvIAB zc& Zi`nVTaEhdCDbg^M<{Fnt)q5*W zH|+My?OigL)E&o4%$hXCz8yK$?6!5MUQi#mjaoxaT&ui;-_}ay1gEfRS4D8Hq|bRb z>rKXt9PMJh*h<8*7lQ`AGfT=}A~skh!yEKZub{O3t3e8dL(y!RGY36~e%rX)khS(s z)|rtiu#gwfBcV}=>sDf5{b -0~;YrO*zpI|Pjjdyy z7S-iQ97cQVF}X+G5cOgvIo0Iw|A76X72LfIQIk_dClPn~%jUGQFnxlFuZka}i0}-` z;LrcmChd-cJVo1#5u*8hg7t8GUnQ}tYT67`1wstc+p?XuPUwc9J4jqsEKYd1ZpG=V zcmAiBud)9{#Rqe#hOHdJSXg%ukwgetSXE^Ayl{i~`}ONE5_6^ta(+n!m1uSE8UNgZ z^OpKINF3P18y@isFyebsnH1ITW(<2kwoK+O%oDq8xP L0 zH&S7l&I}RXnM#QD863#BdyoFY9r4Gc&g50<#OR6?RY5Vigy^_>MB$DZ$C#-RM^dHz z6HpI_<^@%|{x`psZ!8x()MuzUx)_+mP;o_V%9P6pZ=n@aQLFGjhay;|iSzFKk!!6Z z2?aWp)>^Tdzd@!cPB*VW1eD+fjgLgTcN8qm%h R_{ zsyC9DiUrS=7@0-1x{sPy@zUV3*(ZS@YxiNQ_uCG zRk=`;Y<jT^ UGnw|S5PL*%-h^T!?(`(F9SqRN57++8@XX>u9Jx%|dY0JElXL#{O0CBMXzG`ex zvVpD<;>}>R6YyAGjMyNI!e&q_I!4g)b^MRI0Q}h$uPL3)&f;qDHowoI-92CsF17b! z^S}6`X%h-@4hrrwO|*Pf=+$7fA#dwh;&eG4&$X)M%lCmJZArfVCZ*Dp!;$-GsV+&& zy8~E=L?F;RM!c}^fQ*yLr6ps0_Yy3p #DdrgLc#dmI_>KCt9b>Jck6V-m0&oKo3fXmL0on8D(DZ< zNHnwgWDRu}!)_)t&Hf%R{_=OEs;LEAhv4r|9m>+B$7!qF0XU~c2XhQA-@Ar#lQm;r zSY30qoybSb!94E)zD8?>bVX)tXQ01qzw{ht7yf4@Z;;R+^z9_^-|N36Q~hJ_7GwX% zrhl*K9)L7jdl0Hfc>X;=r24Dgm)TVPUfpY=kwj=gD^;iNwfX}izpfNQqfI|f8&e;= zkmP`;E}X-26t$Ge{B`t%!tKnnpz14?Ud_K;KQ&vB&DGO!encY?z&Bsm*M7vu4Hr;D zw0a7AeW*Dfb`TEb OGpGI*|BN@% zG3WYU->%E+ghhC2oF0a%Me&4gGbRD^6{~AF()e7kL|{3p{xTB?lrktgJSyT;Ja@gD z_))gL>ReH2%N3-v>B@Jj@>TrMUc@w&s#J)%abG#XH`=EOwT%3*H=hMjH{m*uyg*dO z7K{cV@Gb`#lGULzpN0a|#bn ~rq
RojexXn1- zkHFlm%a2MIppQeM5_Cz`My|()kAir%7h$?GHOuylkXV3#ILASqX9~2B&Q+bDRaRFl z=2#3RY~<=AVv9Fh_%nr)a@z=Y$XuqWl8iVduOrfXn^7mSV*H;nchb+i_`slL1NAM9 zxYO@GbCvyBr66hU+0~mtcLq%($GlVTk8O1`Wn2V!t|ZL+!pN|GDq9h&eq*Oi*bcGD zFR4m9?SjYUpKM-eM-NRI)aN{lM1S0YXw;5^yM3zt`NR(EZ*Bdwk$9cSr#|<91_>3u zEQkXsaKBq@?CY&3P@7T4)MV{e{vMFb`*XTe=dUkYk14;AGc5)+QOQ5;MtQV&q9C9L zW6b?g`>X2j4Z|hVAE-ibj y~JE zb(2V)o1H?7bb0nGn$D6+EqrKJ&Ef2huI?V-dUX%5nY;iSp`8VukR;{)Oxe8$wC3Fd z(x$pLhDWCs6b>WR ctbYy^&+IW`trOsxkN*`{d>1phQa7N1WmD- z#DpPb!0an$z9rMvC48TXyz9aCONz5M;y-&01l`?L6E-PYkB1?`*>NO5Z$pCq{KCo| z!vs`vrP;7t;aUEOxrT7q;FVd_XnRa6qJ1{2aYQ#QHvL!8PS`#>7b7#D+Yj*1XTy(! zmC7Sdieko?RH@?Ly><2Ur_DK2qDr6PeI{bAv&8te`^;1qb|jZPv^N>asv7mP*C?@= zBWbO@cE}SsZkpyyE|#*a0fM{&tntNdic`y&*ht|;Z0o|%26=G?8c()iRi=U!wZox4 zeInD@9+t!!BM~crl0V}u2yd;V;ItuW%VhF>1^;(ABmXT*dVQYYz9s{WYAQ77$;Tfv zmzh(zuLdtZ5ks*kn}g6VZQ3^h&ir=Ah2&c~Jt`Dpqam|j1Bc Y}`z3TsYNuwAHC z_+?Vs+UY%L`e}d<*a6xQK0;fFgx@K(-!crNvwdE{VXU+YC%B4CSZ!Of6!<{AL8$+x zjs+9rJpin5e*BiPOt6qQoEFuaZCri)<5-E}v6+*zQ&5|DjL-$m9+lB?c+BkJEGoO( zrAz_rz7MZu&TC7#V-Q(BwPF-iFw~;7Z5M8SI9!8#td#v2y0}WB30a8oqIlV9BpB z*5sx;gMrGX7QC3&;QQ}?J!=IN- 3Wrhd|d%pktivfIaQlgrv0dl^13~nG7zAXyX z)97!x)kGZ4U#v5u!_qHsNXpc&sV`e$FdEjF+#J81^M3k}E}~&EvTfP)i&&Sf`O yja78R%oAM#LZU}vPCS%L4nkZP!P_QA68$NV;3P;)b<6pUoQmB z!LI>Q?8Zx$X;MbI7Y N66`R#NE lNv=2@>Q6Vqy;c$~>T3r-qWA z-;$XxCSoBa-wzex)&l8XNyR{#)50$w|DFE|4looc7i9bXuy(h^5)R`@Mp`#Mou!_k zq09xZQ&_n!irZ;U%e&O)Z&Ab|867gC4zj&^OY-L#)n8|73rC!@L$lF1cLkjuC0GnP z;koCMcAY?tWT|#2wZCm`PQ~O~Y{~qM70JXew_Tk*mvj$}$nDY`EZv^v^G^jC#mZ); z>U$oT*_<=x0Q*u5?3PO;PPKqo`fgQ0ptc^?(1@em_*B=8 5oyjjc7oK^&0Amf zz6X@FFx}DSo=d9nIZKyA*MZYpezy;Q0U!D`dade+5Cj4#-w3z3k)Z_nt78q7Y-qPI zal@Hrj@iFdP$t{3h7j~uM2n{ShZO)D{%0eh_$6fkGr!o2p|n=yBqN7Vk9BT)EvA}A zC%q{@95>Bpi}#q$M*bh|X={>pb|gO|t;7C0X-auuk!?L3)TN%Cs{dULr^K<3q3SU* z?N>h98Lb4;`L^N~1q~7o(Xe!G&I*3zKs|0Bvz5x;B_?vJV}?&DCs&ZpQ$uZTqVe&o z+pQOI*LqC#1!QQZ96Y+fJLzT*Qg5d*Wx9bvKGpa%$;M{BVXIlmU=)rF|6TGF?=zjX z_^HA2Oyg`;^Kt^Ed`oYhf0o=F7ps`}_yv5?ImwfQozml_ln*KCHWdq3X1KNQ-ha)5 zrpjrQ! y8KyJcl%R!#JD$${|aZw%ZOVtL1y{!ptF{HIJfkUOy|s^Y^X+W)M4@T zD8B$CM#{CAe$=}3R~XhQC|CORM>=G^X =K`Re&xKOqr;vD%@&)60 49PHm7U~8iWPlUi2}|l8FAAwsMq+JM#Z7 cge0 zE;Hb4$>38lAh>L~;q!~b0y7IpEFA@%RWyG(c{ro15N{A+O<*xNcxJ(sh;!IzJ5hJT z!djv*KecN2;ig9+)AO68A7N!q)%3ZwO+0PcrL~km&fFJMXm?q2lKXL}d^N+pG@g2^ zM5o^^11hY$NT|7;p6>9bW+XAt`>98U9o(+cg$ps${&bxvFIa#}&kQxJ%q`1UoYLEv zKp%&Yj`aE%XRbDK$1#xo99I|=rX#C-S h23Tt`Bd|{TN3tbnq&ao({1gZ*7Sx8}qww+RJEe=c@dR210UuFH-$PFoc zgdZqpAU@ku8VsR)%-2SK*Y`(n0YWxJ8xn&@HyE=|y5YMwOuQ(2P`|3AoFEHxfA-b( zzkQYHQqu3qxWq@aAM8azaKqh8WFN27uM&p5)<&fx5-=OUzrkVL){EC1b8rxux!PES e=JpKbvcCQ1s2m|)DxB#rphKT^Qpke3U;01gs#Z<_ literal 0 HcmV?d00001 diff --git a/2013/daemon/public/static/badziew4.jpg b/2013/daemon/public/static/badziew4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..24e1473ab539c15e915503da805d0654e83fd460 GIT binary patch literal 13476 zcmb7qWl&vBwCuqhLU6YO1P=svKe)TQySoN=2=4Cg4#DN%?oMzCF5&Xkt9o_+-nDDb zpH;i3r)rP%>iJmt*a3Wz5| fP?}-qr;HFeq|Pd!%#HD zBzFvCNyvxCq7YW9@Bd-s6f}E{qYM-=PAq8H{Y1cr06;?hZ~RXLz<*tQ=9tg{kdP42 z|8)Qh_5TP!K%zrIe 0zte?Fx+P(f*10X|vqM<{f0|Wr8!remv zHe`E+Fm-ix6|#k|*7HJ!$)qY!Fx?&&%m?4V@ONdR@Gi9|(1hy66|jA>^omTZ4M6Kt zaG*qKZyi#CJCYZ)!(gr;?7=)5&RKX8d)kZa;%TcQN`2aK{c0GMKw0j1zWSqje4lCa z;#252^x<)&Q{xwoZLaaS=dW&8-Dco&jZI4b*K_SSq*0CAjnV08qL9flj L;0 zEaHFTbQ+iV9!8NePK|X6zO7fPmkCwc(g&hUtBE4FosEV3tGvA-nYMrH0FNbGWQ%#+ z#Kp(XtQcB4tf)_a5uN_g$NWxo;`7RB1}!gJ*cZDiQe}W!fx{+T$nIbcjApj$UJQ|n z69uganx{Xu^V1+0ZQeqldD*n1v`it7x)PI9lT+ycPM@UiiETb$Cwg9LlimOdgj5Hs zvKL;Szo%5Wp-8#Bl!E%iHz?RVf(gmr-&pj>c+CefYf zglB^d;=qWbfFM2gcQ^sJ8M5 ~N)RhH+XYMoL>*;o3D|4x;RPmiTFDyapPR%0HgWps$XY6o zYXPy0vTCv&ov#~)X<11&ab?+Q>(7IA`Urb?J||HtE)|s32 ?~=lgd|1xe1ID;)?733hF?SbUQRW77g#G9P8_36u0|iO#L~?!mxXgYy zy7DrMOSigo3X;b$CG8aB*fS-P8UZ8Uf9H$ pMAnFJpA<*swtNc zp`{bQS`;2?%8-5lN~-+fX)%e9F$Zd>3S6_SY_J>&bjb;apecvRr Fm-~BIf;FbqU3i zBwmXX3~iv)w=v-CjW)ARf?gAH6RcziVNzg{G($B(Qu^>wNa^nqLG5NfAsZ6tm@;-= zV`Jg9rZCgK`&!BtQm2B0g#_C$MnGSINvq1A&J1jxKq71nE?=-K@A`T=qn}{_U4 7R@gSg6*#Y7AksMyGi>J7Y9Md- zw^eh!s(w&=iLXdFtGA0GRiRZC#L9T@V}aU==)CjS_N`##gdZE)jd^AWr0SfgW%As| zCvh#lG>Ni`rm)%+M14F#c1F=8r98;*<)JUOUeZm8Ou?;)yTR=FHV)qC5h%#<%2C21 z!1^(%h+mLk#!}OENv<&iz%bl#Z?XD@R?zzuUiP4#@~+BdA98N#Qnsrd<-+C94BzUI z4mr?|hV9>@&bhxuj$7rn7Ul~qbmYsn8oHzF5)spDJk{uZea&vP5$sYnWvxSxI~Z%HSp3Ixz@mX&jQatfbrO(-uj97z8< z4%Lh!O9&v`fs_|^8~X#&!B5v<3`M7~l^U7mae6)j=ayKv64l30Oq^#OZFCYaXY*}UE(b1Z_ATbSkyDouI>C6Y ve9It+}~9=SU?2r;^72G2G&2 zV^bfOp6qXp19G%B0`9%@`iiSS`qyyQCwf!K8Z(a6!lDMWlpos({;6l}kHx0f9W7qV z_{sYarK|8W$4`%*LiY$ynOEa{XNxmY5>&m5HKsN`W9VN-UVQdH03FE`a?b;V*< d&OD)m%yCMaFX%1qc`TMs$hu4-nx4byFoU_TsU$=8;9E;Mn_Xd; z>Lw|URaL=~Y`J^-{I7a?b !s9bz5qPq=QytbM*&RB6h}$-auJm5EWxI-ydMo8 zfP0ei>AT?ARVx}K`bfTpz6H*xQ2JQLdp>fE4ZcBx4}ce?M{Nl957K29&aiv71>?6R z>68zE!j1FbMEZHKrlnd%zM|W0p<>ATODx*FK-zYQUFkmMydaAxP=egKN1G?N#rdIv z#XK5WUX_~3#+E}oW6vI)Q(hG1bwpDp OcupQ`ks5x!nk4w?L>LbL*Woc zwQ=0N9{HZO_Pf4{YUbpqdpHXW5yjyMBkB{J&FfOY4$0TNo{pAzCgVuvZwG+*iV-3% zFE!!WEQU#0pCdNqaX%Z_tKd`ile 9HOpV-Dj{aee%P2FTp=DuAoQ?urp?_x=@Daw7!q&oZjH;==ZV7lN9bQMYE!uD7z z>wt747Up%U!=DfV+)7I#F(Cj-B-EKNwN`AQE45gjT0h;(@Og#AV?toWkht<#1hqQ4 z`K&9@3j_$SaKHq%8(y=imWEYYZ)^t!JBO$aqgxf;ByViE@As90SoTd08rg@;6M(nK zx_66LU$-^goNqGOSY#cVgt>y9gzpto$g@hiFxw7?n?Ji|3%Ayj8Vf!E=%Cls-bH^h z$-4{MeXVxp)!rma0weD#b7-Pjh|~8t)2pOr3D6Z%n=q7Psn!>n7#c`3lDRyLiCpC% z9^^~Zrxa))eVL%ovn5>CQ_%dxlK-;KlXUx>LV&GN8L8L04;#XEOpjGwuf< zeB}>-0Q#=~@SbZdf5zUOp8rVGPF3$lC|3M1Xo%p@?PAJ5vt3{u){PVFmhXz1Zj%y^ zm2+#KnOe$Yn`$pcfMyu*tu&qW%(6K~H6We|=vWFKMZguiH{*Ochm+m!m};~xJW16$ zYGzg2X5ua7I#7HvCT^RyE6UZPL86%>@K{!Boo#x)x<~4tEPvD7wm~~lT=7QjIcnS- zd})p77<>!T)+4E#Y^YypfptIEi487@IgzXMt@Y>~lEoypO$o7i-hPHN+RPAHaUm(M zbKRtx4}Ih6oV%O79s>%X;StnZ@+%EK@+4)L|K@5)t+*O_Bok;n{Hx(`RPB$N*f2QP zxikI;w<37#_KW0(F^_9`E1H#4@7HO1FViS#b6a40>^6+{1aP-8r;~7w1fG@7{+*lU zM@JEkGbCoLk~|IcMZDinTO~dGSOOF#-ELGZ2t(R7@hNF6)u|7_?Z{S}R%|B6iT 5CK zyCckfclZ1VA~DE^%mHH9D`B4X1JK{t433Vk-IH`CsqZs `aUZ2DE zOMKH)yX@sJq~E>fDLX&KzbTHGGK)s=pbrdh78tqR*g9!8>^5g(z<8^;w{hrYpc+VQ zY4W6|Hg=Uf@?5!P!QhI*ZH=@--rk&!&qa~onY{41v)L_xHqAk@N^z%RT@*L+AzjjQ zC`(~(*q4;BN8=(9hWu@pJs*IzQn+f9TAVJVJz~hkuyA6OO}!zuPCV+mpGTs4rSbmU zUinj7&5YL^59u>cIT9Y;F-v@K;Sjf7BhL)S_PsB*=WdS|nzaYZcuxuW@>&l;bEMJZ zx;}MWQRX&YOAdP^I8^!stQW%5s$F5B46}Qk0r8@JCEEi>Jb#gc0|W)E$Z{n1D_P^& z{*d0WWw^v)@73LCJ!&j)1}nDhmNpw>wXkbyM5C&aA_M%d=c(ZwpJzBrBV38l6rK*3 zsL-q)W5I`_!3>iDJH0T}p|cQAsX|7d&}@#Pa)hI9FXc}pN*0WTRr!s)5v>=Ry}ga2%H`9<)wamM z6uzBk&Vg6*VrZ1tVKkVMjbn*kf!(0yOEQfeZ$7fZzo=lxr({0i4}h{?aF1i8^#U8x zrZjWM8(;kg;DExU?gQZM{<%i1|5J&GM-4zmRt755sEdpsow6$hTOzXvd9-^PCO6!^ zwAmjMzl-+;&gI%M!nb%wl}c-Sq%vMQV5)k%Rep!xI33>@{WGLV@RA(5^BM`N+(xoC zUtr6YIy&Nn6X2W7>gW4jX!EdQGF#C7M|1EEWRvx>RY;+2xs>+?WaKxaw*Ma6buq)F zz&AF;Q=x0E2edYWJqr3w5udHJDYpTBS3=J%od@0@4%hC%p@;9Nhfbl)YP$x?ZCtFh zpYw0BSdGjzt@EKtfB6lqAj*{k;;Fx!^tC85N%G8n^Y48p`n|@OIq;&SxmqA(3r{~e zf_HDm-*XRr;~jZH)e`!9F;LZ*l-g*UMq%Z=mwlvnt|&{na&>JTp|%;Gv?=Z=8J~oe z`tI*H3PyR+i-kMR=`7{)O>15u2ZpQ-oU{r~JEK$7N%^xEHTsU2X&Wz8(-zFA#(rP% zi!P1@ek_Cd5l=EzS4QbuMqDOg!9Q{iAbCl%#^F>#fj*o^)Oh{TM{>;A3z47#77aoO z@Ew0M*0r^CDrjdp^i(*BAaR5OjxeJ4>_w57Y?zVL4?fsg{1Hcq2F^$@1oZrNmYIuh zpJYZjAIhzV^F*%j6kN3ZXYJ?9F|@^F{BWeww~>L-t|^>DQ?B9XA$70V(tiAcj@6Zo z>&3MXKryftg|9OggiBw#>BI-}k{+}L($5YbI&tZGLG?JkAV%t8rFa-}aoIGBMbJ1H z#9AH|^ZOUjZ^O5d@2f5u$8wQpbw*3HA8?T(o0t>4xPYw~zwbcuvg#4e8A={YDw!P_ z L(F|yDUVr@g)3`1JZyHOL$=^m;_yO3+ZPxX%EN74U{rJ)#NNsJ={u?7e zPVfW3w$zUj`IcF~udQtc+KHumpC4k+MX)B+X{<778PZ`UT!Ds)&S|gC?$3zUijA-* z$Nnp7Wr_^X#dG|}oh|l_YmUs1sb#?r%O3dGXPQcRykeyN1w)G@hI0=U2|M9rW;L^< z#o-JX1~|ZiMi~QUPnVx%tf%v(4D}~6`{&z4PUSYWhVtXw7|E7VmD?6NN xc%iiNw>-s}(Zzkf$N15JILZ_&c@}wXl84YO{IA7Ik|N!DAC<`AX7u5=LfZ zTtPTU*bPYjPh~+nN^IPT5@6s`Q!80h;4+MR2j>j-^rjMcxM=+H+9FL-Pa-8>Y2l_P z6uU(RK}|h@J9f`sQgoE03z|wF-itJK3aSug+!ncQFSHJ(=5T83FNs %Z4(a zER|ZurWz|UHQJXYa=G4^c;N-_NBFJE_B+AeI?H5{M+@2(e+J^kXh)qlH=q(h1_EI9 z)GXJsZT%s0YgerJ6u}&yQZgZdh}b90iQ-cq&8joY;&rPM&C>WHy=ZX<*iCu+vWg|m zA4pjZBQ+$jIlFW{U+QONYm)rYu3OV}u+mo^X$6hsWy9*qdLuS`E%SaGTAO8fVlbC% z)W7>`zq)OEHTtk#pH2ilY45u}74|MO7Qz~nhkh~f!+N~u?`E%B-B*3cwjG+L{Ao*i z3=|_f)|hfXTc%#-tY|Rxq&z9~)8YEQqp43UFaw?*{zs})+_A1BZ%r6#pv{|*2mmIv ze1|>wR5|e$Yi7Cq8=z9H2}Aoq9Ouch8;}5PWBn@D3-h%8;ko2ieuCq_k_fEGs_)vV z&zY$}?}Nu7q|ke`ZC*!HhU1~`E;%VACy_iryg1*rEbj1k9b5;nJUwPxOLcD)lN{w9 zuyrv~cbFbL*cYy*>LzxGtHTJ|1zfZ&B k{EC%ROMoXO@v%c&%5#vY^oe8IrHA7>xALtyb#t3) zu`kA8;$& cdxt<~UdJq-Z={?3ljEDl;;247 zeXgZP_%veuo1LY<^rX_=kzO&+k_5qyD-&3@@xUjzUJW;dDzKNWbtT{0u~d=>z7)mi z|8iRhj0cUg8{SVbFI6xCcHZ@4IGgV-(OYmPF)|KSpN)Q+6HboU5I$_rHR0es3AhgY zBicCg5vi$O%D1TZVPzki!1Z(0X=FclOsd108?uXTuQ&Obg|Q3ogB^LSu(ZUl&;W8v zkn)B(g3c|0(a5sjeW)GYFn^s6o14>RtDcc|ba*SHQqB(LYQepb)o%<*lxD@R{_P~1 z;3Il5@f64O4Q}}cZM;b>%o-~voAA3hg&LKd;z~U=iOpV%c`72MmyeX&s*;2I`PuIb z*@NYm=70I+V4mkh?V=JSKbxc{mA}N@>lbPd5rvlAl(hqv__oDAfd|E1v;n!wv85Yx zGbqSvS`vST(c>g;yIeHC6qcD92EmV#hr)O(I3VT=ssbSDAVVS_I`Yw&b#>%;)Kw^R zf>0n{@X?{>f))Xk-6?d|ns$^134 Lm zxNmH0(XO+Hvl`-J7s#0_J^-R1bLW#5!AJfPT yk9j z%OgoqNe|uCzi?`Fuw{kbNa?!v`sV>>BK)mH=Qg`>!{ksBm}up^(-VxG5nJqx3{fz@ z58X)EDY5Q){vCBuQ z>)gv25&VYA?CY9Ea#%V%chxhi}@F+ zjfPSKZ$cHNms_sql89husO@4iixTy>LbqnGWoKszcQ{h>ZkEYaaos59e!dWIZyrf@ zia&6Ze%)osq{6##)7c@=<*DMBF-Wh}R&N~rYA;)rJ{yVBcHvy&Cci=jXrnJBxgLg} zu$cDwdAe^P_qL@P=J#9Y3sVn6wwYM7VI@|KfyN8j4;A{MZD!M;T_j!MD+QM&d;Q5s z=0rs_F^ZYM=LB`?U@vmX^7RcVC?ULOiR~lep68mQBpig%Rx0yWE7QeYwHWNA0rADX zgSO;=HsL%^tHSHom`{ax$)C(0xL6-V%ZEH0L5w@4YbZEysxcoE_3J95mWjn|12vI& zCRnc?svI(DP&3rxoY0QFLN+1@>jL@ Sb}oRx>*tTh z3;O~0tCEqyhpK72vXo`IxVtdsLjhrJ5*@FLxJ%6DkwgV&=Y(9{vlBStYrpJaFBLDl zQoc#I32GjCrdZz&!UNZSxE4BSkNN=|ADSWe7n_@STe}Qa 39R%0f<^v@;k`)A^%#7Q~2%GOSeCZ|)oAxnY^ziER-ES)+ajl+yYyzXq=+p1q| zb%lCCKKn+ud-07nC1uT0vm3%mNT68o4dVJp)(hdPnX4ocK8v9I7pkWTDYF z3yCzv9{u@~8>Q+Y_5%{3&&accl7K2}(!)r?^`5uRwu*uto%Y(8FR#hBBRsA@R#A&O zTDp>uJkW5iuvOdWuJX|fR3_8} @0}jZom6e*j6_1~W z5u}%r+qdg~%kE3EG1s~rroRPJI>gYq$UC3Y$y$wB);Ng&dN`i=+jI!aflHd)|L&}L z72)z4#l{4bi7Yy-SLG*)NZ_0fTAF{d&smnwjBJLN?XsE?p)A@M9qk0<-BCjy`(LQJ zL!Wgp1{Z0KpaD~^z=s>ro@0{^Rj1t13BCGui-}LztUXH^OAnr1c|Xfgg_9Cmx0-Sh zR<#%tY%$TrMS;TvzswJT=67H>mzS8cpBnpOt-D*546piIy6mPo25z_z!XW_E%^&wv zr72FL4$i9T$n|v!WzI=S%GvDCh-jIxMT?#6hS|UfmmhThzQPxp{4&e9xWp7jWOSQa zJZU>JpzUBQkF^-;P%Py8( SZe1BzQznvoogo7b1|>C9 z#oLP^t{6#J{dzCu6~O RG7I_y-1W 8pI>y_|_+pTy=YqR-$>AXeZ`|YmIEy}F}e>205KN2HbwIKt2RnCh{(A69~sIu3- zRQW_JYTyHa%Ib$1$`c{1yUheb3f~vF(7*MUGR0vW87N8t`N8m$oX8LG;|W>2lB7D+ zNjp2h_oEJF1>ZrVHLLWHp2n3r%u0$HjoA^e$%zd{mPZpl_D`?#S>5inBkWd7#P*_) zzAY@-+^p?s+p=r>sUlDwTsm)OLTAXzOA^T>-raOO{rK1JlDHwy#6>#=PFk*J7Ae%# zadgWqX0@i6cEvP~Kh)s?!1MJ(D_sbqyW9H$oqU#x36eS~z9PI5fBJDutIJ`j;ha}O zo$c}|!~0nnxoP6~LN5WdU4Ew$$Gfp>{*>S IR;*F^A8Ml~ToR#lZ6;DyJ ziM~)O5mjp{0G4ski^&&Xo*;3)A&X0@5VGFbr-C}c*>U)l;a8H;o%a~kEi%j2FROZ* zzoYT6x?1QRie6BITcx uJvC~ zF&}_(tw#MpNtoS8?=)MOsy!F+pMS>(1601_ovdW3C11Z&*z(hjdUBcQsr7B-6u#z; z*Ua{a)}XTl?d}>3+?J!n+&PKj5)$tfyshuW1g>f$4w|*j%qC+@FenUm%C?qR>S*<7 z5F1z_1_|!eQ)OKQ2XN0d=@UlRRk<1oK9{c{Ejn;uLChby_VLW$Cy$?_icyHCnV1w# zs^P%~+!~m(E-Wh!kTClw;oT!d<}w7rEhURKALKvVsq=}sip?GEWRGGkxJ8^{0Ne_4 zz3x#L=$+k)8R3WTtnO;@;A@Qwpi$7Qgj@$YP{7keIu>g_jmNxKpQ`DqquduSIagX!ADV864^(e0yN+rT?nkG;dAQq&luZc zKfT>1k3THbfbc g&FU $FbBKrx75?D}OkOk6Fowtpg)#3s(7M!j-*GJF6P?cV} zQkFN~QZZF_4B_;n8)tz}&I`WWxi2xGH;hhg>&{Fru7qdQO@zJ=Kz)^$xpF}}zhXW` z4o#@BgQZ(KUdAELRsp(%rwASTC9P$jG{evcYs@5%iMr;sI;t+(GB#&4JGXkh_*kTG z4ywhoV>9J zsh75UbHQ{1cN=dCBsEm-_UGtyQ 98mHBNphdFKG79Ywnd2?ut Ft3 zHk4PunKJk*!O!Q6Pq(Kh&yVQ?@YPDUeDkl=Y<+)O^UOgf(*lN8VzaQRPclciXtv?o zfl>n%R=Wf=db~T{CFuRMs(oQyX1|-?J(4p&lC^a_ZV=Ybh|m3<_I)1(TJJf9{yU;) z3JaRt&3i2T1OH;K#}+{h_a4Ur>-{fQvHdAui6iJQPVf|`@Z{~vQ6oPDXqJOc>)Z7t z_LY-OPza1~YRg_+1U>*(qt|n{*!sYDN|d&%XjQyjv&z>GK-fhUv8wiH+_ecG)~or- z9UJc)V`)QY M*D`5hRLr_`d23r{|uApgVuLQ0KNF@RYL zaR$*oE4LVlel1l<$i1de#amqwhM4g?{FQiUNj3XDF8l!nMSgMlcZL8fRxuzQY+F1H z$DmV=B;2GI`d0Tb7?HrikyW`4jFe^+-nLyV8n8OKTCiba_d}0{JkXKp+j=!VofDk^ z`eC^KDZc}60j1ltLHW49B;8k!F!$qaUPT(SL^UJWSB$ylxR?zws$9k%69sG@b`{Py zu@sEfGmTEQj`p5K5$(T~L0pJgSbSc~986rAVLVE|)Hm9Eu ?Lczug|> z$eAnTdBr>`x9S*NX*0gShBGGwU}b#(%okD+NEd3hSn4ar$moJ@Ny#MdxeT07dzvk+ zH>BPD(`J(8t^OE>h@l`1Gq;?h*1II?Ge;jVu9kEzAK~Q*4wi#G8TYY@DR`2zzdx-r zk+UQ5n&(jQrTGW9@eeNQ?)jtXSa~50GPHjHh!)E)tAk9#NgR-jUsAWK)>W7{=%X@F zi>>C@H{x+7o6_yRcvTX9vRWXx&}8#?6dxe=W_b*Kc^}TK5X PnR8~qvewdfV!VFEgbX3LJz{$;M?I{UB)W(s?2({>Fss-v9jPhV8 zS=ftE2`BV$RAg{--cm83w2xg04^bUa!;#0R6VXU3qXHmwg;1FzPocEvvYAx1e<70` z5m`QrKMx>hRNhTAA_oLHcrElyT{l_nw(z0o>^VCgP##@KB7;H2dlLN0iYB>}0zdF| zp=Sffac!l=PEA56r@mVZ`*zG@SUC!Iwld#gPW#txB1ntARh~>_VE!0QU`muh6msSO zCt5LXfIRl$exd1WN&D8O*%rm;m?KIB_Pm#uRUgWfeE=*%+S?;FgAH_K$qp{Z+f?74 zg{l?{rP+EhVOsom+i#C#Y{uo_$(u+nP%5*z@*C (uT%ALHSBdhsA~IS_!vAHZ2{V1NXR| z?);4Y0Z^qvk*q~7z|Pn(^N>g(J0157cpC!lcQC;4OVN#VqiQsFmPjHT4pgAGt(Wm< z_bi<3n&mAMVn64Yvp0&7!^P*wumj%_R_;of2ojpD)0znk)V_up`56#E`)33_3Hg+} zm!H^ A|B9prjq}NK z7&7_JN}y#qowX%4jdF3PhD%?3Zot^+fmh8+e x&&5<#^Y< z8TxKXkrtHN(x3^4^VQyT?Hv)r^cfYG=W?6g#p%`!PN 4QSb6OgN)iTzWWY+$Ei79D~9qWiLHy-h~&ra@U*R^Xie1i z(2)JE5e3Lggd$$0d&ZAS?Jt#8*I#_{51KRvb$Zwx9BU8Ks>dT6rrH<;xAPq5S2WbL z@648#bC|i`C~LJZHfxU+X<}`u^_d>_O|9dr@6;suF(qE^Pe^;1e0Azfjt_8dczoSw z^(Q=KPvxfAB=9uN%$7TpHZgGn&h^3@u&hZ}j{kMi&fbSYK5hs+wT3S0Hpw3=L=E~9 zoRw}r!Hfg@-8nF)yK>8Bo;iDq(+8Ywq-sOFqcFFdT1e9EDrf8y7e5N};s8@+7<6!M zg| |2XXACxcqt}TMd8--yEb`wMbw4Vj%D2YE|m3k0FG_CXKvX8Fo zNP1+=h!P~F2w%$6wyM7AgHKwl3U9)s7V`HL=C;;X`WjlfwT7o|oX PD-i z@qcAM*o0;vl)D@HU{#-FgeDrzXZ-PG2);p^G(8f?-{u5B%+D3kp2w&@hPEVXO8dj7 zYpR*)nQVN`7LeILQ8skaY*?vmx|AbKKEwRHksDU&9cvgY1@(e&7HXIsLmzANSXnle zc8t<5jA|v8cUB1%p#97JvCKaW#XWnX$4X(`j<;Z^b=lT5ApUF;uG&>)R^PxlJH(ao z_f?rOZrekCR0!rz&Jis%c3BPM`o7ItL!DMGo=DLq-o1Q!Js|AQQq~a4Arj1XR#+L8 z3f-WxG1mP(+beX-6gBt^3~6MCN({`xr%V?E1XIJ4i8wE-lm|Y3W?#}HiBB(jpC_gf z Yh7QSDIJcxM^c!8#Tt!K%I z&MF+v9n|{H1l_k1@nYH8cH(CWG^(Vpbe5qA>WYdR_0Yj<5%do8wbi>lt= EHNhcaHjd7)2aIpgw(Vk2XfVw6jF#R*A+WjM!^`(aYz90;zEg^ za{)v3dg;j0kBE`MOjQFpI4LRKzMX>W6jW>qR?O(728pf*>qBFNQq5ls#nfbSaASUz z$My3A(T_U${Ukb7WR+g4w52+oBG9(BWjW<)7U00Z{Xw2gE4O1}BfNj5hNy>Utckdv zxlU3n%iAfTYhzb};u-&{6kf_XY>Ai2SD7M`F=rs-Bpp06GueJ6dqyROVlpI!5*eL# zr7I~Ntg94Z1o>2}8dpvEHTLKyC8pH5PJ^u;Z&MPumJ2MnQf)eXu+SU!L>JyH?b7k& zab8X2nc`h~h%B&HNblDMwUz1fqj}zr?SzfNjwV-Xp8SUdZ`TNLRZn7!hbY2M-!P}M z`?HE1`Qmc8kHJdMZ=yzO;^!KsTJG^)e|~Dw J}nk&c~3WbS%lZGrza}+ zB#ukHk#r8{1sb|=-m6Wnas{@v$}X`KCSCRxvpc2|kNMISahN`LI-5iN;i%ZxSkfG* zrN2D5NB{l-zyjykB$*rGI*O?%0(al#B#&ZATX|1c`yf@IR^|PgO&nafqstvVeIP07 z5qW#m+@{V!cFD>f{$*7-6ZF?pLs?(G{ WO3vvMLhaMP8{iwHnnrArK{Nd=6ETvC}H72>b!Sji`xzddt#Hd?GrV zl^M`)?xW%fK$G8Lzq7$Vj1D-Q^f)DwATK$*MXB~65-K!y+{!FiurblIxZE+kAT2cR zYIZ|xMgQszhbbZD!7^#3BWaEPxxJAIpgR;^|CGHRnQ5hc2g8jpd^!hher~ZA#*u&O zzlcI%Zv+3j^7&TuUFYaB$?V|tsKZ_ =g>9?qXreqiLfLL%e^uRq)MWv~~MN_NZ0cJeh+h2RL6KTGw*#vPDlDz?RO? zo&m_*9{_e=F~>$LbDCxxP{I5k#{v9b$*Wpcf32~7IQWx|;-&DFY0Q)ea}@X-*XO&H zUrORCw|Hb7&5l4s;wvwg&RVsKvo?oLA%M|S7}5J~{Ivy0KL_wQdp}mJoV$HeG5mgY zxnkCQNU=quKqkh)!MBUY0+ZdJ!qOW$zQ5yc)Gvl3>@>=4d-rAVYr?F9So~=b_4B6C zbuc&7*q5li&kE0p9A6-ms*whfz~pGOTq$C@Nr+{jWhnnzE!0j! bxl#1i_Got+#f7T7%R6mNS|e|gVNR$gzW1R{;g`{5eWF~j zsQ)bK?Jyd BaHxA9Yl=pyUG+=^K34w+w9?Cn literal 0 HcmV?d00001 diff --git a/2013/daemon/public/static/badziew5.jpg b/2013/daemon/public/static/badziew5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..613cc31fed29b9df5190e920a6a9b19f11ab2b9a GIT binary patch literal 19881 zcmb??V|XS(v*;V!wl;P)wr$(CwXuzjZQHhOn;Rz^J2(5CbI qWZ|7*>Y-0Rf_P=HP zs|F`*=S(POU|~zh#6bFw@c%RY>&O4%_rLw~zqJ3AWaQ%L`d?4~=|n^TA%FqE50D2K z0DJ+tfFZyI5C)JikPA>L&^9nSup)3A@GJ-lhz3X=$OR||Xc*`^7#&zJ*fux^cry4S zgc?K#Br#+J j5kzyiW@$~w!|#$Lz~$LYgm z!>!Asz$?PX#s5=)MvzR1SeQ_RK$K97M4ViLR+3qYTUty;NmgIZPTp4`Ua?52U3pIB zR1H`iUxQOqUCUiNO{Y_LS0CJf)=<^R&$!ZL%?#3<-NM2$%WBRB!j{j@-M-%8%8A<9 z(xu$>!kym3*|XgX$Xn7U$#>76F2E~rDi}Y+A#^AlC&DpuBAPVDKXxmg`*%jdN0MIh zKq^IAZ2D8Ce%3?|TW(Q4YC&+}W3ffacA0YdM5S<5cMV5vb3JoIV-s_8ODktvPlrh7 zRJUr+Zl87k%V6j*#z^TH*ZB0L(bVhA?>XxEp+$qG&y~zI?)9xr-))ke(LLJ(^uwNG zixbq-zH_@vf~(n^fLoTkvxnR#wHL(K@%P|Qfv>Nx4S)~;3 -ppP%t16NZ_w^0OUU{003&^2LR_cfBFFcxK@vZ z(7p!d-&FQ0@(ciEXeJLM8AuF20Naj1`e-A{# tSlkoPxLo2VyuKlrH@G8>T)t3|j@Pr =YRRAb-i 8G~lb; E$6a)8ScovuV4|9mQ`Yczusnu*0c4_^BmN$LHdqlxuC=MZ ztZgMsW#JhM+%UZ|68(CXWuS>%IA=cF#}PVpmvMbg!A+;^ntAqKN@iBo43y5-!p`nK zgOTmcxJ8UOu6HoM*#G5$vEpm{sW!=}B-hM=d-}Ig!AAaA?<6WScg3Tft7~>U4To;Z zw8MsV!r|^=1wDH3VO~0u0-Zn?*rlhx( pK!UXh619o`jop1I9h*>1T6s+WR8~#*hNDDu2_Vu)mqM;c7YB(E(i0hL zZ_-?)nK!fb11^|7 mpB>${sUxi82l~_gRx+O;&TF>Z zfl&d{L61Yi3q?OtVR)`-z&ZnfTy&b*`MD$VA!yuz&*}5Y*}!0x*%>WEw9?RB#3c{# zTAC~0!I}H{X{hr%MwVo@Gn=8jiny=7 $_WD|LG8VL;w&d zFaQknI|u&H!T=Nq7z7Lej)aU#$N+(e!jFzdL_+$5k(i8`Rp7tX3J@6ZYs+Dk>h6+= z-MThtasol*Vi=myoQ-EZ@75mj+$KPOICgJi= #U45maqK1aH zdmI(R8Ep)IL-sJmp~BY3n#{+Jm+nfI>`D!(#nc!_h7rr0Pttu*nz`v$VwH<`lgHGB zc0o>6#_3gJmbDjmX(xJxNj(S^Ys8%c8XEiYqMevnuJ8F5K*=%JS?+G$8hp7*rq$KK zGn5?qS}x_4pyWqXbaRG`epMqG@x %}raClO2mJyOFhs z=QrI>rul<+U|4(>1uZ81N4aT{6)-YTV8_Lo80BKN1peq+olU00l^Rzw#pb&+bR4O1 zZ0cSiVh>;O!WV!%=J8S$i|X{PRPwEMQ>`2uS6DB^v58AE G$qXqfA-Sd%x z+8oD&y_~0foJYQgjEzAdzi;!&hW)9&^t=(TNSt?rDa_h>orKFi*RgvjM1VqCvz)Rz zWu?RvL*2_jwV=gw@`wfVAZ_&*Z7g!wVNw#q=P}$pRMuQNVqf(E?znG=<7&KA`DosX z@9c)tMT hy!e_2%895qxT1qvoHG%FqgptlE$ZQGwEK7=AXrpfKZ4(`AB_yQU@l z0#HhO{Z)mW70_PU`vSDYYfo^r#G9(~@P$u7mhS(iEI=2vrU~Fb(;Pw6GDxrK(Kf^8 zC=-YFv|_go893Kx5i2YYL?aI182fRA-D=rt>^Vl>CCwv}^y&a0+E#G1dX%Mo?doSt z%-}x 5vX_e~xBImC-DYx=*yKPFf}`fpr>?0e z!?2ic4nr6;B!?>D$I57ePSeGi*L$R9cAX4nS2~4x(OAGK`j*B}WP9Joee{v9Z8)sy zkQ(=cV~OiLmvo?ooiIUn2lP+P4U)YYt1@n(P;b-FPc3e#mrBeGM>?^F-=eT7WQK-E z_KJP(;Taf;lq6R*(zeGWb*!(QI{gjgn-DLE8w-kqA^}E5zl<)IY hs^jc6~0S~DxE258(`{$*6sVPX>zU}GrgXZD%6 zrg50SeLdoC70pJ@YtPt~n=?9&XcadQ-`kahTGG62_z_(}S^JLiW@!!EPs2R6mdJ|E zi~=ctG_=vSeorK)y(d%}ezztLYWWf7jQL@4%7XR|LrYlK%$OfMRrj}xA^x|C%Av1n z_|FItm8> xO-q%VYi16IWO2HYh z@4&n(T;`55u7f$|t54EU)Go=L@ zoYSmRn7V49!rra0n(nEiV-#H1K+)s Sug>!W>I%I=(VXXW{gi9RV(Pv{9?I+~ 8HetRzVsedbGM5& znTunxFxmlMy2$;OZw(zrn`D_^$zZIg`*eJhstj(gVoN1~$wY}<&6u+$v8I#KAF`{7NGW@UK_&Gt*ghP&C&~Sf+u$-RTAhWMNI1DhZYr3aH-1( zs(Soj8jj)MsO*PbOHIv(ssmUiUjT*|E9Kwgby41#*RcV@*eyn0mTpbY(7YvueK!z@ zC;jgS*YUDgqJ`mxb!iuAd_y>|lQS$FUJ%ZQP7<7{fC$6h;w9V1DB6297b@V$$cv zF@1FnmCjMMb#Vqi9J{3}Vz6k~!d{xr{(8E4w32xS*$slHNZfS}Stdu6wr8Ew=*wAA zou=}$3CXZoJl6HT=>Mp6P%38Cz#z?5ZC?D7Lf)LC%2N!UFb(W6nodFNVsvdYZrDbX z9#uE`<2Q$kcW}tag}GoFwn`jV4$nQ*Yf&tIhkgP+ZROt~Jo}p;2lgJ? GD^)|6asqc%y0qeg}BFcYh$JiOa`(SyBaV12IO&%jAz`=kr zv|PluV_#cpKBXC`j}Uuv^?A5%-S!kW&B$bZ3RhakcM4e)R@Gwn)BnPH!DB>rY#lc7 zH>!&ZTKi$mNPLMK4(=Q=E~D8Mhx9-bs!_C7F+X0a@Pn9MPW!O)v9>J6BB&ug;Y}ln zOW|nMFx#t;ffbCN!%N{0osBOst+`^F{>^MC3vZi&Pg0}m3Tubk6N5Rd|1E*&9!)Q8 z%fi5-^U-=c&?{r+WB&WWjdOlEG82JGRWNqS!f?aKFMfksbiRb!tA&U3`kd!fSfP!H zcsR`!FNPa0i|Hs+WNH&ubSC!C{&Hev39OZ@;|y4d!RF!ZymMl{SYng@(Y}07UcS0U zT;a#va7ZCH#3N(oWGFz;DwCJs-gGf+)M#pb`tOla+$YJ%H}|i9Yd+cUH6Ji25I8hA z1UM)N)ITfnd+7&?giHj6Ld+ z#qGZ;+cdD*Y}U1&UAF+DCvEe;)YmsV(h=COC1%tr{VR-veES7}^$!06xX0GEe2DV) z&pcobOXQp}-iMPSw3b3DmD^srl|Z`0M+i2;-(UEh!&5dz=Oahu&T6{0MmuPDB%+*U zB2d}7Q pjv2K z{EDyG0Xr^Ipg}Oe?gFA?txt*;whnjAk>j0@o> ;F& zhrf}U+HUdC)`}-IM@16J+f&5MS7~=LgDq;q($^Q^+vsH1*aIda?X+M+{#TVY!9XtS zYz}E!x{UHc!S6munyErtpp;>AoS_<8_FxXR6d1k^>^-r5z3I3jG|{xUb->IqJj$D% z>8v~E5n_$&Rd8`Y!U4iCNQdk!mA7nAWU2@SWR-oWX1qOcJ4e#Qip2pN`u0?EQ(jWE zUK-0ft)lTSK_VJ8vWH}&@(<- =hGDUC|}-C&(okl9|3TYH&jOp+!#O7$vZ**P*(a4Y52@;cEhZh|3E6D^qQ^ z_b@qUoEdZv`iPbY0*V%(bQaX|7X>I_ZF~hRoi0&m6u%29v;B)vp{y-9b~#XJ303s9 zbLgZJF$>iS_pP`udWN{mrQ7A@>ZMVQkC%L_cV7e!P-GE*u?!Sw?}sG(5xa_!9_+c< zBng;{kQ59}-PKganU|qzzACg{u`RbeS<~Qzce1=YGK}*wx^OIRzmf(cy69a;rpBD? z!CEQKQH)iolPrhJ4|JoY%~g?P2F6MOz^((^AGSA??V__YIges*(3XBI^d6OYh|X-q z@v*B<^(fNIoXghL?9Bz+vV>&gHG(;V{fXfTvQY?@mZmG`gAY*YuP##xa`*L7E}Xtm z=qo>xlFsyc#%q&~M%a!Bvl$n!%u^4FHbnO;EL8)}(!tHErj+Tv(kv!J<7YuozY3P? zk?=4mlUhuC5)dw5+(!xi(=-8vJ(e4q^85J0Q8&5*Tf1FF9&@7J3c{!qd;di(;3>Kw zu7tI5U*-0tU!ilJsWY9SA_D3&=tdeQK*A0>-6j~q6mw9~e_r}W`XV&4CtFc-bNdUv zic#zl^QNy8oo7|d=kq>t 0t9;v=mF4OwSlGkCka-|x=`3=9 -C1^fd BHC+l9ogtx_ZLX}}+pOZ^RAfu= z2;xp0gB}>|O>yqNRfI**nGFz0u((Rw;x4XiV=6M1YDE%{7|TLG@_{9^V6Xt