Krzysztof Sikorski commited on 2010-11-18 23:01:29
Showing 269 changed files, with 16178 additions and 0 deletions.
... | ... |
@@ -0,0 +1,182 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+chdir(dirname(__FILE__)); |
|
4 |
+$cfg = require_once './public/_init.php'; |
|
5 |
+ini_set('expose_php', '0'); |
|
6 |
+ini_set('default_mimetype', ''); |
|
7 |
+ini_set('default_charset', ''); |
|
8 |
+$dbClient = Daemon::createDbClient($cfg); |
|
9 |
+$dbCfg = new Daemon_DbConfig($dbClient); |
|
10 |
+$forum = new Daemon_Forum($dbClient); |
|
11 |
+ |
|
12 |
+//cleanup |
|
13 |
+$queries = array( |
|
14 |
+ "CREATE TEMPORARY TABLE junk(id INT NOT NULL PRIMARY KEY)", |
|
15 |
+ //delete inactive players |
|
16 |
+ "INSERT INTO junk(id) SELECT player_id FROM players WHERE last_login < (NOW() - INTERVAL 1 MONTH)", |
|
17 |
+ "DELETE FROM players WHERE player_id IN (SELECT id FROM junk)", |
|
18 |
+ "DELETE FROM user_agents WHERE player_id IN (SELECT id FROM junk)", |
|
19 |
+ "UPDATE characters SET player_id = NULL WHERE player_id IN (SELECT id FROM junk)", |
|
20 |
+ "TRUNCATE TABLE junk", |
|
21 |
+ //delete inactive characters |
|
22 |
+ "INSERT INTO junk(id) SELECT character_id FROM characters WHERE player_id IS NULL OR last_action < (NOW() - INTERVAL 1 MONTH)", |
|
23 |
+ "DELETE FROM characters WHERE character_id IN (SELECT id FROM junk)", |
|
24 |
+ "DELETE FROM character_data WHERE character_id IN (SELECT id FROM junk)", |
|
25 |
+ "DELETE FROM character_missions WHERE character_id IN (SELECT id FROM junk)", |
|
26 |
+ "DELETE FROM character_regions WHERE character_id IN (SELECT id FROM junk)", |
|
27 |
+ "DELETE FROM character_statistics WHERE character_id IN (SELECT id FROM junk)", |
|
28 |
+ "DELETE FROM character_titles WHERE character_id IN (SELECT id FROM junk)", |
|
29 |
+ "DELETE FROM combat_units WHERE combat_unit_id IN (SELECT CONCAT('character-', id) FROM junk)", |
|
30 |
+ "DELETE FROM inventory WHERE character_id IN (SELECT id FROM junk)", |
|
31 |
+ "UPDATE clans SET leader_id = NULL WHERE leader_id IN (SELECT id FROM junk)", |
|
32 |
+ "DELETE FROM clan_invitations WHERE character_id IN (SELECT id FROM junk)", |
|
33 |
+ "TRUNCATE TABLE junk", |
|
34 |
+ //dump abandoned clans & old invitations |
|
35 |
+ "DELETE FROM clans WHERE leader_id IS NULL", |
|
36 |
+ "DELETE FROM clan_invitations WHERE date_added < (NOW() - INTERVAL 1 WEEK)", |
|
37 |
+); |
|
38 |
+foreach($queries as $sql) |
|
39 |
+ $dbClient->query($sql, array()); |
|
40 |
+unset($queries); |
|
41 |
+ |
|
42 |
+//check for endgame |
|
43 |
+if(!$dbCfg->rolloversEnabled) |
|
44 |
+ return; |
|
45 |
+ |
|
46 |
+//create rollover entry |
|
47 |
+$sql = "INSERT INTO rollovers(date_added) VALUES (now())"; |
|
48 |
+$dbClient->query($sql); |
|
49 |
+$rolloverId = $dbClient->lastInsertId(); |
|
50 |
+ |
|
51 |
+//give turns |
|
52 |
+$sql = "UPDATE character_data SET turns = LEAST(:limit, turns + :delta)"; |
|
53 |
+$params = array('delta' => (int) $dbCfg->turnDelta, 'limit' => (int) $dbCfg->turnLimit); |
|
54 |
+$dbClient->query($sql, $params); |
|
55 |
+ |
|
56 |
+//run caern sieges |
|
57 |
+$sql = "SELECT l.location_id FROM locations l JOIN character_data cd USING(location_id) |
|
58 |
+ WHERE l.type='caern' AND cd.faction_id IS NOT NULL AND cd.faction_id != l.faction_id |
|
59 |
+ GROUP BY l.location_id"; |
|
60 |
+$locations = $dbClient->selectColumn($sql); |
|
61 |
+foreach ((array) $locations as $siegeLocationId) |
|
62 |
+{ |
|
63 |
+ $combat = new Daemon_CaernSiege(); |
|
64 |
+ $combat->attachDbClient($dbClient); |
|
65 |
+ $combat->execute($siegeLocationId); |
|
66 |
+ $sql = "INSERT INTO battles(rollover_id, location_id, type, combat_log) |
|
67 |
+ VALUES (:rolloverId, :locationId, 'caern', :combatLog)"; |
|
68 |
+ $params = array('rolloverId' => $rolloverId, 'locationId' => $siegeLocationId, |
|
69 |
+ 'combatLog' => $combat->getCombatLog()); |
|
70 |
+ $dbClient->query($sql, $params); |
|
71 |
+ $dbCfg->siegeLocationId = null; |
|
72 |
+ $siegeLocationId = null; |
|
73 |
+ unset($combat); |
|
74 |
+} |
|
75 |
+ |
|
76 |
+//update faction power |
|
77 |
+$decay = (float) $dbCfg->factionDecay; |
|
78 |
+$sql = "UPDATE factions f SET f.power = FLOOR(:decay * f.power) + COALESCE(( |
|
79 |
+ SELECT SUM(l.faction_value) FROM locations l WHERE l.type='caern' AND l.faction_id=f.faction_id |
|
80 |
+), 0)"; |
|
81 |
+$dbClient->query($sql, array('decay' => $decay)); |
|
82 |
+ |
|
83 |
+//activate bosses |
|
84 |
+$sql = "SELECT MAX(level) As max_level, MAX(rank_id) AS max_rank FROM character_data"; |
|
85 |
+$row = $dbClient->selectRow($sql, array()); |
|
86 |
+$unlockBosses = ($row['max_level'] >= $dbCfg->bossUnlockLevel) && ($row['max_rank'] >= $dbCfg->bossUnlockRank); |
|
87 |
+if($unlockBosses) |
|
88 |
+{ |
|
89 |
+ $dbClient->query("UPDATE locations SET boss_status='active' WHERE type='boss' AND boss_status != 'defeated'"); |
|
90 |
+ if(!$dbCfg->endgame) |
|
91 |
+ $dbCfg->endgame = 1; |
|
92 |
+} |
|
93 |
+ |
|
94 |
+//run boss sieges |
|
95 |
+$sql = "SELECT location_id, name FROM locations WHERE type='boss' AND boss_status = 'active'"; |
|
96 |
+$locations = $dbClient->selectAll($sql); |
|
97 |
+if($locations) |
|
98 |
+{ |
|
99 |
+ $factionPowers = array(); |
|
100 |
+ $sql = "SELECT faction_id, power FROM factions"; |
|
101 |
+ foreach($dbClient->selectAll($sql) as $row) |
|
102 |
+ $factionPowers[$row['faction_id']] = $row['power']; |
|
103 |
+ foreach($locations as $row) |
|
104 |
+ { |
|
105 |
+ $combat = new Daemon_BossCombat(); |
|
106 |
+ $combat->attachDbClient($dbClient); |
|
107 |
+ $combat->execute($row['location_id'], $factionPowers); |
|
108 |
+ $combatLog = $combat->getCombatLog(); |
|
109 |
+ if($combatLog) |
|
110 |
+ { |
|
111 |
+ $sql = "INSERT INTO battles(rollover_id, location_id, type, combat_log) |
|
112 |
+ VALUES (:rolloverId, :locationId, 'boss', :combatLog)"; |
|
113 |
+ $params = array('rolloverId' => $rolloverId, 'locationId' => $row['location_id'], 'combatLog' => $combatLog); |
|
114 |
+ $dbClient->query($sql, $params); |
|
115 |
+ $forum->addChat(null, 'public', "Siedziba bossa \"$row[name]\" zaatakowana!"); |
|
116 |
+ } |
|
117 |
+ } |
|
118 |
+} |
|
119 |
+ |
|
120 |
+ |
|
121 |
+//check for ending |
|
122 |
+$factions = array(); |
|
123 |
+$sql = "SELECT faction_id, name FROM factions"; |
|
124 |
+foreach($dbClient->selectAll($sql) as $row) |
|
125 |
+ $factions[$row['faction_id']] = array('name' => $row['name'], 'active' => 0, 'defeated' => 0); |
|
126 |
+$sql = "SELECT faction_id, (boss_status!='defeated') AS active |
|
127 |
+ FROM locations WHERE type = 'boss' GROUP BY faction_id, active"; |
|
128 |
+foreach($dbClient->selectAll($sql) as $row) |
|
129 |
+{ |
|
130 |
+ if($row['active']) |
|
131 |
+ $factions[$row['faction_id']]['active']++; |
|
132 |
+ else |
|
133 |
+ $factions[$row['faction_id']]['defeated']++; |
|
134 |
+} |
|
135 |
+$active = array(); |
|
136 |
+$defeated = array(); |
|
137 |
+foreach($factions as $factionId => $row) |
|
138 |
+{ |
|
139 |
+ if($row['active'] || !$row['defeated']) |
|
140 |
+ $active[$factionId] = $row['name']; |
|
141 |
+ else |
|
142 |
+ $defeated[$factionId] = $row['name']; |
|
143 |
+} |
|
144 |
+$endgame = (count($active) < 2); |
|
145 |
+if($endgame) |
|
146 |
+{ |
|
147 |
+ //final messages |
|
148 |
+ $active = implode(', ', $active); |
|
149 |
+ switch($active) |
|
150 |
+ { |
|
151 |
+ case 'blue': |
|
152 |
+ $msg = "Rewolucja została stłumiona. Niech żyje Porządek!"; |
|
153 |
+ break; |
|
154 |
+ case 'red': |
|
155 |
+ $msg = "Cesarz został obalony. Niech żyje Rewolucja!"; |
|
156 |
+ break; |
|
157 |
+ default: |
|
158 |
+ $msg = "Wojna dobiegła końca, lecz brak w niej zwycięzców. Czas pokaże, kto zajmie miejsce dawnych potęg..."; |
|
159 |
+ } |
|
160 |
+ $forum->addChat(null, 'public', $msg); |
|
161 |
+ //cleanup |
|
162 |
+ $dbCfg->globalMessage = $msg; |
|
163 |
+ $dbCfg->rolloversEnabled = 0; |
|
164 |
+ $dbCfg->turnDelta = 0; |
|
165 |
+ $dbCfg->defaultRespawn = ''; |
|
166 |
+ $sql = "UPDATE character_data SET turns = 0, location_id = NULL"; |
|
167 |
+ $dbClient->query($sql); |
|
168 |
+ $sql = "TRUNCATE TABLE character_regions"; |
|
169 |
+ $dbClient->query($sql); |
|
170 |
+} |
|
171 |
+ |
|
172 |
+//update rollover data |
|
173 |
+$sql = "SELECT COUNT(1) FROM players"; |
|
174 |
+$nPlayers = $dbClient->selectValue($sql); |
|
175 |
+$sql = "SELECT COUNT(1) FROM characters"; |
|
176 |
+$nChars = $dbClient->selectValue($sql); |
|
177 |
+$sql = "SELECT COUNT(1) FROM clans"; |
|
178 |
+$nClans = $dbClient->selectValue($sql); |
|
179 |
+$sql = "UPDATE rollovers SET players_total = :players, characters_total = :chars, |
|
180 |
+ clans_total = :clans WHERE rollover_id = :id"; |
|
181 |
+$params = array('id' => $rolloverId, 'players' => $nPlayers, 'chars' => $nChars, 'clans' => $nClans); |
|
182 |
+$dbClient->query($sql, $params); |
... | ... |
@@ -0,0 +1,102 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+//container for miscelaneous functions |
|
4 |
+class Daemon |
|
5 |
+{ |
|
6 |
+ |
|
7 |
+ |
|
8 |
+ //class autoloader |
|
9 |
+ public static function autoload($className) |
|
10 |
+ { |
|
11 |
+ $className = mb_strtolower(str_replace('_', DIRECTORY_SEPARATOR, $className)); |
|
12 |
+ spl_autoload($className, '.php'); |
|
13 |
+ } |
|
14 |
+ |
|
15 |
+ |
|
16 |
+ //creates Daemon_DbClient object using specified config |
|
17 |
+ public static function createDbClient(Daemon_Config $cfg) |
|
18 |
+ { |
|
19 |
+ $dsn = sprintf('mysql:host=%s;dbname=%s', $cfg->dbHost, $cfg->dbSchema); |
|
20 |
+ $params = array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8', time_zone = '+1:00'"); |
|
21 |
+ $dbh = new PDO($dsn, $cfg->dbUser, $cfg->dbPassword, $params); |
|
22 |
+ $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
|
23 |
+ return new Daemon_DbClient($dbh); |
|
24 |
+ } |
|
25 |
+ |
|
26 |
+ |
|
27 |
+ //prepares multiline text for displaying, also inserts some basic tags |
|
28 |
+ public static function formatMessage($txt, $markup = false) |
|
29 |
+ { |
|
30 |
+ $txt = nl2br(htmlspecialchars($txt, ENT_QUOTES)); |
|
31 |
+ if($markup) |
|
32 |
+ { |
|
33 |
+ $txt = preg_replace('@\[img\](.+)\[/img\]@uU', '<img src="$1" alt="$1" class="bbcode"/>', $txt); |
|
34 |
+ $txt = preg_replace('@\[url=(.+)\](.+)\[/url\]@uU', '<a href="$1" rel="nofollow">$2</a>', $txt); |
|
35 |
+ $txt = preg_replace('@\[url\]([img]){0}(.+)\[/url\]@uU', '<a href="$1" rel="nofollow">$1</a>', $txt); |
|
36 |
+ $txt = preg_replace('@(^|>|\s)(https://\S+)($|<|\s)@muU', '$1<a href="$2" rel="nofollow">$2</a>$3', $txt); |
|
37 |
+ $txt = preg_replace('@(^|>|\s)(http://\S+)($|<|\s)@muU', '$1<a href="$2" rel="nofollow">$2</a>$3', $txt); |
|
38 |
+ $txt = preg_replace('@\[b\](.+)\[/b\]@uU', '<b>$1</b>', $txt); |
|
39 |
+ $txt = preg_replace('@\[i\](.+)\[/i\]@uU', '<i>$1</i>', $txt); |
|
40 |
+ $txt = preg_replace('@\[u\](.+)\[/u\]@uU', '<u>$1</u>', $txt); |
|
41 |
+ $txt = preg_replace('@\[s\](.+)\[/s\]@uU', '<s>$1</s>', $txt); |
|
42 |
+ $txt = preg_replace('@\[sub\](.+)\[/sub\]@uU', '<sub>$1</sub>', $txt); |
|
43 |
+ $txt = preg_replace('@\[sup\](.+)\[/sup\]@uU', '<sup>$1</sup>', $txt); |
|
44 |
+ } |
|
45 |
+ return $txt; |
|
46 |
+ } |
|
47 |
+ |
|
48 |
+ |
|
49 |
+ //returns value from array, or defaults if it doesn't exist |
|
50 |
+ public static function getArrayValue(array $a, $name, $default = null) |
|
51 |
+ { |
|
52 |
+ return isset($a[$name]) ? $a[$name] : $default; |
|
53 |
+ } |
|
54 |
+ |
|
55 |
+ |
|
56 |
+ //implodes whitespace, optionally preserving newlines |
|
57 |
+ public static function normalizeString($string, $preserveNewlines = false) |
|
58 |
+ { |
|
59 |
+ $string = str_replace(array("\r\n","\r"), "\n", $string); //unix newlines |
|
60 |
+ if($preserveNewlines) |
|
61 |
+ $string = preg_replace('/[^\S\n]+/', ' ', $string); |
|
62 |
+ else $string = preg_replace('/\s+/', ' ', $string); |
|
63 |
+ $string = trim($string); |
|
64 |
+ return $string; |
|
65 |
+ } |
|
66 |
+ |
|
67 |
+ |
|
68 |
+ //generates a password hash |
|
69 |
+ public static function passwordHash($salt, $text) |
|
70 |
+ { |
|
71 |
+ return sha1($salt . $text); |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ |
|
75 |
+ //returns random salt for password hashing |
|
76 |
+ public static function passwordSalt() |
|
77 |
+ { |
|
78 |
+ $c0 = ord('0'); |
|
79 |
+ $cA = ord('a'); |
|
80 |
+ $cZ = ord('z'); |
|
81 |
+ $max = 10+$cZ-$cA; |
|
82 |
+ $salt = ''; |
|
83 |
+ for($i = 0; $i < 8; ++$i) |
|
84 |
+ { |
|
85 |
+ $x = mt_rand(0, $max); |
|
86 |
+ if($x < 10) |
|
87 |
+ $salt .= chr($c0 + $x); |
|
88 |
+ else $salt .= chr($cA + $x - 10); |
|
89 |
+ } |
|
90 |
+ return $salt; |
|
91 |
+ } |
|
92 |
+ |
|
93 |
+ |
|
94 |
+ //redirects to selected url |
|
95 |
+ public static function redirect($url) |
|
96 |
+ { |
|
97 |
+ session_write_close(); //just in case |
|
98 |
+ header('Content-Type: text/html; charset=UTF-8'); |
|
99 |
+ header(sprintf('Location: %s', $url), true, 303); //"See Other" status |
|
100 |
+ printf('<!DOCTYPE html><html lang="en"><title>Redirect</title><p><a href="%s">continue</a></p>', $url); |
|
101 |
+ } |
|
102 |
+} |
... | ... |
@@ -0,0 +1,100 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+class Daemon_BossCombat extends Daemon_CaernSiege |
|
4 |
+{ |
|
5 |
+ public function execute($locationId, array $factionPowers = array()) |
|
6 |
+ { |
|
7 |
+ //read location |
|
8 |
+ $location = $this->getLocation($locationId); |
|
9 |
+ $bossFactionId = $location['faction_id']; |
|
10 |
+ $powerMod = Daemon_Math::factionPowerMult($bossFactionId, $factionPowers); |
|
11 |
+ //prepare units |
|
12 |
+ $this->kickNeutrals($locationId); |
|
13 |
+ $units = $this->getCharacterUnits($locationId); |
|
14 |
+ //check for empty caern |
|
15 |
+ if(empty($units)) |
|
16 |
+ return null; |
|
17 |
+ $attackers = false; |
|
18 |
+ foreach($units as $factionId => $faction) |
|
19 |
+ { |
|
20 |
+ if($factionId != $bossFactionId) |
|
21 |
+ $attackers = true; |
|
22 |
+ } |
|
23 |
+ if(!$attackers) |
|
24 |
+ return null; |
|
25 |
+ unset($attackers, $factionId, $faction); |
|
26 |
+ //prepare boss |
|
27 |
+ $boss = $this->getBossUnit($location['boss_id'], $bossFactionId, $powerMod); |
|
28 |
+ if(empty($boss)) |
|
29 |
+ return null; |
|
30 |
+ $units[$bossFactionId][$boss->_id] = $boss; |
|
31 |
+ //add monster support |
|
32 |
+ $supportCount = 0; |
|
33 |
+ foreach($units as $factionId => $faction) |
|
34 |
+ { |
|
35 |
+ if($factionId != $bossFactionId) |
|
36 |
+ $supportCount += count($faction); |
|
37 |
+ else |
|
38 |
+ $supportCount -= count($faction); |
|
39 |
+ } |
|
40 |
+ if($supportCount > 0) |
|
41 |
+ { |
|
42 |
+ $support = $this->getLocationMonsters($locationId, $bossFactionId, $supportCount); |
|
43 |
+ foreach($support as $unit) |
|
44 |
+ $units[$bossFactionId][$unit->_id] = $unit; |
|
45 |
+ } |
|
46 |
+ unset($support, $supportCount); |
|
47 |
+ //execute combat |
|
48 |
+ $this->combatLog = $this->runCombat($units, $bossFactionId); |
|
49 |
+ //save characters |
|
50 |
+ $this->putCharacterUnits($units); |
|
51 |
+ //find winner |
|
52 |
+ $winnerId = $this->getWinnerFaction($units); |
|
53 |
+ //kick losers |
|
54 |
+ $this->kickEnemies($locationId, $winnerId); |
|
55 |
+ //update location, send message |
|
56 |
+ if($winnerId != $bossFactionId) |
|
57 |
+ { |
|
58 |
+ $sql = "UPDATE locations SET boss_status='defeated' WHERE location_id=:id"; |
|
59 |
+ $params = array('id' => $locationId); |
|
60 |
+ $this->dbClient->query($sql, $params); |
|
61 |
+ $msg = "Boss $boss->name z lokacji $location[name] został pokonany!"; |
|
62 |
+ $forum = new Daemon_Forum($this->dbClient); |
|
63 |
+ $forum->addChat(null, 'public', $msg); |
|
64 |
+ } |
|
65 |
+ } |
|
66 |
+ |
|
67 |
+ |
|
68 |
+ private function getBossUnit($monsterId, $factionId, $powerMod) |
|
69 |
+ { |
|
70 |
+ //read base stats |
|
71 |
+ $sql = "SELECT monster_id, name, combat_unit_id |
|
72 |
+ FROM monsters WHERE monster_id=:id"; |
|
73 |
+ $row = $this->dbClient->selectRow($sql, array('id' => $monsterId)); |
|
74 |
+ $unit = new Daemon_Combat_Unit(); |
|
75 |
+ $unit->attachDbClient($this->dbClient); |
|
76 |
+ $unit->get(array('combat_unit_id' => $row['combat_unit_id'])); |
|
77 |
+ $unit->name = $row['name']; |
|
78 |
+ $unit->faction_id = $factionId; |
|
79 |
+ $unit->_id = 'boss'; |
|
80 |
+ //modify stats |
|
81 |
+ $modified = array( |
|
82 |
+ 'str1', 'atk1', 'str2', 'atk2', |
|
83 |
+ 'pdef', 'pres', 'mdef', 'mres', |
|
84 |
+ 'speed', 'armor', 'regen', 'healthMax', |
|
85 |
+ ); |
|
86 |
+ foreach($modified as $name) |
|
87 |
+ $unit->$name *= $powerMod; |
|
88 |
+ $unit->health = $unit->healthMax; |
|
89 |
+ return $unit; |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ |
|
93 |
+ private function getLocation($locationId) |
|
94 |
+ { |
|
95 |
+ $sql = "SELECT l.faction_id, l.boss_id, l.name, f.name AS faction_name |
|
96 |
+ FROM locations l JOIN factions f USING(faction_id) |
|
97 |
+ WHERE location_id = :id"; |
|
98 |
+ return $this->dbClient->selectRow($sql, array('id' => $locationId)); |
|
99 |
+ } |
|
100 |
+} |
... | ... |
@@ -0,0 +1,270 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+class Daemon_CaernSiege |
|
4 |
+{ |
|
5 |
+ protected $dbClient = null; |
|
6 |
+ protected $combatLog; |
|
7 |
+ |
|
8 |
+ |
|
9 |
+ protected function addCharacters(Daemon_Combat $combat, array $units, $locationFactionId) |
|
10 |
+ { |
|
11 |
+ foreach($units as $factionId => $faction) |
|
12 |
+ { |
|
13 |
+ foreach($faction as $unit) |
|
14 |
+ { |
|
15 |
+ $attacker = ($factionId != $locationFactionId); |
|
16 |
+ $combat->addUnit($unit->_id, $unit, $attacker); |
|
17 |
+ } |
|
18 |
+ } |
|
19 |
+ } |
|
20 |
+ |
|
21 |
+ |
|
22 |
+ public function attachDbClient(Daemon_DbClient $dbClient) |
|
23 |
+ { |
|
24 |
+ $this->dbClient = $dbClient; |
|
25 |
+ } |
|
26 |
+ |
|
27 |
+ |
|
28 |
+ //executes the siege, generates the report |
|
29 |
+ public function execute($locationId) |
|
30 |
+ { |
|
31 |
+ //prepare units |
|
32 |
+ $this->kickNeutrals($locationId); |
|
33 |
+ $faction = $this->getLocationFaction($locationId); |
|
34 |
+ $caernFactionId = $faction['id']; |
|
35 |
+ $units = $this->getCharacterUnits($locationId); |
|
36 |
+ //check for empty caern |
|
37 |
+ if(empty($units)) |
|
38 |
+ { |
|
39 |
+ $this->combatLog = '<p>Caern utracony z braku obrońców.</p>'; |
|
40 |
+ $winnerId = null; |
|
41 |
+ } |
|
42 |
+ else |
|
43 |
+ { |
|
44 |
+ //add monster support |
|
45 |
+ $supportCount = 0; |
|
46 |
+ foreach($units as $factionId => $faction) |
|
47 |
+ { |
|
48 |
+ if($factionId != $caernFactionId) |
|
49 |
+ $supportCount += count($faction); |
|
50 |
+ else |
|
51 |
+ $supportCount -= count($faction); |
|
52 |
+ } |
|
53 |
+ if($supportCount > 0) |
|
54 |
+ { |
|
55 |
+ $support = $this->getLocationMonsters($locationId, $caernFactionId, $supportCount); |
|
56 |
+ foreach($support as $unit) |
|
57 |
+ $units[$caernFactionId][$unit->_id] = $unit; |
|
58 |
+ } |
|
59 |
+ unset($support, $supportCount); |
|
60 |
+ //execute combat |
|
61 |
+ $this->combatLog = $this->runCombat($units, $caernFactionId); |
|
62 |
+ //save characters |
|
63 |
+ $this->putCharacterUnits($units); |
|
64 |
+ //find winner (units modified by combat) |
|
65 |
+ $winnerId = $this->getWinnerFaction($units); |
|
66 |
+ if(!$winnerId) |
|
67 |
+ $msg = 'utracony'; |
|
68 |
+ elseif($winnerId != $caernFactionId) |
|
69 |
+ $msg = 'przejęty'; |
|
70 |
+ else |
|
71 |
+ $msg = 'utrzymany'; |
|
72 |
+ $this->combatLog .= "<p><b>Caern $msg!</b></p>"; |
|
73 |
+ } |
|
74 |
+ //kick losers |
|
75 |
+ $this->kickEnemies($locationId, $winnerId); |
|
76 |
+ //update location |
|
77 |
+ $sql = "UPDATE locations SET faction_id=:fid WHERE location_id=:id"; |
|
78 |
+ $params = array('fid' => $winnerId, 'id' => $locationId); |
|
79 |
+ $this->dbClient->query($sql, $params); |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ |
|
83 |
+ protected function getCharacterUnits($locationId) |
|
84 |
+ { |
|
85 |
+ $units = array(); |
|
86 |
+ $sql = "SELECT cd.character_id, c.name, cd.faction_id, cd.health, cd.health_max, cd.combat_unit_id |
|
87 |
+ FROM character_data cd JOIN characters c USING(character_id) |
|
88 |
+ WHERE location_id=:id AND cd.faction_id IS NOT NULL ORDER BY xp_used DESC"; |
|
89 |
+ $data = $this->dbClient->selectAll($sql, array('id' => $locationId)); |
|
90 |
+ foreach($data as $row) |
|
91 |
+ { |
|
92 |
+ $unit = new Daemon_Combat_Unit(); |
|
93 |
+ $unit->attachDbClient($this->dbClient); |
|
94 |
+ $unit->get(array('combat_unit_id' => $row['combat_unit_id'])); |
|
95 |
+ $unit->name = $row['name']; |
|
96 |
+ $unit->faction_id = $row['faction_id']; |
|
97 |
+ $unit->health = $row['health_max']; |
|
98 |
+ $unit->health_max = $row['health_max']; |
|
99 |
+ $unit->_id = $row['character_id']; |
|
100 |
+ $units[$row['faction_id']][$unit->_id] = $unit; |
|
101 |
+ } |
|
102 |
+ return $units; |
|
103 |
+ } |
|
104 |
+ |
|
105 |
+ |
|
106 |
+ public function getCombatLog() |
|
107 |
+ { |
|
108 |
+ return $this->combatLog; |
|
109 |
+ } |
|
110 |
+ |
|
111 |
+ |
|
112 |
+ protected function getLocationFaction($locationId) |
|
113 |
+ { |
|
114 |
+ $sql = "SELECT faction_id AS id, f.name |
|
115 |
+ FROM locations l JOIN factions f USING(faction_id) |
|
116 |
+ WHERE location_id=:id"; |
|
117 |
+ return $this->dbClient->selectRow($sql, array('id' => $locationId)); |
|
118 |
+ } |
|
119 |
+ |
|
120 |
+ |
|
121 |
+ protected function getLocationMonsters($locationId, $factionId, $desiredCount) |
|
122 |
+ { |
|
123 |
+ $result = array(); |
|
124 |
+ $sql = "SELECT m.monster_id, m.name, m.combat_unit_id |
|
125 |
+ FROM location_monsters lm JOIN monsters m USING(monster_id) |
|
126 |
+ WHERE location_id=:id"; |
|
127 |
+ $mobs = $this->dbClient->selectAll($sql, array('id' => $locationId)); |
|
128 |
+ if(empty($mobs)) |
|
129 |
+ return array(); |
|
130 |
+ for($i = 0; $i < $desiredCount; ++$i) |
|
131 |
+ { |
|
132 |
+ $row = $mobs[array_rand($mobs)]; |
|
133 |
+ $unit = new Daemon_Combat_Unit(); |
|
134 |
+ $unit->attachDbClient($this->dbClient); |
|
135 |
+ $unit->get(array('combat_unit_id' => $row['combat_unit_id'])); |
|
136 |
+ $unit->name = $row['name']; |
|
137 |
+ $unit->faction_id = $factionId; |
|
138 |
+ $unit->_id = "mob_$i"; |
|
139 |
+ $result[$unit->_id] = $unit; |
|
140 |
+ } |
|
141 |
+ return $result; |
|
142 |
+ } |
|
143 |
+ |
|
144 |
+ |
|
145 |
+ protected function getWinnerFaction(array $units) |
|
146 |
+ { |
|
147 |
+ $survivors = array(); |
|
148 |
+ foreach($units as $factionId => $faction) |
|
149 |
+ { |
|
150 |
+ $survivors[$factionId] = 0; |
|
151 |
+ foreach($faction as $unit) |
|
152 |
+ { |
|
153 |
+ if($unit->health > 0) |
|
154 |
+ $survivors[$unit->faction_id] += 1; |
|
155 |
+ } |
|
156 |
+ } |
|
157 |
+ $winnerId = null; |
|
158 |
+ $winnerCount = 0; |
|
159 |
+ foreach($survivors as $factionId => $count) |
|
160 |
+ { |
|
161 |
+ if($winnerCount < $count) |
|
162 |
+ { |
|
163 |
+ $winnerId = $factionId; |
|
164 |
+ $winnerCount = $count; |
|
165 |
+ } |
|
166 |
+ } |
|
167 |
+ return $winnerId; |
|
168 |
+ } |
|
169 |
+ |
|
170 |
+ |
|
171 |
+ //kicks enemies out of the caern |
|
172 |
+ public function kickEnemies($locationId, $factionId) |
|
173 |
+ { |
|
174 |
+ $sql = "UPDATE character_data SET location_id=NULL WHERE location_id=:locationId"; |
|
175 |
+ $params = array('locationId' => $locationId); |
|
176 |
+ if($factionId) |
|
177 |
+ { |
|
178 |
+ $sql .= " AND faction_id!=:factionId"; |
|
179 |
+ $params['factionId'] = $factionId; |
|
180 |
+ } |
|
181 |
+ $this->dbClient->query($sql, $params); |
|
182 |
+ } |
|
183 |
+ |
|
184 |
+ |
|
185 |
+ //kicks neutral characters out of the caern |
|
186 |
+ protected function kickNeutrals($locationId) |
|
187 |
+ { |
|
188 |
+ $sql = "UPDATE character_data SET location_id=NULL WHERE location_id=:id AND faction_id IS NULL"; |
|
189 |
+ $this->dbClient->query($sql, array('id' => $locationId)); |
|
190 |
+ } |
|
191 |
+ |
|
192 |
+ |
|
193 |
+ protected function putCharacterUnits(array $units) |
|
194 |
+ { |
|
195 |
+ $sqlLive = "UPDATE character_data SET health=:hp WHERE character_id=:id"; |
|
196 |
+ $sqlDie = "UPDATE character_data SET health=0, location_id=null WHERE character_id=:id"; |
|
197 |
+ foreach($units as $faction) |
|
198 |
+ { |
|
199 |
+ foreach($faction as $unit) |
|
200 |
+ { |
|
201 |
+ if(!is_numeric($unit->_id)) |
|
202 |
+ continue;//monsters have string IDs |
|
203 |
+ if($unit->health > 0) |
|
204 |
+ { |
|
205 |
+ $params = array('id' => $unit->_id, 'hp' => $unit->health); |
|
206 |
+ $this->dbClient->query($sqlLive, $params); |
|
207 |
+ } |
|
208 |
+ else |
|
209 |
+ { |
|
210 |
+ $params = array('id' => $unit->_id); |
|
211 |
+ $this->dbClient->query($sqlDie, $params); |
|
212 |
+ } |
|
213 |
+ } |
|
214 |
+ } |
|
215 |
+ } |
|
216 |
+ |
|
217 |
+ |
|
218 |
+ public function runCombat(array $units, $locationFactionId) |
|
219 |
+ { |
|
220 |
+ $combat = new Daemon_Combat(); |
|
221 |
+ $logger = new Daemon_Combat_Log(); |
|
222 |
+ $combat->attachLogger($logger); |
|
223 |
+ foreach($units as $factionId => $faction) |
|
224 |
+ { |
|
225 |
+ foreach($faction as $unit) |
|
226 |
+ { |
|
227 |
+ $attacker = ($factionId != $locationFactionId); |
|
228 |
+ $combat->addUnit($unit->_id, $unit, $attacker); |
|
229 |
+ } |
|
230 |
+ } |
|
231 |
+ $combat->execute(); |
|
232 |
+ foreach($combat->units as &$unit) |
|
233 |
+ $unit->health = max(0, ceil($unit->health)); |
|
234 |
+ //prepare summary |
|
235 |
+ $summary = array(); |
|
236 |
+ $summary[] = '<table class="border">'; |
|
237 |
+ $summary[] = '<caption>Podsumowanie bitwy</caption>'; |
|
238 |
+ $summary[] = '<tr>'; |
|
239 |
+ $summary[] = '<th rowspan="2">Strona</th><th colspan="3">Postać</th>'; |
|
240 |
+ $summary[] = '<th colspan="3">Wykonane ataki</th><th colspan="3">Otrzymane ataki</th>'; |
|
241 |
+ $summary[] = '</tr>'; |
|
242 |
+ $summary[] = '<tr>'; |
|
243 |
+ $summary[] = '<th>ImiÄ™</th><th>Frakcja</th><th>Zdrowie</th>'; |
|
244 |
+ $summary[] = '<th>Ataki</th><th>Obrażenia</th><th>Średnia</th>'; |
|
245 |
+ $summary[] = '<th>Ataki</th><th>Obrażenia</th><th>Średnia</th>'; |
|
246 |
+ $summary[] = '</tr>'; |
|
247 |
+ foreach($units as $factionId => $faction) |
|
248 |
+ { |
|
249 |
+ foreach($faction as $unit) |
|
250 |
+ { |
|
251 |
+ $attackerTxt = $unit->_attacker ? 'atak' : 'obrona'; |
|
252 |
+ $dmgTotal = $unit->_dmgDealt + $unit->_dmgTaken; |
|
253 |
+ $health = max(0, ceil($unit->health_max - $unit->_dmgTaken)); |
|
254 |
+ $healthPercent = $unit->health_max ? round(100 * $health / $unit->health_max, 2) : 0; |
|
255 |
+ $avgDealt = $unit->_cntDealt ? $unit->_dmgDealt / $unit->_cntDealt : null; |
|
256 |
+ $avgTaken = $unit->_cntTaken ? $unit->_dmgTaken / $unit->_cntTaken : null; |
|
257 |
+ $summary[] = '<tr>'; |
|
258 |
+ $summary[] = sprintf('<td>%s</td><td>%s</td><td>%s</td><td>%.2f%%</td>', |
|
259 |
+ $attackerTxt, $unit->name, $unit->faction_id, $healthPercent); |
|
260 |
+ $summary[] = sprintf('<td>%d</td><td>%.3f</td><td>%.3f</td>', |
|
261 |
+ $unit->_cntDealt, $unit->_dmgDealt, $avgDealt); |
|
262 |
+ $summary[] = sprintf('<td>%d</td><td>%.3f</td><td>%.3f</td>', |
|
263 |
+ $unit->_cntTaken, $unit->_dmgTaken, $avgTaken); |
|
264 |
+ $summary[] = '</tr>'; |
|
265 |
+ } |
|
266 |
+ } |
|
267 |
+ $summary[] = '</table>'; |
|
268 |
+ return implode('', $summary) . (string) $logger; |
|
269 |
+ } |
|
270 |
+} |
... | ... |
@@ -0,0 +1,164 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+//combat engine |
|
4 |
+//usage: |
|
5 |
+//$combat = new Daemon_Combat(); |
|
6 |
+//$logger = new Daemon_Combat_Log(); |
|
7 |
+//$combat->attachLogger($logger); |
|
8 |
+//$combat->addUnit('a', $unit1, true); |
|
9 |
+//$combat->addUnit('b', $unit2, false); |
|
10 |
+//$combat->execute(); |
|
11 |
+//$combatLog = (string) $logger; |
|
12 |
+class Daemon_Combat |
|
13 |
+{ |
|
14 |
+ public $units = array(); |
|
15 |
+ public $sideA = array(); |
|
16 |
+ public $sideB = array(); |
|
17 |
+ public $round = 0; |
|
18 |
+ public $roundLimit = 120; |
|
19 |
+ public $tickLimit = 1; |
|
20 |
+ private $_logger = null; |
|
21 |
+ |
|
22 |
+ |
|
23 |
+ public function addUnit($unitId, Daemon_Combat_Unit $unit, $attacker) |
|
24 |
+ { |
|
25 |
+ $unit->attachLogger($this->_logger); |
|
26 |
+ $unit->_id = $unitId; |
|
27 |
+ $unit->_ticks = 0; |
|
28 |
+ $unit->_initiative = mt_rand(0, 32 * $unit->speed); |
|
29 |
+ $unit->_target = null; |
|
30 |
+ $unit->_threat = array(); |
|
31 |
+ $unit->_dmgDealt = 0; |
|
32 |
+ $unit->_dmgTaken = 0; |
|
33 |
+ $this->units[$unitId] = $unit; |
|
34 |
+ $unit->_attacker = (bool) $attacker; |
|
35 |
+ if($unit->_attacker) |
|
36 |
+ { |
|
37 |
+ $this->sideA[$unitId] = $unit; |
|
38 |
+ $unit->_allies = &$this->sideA; |
|
39 |
+ $unit->_enemies = &$this->sideB; |
|
40 |
+ } |
|
41 |
+ else |
|
42 |
+ { |
|
43 |
+ $this->sideB[$unitId] = $unit; |
|
44 |
+ $unit->_allies = &$this->sideB; |
|
45 |
+ $unit->_enemies = &$this->sideA; |
|
46 |
+ } |
|
47 |
+ } |
|
48 |
+ |
|
49 |
+ |
|
50 |
+ public function attachLogger(Daemon_Combat_Log $logger) |
|
51 |
+ { |
|
52 |
+ $this->_logger = $logger; |
|
53 |
+ foreach($this->units as $unit) |
|
54 |
+ $unit->attachLogger($logger); |
|
55 |
+ } |
|
56 |
+ |
|
57 |
+ |
|
58 |
+ protected function callbackActive($unit) |
|
59 |
+ { |
|
60 |
+ return $unit->_ticks > $this->tickLimit; |
|
61 |
+ } |
|
62 |
+ |
|
63 |
+ |
|
64 |
+ protected function callbackSpeedSum($prev, $unit) |
|
65 |
+ { |
|
66 |
+ return $prev + $unit->speed; |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ |
|
70 |
+ protected function callbackTickCompare($unit1, $unit2) |
|
71 |
+ { |
|
72 |
+ if($unit1->_ticks == $unit2->_ticks) |
|
73 |
+ return $unit1->_initiative - $unit2->_initiative; |
|
74 |
+ else return ($unit1->_ticks < $unit2->_ticks) ? -1 : +1; |
|
75 |
+ } |
|
76 |
+ |
|
77 |
+ |
|
78 |
+ protected function callbackTickInc($unit, $key) |
|
79 |
+ { |
|
80 |
+ if($unit->health > 0) |
|
81 |
+ $unit->_ticks += $unit->speed; |
|
82 |
+ else $unit->_ticks = null; |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ |
|
86 |
+ protected function debug($round, array $units) |
|
87 |
+ { |
|
88 |
+ if($this->_logger) |
|
89 |
+ { |
|
90 |
+ $result = array("Segment $round"); |
|
91 |
+ foreach($units as $unit) |
|
92 |
+ $result[] = (string) $unit; |
|
93 |
+ $this->_logger->add($result); |
|
94 |
+ } |
|
95 |
+ } |
|
96 |
+ |
|
97 |
+ |
|
98 |
+ public function execute($noCleanup = false) |
|
99 |
+ { |
|
100 |
+ if(!$this->units) |
|
101 |
+ return; |
|
102 |
+ $unitCount = count($this->units); |
|
103 |
+ if ($this->_logger) |
|
104 |
+ $this->_logger->groupCombat = ($unitCount > 2); |
|
105 |
+ $round = 0; |
|
106 |
+ $roundLimit = 100 + 10 * $unitCount; |
|
107 |
+ $speedSum = array_reduce($this->units, array($this, 'callbackSpeedSum'), 0); |
|
108 |
+ $this->tickLimit = 1 + ceil(2 * $speedSum / count($this->units)); |
|
109 |
+ while($round < $roundLimit) |
|
110 |
+ { |
|
111 |
+ $victory = false; |
|
112 |
+ array_walk($this->units, array($this, 'callbackTickInc')); |
|
113 |
+ while($actor = $this->getActiveUnit()) |
|
114 |
+ { |
|
115 |
+ ++$round; |
|
116 |
+ $actor->_ticks -= $this->tickLimit; |
|
117 |
+ $actor->executeRound($round); |
|
118 |
+ //victory check, sides should be updated by units |
|
119 |
+ $victory = (!count($this->sideA) || !count($this->sideB)); |
|
120 |
+ if($victory) |
|
121 |
+ break; |
|
122 |
+ } |
|
123 |
+ if($victory) |
|
124 |
+ break; |
|
125 |
+ } |
|
126 |
+ //after-combat regen & fixes |
|
127 |
+ if (!$noCleanup) |
|
128 |
+ { |
|
129 |
+ foreach($this->units as $unit) |
|
130 |
+ { |
|
131 |
+ if ($unit->health < 1) |
|
132 |
+ { |
|
133 |
+ $unit->health = 0; |
|
134 |
+ } |
|
135 |
+ elseif (($unit->regen > 0) && ($unit->health < $unit->health_max)) |
|
136 |
+ { |
|
137 |
+ $unit->health = $unit->health_max; |
|
138 |
+ if($this->_logger) |
|
139 |
+ $this->_logger->add("<i>$unit->name</i> regeneruje pełnię zdrowia.<br>"); |
|
140 |
+ } |
|
141 |
+ else |
|
142 |
+ { |
|
143 |
+ $unit->health = Daemon_Math::round($unit->health); |
|
144 |
+ } |
|
145 |
+ } |
|
146 |
+ } |
|
147 |
+ } |
|
148 |
+ |
|
149 |
+ |
|
150 |
+ protected function getActiveUnit() |
|
151 |
+ { |
|
152 |
+ $active = array_filter($this->units, array($this, 'callbackActive')); |
|
153 |
+ uasort($active, array($this, 'callbackTickCompare')); |
|
154 |
+ return array_pop($active); |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ |
|
158 |
+ public function reset() |
|
159 |
+ { |
|
160 |
+ $this->units = array(); |
|
161 |
+ $this->sideA = array(); |
|
162 |
+ $this->sideB = array(); |
|
163 |
+ } |
|
164 |
+} |
... | ... |
@@ -0,0 +1,117 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+class Daemon_Combat_Log |
|
4 |
+{ |
|
5 |
+ private $buffer; |
|
6 |
+ public $groupCombat; |
|
7 |
+ |
|
8 |
+ |
|
9 |
+ public function __construct() |
|
10 |
+ { |
|
11 |
+ $this->clear(); |
|
12 |
+ } |
|
13 |
+ |
|
14 |
+ |
|
15 |
+ public function __toString() |
|
16 |
+ { |
|
17 |
+ return $this->buffer; |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ |
|
21 |
+ public function add($text) |
|
22 |
+ { |
|
23 |
+ $this->buffer .= "$text\n"; |
|
24 |
+ } |
|
25 |
+ |
|
26 |
+ |
|
27 |
+ public function clear() |
|
28 |
+ { |
|
29 |
+ $this->buffer = ''; |
|
30 |
+ } |
|
31 |
+ |
|
32 |
+ |
|
33 |
+ public static function escape($str) |
|
34 |
+ { |
|
35 |
+ return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); |
|
36 |
+ } |
|
37 |
+ |
|
38 |
+ |
|
39 |
+ public function txtAttackHit($name, $dmg, $magical, $critical, $poison, $shockDmg, $stun, $vampRegen) |
|
40 |
+ { |
|
41 |
+ $txt = $critical ? 'Trafienie krytyczne! ' : ''; |
|
42 |
+ $txt .= sprintf('<i>%s</i> zadaje <b>%.2f</b> obrażeń %s', self::escape($name), |
|
43 |
+ $dmg, $magical ? 'magicznych' : 'fizycznych'); |
|
44 |
+ if($poison) |
|
45 |
+ $txt .= ', zatruwajÄ…c cel'; |
|
46 |
+ if($stun) |
|
47 |
+ $txt .= ', ogłuszając cel'; |
|
48 |
+ if($vampRegen) |
|
49 |
+ $txt .= sprintf(', regenerujÄ…c <b>%.2f</b> HP', $vampRegen); |
|
50 |
+ if($shockDmg) |
|
51 |
+ $txt .= sprintf('. Otrzymuje <b>%.2f</b> obrażeń od porażenia', $shockDmg); |
|
52 |
+ $txt .= '.<br>'; |
|
53 |
+ $this->add($txt); |
|
54 |
+ } |
|
55 |
+ |
|
56 |
+ |
|
57 |
+ public function txtAttackMiss($name, $magical) |
|
58 |
+ { |
|
59 |
+ if($magical) |
|
60 |
+ $this->add('Cel odbił zaklęcie.<br>'); |
|
61 |
+ else $this->add(sprintf('<i>%s</i> chybił.<br>', self::escape($name))); |
|
62 |
+ } |
|
63 |
+ |
|
64 |
+ |
|
65 |
+ public function txtDeath($name, $flawlessName = null) |
|
66 |
+ { |
|
67 |
+ $txt = sprintf('<i>%s</i> umiera.<br>', $name); |
|
68 |
+ if($flawlessName) |
|
69 |
+ { |
|
70 |
+ $atxt = array('<i>%s</i> ziewa.', '<i>%s</i> śmieje się szyderczo.'); |
|
71 |
+ $txt .= sprintf($atxt[array_rand($atxt)], self::escape($flawlessName)).'<br>'; |
|
72 |
+ } |
|
73 |
+ $this->add($txt); |
|
74 |
+ } |
|
75 |
+ |
|
76 |
+ |
|
77 |
+ public function txtDemon($regen) |
|
78 |
+ { |
|
79 |
+ $this->add(sprintf('Demon w zbroi wchłonął zaklęcie. Cel regeneruje %.2f obrażeń.<br>', $regen)); |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ |
|
83 |
+ public function txtPoison($name, $dmg) |
|
84 |
+ { |
|
85 |
+ $this->add(sprintf('<i>%s</i> otrzymuje %.2f obrażeń od trucizny.<br>', self::escape($name), $dmg)); |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ |
|
89 |
+ public function txtRegen($name, $regen) |
|
90 |
+ { |
|
91 |
+ $this->add(sprintf('<i>%s</i> regeneruje %.2f obrażeń.<br>', self::escape($name), $regen)); |
|
92 |
+ } |
|
93 |
+ |
|
94 |
+ |
|
95 |
+ public function txtRoundFooter() |
|
96 |
+ { |
|
97 |
+ $this->add('</p>'); |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ |
|
101 |
+ public function txtRoundHeader($round) |
|
102 |
+ { |
|
103 |
+ $this->add(sprintf('<h3>Akcja %d</h3><p>', $round)); |
|
104 |
+ } |
|
105 |
+ |
|
106 |
+ |
|
107 |
+ public function txtTargetHeader($actorName, $targetName) |
|
108 |
+ { |
|
109 |
+ if($targetName) |
|
110 |
+ { |
|
111 |
+ if ($this->groupCombat) |
|
112 |
+ $this->add(sprintf('<i>%s</i> wybiera cel: <i>%s</i>.<br>', |
|
113 |
+ self::escape($actorName), self::escape($targetName))); |
|
114 |
+ } |
|
115 |
+ else $this->add(sprintf('<i>%s</i> nie ma już przeciwników.<br>', self::escape($actorName))); |
|
116 |
+ } |
|
117 |
+} |
... | ... |
@@ -0,0 +1,261 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+//combat stats (daemon or monster) |
|
4 |
+class Daemon_Combat_Unit extends Daemon_DbObject_CombatUnit |
|
5 |
+{ |
|
6 |
+ private $_logger = null; |
|
7 |
+ //combat variables |
|
8 |
+ public $_id = null; |
|
9 |
+ public $_flawless = true; |
|
10 |
+ public $_ticks = 0; |
|
11 |
+ public $_initiative = 0; |
|
12 |
+ public $_poison = 0; |
|
13 |
+ public $_attacker = null; //attacker or defender |
|
14 |
+ public $_target = null; //current target |
|
15 |
+ public $_threat = array(); //current threat |
|
16 |
+ public $_allies = array(); //global sideA/sideB list |
|
17 |
+ public $_enemies = array(); //global sideB/sideA list |
|
18 |
+ public $_cntDealt = 0; |
|
19 |
+ public $_dmgDealt = 0; |
|
20 |
+ public $_cntTaken = 0; |
|
21 |
+ public $_dmgTaken = 0; |
|
22 |
+ |
|
23 |
+ |
|
24 |
+ public function __toString() |
|
25 |
+ { |
|
26 |
+ return "[id: $this->_id, name: $this->name, ticks: $this->_ticks, health: $this->health/$this->health_max]"; |
|
27 |
+ } |
|
28 |
+ |
|
29 |
+ |
|
30 |
+ public function attachLogger(Daemon_Combat_Log $logger = null) |
|
31 |
+ { |
|
32 |
+ $this->_logger = $logger; |
|
33 |
+ } |
|
34 |
+ |
|
35 |
+ |
|
36 |
+ protected function calculateAttack(array $params) |
|
37 |
+ { |
|
38 |
+ //attack count - check for swarm |
|
39 |
+ if(self::SP_SWARM == $params['sp_type']) |
|
40 |
+ $attackCount = ceil($params['count'] * $this->health / $this->health_max); |
|
41 |
+ else $attackCount = $params['count']; |
|
42 |
+ //prepare variables |
|
43 |
+ $magical = ('m' == $params['type']); |
|
44 |
+ if($magical) |
|
45 |
+ { |
|
46 |
+ $targetDef = $this->_target->mdef; |
|
47 |
+ $targetRes = $this->_target->mres; |
|
48 |
+ $armor = 0; |
|
49 |
+ } |
|
50 |
+ else |
|
51 |
+ { |
|
52 |
+ $targetDef = $this->_target->pdef; |
|
53 |
+ $targetRes = $this->_target->pres; |
|
54 |
+ $armor = $this->_target->armor; |
|
55 |
+ if(self::SP_ETHER == $params['sp_type']) |
|
56 |
+ $armor *= 1 - $params['sp_param']/100; |
|
57 |
+ } |
|
58 |
+ //calculate basic data |
|
59 |
+ $toHit = 100 * $params['atk'] / ($params['atk'] + $targetDef); |
|
60 |
+ $baseDmg = $params['str'] * $params['str'] / ($params['str'] + $targetRes); |
|
61 |
+ //faction effects |
|
62 |
+ if($this->faction_id && $this->_target->faction_id && ($this->faction_id != $this->_target->faction_id)) |
|
63 |
+ { |
|
64 |
+ if(self::SP_FACTION == $params['sp_type']) |
|
65 |
+ $baseDmg *= 1 + $params['sp_param']/100; |
|
66 |
+ if(self::SP_FACTION == $this->_target->armor_sp_type) |
|
67 |
+ $baseDmg /= 1 + $this->_target->armor_sp_param/100; |
|
68 |
+ } |
|
69 |
+ //execute attacks |
|
70 |
+ for($i=0; $i < $attackCount; $i++) |
|
71 |
+ { |
|
72 |
+ $d100 = mt_rand(0,99); |
|
73 |
+ //check for demon |
|
74 |
+ $demon = $magical && (self::SP_DEMON == $this->_target->armor_sp_type); |
|
75 |
+ if($demon && mt_rand(0,99) < $this->_target->armor_sp_param) |
|
76 |
+ { |
|
77 |
+ $regen = min($this->_target->health_max - $this->_target->health, $baseDmg * $this->_target->armor_sp_param / 100); |
|
78 |
+ $this->_target->health += $regen; |
|
79 |
+ if($this->_logger) |
|
80 |
+ $this->_logger->txtDemon($regen); |
|
81 |
+ } |
|
82 |
+ //check for hit |
|
83 |
+ elseif($d100 < $toHit) |
|
84 |
+ { |
|
85 |
+ //calculate damage |
|
86 |
+ $critical = $d100>45; |
|
87 |
+ if($critical) |
|
88 |
+ { |
|
89 |
+ $dmgMult = 2; |
|
90 |
+ if(self::SP_BLOODY == $params['sp_type']) |
|
91 |
+ $dmgMult += $params['sp_param']/100; |
|
92 |
+ } |
|
93 |
+ else $dmgMult = 1 + mt_rand(0,127)/256; |
|
94 |
+ $dmg = max(0, $baseDmg * $dmgMult - $armor); |
|
95 |
+ $this->_target->health -= $dmg; |
|
96 |
+ //update statistics |
|
97 |
+ $this->_target->_flawless = false; |
|
98 |
+ $this->_target->_cntTaken += 1; |
|
99 |
+ $this->_target->_dmgTaken += $dmg; |
|
100 |
+ $this->_cntDealt += 1; |
|
101 |
+ $this->_dmgDealt += $dmg; |
|
102 |
+ //check for poison |
|
103 |
+ if(self::SP_POISON == $params['sp_type']) |
|
104 |
+ { |
|
105 |
+ $poison = $dmg>0 ? $params['sp_param'] : 0; |
|
106 |
+ if(self::SP_ANTIPOISON == $this->_target->armor_sp_type) |
|
107 |
+ $poison *= 1 - $this->_target->armor_sp_param / 100; |
|
108 |
+ $this->_target->_poison += $poison; |
|
109 |
+ } |
|
110 |
+ else $poison = 0; |
|
111 |
+ //check for shock |
|
112 |
+ if(self::SP_SHOCK == $this->_target->armor_sp_type) |
|
113 |
+ { |
|
114 |
+ $shockDmg = $dmg * $this->_target->armor_sp_param / 100; |
|
115 |
+ $this->health -= $shockDmg; |
|
116 |
+ } |
|
117 |
+ else $shockDmg = 0; |
|
118 |
+ //check for stun |
|
119 |
+ $stun = $critical && (self::SP_STUN == $params['sp_type']); |
|
120 |
+ if($stun) |
|
121 |
+ $this->_target->_ticks = 0; |
|
122 |
+ //check for vampirism |
|
123 |
+ if(self::SP_VAMPIRE == $params['sp_type']) |
|
124 |
+ { |
|
125 |
+ $vampRegen = min($this->health_max - $this->health, $dmg * $params['sp_param']/100); |
|
126 |
+ if(self::SP_ANTIVAMP == $this->_target->armor_sp_type) |
|
127 |
+ $vampRegen *= 1 - $this->_target->armor_sp_param / 100; |
|
128 |
+ $this->health += $vampRegen; |
|
129 |
+ } |
|
130 |
+ else $vampRegen = 0; |
|
131 |
+ //print info |
|
132 |
+ if($this->_logger) |
|
133 |
+ { |
|
134 |
+ $this->_logger->txtAttackHit($this->name, $dmg, $magical, $critical, |
|
135 |
+ $poison, $shockDmg, $stun, $vampRegen); |
|
136 |
+ } |
|
137 |
+ } |
|
138 |
+ else |
|
139 |
+ { |
|
140 |
+ if($this->_logger) |
|
141 |
+ $this->_logger->txtAttackMiss($this->name, $magical); |
|
142 |
+ } |
|
143 |
+ } |
|
144 |
+ } |
|
145 |
+ |
|
146 |
+ |
|
147 |
+ protected function executeAttacks() |
|
148 |
+ { |
|
149 |
+ if(!$this->_target) |
|
150 |
+ return; |
|
151 |
+ $this->_target->_threat[$this->_id] = $this; |
|
152 |
+ if($this->count1 && $this->type1) |
|
153 |
+ { |
|
154 |
+ $attackParams = array( |
|
155 |
+ 'str' => $this->str1, |
|
156 |
+ 'atk' => $this->atk1, |
|
157 |
+ 'type' => $this->type1, |
|
158 |
+ 'count' => $this->count1, |
|
159 |
+ 'sp_type' => $this->sp1_type, |
|
160 |
+ 'sp_param' => $this->sp1_param, |
|
161 |
+ ); |
|
162 |
+ $this->calculateAttack($attackParams); |
|
163 |
+ } |
|
164 |
+ if($this->count2 && $this->type2) |
|
165 |
+ { |
|
166 |
+ $attackParams = array( |
|
167 |
+ 'str' => $this->str2, |
|
168 |
+ 'atk' => $this->atk2, |
|
169 |
+ 'type' => $this->type2, |
|
170 |
+ 'count' => $this->count2, |
|
171 |
+ 'sp_type' => $this->sp2_type, |
|
172 |
+ 'sp_param' => $this->sp2_param, |
|
173 |
+ ); |
|
174 |
+ $this->calculateAttack($attackParams); |
|
175 |
+ } |
|
176 |
+ //check for target death |
|
177 |
+ if($this->_target->health <= 0) |
|
178 |
+ { |
|
179 |
+ $this->_target->_ticks = null; |
|
180 |
+ unset($this->_threat[$this->_target->_id]); |
|
181 |
+ unset($this->_enemies[$this->_target->_id]); |
|
182 |
+ if($this->_logger) |
|
183 |
+ $this->_logger->txtDeath($this->_target->name, $this->_flawless ? $this->name : null); |
|
184 |
+ } |
|
185 |
+ } |
|
186 |
+ |
|
187 |
+ |
|
188 |
+ public function executeRound($round) |
|
189 |
+ { |
|
190 |
+ if($this->_logger) |
|
191 |
+ $this->_logger->txtRoundHeader($round); |
|
192 |
+ $this->regen(); |
|
193 |
+ $this->pickTarget(); |
|
194 |
+ if($this->_target) |
|
195 |
+ $this->executeAttacks(); |
|
196 |
+ $this->poison(); |
|
197 |
+ if($this->_logger) |
|
198 |
+ $this->_logger->txtRoundFooter(); |
|
199 |
+ } |
|
200 |
+ |
|
201 |
+ |
|
202 |
+ protected function pickTarget() |
|
203 |
+ { |
|
204 |
+ //clear old target |
|
205 |
+ $this->_target = null; |
|
206 |
+ //try current threat |
|
207 |
+ while($this->_threat && !$this->_target) |
|
208 |
+ { |
|
209 |
+ $targetId = array_rand($this->_threat); |
|
210 |
+ $this->_target = $this->_threat[$targetId]; |
|
211 |
+ if($this->_target->health <= 0) //killed by someone else |
|
212 |
+ { |
|
213 |
+ $this->_target = null; |
|
214 |
+ unset($this->_threat[$targetId]); |
|
215 |
+ } |
|
216 |
+ } |
|
217 |
+ //no threat, try other enemies |
|
218 |
+ if($this->_enemies && !$this->_target) |
|
219 |
+ { |
|
220 |
+ $targetId = array_rand($this->_enemies); |
|
221 |
+ $this->_target = $this->_enemies[$targetId]; |
|
222 |
+ } |
|
223 |
+ //print message |
|
224 |
+ if($this->_logger) |
|
225 |
+ { |
|
226 |
+ if($this->_target) |
|
227 |
+ $this->_logger->txtTargetHeader($this->name, $this->_target->name); |
|
228 |
+ else $this->_logger->txtTargetHeader($this->name, null); |
|
229 |
+ } |
|
230 |
+ } |
|
231 |
+ |
|
232 |
+ |
|
233 |
+ protected function poison() |
|
234 |
+ { |
|
235 |
+ if($this->_poison) |
|
236 |
+ { |
|
237 |
+ $dmg = $this->_poison * $this->_poison / ($this->_poison + $this->pres); |
|
238 |
+ $this->health -= $dmg; |
|
239 |
+ if($this->_logger) |
|
240 |
+ $this->_logger->txtPoison($this->name, $dmg); |
|
241 |
+ if($this->health <= 0) |
|
242 |
+ { |
|
243 |
+ unset($this->_allies[$this->_id]); |
|
244 |
+ if($this->_logger) |
|
245 |
+ $this->_logger->txtDeath($this->name, false); |
|
246 |
+ } |
|
247 |
+ } |
|
248 |
+ } |
|
249 |
+ |
|
250 |
+ |
|
251 |
+ protected function regen() |
|
252 |
+ { |
|
253 |
+ if($this->regen && $this->health < $this->health_max) |
|
254 |
+ { |
|
255 |
+ $delta = min($this->health_max - $this->health, $this->regen); |
|
256 |
+ $this->health += $delta; |
|
257 |
+ if($this->_logger) |
|
258 |
+ $this->_logger->txtRegen($this->name, $delta); |
|
259 |
+ } |
|
260 |
+ } |
|
261 |
+} |
... | ... |
@@ -0,0 +1,56 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+class Daemon_Config |
|
4 |
+{ |
|
5 |
+ public $applicationRoot; |
|
6 |
+ public $applicationTitle = 'Daemon 2'; |
|
7 |
+ public $applicationUrl; |
|
8 |
+ public $applicationMail; |
|
9 |
+ public $dbHost; |
|
10 |
+ public $dbSchema; |
|
11 |
+ public $dbUser; |
|
12 |
+ public $dbPassword; |
|
13 |
+ public $dbPrefix; |
|
14 |
+ public $minifyHtml = false; |
|
15 |
+ public $sessionName = 'sessid'; |
|
16 |
+ public $tsDelta = 0.5; |
|
17 |
+ |
|
18 |
+ |
|
19 |
+ public function __construct($applicationRoot = null) |
|
20 |
+ { |
|
21 |
+ $this->applicationRoot = (string) $applicationRoot; |
|
22 |
+ $hostname = mb_strtolower(getenv('SERVER_NAME')); |
|
23 |
+ if(!$hostname) |
|
24 |
+ $hostname = '_cron'; |
|
25 |
+ $fileName = $hostname . '.php'; |
|
26 |
+ $this->loadFile($this->getFilePath('cfg', $fileName)); |
|
27 |
+ } |
|
28 |
+ |
|
29 |
+ |
|
30 |
+ //loads config from file |
|
31 |
+ public function loadFile($path) |
|
32 |
+ { |
|
33 |
+ if(is_readable($path)) |
|
34 |
+ { |
|
35 |
+ $data = (array) include $path; |
|
36 |
+ foreach($data as $key=>$value) |
|
37 |
+ $this->$key = $value; |
|
38 |
+ } |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ |
|
42 |
+ //implodes parameters into relative path and prepends it with root |
|
43 |
+ public function getFilePath(/*...*/) |
|
44 |
+ { |
|
45 |
+ $aPath = array_filter(func_get_args()); |
|
46 |
+ array_unshift($aPath, $this->applicationRoot); |
|
47 |
+ return implode(DIRECTORY_SEPARATOR, $aPath); |
|
48 |
+ } |
|
49 |
+ |
|
50 |
+ |
|
51 |
+ //generates URL from relative path |
|
52 |
+ public function getUrl($path) |
|
53 |
+ { |
|
54 |
+ return $path ? "$this->applicationUrl$path" : $this->applicationUrl; |
|
55 |
+ } |
|
56 |
+} |
... | ... |
@@ -0,0 +1,158 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+//prototype for page controller |
|
4 |
+class Daemon_Controller |
|
5 |
+{ |
|
6 |
+ //global objects |
|
7 |
+ protected $cfg = null; |
|
8 |
+ protected $dbClient = null; |
|
9 |
+ protected $dbCfg = null; |
|
10 |
+ protected $activeCharacter = null; |
|
11 |
+ protected $characterData = null; |
|
12 |
+ protected $location = null; |
|
13 |
+ protected $player = null; |
|
14 |
+ protected $view = null; |
|
15 |
+ //execution parameters |
|
16 |
+ protected $disableMessages = false; |
|
17 |
+ protected $disablePlayer = false; |
|
18 |
+ protected $requireActiveChar = false; |
|
19 |
+ protected $requireAuthentication = false; |
|
20 |
+ protected $requireFactionMatch = false; |
|
21 |
+ protected $requireLocation = false; |
|
22 |
+ protected $requireNoEvents = false; |
|
23 |
+ protected $requiredRole = null; |
|
24 |
+ //output parameters |
|
25 |
+ protected $pageOutputMode = Daemon_View::MODE_HTML; |
|
26 |
+ protected $pageSubtitle = null; |
|
27 |
+ protected $pageSubtitleDetails = null; |
|
28 |
+ protected $pageSubtitleUseQuery = false; |
|
29 |
+ protected $pageTemplatePath; |
|
30 |
+ |
|
31 |
+ |
|
32 |
+ final public function __construct(Daemon_Config $cfg) |
|
33 |
+ { |
|
34 |
+ $this->cfg = $cfg; |
|
35 |
+ session_name($this->cfg->sessionName); |
|
36 |
+ session_cache_limiter(null); |
|
37 |
+ session_start(); |
|
38 |
+ $this->dbClient = Daemon::createDbClient($this->cfg); |
|
39 |
+ $this->dbCfg = new Daemon_DbConfig($this->dbClient); |
|
40 |
+ if(!$this->disablePlayer) |
|
41 |
+ { |
|
42 |
+ $this->player = new Daemon_DbObject_Player($this->dbClient); |
|
43 |
+ $this->activeCharacter = $this->player->getActiveCharacter(); |
|
44 |
+ $this->activeCharacter->updateLastAction(); |
|
45 |
+ $this->characterData = $this->activeCharacter->getCharacterData(); |
|
46 |
+ $this->location = $this->characterData->getLocation(); |
|
47 |
+ } |
|
48 |
+ $this->view = new Daemon_View($this->cfg); |
|
49 |
+ } |
|
50 |
+ |
|
51 |
+ |
|
52 |
+ //checks last action's timestamp |
|
53 |
+ final private function checkActionTimestamp() |
|
54 |
+ { |
|
55 |
+ $lastAction = isset($_SESSION['ts']) ? $_SESSION['ts'] : 0.0; |
|
56 |
+ $_SESSION['ts'] = microtime(true); |
|
57 |
+ return (bool) ($_SESSION['ts'] >= $lastAction + $this->cfg->tsDelta); |
|
58 |
+ } |
|
59 |
+ |
|
60 |
+ |
|
61 |
+ final public function execute() |
|
62 |
+ { |
|
63 |
+ //prepare controller |
|
64 |
+ $this->prepareModel(); |
|
65 |
+ $this->validatePlayer(); |
|
66 |
+ //check last action's timestamp |
|
67 |
+ if($_POST && !$this->checkActionTimestamp()) |
|
68 |
+ { |
|
69 |
+ Daemon_MsgQueue::add('Operacja anulowana: za duża częstość.'); |
|
70 |
+ $_POST = array(); |
|
71 |
+ } |
|
72 |
+ //execute commands |
|
73 |
+ $cmdExecuted = (bool) $this->runCommands(); |
|
74 |
+ //display page |
|
75 |
+ $this->prepareView(); |
|
76 |
+ if($this->pageSubtitleUseQuery) |
|
77 |
+ { |
|
78 |
+ if($qs = getenv('QUERY_STRING')) |
|
79 |
+ $this->pageSubtitleDetails = urldecode($qs); |
|
80 |
+ } |
|
81 |
+ $this->view->setPageTitle($this->pageSubtitle, $this->pageSubtitleDetails, $cmdExecuted); |
|
82 |
+ if(!$this->disablePlayer) |
|
83 |
+ { |
|
84 |
+ $this->view->setGameHeader($this->player->getPlayerId(), |
|
85 |
+ $this->activeCharacter, $this->characterData, $this->location); |
|
86 |
+ $this->view->setPageSkin($this->player->skin); |
|
87 |
+ } |
|
88 |
+ else $this->view->setPageSkin(null); |
|
89 |
+ if(!$this->disableMessages) |
|
90 |
+ $messages = Daemon_MsgQueue::getAll(); |
|
91 |
+ else $messages = array(); |
|
92 |
+ if($this->dbCfg->globalMessage) |
|
93 |
+ $messages[] = $this->dbCfg->globalMessage; |
|
94 |
+ $this->view->setMessages($messages); |
|
95 |
+ $this->view->display($this->pageTemplatePath, $this->pageOutputMode); |
|
96 |
+ } |
|
97 |
+ |
|
98 |
+ |
|
99 |
+ //page-specific |
|
100 |
+ protected function prepareModel() |
|
101 |
+ { |
|
102 |
+ } |
|
103 |
+ |
|
104 |
+ |
|
105 |
+ //page-specific |
|
106 |
+ protected function prepareView() |
|
107 |
+ { |
|
108 |
+ } |
|
109 |
+ |
|
110 |
+ |
|
111 |
+ //page-specific |
|
112 |
+ protected function runCommands() |
|
113 |
+ { |
|
114 |
+ return false; |
|
115 |
+ } |
|
116 |
+ |
|
117 |
+ |
|
118 |
+ final private function validatePlayer() |
|
119 |
+ { |
|
120 |
+ if($this->disablePlayer) |
|
121 |
+ return; |
|
122 |
+ if($this->requireAuthentication && !$this->player->getPlayerId()) |
|
123 |
+ { |
|
124 |
+ Daemon_MsgQueue::add('Strona dostępna tylko dla zalogowanych użytkowników.'); |
|
125 |
+ Daemon::redirect($this->cfg->getUrl(null)); |
|
126 |
+ exit; |
|
127 |
+ } |
|
128 |
+ if($this->requireActiveChar && !$this->player->getCharacterId()) |
|
129 |
+ { |
|
130 |
+ Daemon_MsgQueue::add('Musisz najpierw wybrać aktywną postać.'); |
|
131 |
+ Daemon::redirect($this->cfg->getUrl('account')); |
|
132 |
+ exit; |
|
133 |
+ } |
|
134 |
+ if($this->requiredRole && !$this->player->hasRole($this->requiredRole)) |
|
135 |
+ { |
|
136 |
+ Daemon_MsgQueue::add('Nie masz uprawnień do korzystania z tej funkcji.'); |
|
137 |
+ Daemon::redirect($this->cfg->getUrl('account')); |
|
138 |
+ exit; |
|
139 |
+ } |
|
140 |
+ if($this->requireLocation && !$this->location->location_id) |
|
141 |
+ { |
|
142 |
+ Daemon::redirect($this->cfg->getUrl('respawn')); |
|
143 |
+ exit; |
|
144 |
+ } |
|
145 |
+ if($this->requireNoEvents && $this->characterData->getLocationEvent()) |
|
146 |
+ { |
|
147 |
+ Daemon::redirect($this->cfg->getUrl('map')); |
|
148 |
+ exit; |
|
149 |
+ } |
|
150 |
+ if($this->requireFactionMatch && $this->location->faction_id && $this->characterData->faction_id |
|
151 |
+ && ($this->location->faction_id != $this->characterData->faction_id)) |
|
152 |
+ { |
|
153 |
+ Daemon_MsgQueue::add('Odejdź! Nie przyjmujemy takich jak ty!'); |
|
154 |
+ Daemon::redirect($this->cfg->getUrl('map')); |
|
155 |
+ exit; |
|
156 |
+ } |
|
157 |
+ } |
|
158 |
+} |
... | ... |
@@ -0,0 +1,148 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+//abstraction for standard db queries |
|
4 |
+class Daemon_DbClient |
|
5 |
+{ |
|
6 |
+ protected $dbh; |
|
7 |
+ |
|
8 |
+ |
|
9 |
+ public function __construct(PDO $dbHandle) |
|
10 |
+ { |
|
11 |
+ $this->dbh = $dbHandle; |
|
12 |
+ } |
|
13 |
+ |
|
14 |
+ |
|
15 |
+ //reads maximum length of columns of selected table |
|
16 |
+ public function getColumnMaxLength($table, $column) |
|
17 |
+ { |
|
18 |
+ $sql = 'SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS |
|
19 |
+ WHERE TABLE_SCHEMA = SCHEMA() AND TABLE_NAME = :table AND COLUMN_NAME = :column'; |
|
20 |
+ $params = array('table' => $table, 'column' => $column); |
|
21 |
+ return $this->selectColumn($sql, $params); |
|
22 |
+ } |
|
23 |
+ |
|
24 |
+ |
|
25 |
+ //returns internal PDO handle |
|
26 |
+ public function getDbHandle() |
|
27 |
+ { |
|
28 |
+ return $this->dbh; |
|
29 |
+ } |
|
30 |
+ |
|
31 |
+ |
|
32 |
+ //internal exception handler |
|
33 |
+ private function exceptionHandler(PDOException $e, $duplicateMsg = null) |
|
34 |
+ { |
|
35 |
+ //prepare params |
|
36 |
+ $sqlstate = $e->getCode(); |
|
37 |
+ $dbMessage = $e->getMessage(); |
|
38 |
+ //check error type |
|
39 |
+ if('23000' == $sqlstate && false !== stripos($dbMessage, 'duplicate')) |
|
40 |
+ { |
|
41 |
+ $message = $duplicateMsg ? $duplicateMsg : 'Wybrany obiekt już istnieje.'; |
|
42 |
+ Daemon_MsgQueue::add($message); |
|
43 |
+ } |
|
44 |
+ else throw $e; |
|
45 |
+ } |
|
46 |
+ |
|
47 |
+ |
|
48 |
+ //executes a query, returns the statement resource |
|
49 |
+ public function execute($sql, array $params = array(), $duplicateMsg = null) |
|
50 |
+ { |
|
51 |
+ try |
|
52 |
+ { |
|
53 |
+ $sth = $this->dbh->prepare($sql); |
|
54 |
+ foreach((array)$params as $name => $value) |
|
55 |
+ $sth->bindValue(':'.$name, $value, self::paramType($value)); |
|
56 |
+ $sth->execute(); |
|
57 |
+ return $sth; |
|
58 |
+ } |
|
59 |
+ catch(PDOException $e) |
|
60 |
+ { |
|
61 |
+ $this->exceptionHandler($e, $duplicateMsg); |
|
62 |
+ return null; |
|
63 |
+ } |
|
64 |
+ } |
|
65 |
+ |
|
66 |
+ |
|
67 |
+ //returns ID of last inserted row |
|
68 |
+ public function lastInsertId() |
|
69 |
+ { |
|
70 |
+ return $this->dbh->lastInsertId(); |
|
71 |
+ } |
|
72 |
+ |
|
73 |
+ |
|
74 |
+ //returns appriopriate PDO::PARAM_X constant |
|
75 |
+ public static function paramType($value) |
|
76 |
+ { |
|
77 |
+ if(is_null($value)) |
|
78 |
+ return PDO::PARAM_NULL; |
|
79 |
+ elseif(is_int($value)) |
|
80 |
+ return PDO::PARAM_INT; |
|
81 |
+ else return PDO::PARAM_STR; |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ |
|
85 |
+ //executes a generic non-select query |
|
86 |
+ public function query($sql, array $params = array(), $duplicateMsg = null) |
|
87 |
+ { |
|
88 |
+ $sth = $this->execute($sql, $params, $duplicateMsg); |
|
89 |
+ return !is_null($sth); |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ |
|
93 |
+ //quotes value for safe use in query |
|
94 |
+ public function quote($value) |
|
95 |
+ { |
|
96 |
+ return $this->dbh->quote($value, self::paramType($value)); |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ |
|
100 |
+ //select multiple rows from table |
|
101 |
+ public function selectAll($sql, array $params = array()) |
|
102 |
+ { |
|
103 |
+ $sth = $this->execute($sql, $params); |
|
104 |
+ if(is_null($sth)) |
|
105 |
+ return null; |
|
106 |
+ return $sth->fetchAll(PDO::FETCH_ASSOC); |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ |
|
110 |
+ //select single column from table |
|
111 |
+ public function selectColumn($sql, array $params = array()) |
|
112 |
+ { |
|
113 |
+ $sth = $this->execute($sql, $params); |
|
114 |
+ if(is_null($sth)) |
|
115 |
+ return null; |
|
116 |
+ return $sth->fetchAll(PDO::FETCH_COLUMN, 0); |
|
117 |
+ } |
|
118 |
+ |
|
119 |
+ |
|
120 |
+ //select single row from table (as array) |
|
121 |
+ public function selectRow($sql, array $params = array()) |
|
122 |
+ { |
|
123 |
+ $sth = $this->execute($sql, $params); |
|
124 |
+ if(is_null($sth)) |
|
125 |
+ return null; |
|
126 |
+ return $sth->fetch(PDO::FETCH_ASSOC); |
|
127 |
+ } |
|
128 |
+ |
|
129 |
+ |
|
130 |
+ //select single row from table (as object) |
|
131 |
+ public function selectObject($sql, array $params = array(), $className = 'stdClass') |
|
132 |
+ { |
|
133 |
+ $sth = $this->execute($sql, $params); |
|
134 |
+ if(is_null($sth)) |
|
135 |
+ return null; |
|
136 |
+ return $sth->fetchObject($className); |
|
137 |
+ } |
|
138 |
+ |
|
139 |
+ |
|
140 |
+ //select single value from table |
|
141 |
+ public function selectValue($sql, array $params = array()) |
|
142 |
+ { |
|
143 |
+ $sth = $this->execute($sql, $params); |
|
144 |
+ if(is_null($sth)) |
|
145 |
+ return null; |
|
146 |
+ return $sth->fetchColumn(0); |
|
147 |
+ } |
|
148 |
+} |
... | ... |
@@ -0,0 +1,87 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+//manager for game world variables |
|
4 |
+class Daemon_DbConfig |
|
5 |
+{ |
|
6 |
+ private $dbClient; |
|
7 |
+ private $data; |
|
8 |
+ |
|
9 |
+ |
|
10 |
+ public function __construct(Daemon_DbClient $dbClient) |
|
11 |
+ { |
|
12 |
+ $this->dbClient = $dbClient; |
|
13 |
+ } |
|
14 |
+ |
|
15 |
+ |
|
16 |
+ public function __get($name) |
|
17 |
+ { |
|
18 |
+ return $this->get($name); |
|
19 |
+ } |
|
20 |
+ |
|
21 |
+ |
|
22 |
+ public function __set($name, $value) |
|
23 |
+ { |
|
24 |
+ return $this->set($name, $value); |
|
25 |
+ } |
|
26 |
+ |
|
27 |
+ |
|
28 |
+ public function get($name) |
|
29 |
+ { |
|
30 |
+ if(!isset($this->data[$name])) |
|
31 |
+ { |
|
32 |
+ $sql = "SELECT value FROM parameters WHERE name=:name"; |
|
33 |
+ $params = array('name' => $name); |
|
34 |
+ $this->data[$name] = $this->dbClient->selectValue($sql, $params); |
|
35 |
+ } |
|
36 |
+ return $this->data[$name]; |
|
37 |
+ } |
|
38 |
+ |
|
39 |
+ |
|
40 |
+ public function set($name, $value) |
|
41 |
+ { |
|
42 |
+ if (empty($value)) |
|
43 |
+ $value = ''; |
|
44 |
+ $sql = "INSERT INTO parameters(name, value) VALUES (:name, :value) ON DUPLICATE KEY UPDATE value=:value"; |
|
45 |
+ $params = array('name' => $name, 'value' => $value); |
|
46 |
+ $this->dbClient->query($sql, $params); |
|
47 |
+ $this->data[$name] = $value; |
|
48 |
+ } |
|
49 |
+ |
|
50 |
+ |
|
51 |
+ public function getGeneratorWeights($type) |
|
52 |
+ { |
|
53 |
+ $keys = array( |
|
54 |
+ 'pstr_p', 'pstr_c', 'patk_p', 'patk_c', 'pdef_p', 'pdef_c', 'pres_p', 'pres_c', |
|
55 |
+ 'mstr_p', 'mstr_c', 'matk_p', 'matk_c', 'mdef_p', 'mdef_c', 'mres_p', 'mres_c', |
|
56 |
+ 'armor', 'speed', 'regen', 'special_param'); |
|
57 |
+ return $this->getGeneratorOptions("w_$type", $keys); |
|
58 |
+ } |
|
59 |
+ |
|
60 |
+ |
|
61 |
+ private function getGeneratorOptions($key, array $keys) |
|
62 |
+ { |
|
63 |
+ $result = json_decode($this->get("generator_$key"), true); |
|
64 |
+ if (!is_array($result)) |
|
65 |
+ $result = array(); |
|
66 |
+ foreach ($keys as $key) |
|
67 |
+ { |
|
68 |
+ if (empty($result[$key])) |
|
69 |
+ $result[$key] = 0; |
|
70 |
+ } |
|
71 |
+ return $result; |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ |
|
75 |
+ public function setGeneratorWeights($type, array $options) |
|
76 |
+ { |
|
77 |
+ $this->setGeneratorOptions("w_$type", $options); |
|
78 |
+ } |
|
79 |
+ |
|
80 |
+ |
|
81 |
+ private function setGeneratorOptions($key, array $options) |
|
82 |
+ { |
|
83 |
+ foreach ($options as &$val) |
|
84 |
+ $val = floatval(str_replace(',', '.', $val)); |
|
85 |
+ $this->set("generator_$key", json_encode($options)); |
|
86 |
+ } |
|
87 |
+} |
... | ... |
@@ -0,0 +1,108 @@ |
1 |
+<?php |
|
2 |
+//@author Krzysztof Sikorski |
|
3 |
+//active record pattern |
|
4 |
+class Daemon_DbObject |
|
5 |
+{ |
|
6 |
+ protected $_dbClient; |
|
7 |
+ protected $_tableName; |
|
8 |
+ protected $_index = array();//names of index columns |
|
9 |
+ |
|
10 |
+ |
|
11 |
+ public function __construct() |
|
12 |
+ { |
|
13 |
+ if($params = func_get_args()) |
|
14 |
+ $this->import($params); |
|
15 |
+ } |
|
16 |
+ |
|
17 |
+ |
|
18 |
+ public function attachDbClient(Daemon_DbClient $dbClient = null) |
|
19 |
+ { |
|
20 |
+ $this->_dbClient = $dbClient; |
|
21 |
+ } |
|
22 |
+ |
|
23 |
+ |
|
24 |
+ //deletes object data from database |
|
25 |
+ public function delete() |
|
26 |
+ { |
|
27 |
+ $cond = array(); |
|
28 |
+ $params = array(); |
|
29 |
+ foreach($this->_index as $col) |
|
30 |
+ { |
|
31 |
+ $cond[] = "$col=:$col"; |
|
32 |
+ $params[$col] = $this->$col; |
|
33 |
+ } |
|
34 |
+ $cond = implode(' AND ', $cond); |
|
35 |
+ if(!$cond) |
|
36 |
+ throw new RuntimeException('Index not specified.'); |
|
37 |
+ $sql = "DELETE FROM $this->_tableName WHERE $cond"; |
|
38 |
+ $this->_dbClient->query($sql, $params); |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ |
|
42 |
+ //retrieves object data from database |
|
43 |
+ public function get(array $params, $ignoreDuplicates = false) |
|
44 |
+ { |
|
45 |
+ if(!$params) |
|
46 |
+ throw new RuntimeException('Params not specified.'); |
|
47 |
+ $cond = array(); |
|
48 |
+ foreach(array_keys($params) as $key) |
|
49 |
+ $cond[] = "$key=:$key"; |
|
50 |
+ $cond = implode(' AND ', $cond); |
|
51 |
+ $sql = "SELECT * FROM $this->_tableName WHERE $cond ORDER BY RAND() LIMIT 2"; |
|
52 |
+ $data = $this->_dbClient->selectAll($sql, $params); |
|
53 |
+ if(is_array($data) && isset($data[0])) |
|
54 |
+ { |
|
55 |
+ if(!$ignoreDuplicates && (count($data) > 1)) |
|
56 |
+ throw new RuntimeException('Multiple rows found.'); |
|
57 |
+ foreach($data[0] as $key => $val) |
|
58 |
+ $this->$key = $val; |
|
59 |
+ } |
|
60 |
+ return true; |
|
61 |
+ } |
|
62 |
+ |
|
63 |
+ |
|
64 |
+ //copies params into object data |
|
65 |
+ public function import($params) |
|
66 |
+ { |
|
67 |
+ $keys = array_keys(get_object_vars($this)); |
|
68 |
+ foreach($keys as $key) |
|
69 |
+ { |
|
70 |
+ if(isset($params[$key]) && ($key[0] != '_')) |
|
71 |
+ $this->$key = $params[$key]; |
|
72 |
+ } |
|
73 |
+ $this->validate(); |
|
74 |
+ } |
|
75 |
+ |
|
76 |
+ |
|
77 |
+ //stores object data in the database |
|
78 |
+ public function put() |
|
79 |
+ { |
|
80 |
+ $this->validate(); |
|