Save all changes

This commit is contained in:
Krzysztof Sikorski 2024-11-29 16:23:22 +01:00
commit 62acb50d14
Signed by: krzysztof-sikorski
GPG key ID: 4EB564BD08FE8476
609 changed files with 96974 additions and 0 deletions

18
.editorconfig Normal file
View file

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
max_line_length = 80
[*.php]
# settings required by PSR-12 standard
indent_size = 4
max_line_length = 120
[Makefile]
indent_style = tab

View file

@ -0,0 +1,6 @@
{
"Exclude": [
"^.git/",
"^.idea/"
]
}

6
2013/daemon/.bzrignore Normal file
View file

@ -0,0 +1,6 @@
./cfg/*
./log/*
./tmp/*
./lib/PHPTAL*
./public/google*
./public/scyzoryk/.htaccess

17
2013/daemon/.editorconfig Normal file
View file

@ -0,0 +1,17 @@
[*]
indent_style = tab
max_line_length = unset
[*.css]
max_line_length = 140
[*.js] # minified files
insert_final_newline = unset
[*.sql]
indent_style = space
[lib/jsmin.php] # vendored file
insert_final_newline = unset
indent_style = space
indent_size = 2

20
2013/daemon/LICENSE Normal file
View file

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Krzysztof Sikorski
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

68
2013/daemon/README.md Normal file
View file

@ -0,0 +1,68 @@
INTRODUCTION
===
The game was never planned to be released in public,
so it's designed for the LAMP stack it was originally hosted on.
Basically the code assumes some software versions and settings,
and will propably break on different configuration.
REQUIRED SOFTWARE
===
* *Apache 2.x*, with enabled mod_rewrite and .htaccess files.
* *PHP 5.2.x* with settings like in attached php.ini file.
* *MySQL 5.1*, may also work on other 5.x versions.
* *PHPTAL 1.2.1* - newer releases are propably also safe.
* The hosting **MUST** also offer crontab or other similar services.
INSTALLATION
===
1. Prepare database tables using attached SQL file.
2. Configure PHP using attached INI file.
3. Upload game code into server.
4. Download PHPTAL library and upload it into lib subdirectory,
you should now have `lib/PHPTAL.php`, `lib/PHPTAL/Context.php` etc.
5. Enter your hosting's configuration panel and set the `public` subdir
as a domain root, so other files won't be accessible from the Net.
6. Configure crontab: set the `cron.php` file to be executed every day.
7. And now the tricky part: create a config file (or files) and upload
it into `cfg` subdirectory. It's a long and weird topic, details below.
CONFIGURATION
===
The game configuration is handled by the `Daemon_Config` class
(`lib/daemon/config.php` file), which uses it to overwrite its public
properties. Consult the class' constructor for details.
The file's content is simple, it should do nothing more than to return
an associative array of settings. Here's an example of minimal config:
<?php
return array(
'applicationUrl' => 'http://example.com/foo/bar/baz/',
'applicationMail' => 'daemon@example.com',
'dbHost' => 'localhost',
'dbSchema' => 'daemon_db',
'dbUser' => 'username',
'dbPassword' => 'some_password',
);
The tricky part is the config's filename. As you can see in the class'
constructor, it _must_ be exactly the same as the domain on which
the game is hosted, plus the `.php` extension.
For example, if the game is hosted on `example.com`, then the filename
is `example.com.php`. This is designed to prevent accidental overwrites
with config for other machines...
There is also another tricky part: if you execute a script from the
command line instead of URL, then the script uses a special `_cron.php`
file instead of normal config. So you should create that file too.
Or change the `Daemon_Config`'s constructor ;)

182
2013/daemon/cron.php Normal file
View file

@ -0,0 +1,182 @@
<?php
//@author Krzysztof Sikorski
chdir(dirname(__FILE__));
$cfg = require_once './public/_init.php';
ini_set('expose_php', '0');
ini_set('default_mimetype', '');
ini_set('default_charset', '');
$dbClient = Daemon::createDbClient($cfg);
$dbCfg = new Daemon_DbConfig($dbClient);
$forum = new Daemon_Forum($dbClient);
//cleanup
$queries = array(
"CREATE TEMPORARY TABLE junk(id INT NOT NULL PRIMARY KEY)",
//delete inactive players
"INSERT INTO junk(id) SELECT player_id FROM players WHERE last_login < (NOW() - INTERVAL 1 MONTH)",
"DELETE FROM players WHERE player_id IN (SELECT id FROM junk)",
"DELETE FROM user_agents WHERE player_id IN (SELECT id FROM junk)",
"UPDATE characters SET player_id = NULL WHERE player_id IN (SELECT id FROM junk)",
"TRUNCATE TABLE junk",
//delete inactive characters
"INSERT INTO junk(id) SELECT character_id FROM characters WHERE player_id IS NULL OR last_action < (NOW() - INTERVAL 1 MONTH)",
"DELETE FROM characters WHERE character_id IN (SELECT id FROM junk)",
"DELETE FROM character_data WHERE character_id IN (SELECT id FROM junk)",
"DELETE FROM character_missions WHERE character_id IN (SELECT id FROM junk)",
"DELETE FROM character_regions WHERE character_id IN (SELECT id FROM junk)",
"DELETE FROM character_statistics WHERE character_id IN (SELECT id FROM junk)",
"DELETE FROM character_titles WHERE character_id IN (SELECT id FROM junk)",
"DELETE FROM combat_units WHERE combat_unit_id IN (SELECT CONCAT('character-', id) FROM junk)",
"DELETE FROM inventory WHERE character_id IN (SELECT id FROM junk)",
"UPDATE clans SET leader_id = NULL WHERE leader_id IN (SELECT id FROM junk)",
"DELETE FROM clan_invitations WHERE character_id IN (SELECT id FROM junk)",
"TRUNCATE TABLE junk",
//dump abandoned clans & old invitations
"DELETE FROM clans WHERE leader_id IS NULL",
"DELETE FROM clan_invitations WHERE date_added < (NOW() - INTERVAL 1 WEEK)",
);
foreach($queries as $sql)
$dbClient->query($sql, array());
unset($queries);
//check for endgame
if(!$dbCfg->rolloversEnabled)
return;
//create rollover entry
$sql = "INSERT INTO rollovers(date_added) VALUES (now())";
$dbClient->query($sql);
$rolloverId = $dbClient->lastInsertId();
//give turns
$sql = "UPDATE character_data SET turns = LEAST(:limit, turns + :delta)";
$params = array('delta' => (int) $dbCfg->turnDelta, 'limit' => (int) $dbCfg->turnLimit);
$dbClient->query($sql, $params);
//run caern sieges
$sql = "SELECT l.location_id FROM locations l JOIN character_data cd USING(location_id)
WHERE l.type='caern' AND cd.faction_id IS NOT NULL AND cd.faction_id != l.faction_id
GROUP BY l.location_id";
$locations = $dbClient->selectColumn($sql);
foreach ((array) $locations as $siegeLocationId)
{
$combat = new Daemon_CaernSiege();
$combat->attachDbClient($dbClient);
$combat->execute($siegeLocationId);
$sql = "INSERT INTO battles(rollover_id, location_id, type, combat_log)
VALUES (:rolloverId, :locationId, 'caern', :combatLog)";
$params = array('rolloverId' => $rolloverId, 'locationId' => $siegeLocationId,
'combatLog' => $combat->getCombatLog());
$dbClient->query($sql, $params);
$dbCfg->siegeLocationId = null;
$siegeLocationId = null;
unset($combat);
}
//update faction power
$decay = (float) $dbCfg->factionDecay;
$sql = "UPDATE factions f SET f.power = FLOOR(:decay * f.power) + COALESCE((
SELECT SUM(l.faction_value) FROM locations l WHERE l.type='caern' AND l.faction_id=f.faction_id
), 0)";
$dbClient->query($sql, array('decay' => $decay));
//activate bosses
$sql = "SELECT MAX(level) As max_level, MAX(rank_id) AS max_rank FROM character_data";
$row = $dbClient->selectRow($sql, array());
$unlockBosses = ($row['max_level'] >= $dbCfg->bossUnlockLevel) && ($row['max_rank'] >= $dbCfg->bossUnlockRank);
if($unlockBosses)
{
$dbClient->query("UPDATE locations SET boss_status='active' WHERE type='boss' AND boss_status != 'defeated'");
if(!$dbCfg->endgame)
$dbCfg->endgame = 1;
}
//run boss sieges
$sql = "SELECT location_id, name FROM locations WHERE type='boss' AND boss_status = 'active'";
$locations = $dbClient->selectAll($sql);
if($locations)
{
$factionPowers = array();
$sql = "SELECT faction_id, power FROM factions";
foreach($dbClient->selectAll($sql) as $row)
$factionPowers[$row['faction_id']] = $row['power'];
foreach($locations as $row)
{
$combat = new Daemon_BossCombat();
$combat->attachDbClient($dbClient);
$combat->execute($row['location_id'], $factionPowers);
$combatLog = $combat->getCombatLog();
if($combatLog)
{
$sql = "INSERT INTO battles(rollover_id, location_id, type, combat_log)
VALUES (:rolloverId, :locationId, 'boss', :combatLog)";
$params = array('rolloverId' => $rolloverId, 'locationId' => $row['location_id'], 'combatLog' => $combatLog);
$dbClient->query($sql, $params);
$forum->addChat(null, 'public', "Siedziba bossa \"$row[name]\" zaatakowana!");
}
}
}
//check for ending
$factions = array();
$sql = "SELECT faction_id, name FROM factions";
foreach($dbClient->selectAll($sql) as $row)
$factions[$row['faction_id']] = array('name' => $row['name'], 'active' => 0, 'defeated' => 0);
$sql = "SELECT faction_id, (boss_status!='defeated') AS active
FROM locations WHERE type = 'boss' GROUP BY faction_id, active";
foreach($dbClient->selectAll($sql) as $row)
{
if($row['active'])
$factions[$row['faction_id']]['active']++;
else
$factions[$row['faction_id']]['defeated']++;
}
$active = array();
$defeated = array();
foreach($factions as $factionId => $row)
{
if($row['active'] || !$row['defeated'])
$active[$factionId] = $row['name'];
else
$defeated[$factionId] = $row['name'];
}
$endgame = (count($active) < 2);
if($endgame)
{
//final messages
$active = implode(', ', $active);
switch($active)
{
case 'blue':
$msg = "Rewolucja została stłumiona. Niech żyje Porządek!";
break;
case 'red':
$msg = "Cesarz został obalony. Niech żyje Rewolucja!";
break;
default:
$msg = "Wojna dobiegła końca, lecz brak w niej zwycięzców. Czas pokaże, kto zajmie miejsce dawnych potęg...";
}
$forum->addChat(null, 'public', $msg);
//cleanup
$dbCfg->globalMessage = $msg;
$dbCfg->rolloversEnabled = 0;
$dbCfg->turnDelta = 0;
$dbCfg->defaultRespawn = '';
$sql = "UPDATE character_data SET turns = 0, location_id = NULL";
$dbClient->query($sql);
$sql = "TRUNCATE TABLE character_regions";
$dbClient->query($sql);
}
//update rollover data
$sql = "SELECT COUNT(1) FROM players";
$nPlayers = $dbClient->selectValue($sql);
$sql = "SELECT COUNT(1) FROM characters";
$nChars = $dbClient->selectValue($sql);
$sql = "SELECT COUNT(1) FROM clans";
$nClans = $dbClient->selectValue($sql);
$sql = "UPDATE rollovers SET players_total = :players, characters_total = :chars,
clans_total = :clans WHERE rollover_id = :id";
$params = array('id' => $rolloverId, 'players' => $nPlayers, 'chars' => $nChars, 'clans' => $nClans);
$dbClient->query($sql, $params);

525
2013/daemon/daemon.sql Normal file
View file

@ -0,0 +1,525 @@
CREATE TABLE battles (
battle_id int NOT NULL AUTO_INCREMENT,
rollover_id smallint NOT NULL,
location_id varchar(32) NOT NULL,
"type" enum('caern','boss') NOT NULL,
combat_log text NOT NULL,
PRIMARY KEY (battle_id),
KEY rollover_id (rollover_id,battle_id)
);
CREATE TABLE characters (
character_id int NOT NULL AUTO_INCREMENT,
player_id int DEFAULT NULL,
"name" varchar(255) NOT NULL,
gender enum('f','m','n') NOT NULL,
date_created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_action datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
show_player tinyint(1) NOT NULL DEFAULT 0,
last_mail_id int NOT NULL DEFAULT 0,
clan_id varchar(8) DEFAULT NULL,
avatar_url tinytext,
quote text,
description text,
PRIMARY KEY (character_id),
UNIQUE KEY "name" ("name"),
KEY clan (clan_id,character_id),
KEY date_created (date_created,character_id),
KEY player_id (player_id,character_id),
KEY last_action (last_action)
);
CREATE TABLE character_data (
character_id int NOT NULL,
location_id varchar(32) DEFAULT NULL COMMENT 'current location',
faction_id varchar(32) DEFAULT NULL,
faction_points smallint NOT NULL DEFAULT 0,
rank_id tinyint DEFAULT NULL,
turns smallint NOT NULL DEFAULT 30,
gold_purse int NOT NULL DEFAULT 0,
gold_bank int NOT NULL DEFAULT 0,
"level" smallint NOT NULL DEFAULT 0,
xp_free int NOT NULL DEFAULT 29,
xp_used int NOT NULL DEFAULT 0,
deaths int NOT NULL DEFAULT 0,
health smallint NOT NULL DEFAULT 0,
health_max smallint NOT NULL DEFAULT 70,
mana smallint NOT NULL DEFAULT 0,
mana_max smallint NOT NULL DEFAULT 35,
mana_regen smallint NOT NULL DEFAULT 1,
a_str smallint NOT NULL DEFAULT 7,
a_dex smallint NOT NULL DEFAULT 7,
a_vit smallint NOT NULL DEFAULT 7,
a_pwr smallint NOT NULL DEFAULT 7,
a_wil smallint NOT NULL DEFAULT 7,
s_pstr smallint NOT NULL DEFAULT 0,
s_patk smallint NOT NULL DEFAULT 0,
s_pdef smallint NOT NULL DEFAULT 0,
s_pres smallint NOT NULL DEFAULT 0,
s_preg smallint NOT NULL DEFAULT 0,
s_mstr smallint NOT NULL DEFAULT 0,
s_matk smallint NOT NULL DEFAULT 0,
s_mdef smallint NOT NULL DEFAULT 0,
s_mres smallint NOT NULL DEFAULT 0,
s_mreg smallint NOT NULL DEFAULT 0,
sp_scout tinyint NOT NULL DEFAULT 0,
sp_identify tinyint NOT NULL DEFAULT 0,
sp_vchar tinyint NOT NULL DEFAULT 0,
sp_vmonster tinyint NOT NULL DEFAULT 0,
sp_vitem tinyint NOT NULL DEFAULT 0,
combat_unit_id varchar(32) DEFAULT NULL,
location_event text COMMENT 'event parameters',
PRIMARY KEY (character_id),
KEY "level" ("level",character_id),
KEY xp_used (xp_used,character_id),
KEY faction_id (faction_id,character_id),
KEY location_id (location_id,character_id)
);
CREATE TABLE character_missions (
character_id int NOT NULL,
rollover_id int NOT NULL,
service_id varchar(32) NOT NULL,
"type" enum('monster','item') NOT NULL,
params varchar(32) NOT NULL,
progress enum('active','completed','rewarded') NOT NULL DEFAULT 'active',
PRIMARY KEY (character_id,rollover_id)
);
CREATE TABLE character_regions (
character_id int NOT NULL,
region_id varchar(32) NOT NULL,
PRIMARY KEY (character_id,region_id)
);
CREATE TABLE character_statistics (
character_id int NOT NULL,
missions smallint NOT NULL DEFAULT 0,
duel_wins smallint NOT NULL DEFAULT 0,
duel_losses smallint NOT NULL DEFAULT 0,
kills_mob1 smallint NOT NULL DEFAULT 0,
kills_mob2 smallint NOT NULL DEFAULT 0,
kills_mob3 smallint NOT NULL DEFAULT 0,
kills_mob4 smallint NOT NULL DEFAULT 0,
PRIMARY KEY (character_id),
KEY duel_wins (duel_wins,character_id),
KEY duel_losses (duel_losses,character_id)
);
CREATE TABLE character_titles (
character_id int NOT NULL,
title_id varchar(32) NOT NULL,
PRIMARY KEY (character_id,title_id)
);
CREATE TABLE chat (
message_id int NOT NULL AUTO_INCREMENT,
date_added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
channel_id varchar(255) NOT NULL,
sender_id int DEFAULT NULL,
content text NOT NULL,
PRIMARY KEY (message_id),
KEY sort (channel_id,message_id)
);
CREATE TABLE clans (
clan_id varchar(8) NOT NULL,
"name" varchar(255) NOT NULL,
date_created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
leader_id int DEFAULT NULL,
description text,
PRIMARY KEY (clan_id),
UNIQUE KEY "name" ("name")
);
CREATE TABLE clan_invitations (
clan_id varchar(32) NOT NULL,
character_id int NOT NULL,
date_added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
description text,
PRIMARY KEY (clan_id,character_id),
KEY search (character_id,clan_id)
);
CREATE TABLE combat_units (
combat_unit_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
faction_id varchar(32) DEFAULT NULL,
health int NOT NULL DEFAULT 70,
health_max int NOT NULL DEFAULT 70,
str1 smallint NOT NULL DEFAULT 7,
atk1 smallint NOT NULL DEFAULT 7,
type1 enum('p','m') DEFAULT 'p',
count1 smallint NOT NULL DEFAULT 1,
sp1_type varchar(32) DEFAULT NULL,
sp1_param smallint DEFAULT NULL,
str2 smallint NOT NULL DEFAULT 7,
atk2 smallint NOT NULL DEFAULT 7,
type2 enum('p','m') DEFAULT NULL,
count2 smallint NOT NULL DEFAULT 0,
sp2_type varchar(32) DEFAULT NULL,
sp2_param smallint DEFAULT NULL,
pdef smallint NOT NULL DEFAULT 7,
pres smallint NOT NULL DEFAULT 7,
mdef smallint NOT NULL DEFAULT 7,
mres smallint NOT NULL DEFAULT 7,
speed smallint NOT NULL DEFAULT 7,
armor smallint NOT NULL DEFAULT 0,
armor_sp_type varchar(32) DEFAULT NULL,
armor_sp_param smallint DEFAULT NULL,
regen float NOT NULL DEFAULT 0,
PRIMARY KEY (combat_unit_id)
);
CREATE TABLE duels (
duel_id int NOT NULL AUTO_INCREMENT,
date_added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
rollover_id smallint DEFAULT NULL,
attacker_id int NOT NULL,
defender_id int NOT NULL,
"type" enum('normal','arena') NOT NULL,
winner enum('a','b') DEFAULT NULL,
combat_log text,
PRIMARY KEY (duel_id)
);
CREATE TABLE "events" (
event_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
handle varchar(255) DEFAULT NULL,
description text,
PRIMARY KEY (event_id)
);
CREATE TABLE factions (
faction_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
power smallint NOT NULL DEFAULT 0,
PRIMARY KEY (faction_id)
);
CREATE TABLE faction_ranks (
faction_id varchar(32) NOT NULL,
rank_id tinyint NOT NULL,
min_points smallint NOT NULL DEFAULT 1,
title_id varchar(32) DEFAULT NULL,
PRIMARY KEY (faction_id,rank_id)
);
CREATE TABLE inventory (
inventory_id int NOT NULL AUTO_INCREMENT,
character_id int NOT NULL,
item_id varchar(32) NOT NULL,
"status" enum('inventory','storage') NOT NULL DEFAULT 'inventory',
flags set('bound','identified') NOT NULL,
equipped enum('hand_a','hand_b','armor','helmet','gloves','boots','pendant','accesory_a','accesory_b') DEFAULT NULL,
PRIMARY KEY (inventory_id),
KEY sort (character_id,"status")
);
CREATE TABLE items (
item_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
"type" enum('weapon1h','weapon2h','armor','helmet','gloves','boots','pendant','accesory','item') NOT NULL DEFAULT 'item',
"value" int NOT NULL DEFAULT 0,
suggested_value float NOT NULL DEFAULT 0,
damage_type enum('p','m') DEFAULT NULL,
special_type varchar(32) DEFAULT NULL,
special_param varchar(255) DEFAULT NULL,
pstr_p smallint NOT NULL DEFAULT 0,
pstr_c smallint NOT NULL DEFAULT 0,
patk_p smallint NOT NULL DEFAULT 0,
patk_c smallint NOT NULL DEFAULT 0,
pdef_p smallint NOT NULL DEFAULT 0,
pdef_c smallint NOT NULL DEFAULT 0,
pres_p smallint NOT NULL DEFAULT 0,
pres_c smallint NOT NULL DEFAULT 0,
mstr_p smallint NOT NULL DEFAULT 0,
mstr_c smallint NOT NULL DEFAULT 0,
matk_p smallint NOT NULL DEFAULT 0,
matk_c smallint NOT NULL DEFAULT 0,
mdef_p smallint NOT NULL DEFAULT 0,
mdef_c smallint NOT NULL DEFAULT 0,
mres_p smallint NOT NULL DEFAULT 0,
mres_c smallint NOT NULL DEFAULT 0,
armor smallint NOT NULL DEFAULT 0,
speed smallint NOT NULL DEFAULT 0,
regen smallint NOT NULL DEFAULT 0,
description text,
PRIMARY KEY (item_id)
);
CREATE TABLE item_specials (
special_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
handle varchar(255) DEFAULT NULL,
description text,
PRIMARY KEY (special_id)
);
CREATE TABLE item_templates (
id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
pstr_p_p smallint NOT NULL DEFAULT 1,
pstr_p_m smallint NOT NULL DEFAULT 1,
pstr_c_p smallint NOT NULL DEFAULT 1,
pstr_c_m smallint NOT NULL DEFAULT 1,
patk_p_p smallint NOT NULL DEFAULT 1,
patk_p_m smallint NOT NULL DEFAULT 1,
patk_c_p smallint NOT NULL DEFAULT 1,
patk_c_m smallint NOT NULL DEFAULT 1,
pdef_p_p smallint NOT NULL DEFAULT 1,
pdef_p_m smallint NOT NULL DEFAULT 1,
pdef_c_p smallint NOT NULL DEFAULT 1,
pdef_c_m smallint NOT NULL DEFAULT 1,
pres_p_p smallint NOT NULL DEFAULT 1,
pres_p_m smallint NOT NULL DEFAULT 1,
pres_c_p smallint NOT NULL DEFAULT 1,
pres_c_m smallint NOT NULL DEFAULT 1,
mstr_p_p smallint NOT NULL DEFAULT 1,
mstr_p_m smallint NOT NULL DEFAULT 1,
mstr_c_p smallint NOT NULL DEFAULT 1,
mstr_c_m smallint NOT NULL DEFAULT 1,
matk_p_p smallint NOT NULL DEFAULT 1,
matk_p_m smallint NOT NULL DEFAULT 1,
matk_c_p smallint NOT NULL DEFAULT 1,
matk_c_m smallint NOT NULL DEFAULT 1,
mdef_p_p smallint NOT NULL DEFAULT 1,
mdef_p_m smallint NOT NULL DEFAULT 1,
mdef_c_p smallint NOT NULL DEFAULT 1,
mdef_c_m smallint NOT NULL DEFAULT 1,
mres_p_p smallint NOT NULL DEFAULT 1,
mres_p_m smallint NOT NULL DEFAULT 1,
mres_c_p smallint NOT NULL DEFAULT 1,
mres_c_m smallint NOT NULL DEFAULT 1,
armor_p smallint NOT NULL DEFAULT 1,
armor_m smallint NOT NULL DEFAULT 1,
speed_p smallint NOT NULL DEFAULT 1,
speed_m smallint NOT NULL DEFAULT 1,
regen_p smallint NOT NULL DEFAULT 1,
regen_m smallint NOT NULL DEFAULT 1,
PRIMARY KEY (id)
);
CREATE TABLE locations (
location_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
"type" enum('normal','arena','caern','boss') NOT NULL DEFAULT 'normal',
chance1 smallint NOT NULL DEFAULT 1,
chance2 smallint NOT NULL DEFAULT 1,
region_id varchar(32) DEFAULT NULL,
faction_id varchar(32) DEFAULT NULL,
faction_value tinyint NOT NULL DEFAULT 1,
description text,
picture_url tinytext,
boss_id varchar(32) DEFAULT NULL,
boss_status enum('hidden','active','defeated') DEFAULT NULL,
PRIMARY KEY (location_id),
KEY region_id (region_id,location_id),
KEY faction_id (faction_id,location_id),
KEY "type" ("type",location_id)
);
CREATE TABLE location_events (
location_id varchar(32) NOT NULL,
event_id varchar(32) NOT NULL,
chance smallint NOT NULL DEFAULT 1,
params varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (location_id,event_id),
KEY event_id (event_id,location_id)
);
CREATE TABLE location_monsters (
location_id varchar(32) NOT NULL,
monster_id varchar(32) NOT NULL,
chance smallint NOT NULL DEFAULT 1,
PRIMARY KEY (location_id,monster_id),
KEY monster_id (monster_id,location_id)
);
CREATE TABLE location_paths (
location_id varchar(32) NOT NULL,
destination_id varchar(32) NOT NULL,
"name" varchar(255) DEFAULT NULL,
cost_gold int NOT NULL DEFAULT 0,
cost_mana smallint NOT NULL DEFAULT 0,
PRIMARY KEY (location_id,destination_id),
KEY destination_id (destination_id,location_id)
);
CREATE TABLE location_services (
location_id varchar(32) NOT NULL,
service_id varchar(32) NOT NULL,
PRIMARY KEY (location_id,service_id)
);
CREATE TABLE mail (
message_id int NOT NULL AUTO_INCREMENT,
date_added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
sender_id int DEFAULT NULL,
recipient_id int NOT NULL,
content text NOT NULL,
PRIMARY KEY (message_id),
KEY sort (recipient_id,message_id)
);
CREATE TABLE maps (
map_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
url varchar(255) NOT NULL,
sort smallint NOT NULL DEFAULT 0,
PRIMARY KEY (map_id)
);
CREATE TABLE monsters (
monster_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
class smallint NOT NULL DEFAULT 1,
"level" smallint NOT NULL DEFAULT 1,
gold int NOT NULL DEFAULT 0,
chance1 smallint NOT NULL DEFAULT 1,
chance2 smallint NOT NULL DEFAULT 1,
title_id varchar(255) DEFAULT NULL,
combat_unit_id varchar(32) DEFAULT NULL,
PRIMARY KEY (monster_id)
);
CREATE TABLE monster_drops (
monster_id varchar(32) NOT NULL,
item_id varchar(32) NOT NULL,
chance smallint NOT NULL DEFAULT 1,
PRIMARY KEY (monster_id,item_id)
);
CREATE TABLE newsfeed (
id varchar(255) NOT NULL,
updated timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
published timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
title varchar(255) DEFAULT NULL,
author varchar(255) DEFAULT NULL,
content text,
PRIMARY KEY (id),
KEY published (published)
);
CREATE TABLE parameters (
"name" varchar(255) NOT NULL,
"value" text NOT NULL,
PRIMARY KEY ("name")
);
CREATE TABLE players (
player_id int NOT NULL AUTO_INCREMENT,
"name" varchar(255) DEFAULT NULL,
login varchar(255) NOT NULL,
"password" varchar(255) NOT NULL,
date_created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_login datetime DEFAULT NULL,
roles set('chat','login') NOT NULL DEFAULT 'chat,login',
skin varchar(255) DEFAULT NULL,
email varchar(255) DEFAULT NULL,
reset_key varchar(255) DEFAULT NULL,
reset_until date DEFAULT NULL,
reset_password varchar(255) DEFAULT NULL,
PRIMARY KEY (player_id),
UNIQUE KEY login (login),
KEY reset_key (reset_key)
);
CREATE TABLE regions (
region_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
respawn_id varchar(32) DEFAULT NULL,
picture_url tinytext,
PRIMARY KEY (region_id)
);
CREATE TABLE rollovers (
rollover_id smallint NOT NULL AUTO_INCREMENT,
date_added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
players_total smallint NOT NULL DEFAULT 0,
characters_total smallint NOT NULL DEFAULT 0,
clans_total smallint DEFAULT NULL,
PRIMARY KEY (rollover_id)
);
CREATE TABLE services (
service_id varchar(32) NOT NULL,
"type" enum('bank','healer','npc','shop','temple') NOT NULL,
"name" varchar(255) NOT NULL,
faction_id varchar(32) DEFAULT NULL,
rank_id tinyint DEFAULT NULL,
description text,
PRIMARY KEY (service_id)
);
CREATE TABLE service_items (
service_id varchar(32) NOT NULL,
item_id varchar(32) NOT NULL,
"type" enum('normal','drop') NOT NULL DEFAULT 'normal',
quantity smallint DEFAULT NULL,
PRIMARY KEY (service_id,item_id),
KEY item_id (item_id,service_id)
);
CREATE TABLE spells (
spell_id varchar(32) NOT NULL,
"name" varchar(255) NOT NULL,
max_level tinyint NOT NULL DEFAULT 7,
max_cost smallint NOT NULL DEFAULT 10,
min_cost smallint NOT NULL DEFAULT 1,
handle varchar(255) DEFAULT NULL,
PRIMARY KEY (spell_id)
);
INSERT INTO spells (spell_id, `name`, max_level, max_cost, min_cost, handle) VALUES
('identify', 'Identyfikacja', 6, 45, 25, 'Identify'),
('scout', 'Badanie Terenu', 4, 35, 20, 'Scout'),
('vchar', 'Poznanie Postaci', 7, 60, 30, 'ScanCharacter'),
('vitem', 'Poznanie Przedmiotu', 6, 40, 20, 'ScanItem'),
('vmonster', 'Poznanie Potwora', 5, 40, 20, 'ScanMonster');
CREATE TABLE titles (
title_id varchar(32) NOT NULL,
name_f varchar(255) NOT NULL DEFAULT '',
name_m varchar(255) NOT NULL DEFAULT '',
name_n varchar(255) NOT NULL DEFAULT '',
"type" enum('normal','special') NOT NULL DEFAULT 'normal',
PRIMARY KEY (title_id),
KEY "type" ("type",title_id)
);

102
2013/daemon/lib/daemon.php Normal file
View file

@ -0,0 +1,102 @@
<?php
//@author Krzysztof Sikorski
//container for miscelaneous functions
class Daemon
{
//class autoloader
public static function autoload($className)
{
$className = mb_strtolower(str_replace('_', DIRECTORY_SEPARATOR, $className));
spl_autoload($className, '.php');
}
//creates Daemon_DbClient object using specified config
public static function createDbClient(Daemon_Config $cfg)
{
$dsn = sprintf('mysql:host=%s;dbname=%s', $cfg->dbHost, $cfg->dbSchema);
$params = array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8', time_zone = '+1:00'");
$dbh = new PDO($dsn, $cfg->dbUser, $cfg->dbPassword, $params);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return new Daemon_DbClient($dbh);
}
//prepares multiline text for displaying, also inserts some basic tags
public static function formatMessage($txt, $markup = false)
{
$txt = nl2br(htmlspecialchars($txt, ENT_QUOTES));
if($markup)
{
$txt = preg_replace('@\[img\](.+)\[/img\]@uU', '<img src="$1" alt="$1" class="bbcode"/>', $txt);
$txt = preg_replace('@\[url=(.+)\](.+)\[/url\]@uU', '<a href="$1" rel="nofollow">$2</a>', $txt);
$txt = preg_replace('@\[url\]([img]){0}(.+)\[/url\]@uU', '<a href="$1" rel="nofollow">$1</a>', $txt);
$txt = preg_replace('@(^|>|\s)(https://\S+)($|<|\s)@muU', '$1<a href="$2" rel="nofollow">$2</a>$3', $txt);
$txt = preg_replace('@(^|>|\s)(http://\S+)($|<|\s)@muU', '$1<a href="$2" rel="nofollow">$2</a>$3', $txt);
$txt = preg_replace('@\[b\](.+)\[/b\]@uU', '<b>$1</b>', $txt);
$txt = preg_replace('@\[i\](.+)\[/i\]@uU', '<i>$1</i>', $txt);
$txt = preg_replace('@\[u\](.+)\[/u\]@uU', '<u>$1</u>', $txt);
$txt = preg_replace('@\[s\](.+)\[/s\]@uU', '<s>$1</s>', $txt);
$txt = preg_replace('@\[sub\](.+)\[/sub\]@uU', '<sub>$1</sub>', $txt);
$txt = preg_replace('@\[sup\](.+)\[/sup\]@uU', '<sup>$1</sup>', $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('<!DOCTYPE html><html lang="en"><title>Redirect</title><p><a href="%s">continue</a></p>', $url);
}
}

View file

@ -0,0 +1,100 @@
<?php
//@author Krzysztof Sikorski
class Daemon_BossCombat extends Daemon_CaernSiege
{
public function execute($locationId, array $factionPowers = array())
{
//read location
$location = $this->getLocation($locationId);
$bossFactionId = $location['faction_id'];
$powerMod = Daemon_Math::factionPowerMult($bossFactionId, $factionPowers);
//prepare units
$this->kickNeutrals($locationId);
$units = $this->getCharacterUnits($locationId);
//check for empty caern
if(empty($units))
return null;
$attackers = false;
foreach($units as $factionId => $faction)
{
if($factionId != $bossFactionId)
$attackers = true;
}
if(!$attackers)
return null;
unset($attackers, $factionId, $faction);
//prepare boss
$boss = $this->getBossUnit($location['boss_id'], $bossFactionId, $powerMod);
if(empty($boss))
return null;
$units[$bossFactionId][$boss->_id] = $boss;
//add monster support
$supportCount = 0;
foreach($units as $factionId => $faction)
{
if($factionId != $bossFactionId)
$supportCount += count($faction);
else
$supportCount -= count($faction);
}
if($supportCount > 0)
{
$support = $this->getLocationMonsters($locationId, $bossFactionId, $supportCount);
foreach($support as $unit)
$units[$bossFactionId][$unit->_id] = $unit;
}
unset($support, $supportCount);
//execute combat
$this->combatLog = $this->runCombat($units, $bossFactionId);
//save characters
$this->putCharacterUnits($units);
//find winner
$winnerId = $this->getWinnerFaction($units);
//kick losers
$this->kickEnemies($locationId, $winnerId);
//update location, send message
if($winnerId != $bossFactionId)
{
$sql = "UPDATE locations SET boss_status='defeated' WHERE location_id=:id";
$params = array('id' => $locationId);
$this->dbClient->query($sql, $params);
$msg = "Boss $boss->name z lokacji $location[name] został pokonany!";
$forum = new Daemon_Forum($this->dbClient);
$forum->addChat(null, 'public', $msg);
}
}
private function getBossUnit($monsterId, $factionId, $powerMod)
{
//read base stats
$sql = "SELECT monster_id, name, combat_unit_id
FROM monsters WHERE monster_id=:id";
$row = $this->dbClient->selectRow($sql, array('id' => $monsterId));
$unit = new Daemon_Combat_Unit();
$unit->attachDbClient($this->dbClient);
$unit->get(array('combat_unit_id' => $row['combat_unit_id']));
$unit->name = $row['name'];
$unit->faction_id = $factionId;
$unit->_id = 'boss';
//modify stats
$modified = array(
'str1', 'atk1', 'str2', 'atk2',
'pdef', 'pres', 'mdef', 'mres',
'speed', 'armor', 'regen', 'healthMax',
);
foreach($modified as $name)
$unit->$name *= $powerMod;
$unit->health = $unit->healthMax;
return $unit;
}
private function getLocation($locationId)
{
$sql = "SELECT l.faction_id, l.boss_id, l.name, f.name AS faction_name
FROM locations l JOIN factions f USING(faction_id)
WHERE location_id = :id";
return $this->dbClient->selectRow($sql, array('id' => $locationId));
}
}

View file

@ -0,0 +1,270 @@
<?php
//@author Krzysztof Sikorski
class Daemon_CaernSiege
{
protected $dbClient = null;
protected $combatLog;
protected function addCharacters(Daemon_Combat $combat, array $units, $locationFactionId)
{
foreach($units as $factionId => $faction)
{
foreach($faction as $unit)
{
$attacker = ($factionId != $locationFactionId);
$combat->addUnit($unit->_id, $unit, $attacker);
}
}
}
public function attachDbClient(Daemon_DbClient $dbClient)
{
$this->dbClient = $dbClient;
}
//executes the siege, generates the report
public function execute($locationId)
{
//prepare units
$this->kickNeutrals($locationId);
$faction = $this->getLocationFaction($locationId);
$caernFactionId = $faction['id'];
$units = $this->getCharacterUnits($locationId);
//check for empty caern
if(empty($units))
{
$this->combatLog = '<p>Caern utracony z braku obrońców.</p>';
$winnerId = null;
}
else
{
//add monster support
$supportCount = 0;
foreach($units as $factionId => $faction)
{
if($factionId != $caernFactionId)
$supportCount += count($faction);
else
$supportCount -= count($faction);
}
if($supportCount > 0)
{
$support = $this->getLocationMonsters($locationId, $caernFactionId, $supportCount);
foreach($support as $unit)
$units[$caernFactionId][$unit->_id] = $unit;
}
unset($support, $supportCount);
//execute combat
$this->combatLog = $this->runCombat($units, $caernFactionId);
//save characters
$this->putCharacterUnits($units);
//find winner (units modified by combat)
$winnerId = $this->getWinnerFaction($units);
if(!$winnerId)
$msg = 'utracony';
elseif($winnerId != $caernFactionId)
$msg = 'przejęty';
else
$msg = 'utrzymany';
$this->combatLog .= "<p><b>Caern $msg!</b></p>";
}
//kick losers
$this->kickEnemies($locationId, $winnerId);
//update location
$sql = "UPDATE locations SET faction_id=:fid WHERE location_id=:id";
$params = array('fid' => $winnerId, 'id' => $locationId);
$this->dbClient->query($sql, $params);
}
protected function getCharacterUnits($locationId)
{
$units = array();
$sql = "SELECT cd.character_id, c.name, cd.faction_id, cd.health, cd.health_max, cd.combat_unit_id
FROM character_data cd JOIN characters c USING(character_id)
WHERE location_id=:id AND cd.faction_id IS NOT NULL ORDER BY xp_used DESC";
$data = $this->dbClient->selectAll($sql, array('id' => $locationId));
foreach($data as $row)
{
$unit = new Daemon_Combat_Unit();
$unit->attachDbClient($this->dbClient);
$unit->get(array('combat_unit_id' => $row['combat_unit_id']));
$unit->name = $row['name'];
$unit->faction_id = $row['faction_id'];
$unit->health = $row['health_max'];
$unit->health_max = $row['health_max'];
$unit->_id = $row['character_id'];
$units[$row['faction_id']][$unit->_id] = $unit;
}
return $units;
}
public function getCombatLog()
{
return $this->combatLog;
}
protected function getLocationFaction($locationId)
{
$sql = "SELECT faction_id AS id, f.name
FROM locations l JOIN factions f USING(faction_id)
WHERE location_id=:id";
return $this->dbClient->selectRow($sql, array('id' => $locationId));
}
protected function getLocationMonsters($locationId, $factionId, $desiredCount)
{
$result = array();
$sql = "SELECT m.monster_id, m.name, m.combat_unit_id
FROM location_monsters lm JOIN monsters m USING(monster_id)
WHERE location_id=:id";
$mobs = $this->dbClient->selectAll($sql, array('id' => $locationId));
if(empty($mobs))
return array();
for($i = 0; $i < $desiredCount; ++$i)
{
$row = $mobs[array_rand($mobs)];
$unit = new Daemon_Combat_Unit();
$unit->attachDbClient($this->dbClient);
$unit->get(array('combat_unit_id' => $row['combat_unit_id']));
$unit->name = $row['name'];
$unit->faction_id = $factionId;
$unit->_id = "mob_$i";
$result[$unit->_id] = $unit;
}
return $result;
}
protected function getWinnerFaction(array $units)
{
$survivors = array();
foreach($units as $factionId => $faction)
{
$survivors[$factionId] = 0;
foreach($faction as $unit)
{
if($unit->health > 0)
$survivors[$unit->faction_id] += 1;
}
}
$winnerId = null;
$winnerCount = 0;
foreach($survivors as $factionId => $count)
{
if($winnerCount < $count)
{
$winnerId = $factionId;
$winnerCount = $count;
}
}
return $winnerId;
}
//kicks enemies out of the caern
public function kickEnemies($locationId, $factionId)
{
$sql = "UPDATE character_data SET location_id=NULL WHERE location_id=:locationId";
$params = array('locationId' => $locationId);
if($factionId)
{
$sql .= " AND faction_id!=:factionId";
$params['factionId'] = $factionId;
}
$this->dbClient->query($sql, $params);
}
//kicks neutral characters out of the caern
protected function kickNeutrals($locationId)
{
$sql = "UPDATE character_data SET location_id=NULL WHERE location_id=:id AND faction_id IS NULL";
$this->dbClient->query($sql, array('id' => $locationId));
}
protected function putCharacterUnits(array $units)
{
$sqlLive = "UPDATE character_data SET health=:hp WHERE character_id=:id";
$sqlDie = "UPDATE character_data SET health=0, location_id=null WHERE character_id=:id";
foreach($units as $faction)
{
foreach($faction as $unit)
{
if(!is_numeric($unit->_id))
continue;//monsters have string IDs
if($unit->health > 0)
{
$params = array('id' => $unit->_id, 'hp' => $unit->health);
$this->dbClient->query($sqlLive, $params);
}
else
{
$params = array('id' => $unit->_id);
$this->dbClient->query($sqlDie, $params);
}
}
}
}
public function runCombat(array $units, $locationFactionId)
{
$combat = new Daemon_Combat();
$logger = new Daemon_Combat_Log();
$combat->attachLogger($logger);
foreach($units as $factionId => $faction)
{
foreach($faction as $unit)
{
$attacker = ($factionId != $locationFactionId);
$combat->addUnit($unit->_id, $unit, $attacker);
}
}
$combat->execute();
foreach($combat->units as &$unit)
$unit->health = max(0, ceil($unit->health));
//prepare summary
$summary = array();
$summary[] = '<table class="border">';
$summary[] = '<caption>Podsumowanie bitwy</caption>';
$summary[] = '<tr>';
$summary[] = '<th rowspan="2">Strona</th><th colspan="3">Postać</th>';
$summary[] = '<th colspan="3">Wykonane ataki</th><th colspan="3">Otrzymane ataki</th>';
$summary[] = '</tr>';
$summary[] = '<tr>';
$summary[] = '<th>Imię</th><th>Frakcja</th><th>Zdrowie</th>';
$summary[] = '<th>Ataki</th><th>Obrażenia</th><th>Średnia</th>';
$summary[] = '<th>Ataki</th><th>Obrażenia</th><th>Średnia</th>';
$summary[] = '</tr>';
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[] = '<tr>';
$summary[] = sprintf('<td>%s</td><td>%s</td><td>%s</td><td>%.2f%%</td>',
$attackerTxt, $unit->name, $unit->faction_id, $healthPercent);
$summary[] = sprintf('<td>%d</td><td>%.3f</td><td>%.3f</td>',
$unit->_cntDealt, $unit->_dmgDealt, $avgDealt);
$summary[] = sprintf('<td>%d</td><td>%.3f</td><td>%.3f</td>',
$unit->_cntTaken, $unit->_dmgTaken, $avgTaken);
$summary[] = '</tr>';
}
}
$summary[] = '</table>';
return implode('', $summary) . (string) $logger;
}
}

View file

@ -0,0 +1,164 @@
<?php
//@author Krzysztof Sikorski
//combat engine
//usage:
//$combat = new Daemon_Combat();
//$logger = new Daemon_Combat_Log();
//$combat->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("<i>$unit->name</i> regeneruje pełnię zdrowia.<br>");
}
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();
}
}

View file

@ -0,0 +1,117 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Combat_Log
{
private $buffer;
public $groupCombat;
public function __construct()
{
$this->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('<i>%s</i> zadaje <b>%.2f</b> 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 <b>%.2f</b> HP', $vampRegen);
if($shockDmg)
$txt .= sprintf('. Otrzymuje <b>%.2f</b> obrażeń od porażenia', $shockDmg);
$txt .= '.<br>';
$this->add($txt);
}
public function txtAttackMiss($name, $magical)
{
if($magical)
$this->add('Cel odbił zaklęcie.<br>');
else $this->add(sprintf('<i>%s</i> chybił.<br>', self::escape($name)));
}
public function txtDeath($name, $flawlessName = null)
{
$txt = sprintf('<i>%s</i> umiera.<br>', $name);
if($flawlessName)
{
$atxt = array('<i>%s</i> ziewa.', '<i>%s</i> śmieje się szyderczo.');
$txt .= sprintf($atxt[array_rand($atxt)], self::escape($flawlessName)).'<br>';
}
$this->add($txt);
}
public function txtDemon($regen)
{
$this->add(sprintf('Demon w zbroi wchłonął zaklęcie. Cel regeneruje %.2f obrażeń.<br>', $regen));
}
public function txtPoison($name, $dmg)
{
$this->add(sprintf('<i>%s</i> otrzymuje %.2f obrażeń od trucizny.<br>', self::escape($name), $dmg));
}
public function txtRegen($name, $regen)
{
$this->add(sprintf('<i>%s</i> regeneruje %.2f obrażeń.<br>', self::escape($name), $regen));
}
public function txtRoundFooter()
{
$this->add('</p>');
}
public function txtRoundHeader($round)
{
$this->add(sprintf('<h3>Akcja %d</h3><p>', $round));
}
public function txtTargetHeader($actorName, $targetName)
{
if($targetName)
{
if ($this->groupCombat)
$this->add(sprintf('<i>%s</i> wybiera cel: <i>%s</i>.<br>',
self::escape($actorName), self::escape($targetName)));
}
else $this->add(sprintf('<i>%s</i> nie ma już przeciwników.<br>', self::escape($actorName)));
}
}

View file

@ -0,0 +1,261 @@
<?php
//@author Krzysztof Sikorski
//combat stats (daemon or monster)
class Daemon_Combat_Unit extends Daemon_DbObject_CombatUnit
{
private $_logger = null;
//combat variables
public $_id = null;
public $_flawless = true;
public $_ticks = 0;
public $_initiative = 0;
public $_poison = 0;
public $_attacker = null; //attacker or defender
public $_target = null; //current target
public $_threat = array(); //current threat
public $_allies = array(); //global sideA/sideB list
public $_enemies = array(); //global sideB/sideA list
public $_cntDealt = 0;
public $_dmgDealt = 0;
public $_cntTaken = 0;
public $_dmgTaken = 0;
public function __toString()
{
return "[id: $this->_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);
}
}
}

View file

@ -0,0 +1,56 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Config
{
public $applicationRoot;
public $applicationTitle = 'Daemon 2';
public $applicationUrl;
public $applicationMail;
public $dbHost;
public $dbSchema;
public $dbUser;
public $dbPassword;
public $dbPrefix;
public $minifyHtml = false;
public $sessionName = 'sessid';
public $tsDelta = 0.5;
public function __construct($applicationRoot = null)
{
$this->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;
}
}

View file

@ -0,0 +1,158 @@
<?php
//@author Krzysztof Sikorski
//prototype for page controller
class Daemon_Controller
{
//global objects
protected $cfg = null;
protected $dbClient = null;
protected $dbCfg = null;
protected $activeCharacter = null;
protected $characterData = null;
protected $location = null;
protected $player = null;
protected $view = null;
//execution parameters
protected $disableMessages = false;
protected $disablePlayer = false;
protected $requireActiveChar = false;
protected $requireAuthentication = false;
protected $requireFactionMatch = false;
protected $requireLocation = false;
protected $requireNoEvents = false;
protected $requiredRole = null;
//output parameters
protected $pageOutputMode = Daemon_View::MODE_HTML;
protected $pageSubtitle = null;
protected $pageSubtitleDetails = null;
protected $pageSubtitleUseQuery = false;
protected $pageTemplatePath;
final public function __construct(Daemon_Config $cfg)
{
$this->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;
}
}
}

View file

@ -0,0 +1,148 @@
<?php
//@author Krzysztof Sikorski
//abstraction for standard db queries
class Daemon_DbClient
{
protected $dbh;
public function __construct(PDO $dbHandle)
{
$this->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);
}
}

View file

@ -0,0 +1,87 @@
<?php
//@author Krzysztof Sikorski
//manager for game world variables
class Daemon_DbConfig
{
private $dbClient;
private $data;
public function __construct(Daemon_DbClient $dbClient)
{
$this->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));
}
}

View file

@ -0,0 +1,108 @@
<?php
//@author Krzysztof Sikorski
//active record pattern
class Daemon_DbObject
{
protected $_dbClient;
protected $_tableName;
protected $_index = array();//names of index columns
public function __construct()
{
if($params = func_get_args())
$this->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() {}
}

View file

@ -0,0 +1,165 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_Character extends Daemon_DbObject
{
protected $_tableName = 'characters';
protected $_index = array('character_id');
public $character_id;
public $player_id;
public $name;
public $gender;
public $date_created;
public $last_action;
public $show_player;
public $last_mail_id;
public $clan_id;
public $avatar_url;
public $quote;
public $description;
private $_player;
private $_characterData;
public function attachPlayer(Daemon_DbObject_Player $player)
{
$this->_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;
}
}

View file

@ -0,0 +1,609 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_CharacterData extends Daemon_DbObject
{
protected $_tableName = 'character_data';
protected $_index = array('character_id');
public $_characterName = null;
public $_gender = null;
public $character_id;
public $location_id;
public $faction_id, $faction_points, $rank_id;
public $turns;
public $gold_purse, $gold_bank;
public $level, $xp_free, $xp_used;
public $deaths, $health, $health_max;
public $mana, $mana_max, $mana_regen;
public $a_str, $a_dex, $a_vit, $a_pwr, $a_wil;
public $s_pstr, $s_patk, $s_pdef, $s_pres, $s_preg;
public $s_mstr, $s_matk, $s_mdef, $s_mres, $s_mreg;
public $sp_scout, $sp_identify, $sp_vchar, $sp_vmonster, $sp_vitem;
protected $location_event; //json packed
public $combat_unit_id;
private $_combatUnit;
//executes selected event, returns event log
public function attack(Daemon_View $view, $targetId, $locationType)
{
if(!$this->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);
}
}

View file

@ -0,0 +1,91 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_Clan extends Daemon_DbObject
{
protected $_tableName = 'clans';
protected $_index = array('clan_id');
public $clan_id;
public $name;
public $leader_id = null;
public $description = null;
//accept an invitation
public function acceptCharacter($characterId, Daemon_Forum $forum)
{
$sql = "SELECT 1 FROM clan_invitations WHERE clan_id=:clanId AND character_id=:charId";
$params = array('clanId' => $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.');
}
}

View file

@ -0,0 +1,63 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_CombatUnit extends Daemon_DbObject
{
protected $_tableName = 'combat_units';
protected $_index = array('combat_unit_id');
public $combat_unit_id;
public $name = null;
public $faction_id = null;
public $health = 70;
public $health_max = 70;
public $str1 = 7;
public $atk1 = 7;
public $type1 = 'p';
public $count1 = 1;
public $sp1_type = null;
public $sp1_param = null;
public $str2 = 7;
public $atk2 = 7;
public $type2 = null;
public $count2 = 0;
public $sp2_type = null;
public $sp2_param = null;
public $pdef = 7;
public $pres = 7;
public $mdef = 7;
public $mres = 7;
public $speed = 7;
public $armor = 0;
public $armor_sp_type = null;
public $armor_sp_param = null;
public $regen = 0;
//special property types
const SP_ANTIPOISON = 'antipoison';
const SP_ANTIVAMP = 'antivamp';
const SP_BLOODY = 'bloody';
const SP_DEMON = 'demon';
const SP_ETHER = 'ether';
const SP_FACTION = 'faction';
const SP_POISON = 'poison';
const SP_SHOCK = 'shock';
const SP_STUN = 'stun';
const SP_SWARM = 'swarm';
const SP_VAMPIRE = 'vampiric';
public function validate()
{
$attackTypes = Daemon_Dictionary::$combatAttackTypes;
$attackSpecials = Daemon_Dictionary::$combatAttackSpecials;
$armorSpecials = Daemon_Dictionary::$combatArmorSpecials;
if (!isset($attackTypes[$this->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;
}
}

View file

@ -0,0 +1,11 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_Event extends Daemon_DbObject
{
protected $_tableName = 'events';
protected $_index = array('event_id');
public $event_id;
public $name;
public $handle = null;
public $description = null;
}

View file

@ -0,0 +1,160 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_Item extends Daemon_DbObject
{
protected $_tableName = 'items';
protected $_index = array('item_id');
public $item_id;
public $name;
public $type = 'item';
public $value = 1, $suggested_value = 0.0;
public $damage_type = null;
public $special_type = null;
public $special_param = null;
public $pstr_p = 0, $pstr_c = 0, $patk_p = 0, $patk_c = 0;
public $pdef_p = 0, $pdef_c = 0, $pres_p = 0, $pres_c = 0;
public $mstr_p = 0, $mstr_c = 0, $matk_p = 0, $matk_c = 0;
public $mdef_p = 0, $mdef_c = 0, $mres_p = 0, $mres_c = 0;
public $armor = 0, $speed = 0, $regen = 0.0;
public $description = null;
//generates a readable description
public function getDescription()
{
$itemTypes = Daemon_Dictionary::$itemTypes;
$itemWeaponTypes = Daemon_Dictionary::$itemWeaponTypes;
$itemDamageTypes = Daemon_Dictionary::$itemDamageTypes;
$itemArmorTypes = Daemon_Dictionary::$itemArmorTypes;
$combatAttackSpecials = Daemon_Dictionary::$combatAttackSpecials;
$combatArmorSpecials = Daemon_Dictionary::$combatArmorSpecials;
//item type
$typeName = $specialName = null;
if (!empty($itemWeaponTypes[$this->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;
}
}
}

View file

@ -0,0 +1,20 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_ItemTemplate extends Daemon_DbObject
{
protected $_tableName = 'item_templates';
protected $_index = array('id');
public $id;
public $name;
public $pstr_p_p = 1, $pstr_p_m = 1, $pstr_c_p = 1, $pstr_c_m = 1;
public $patk_p_p = 1, $patk_p_m = 1, $patk_c_p = 1, $patk_c_m = 1;
public $pdef_p_p = 1, $pdef_p_m = 1, $pdef_c_p = 1, $pdef_c_m = 1;
public $pres_p_p = 1, $pres_p_m = 1, $pres_c_p = 1, $pres_c_m = 1;
public $mstr_p_p = 1, $mstr_p_m = 1, $mstr_c_p = 1, $mstr_c_m = 1;
public $matk_p_p = 1, $matk_p_m = 1, $matk_c_p = 1, $matk_c_m = 1;
public $mdef_p_p = 1, $mdef_p_m = 1, $mdef_c_p = 1, $mdef_c_m = 1;
public $mres_p_p = 1, $mres_p_m = 1, $mres_c_p = 1, $mres_c_m = 1;
public $armor_p = 1, $armor_m = 1;
public $speed_p = 1, $speed_m = 1;
public $regen_p = 1, $regen_m = 1;
}

View file

@ -0,0 +1,303 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_Location extends Daemon_DbObject
{
protected $_tableName = 'locations';
protected $_index = array('location_id');
public $location_id;
public $name;
public $type = 'normal';
public $chance1 = 1, $chance2 = 1;
public $region_id = null;
public $faction_id = null;
public $description = null;
public $picture_url = null;
private $_characterData = null;
//spends one turn on hunting
public function actionHunt()
{
//check turn costs
if(!$this->_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;
}
}
}

View file

@ -0,0 +1,49 @@
<?php
//@author Krzysztof Sikorski
class Daemon_DbObject_Monster extends Daemon_DbObject
{
protected $_tableName = 'monsters';
protected $_index = array('monster_id');
public $monster_id;
public $name;
public $class = 1;
public $level = 1;
public $gold = 0;
public $chance1 = 1;
public $chance2 = 1;
public $title_id = null;
public $combat_unit_id = null;
private $_combatUnit;
//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->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;
}
}
}

View file

@ -0,0 +1,387 @@
<?php
//@author Krzysztof Sikorski
//manages player data, authentication and character list
class Daemon_DbObject_Player extends Daemon_DbObject
{
protected $_tableName = 'players';
protected $_index = array('player_id');
public $player_id;
public $login;
public $password;
public $roles;
public $name;
public $date_created;
public $last_login;
public $skin;
public $email;
//session settings
const SESSION_TIMEOUT = 900; //seconds
const VARNAME_CHARACTER_ID = 'characterId';
const VARNAME_PLAYER_ID = 'playerId';
const VARNAME_PLAYER_ADDR = 'playerAddr';
const VARNAME_TIMESTAMP = 'lastAction';
public function __construct(Daemon_DbClient $dbClient)
{
parent::__construct();
$this->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;
}
}

View file

@ -0,0 +1,85 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Dictionary
{
public static $bossStatuses = array('hidden' => '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');
}

View file

@ -0,0 +1,182 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Duel
{
private $dbClient = null;
private $characterData = null;
private $combatLog;
const WINNER_ACTOR = 'a';
const WINNER_TARGET = 'b';
public function attachCharacterData(Daemon_DbObject_CharacterData $characterData)
{
$this->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
}
}

View file

@ -0,0 +1,28 @@
<?php
//@author Krzysztof Sikorski
//wrapper for mail() function
class Daemon_Email
{
public $from;
public $to;
public $replyTo;
public $subject;
public $message;
public function __construct()
{
$this->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;
}
}

View file

@ -0,0 +1,47 @@
<?php
//@author Krzysztof Sikorski
class Daemon_ErrorHandler
{
public static $logDir;
public static function errorHandler($errno, $errstr, $errfile, $errline)
{
$data = "LEVEL: $errno\nMESSAGE: $errstr\nFILE: $errfile\nLINE: $errline";
$path = sprintf('%s/error.%s.log', self::$logDir, date('Ymd.His'));
self::logError($path, $data);
}
public static function exceptionHandler(Exception $ex)
{
$data = sprintf("MESSAGE: %s\nCODE: %s\nFILE: %s\nLINE: %s\nBACKTRACE:\n%s",
$ex->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'<!DOCTYPE html><html><meta charset="UTF-8"><title>500 Internal Server Error</title>';
echo'<h1>Wystąpił błąd</h1>';
echo'<p>Kod gry zrobił <q>Aaarghhh!</q> i przestał działać. Albo zdechła baza danych. Albo cokolwiek.</p>';
if($stored)
{
echo '<p>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 ;)</p>';
}
else
{
echo '<p>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.</p>';
}
exit;
}
}

View file

@ -0,0 +1,50 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Event
{
private $characterData = null;
private $eventLog;
public function attachCharacterData(Daemon_DbObject_CharacterData $characterData)
{
$this->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();
}
}
}

View file

@ -0,0 +1,14 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Event_Deadend extends Daemon_EventInterface
{
public function execute($params)
{
$this->clearEvent();
ob_start();
$this->view->display('event/deadend.xml');
return ob_get_clean();
}
}

View file

@ -0,0 +1,12 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Event_Help extends Daemon_EventInterface
{
public function execute($params)
{
$this->clearEvent();
return '<b>Aaarghhh!</b>';
}
}

View file

@ -0,0 +1,31 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Event_Intro extends Daemon_EventInterface
{
public function execute($params)
{
$this->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();
}
}

View file

@ -0,0 +1,27 @@
<?php
//@author Krzysztof Sikorski
abstract class Daemon_EventInterface
{
protected $characterData;
protected $dbClient;
protected $view;
final public function __construct(Daemon_DbClient $dbClient,
Daemon_DbObject_CharacterData $characterData, Daemon_View $view)
{
$this->dbClient = $dbClient;
$this->characterData = $characterData;
$this->view = $view;
}
final protected function clearEvent()
{
$this->characterData->setLocationEvent(array());
$this->characterData->put();
}
abstract public function execute($params);
}

View file

@ -0,0 +1,119 @@
<?php
//@author Krzysztof Sikorski
//mail & chat related operations
class Daemon_Forum
{
private $dbClient;
public function __construct(Daemon_DbClient $dbClient)
{
$this->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);
}
}

View file

@ -0,0 +1,154 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Inventory
{
private $dbClient = null;
private $characterData = null;
public function __construct(Daemon_DbClient $dbClient, Daemon_DbObject_CharacterData $characterData)
{
$this->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);
}
}

View file

@ -0,0 +1,54 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Item
{
private $characterData = null;
private $usageLog;
public function attachCharacterData(Daemon_DbObject_CharacterData $characterData)
{
$this->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;
}
}
}

View file

@ -0,0 +1,33 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Item_GiveSpell extends Daemon_ItemInterface
{
public function execute($inventoryId, $spellId)
{
$char = $this->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;
}
}

View file

@ -0,0 +1,32 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Item_Heal extends Daemon_ItemInterface
{
public function execute($inventoryId, $params)
{
$msg = '';
$char = $this->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);
}
}

View file

@ -0,0 +1,15 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Item_Teleport extends Daemon_ItemInterface
{
public function execute($inventoryId, $locationId)
{
$this->characterData->location_id = $locationId;
$this->characterData->put();
$this->deleteItem($inventoryId);
return "Nagle znajdujesz się zupełnie gdzieindziej...";
}
}

View file

@ -0,0 +1,28 @@
<?php
//@author Krzysztof Sikorski
abstract class Daemon_ItemInterface
{
protected $characterData;
protected $dbClient;
protected $view;
final public function __construct(Daemon_DbClient $dbClient,
Daemon_DbObject_CharacterData $characterData, Daemon_View $view)
{
$this->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);
}

View file

@ -0,0 +1,57 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Math
{
//calculates regen value from skill and armor bonus
public static function combatRegen($attribute, $skill)
{
if($sum = $attribute + $skill)
return round($attribute * $skill / $sum, 3);
else return 0.0;
}
//calculates a single stat
public static function combatStat($attribute, $bonus_p, $bonus_c, $skill)
{
return max(0, (int) round($attribute * (1 + $bonus_p/100) + $bonus_c + $skill));
}
//calculates power multiplier for faction boss
public static function factionPowerMult($bossFactionId, array $factionPowers)
{
$sum = array_sum($factionPowers);
if(empty($sum) || !isset($factionPowers[$bossFactionId]))
return 1;
return 1 + $factionPowers[$bossFactionId] / $sum;
}
//calculates mana regen value from skill
public static function manaRegen($attribute, $skill)
{
if($sum = $attribute + $skill)
return max(1, (int) round($attribute * $skill / $sum));
else return 1;
}
//calculates spell cost
public static function spellCost($level, $maxCost, $minCost, $delta)
{
$level = max(1, $level);
return max($minCost, $maxCost - $level * $delta);
}
//randomly rounds a number
public static function round($float)
{
$result = floor($float);
$prob = $float - $result;
if(mt_rand(0,255) < 256 * $prob)
++$result;
return (int) $result;
}
}

View file

@ -0,0 +1,183 @@
<?php
//@author Krzysztof Sikorski
class Daemon_MonsterCombat
{
private $dbClient = null;
private $characterData = null;
private $combatLog;
const WINNER_ACTOR = 'a';
const WINNER_MONSTER = 'm';
public function attachCharacterData(Daemon_DbObject_CharacterData $characterData)
{
$this->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;
}
}

View file

@ -0,0 +1,27 @@
<?php
//@author Krzysztof Sikorski
// session-based message queue
class Daemon_MsgQueue
{
const VARNAME = 'msg';
public static function add($txt)
{
$_SESSION[self::VARNAME][] = (string)$txt;
}
public static function getAll()
{
if(isset($_SESSION[self::VARNAME]))
{
$messages = (array) $_SESSION[self::VARNAME];
unset($_SESSION[self::VARNAME]);
return $messages;
}
else return null;
}
}

View file

@ -0,0 +1,72 @@
<?php
//@author Krzysztof Sikorski
class Daemon_News
{
private $dbClient;
public function __construct(Daemon_DbClient $dbClient)
{
$this->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);
}
}

View file

@ -0,0 +1,73 @@
<?php
//@author Krzysztof Sikorski
require'daemon/scyzoryk/dbrow.php';
class Daemon_Scyzoryk
{
protected $dbClient;
public function __construct(Daemon_DbClient $dbClient)
{
$this->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);
}
}

View file

@ -0,0 +1,235 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Scyzoryk_Browser extends Daemon_Scyzoryk
{
public function findRow($tableName, $indexCol, $id, $name)
{
$sql = "SELECT $indexCol AS id, name FROM $tableName
WHERE $indexCol LIKE CONCAT('%', :id, '%') AND name LIKE CONCAT('%', :name, '%')";
$params = array('id' => $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());
}
}

View file

@ -0,0 +1,81 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Scyzoryk_Controller
{
//global objects
protected $cfg;
protected $browser;
protected $dbClient;
protected $editor;
protected $view;
//execution parameters
protected $editId = null;
protected $editId2 = null;
//output parameters
protected $pageSubtitle = null;
protected $pageSubtitleDetails = null;
protected $pageTemplatePath;
final public function __construct(Daemon_Config $cfg)
{
$this->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;
}
}

View file

@ -0,0 +1,151 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Scyzoryk_DbRow
{
const TABLE_NAME = null;
const INDEX_COL = null;
const INDEX_COL2 = null;
final public function __construct($params = null)
{
foreach((array) $params as $name => $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 = '';
}

View file

@ -0,0 +1,139 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Scyzoryk_Editor extends Daemon_Scyzoryk
{
public function deleteCombatUnits(array $ids)
{
$this->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);
}
}

View file

@ -0,0 +1,43 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Scyzoryk_Filter extends stdClass
{
const SESSION_VARNAME = 'scf';
private $cols;
private $data;
public function __construct($name, array $extraCols = array(), $noSession = false)
{
$this->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]);
}
}

View file

@ -0,0 +1,47 @@
<?php
//@author Krzysztof Sikorski
abstract class Daemon_Service
{
protected $characterData;
protected $dbClient;
protected $view;
protected $isCommand = false;
protected $eventLog = null;
protected $bankEnabled = false;
protected $templeEnabled = false;
final public function __construct(Daemon_DbClient $dbClient,
Daemon_DbObject_CharacterData $characterData, Daemon_View $view,
array $serviceData, $bankEnabled, $templeEnabled)
{
$this->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);
}

View file

@ -0,0 +1,95 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Service_Bank extends Daemon_Service
{
const STORAGE_LIMIT = 5;//base limit
public function execute($params)
{
$inventory = new Daemon_Inventory($this->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();
}
}

View file

@ -0,0 +1,86 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Service_Healer extends Daemon_Service
{
private $deltaHealthMin = 10;
private $deltaHealthMax = 15;
private $deltaManaMin = 5;
private $deltaManaMax = 10;
public function execute($params)
{
$this->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;
}
}

View file

@ -0,0 +1,202 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Service_Shop extends Daemon_Service
{
public function execute($params)
{
$inventoryObj = new Daemon_Inventory($this->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;
}
}

View file

@ -0,0 +1,333 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Service_Temple extends Daemon_Service
{
private $factionId;
private $xpForMission;
private $xpForRegen;
public function execute($params)
{
$this->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);
}
}

View file

@ -0,0 +1,63 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Spell
{
private $characterData = null;
private $usageLog;
public function attachCharacterData(Daemon_DbObject_CharacterData $characterData)
{
$this->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;
}
}
}

View file

@ -0,0 +1,15 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Spell_Identify extends Daemon_SpellInterface
{
public function execute($spellId, $cost)
{
if(!$this->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;
}
}

View file

@ -0,0 +1,69 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Spell_ScanCharacter extends Daemon_SpellInterface
{
public function execute($spellId, $cost)
{
$result = null;
$target = isset($_POST['target']) ? $_POST['target'] : null;
if($target)
{
if(!$this->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));
}
}

View file

@ -0,0 +1,65 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Spell_ScanItem extends Daemon_SpellInterface
{
public function execute($spellId, $cost)
{
$result = null;
$target = isset($_POST['target']) ? $_POST['target'] : null;
if($target)
{
if(!$this->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));
}
}

View file

@ -0,0 +1,93 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Spell_ScanMonster extends Daemon_SpellInterface
{
public function execute($spellId, $cost)
{
$result = null;
$target = isset($_POST['target']) ? $_POST['target'] : null;
if($target)
{
if(!$this->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));
}
}

View file

@ -0,0 +1,56 @@
<?php
//@author Krzysztof Sikorski
class Daemon_Spell_Scout extends Daemon_SpellInterface
{
public function execute($spellId, $cost)
{
if(!$this->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));
}
}

View file

@ -0,0 +1,32 @@
<?php
//@author Krzysztof Sikorski
abstract class Daemon_SpellInterface
{
protected $characterData;
protected $dbClient;
protected $view;
final public function __construct(Daemon_DbClient $dbClient,
Daemon_DbObject_CharacterData $characterData, Daemon_View $view)
{
$this->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;
}
}

View file

@ -0,0 +1,233 @@
<?php
//@author Krzysztof Sikorski
//viewer for world statistics (characters, clans etc)
class Daemon_Statistics
{
private $dbClient;
private $charactersOrderTypes = array(
'name' => 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);
}
}

View file

@ -0,0 +1,133 @@
<?php
//@author Krzysztof Sikorski
//wrapper for PHPTAL engine
require_once'PHPTAL.php';
class Daemon_View_Filter implements PHPTAL_Filter
{
public function filter($str)
{
$str = preg_replace('/\s+/u', ' ', $str);
return preg_replace('/> /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);
}
}

291
2013/daemon/lib/jsmin.php Normal file
View file

@ -0,0 +1,291 @@
<?php
/**
* jsmin.php - PHP implementation of Douglas Crockford's JSMin.
*
* This is pretty much a direct port of jsmin.c to PHP with just a few
* PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
* outputs to stdout, this library accepts a string as input and returns another
* string as output.
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as jsmin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* The Software shall be used for Good, not Evil.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* --
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com>
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.1 (2008-03-02)
* @link http://code.google.com/p/jsmin-php/
*/
class JSMin {
const ORD_LF = 10;
const ORD_SPACE = 32;
protected $a = '';
protected $b = '';
protected $input = '';
protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = '';
// -- Public Static Methods --------------------------------------------------
public static function minify($js) {
$jsmin = new JSMin($js);
return $jsmin->min();
}
// -- Public Instance Methods ------------------------------------------------
public function __construct($input) {
$this->input = str_replace("\r\n", "\n", $input);
$this->inputLength = strlen($this->input);
}
// -- Protected Instance Methods ---------------------------------------------
protected function action($d) {
switch($d) {
case 1:
$this->output .= $this->a;
case 2:
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') {
for (;;) {
$this->output .= $this->a;
$this->a = $this->get();
if ($this->a === $this->b) {
break;
}
if (ord($this->a) <= self::ORD_LF) {
throw new JSMinException('Unterminated string literal.');
}
if ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
}
}
}
case 3:
$this->b = $this->next();
if ($this->b === '/' && (
$this->a === '(' || $this->a === ',' || $this->a === '=' ||
$this->a === ':' || $this->a === '[' || $this->a === '!' ||
$this->a === '&' || $this->a === '|' || $this->a === '?')) {
$this->output .= $this->a . $this->b;
for (;;) {
$this->a = $this->get();
if ($this->a === '/') {
break;
} elseif ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
} elseif (ord($this->a) <= self::ORD_LF) {
throw new JSMinException('Unterminated regular expression '.
'literal.');
}
$this->output .= $this->a;
}
$this->b = $this->next();
}
}
}
protected function get() {
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
} else {
$c = null;
}
}
if ($c === "\r") {
return "\n";
}
if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
return $c;
}
return ' ';
}
protected function isAlphaNum($c) {
return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
}
protected function min() {
$this->a = "\n";
$this->action(3);
while ($this->a !== null) {
switch ($this->a) {
case ' ':
if ($this->isAlphaNum($this->b)) {
$this->action(1);
} else {
$this->action(2);
}
break;
case "\n":
switch ($this->b) {
case '{':
case '[':
case '(':
case '+':
case '-':
$this->action(1);
break;
case ' ':
$this->action(3);
break;
default:
if ($this->isAlphaNum($this->b)) {
$this->action(1);
}
else {
$this->action(2);
}
}
break;
default:
switch ($this->b) {
case ' ':
if ($this->isAlphaNum($this->a)) {
$this->action(1);
break;
}
$this->action(3);
break;
case "\n":
switch ($this->a) {
case '}':
case ']':
case ')':
case '+':
case '-':
case '"':
case "'":
$this->action(1);
break;
default:
if ($this->isAlphaNum($this->a)) {
$this->action(1);
}
else {
$this->action(3);
}
}
break;
default:
$this->action(1);
break;
}
}
}
return $this->output;
}
protected function next() {
$c = $this->get();
if ($c === '/') {
switch($this->peek()) {
case '/':
for (;;) {
$c = $this->get();
if (ord($c) <= self::ORD_LF) {
return $c;
}
}
case '*':
$this->get();
for (;;) {
switch($this->get()) {
case '*':
if ($this->peek() === '/') {
$this->get();
return ' ';
}
break;
case null:
throw new JSMinException('Unterminated comment.');
}
}
default:
return $c;
}
}
return $c;
}
protected function peek() {
$this->lookAhead = $this->get();
return $this->lookAhead;
}
}
// -- Exceptions ---------------------------------------------------------------
class JSMinException extends Exception {}
?>

39
2013/daemon/php.ini Normal file
View file

@ -0,0 +1,39 @@
[PHP]
; language options
short_open_tag = Off
asp_tags = Off
; error handling and logging
error_reporting = E_ALL | E_STRICT
display_errors = On
display_startup_errors = Off
log_errors = On
log_errors_max_len = 0
html_errors = Off
error_log = "path/to/error.log"
; data handling
register_globals = Off
register_long_arrays = Off
register_argc_argv = Off
auto_globals_jit = On
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
default_mimetype = "text/plain"
default_charset = "UTF-8"
always_populate_raw_post_data = Off
[Date]
date.timezone = "Europe/Warsaw"
[iconv]
iconv.input_encoding = "UTF-8"
iconv.internal_encoding = "UTF-8"
iconv.output_encoding = "UTF-8"
[Session]
session.use_cookies = 1
session.use_only_cookies = 1
session.auto_start = 0

View file

@ -0,0 +1,10 @@
DirectoryIndex index.php
Redirect gone ./favicon.ico
Redirect permanent /help http://pl.daemon.wikia.com/wiki/Daemon_Wiki
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*)$ /$1.php [L,QSA]

View file

@ -0,0 +1,31 @@
<?php
//@author Krzysztof Sikorski
ignore_user_abort(true);
error_reporting(E_ALL|E_STRICT);
iconv_set_encoding('internal_encoding', 'UTF-8');
mb_internal_encoding('UTF-8');
date_default_timezone_set('Europe/Warsaw');
setlocale(LC_ALL, 'pl_PL.utf8');
if (isset($_GET['phpinfo']) && $_GET['phpinfo'] == 'Waaghh!')
{
header('Content-Type: text/html; charset=UTF-8');
phpinfo();
exit;
}
if(extension_loaded('zlib') && !ini_get('zlib.output_compression'))
ob_start('ob_gzhandler'); //setting zlib.output_compression doesn't work
header('Content-Type: text/plain; charset=UTF-8');
header('X-UA-Compatible: IE=edge');
$applicationRoot = realpath(dirname(__FILE__).DIRECTORY_SEPARATOR.'..');
set_include_path($applicationRoot.DIRECTORY_SEPARATOR.'lib');
require'daemon.php';
spl_autoload_register(array('Daemon', 'autoload'));
Daemon_ErrorHandler::$logDir = $applicationRoot.DIRECTORY_SEPARATOR.'log';
set_error_handler(array('Daemon_ErrorHandler', 'errorHandler'));
set_exception_handler(array('Daemon_ErrorHandler', 'exceptionHandler'));
return new Daemon_Config($applicationRoot);

View file

@ -0,0 +1,63 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Account extends Daemon_Controller
{
protected $pageSubtitle = 'Konto';
protected $pageTemplatePath = 'account.xml';
protected $requireAuthentication = true;
private $characters;
public function prepareView()
{
$this->characters = $this->player->getCharacters();
//mark active character
$activeCharId = $this->player->getCharacterId();
if($activeCharId && isset($this->characters[$activeCharId]))
$this->characters[$activeCharId]['active'] = true;
else $activeCharId = null;
//prepare view
$this->view->characters = $this->characters;
$this->view->genders = Daemon_Dictionary::$genders;
$this->view->preview = Daemon::formatMessage($this->activeCharacter->description, true);
}
protected function runCommands()
{
$turnDelta = $this->dbCfg->turnDelta;
$turnLimit = $this->dbCfg->turnLimit;
//create character
if(isset($_POST['newName'], $_POST['newGender']))
{
$this->player->addCharacter($_POST['newName'], $_POST['newGender'], $turnDelta, $turnLimit);
return true;
}
//set active character
if(isset($_POST['use']))
{
$this->player->setCharacterId($_POST['use']);
$this->activeCharacter = $this->player->getActiveCharacter();
$this->characterData = $this->activeCharacter->getCharacterData();
$this->activeCharacter->updateLastAction();
return true;
}
//reset or delete character
if(isset($_POST['char'], $_POST['action']))
{
$reset = ($_POST['action'] == 'reset');
$this->player->deleteCharacter($_POST['char'], $reset, $turnDelta, $turnLimit);
$this->activeCharacter = $this->player->getActiveCharacter();
$this->characterData = $this->activeCharacter->getCharacterData();
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Account($cfg);
$ctrl->execute();

View file

@ -0,0 +1,46 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Auth extends Daemon_Controller
{
protected $disableMessages = true;
public function prepareModel()
{
}
public function prepareView()
{
$url = $this->cfg->applicationUrl;
if($this->player->getPlayerId())
$url .= 'account';
Daemon::redirect($url);
exit;
}
protected function runCommands()
{
if(!$this->dbCfg->loginEnabled)
return false;
if(isset($_POST['login'], $_POST['pass']))
{
$this->player->authenticate($_POST['login'], $_POST['pass']);
return true;
}
if(isset($_POST['logout']))
{
$this->player->unauthenticate();
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Auth($cfg);
$ctrl->execute();

View file

@ -0,0 +1,89 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Character extends Daemon_Controller
{
protected $pageSubtitle = 'Postać';
protected $pageTemplatePath = 'character.xml';
protected $requireActiveChar = true;
protected $requireAuthentication = true;
protected $requireNoEvents = true;
private $eventLog;
public function prepareView()
{
if($this->eventLog)
{
$this->pageSubtitle = 'Użycie zaklęcia';
$this->pageTemplatePath = 'event.xml';
$this->view->eventLog = $this->eventLog;
return;
}
//prepare attributes list
$list = array();
foreach(Daemon_Dictionary::$characterAttributes as $key => $name)
{
$col = "a_$key";
$value = $this->characterData->$col;
$list[$key] = array('name' => $name, 'value' => $value, 'inc' => ($value <= $this->characterData->xp_free));
}
$this->view->attributes = $list;
//prepare skill list
$list = array();
foreach(Daemon_Dictionary::$characterSkills as $key => $name)
{
$col = "s_$key";
$value = $this->characterData->$col;
$list[$key] = array('name' => $name, 'value' => $value, 'inc' => ($value <= $this->characterData->xp_free));
}
$this->view->skills = $list;
//prepare spell list
$this->view->spells = $this->characterData->getSpells();
//prepare combat stats
$unit = (array) $this->characterData->getCombatUnit();
$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;
$this->view->combatStats = $unit;
}
protected function runCommands()
{
//improve attribute
if(isset($_POST['incA']))
{
$this->characterData->improveAttribute($_POST['incA']);
return true;
}
//improve skill
if(isset($_POST['incS']))
{
$this->characterData->improveSkill($_POST['incS']);
return true;
}
//cast spell
if(isset($_POST['cast']))
{
$handler = new Daemon_Spell();
$handler->attachCharacterData($this->characterData);
$handler->attachDbClient($this->dbClient);
$handler->execute($this->view, $_POST['cast']);
$this->eventLog = $handler->getUsageLog();
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Character($cfg);
$ctrl->execute();

View file

@ -0,0 +1,61 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Chat extends Daemon_Controller
{
protected $pageSubtitle = 'Ogłoszenia';
protected $pageTemplatePath = 'chat.xml';
private $channelId;
private $channels;
private $forum;
private $writeAccess;
public function prepareModel()
{
$this->forum = new Daemon_Forum($this->dbClient);
$this->channels = $this->activeCharacter->getForumChannels();
$this->channelId = isset($_GET['v']) ? $_GET['v'] : null;
if(!isset($this->channels[$this->channelId]))
$this->channelId = 'public';
$this->writeAccess = !empty($this->channels[$this->channelId]['writable']);
}
public function prepareView()
{
$listLimit = max(1, (int) $this->dbCfg->listLimitMessages);
$listOffset = isset($_GET['n']) ? (int) $_GET['n'] : 0;
$this->pageSubtitleUseQuery = true;
$from = isset($_GET['from']) ? (int) $_GET['from'] : 0;
$data = $this->forum->getChat($listLimit, $from, $this->channelId);
foreach($data['list'] as &$row)
$row['content'] = Daemon::formatMessage($row['content'], true);
$this->view->inputMsg = isset($_POST['msg']) ? $_POST['msg'] : null;
$this->view->list = $data['list'];
$this->view->menu = $this->view->getChatMenu($this->channels, $this->channelId);
$this->view->nextUrl = $data['next'] ? '?from='.urlencode($data['next']) : null;
$this->view->channelId = $this->channelId;
$this->view->writeAccess = $this->writeAccess;
}
protected function runCommands()
{
if($this->writeAccess && isset($_POST['msg']))
{
$this->forum->addChat($this->player->getCharacterId(), $this->channelId, $_POST['msg']);
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Chat($cfg);
$ctrl->execute();

129
2013/daemon/public/clan.php Normal file
View file

@ -0,0 +1,129 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Page extends Daemon_Controller
{
protected $pageSubtitle = 'Klan';
protected $pageTemplatePath = 'clan.xml';
protected $requireActiveChar = true;
protected $requireAuthentication = true;
protected $requireNoEvents = true;
private $clan;
private $isLeader = false;
public function prepareModel()
{
$this->clan = new Daemon_DbObject_Clan;
if($this->activeCharacter->clan_id)
{
$this->clan->attachDbClient($this->dbClient);
$this->clan->get(array('clan_id' => $this->activeCharacter->clan_id));
if($this->clan->leader_id)
$this->isLeader = ($this->clan->leader_id == $this->activeCharacter->character_id);
}
}
public function prepareView()
{
if($this->clan->clan_id)
$this->prepareViewMember();
else $this->prepareViewSolo();
}
private function prepareViewMember()
{
$this->view->clan = $this->clan;
$this->view->preview = Daemon::formatMessage($this->clan->description, true);
$this->view->isLeader = $this->isLeader;
$this->view->members = $this->clan->getMembers($this->activeCharacter->character_id);
$invitations = $this->clan->getInvitations();
foreach($invitations as &$row)
$row['description'] = Daemon::formatMessage($row['description'], true);
$this->view->invitations = $invitations;
}
private function prepareViewSolo()
{
$this->view->invitations = $this->activeCharacter->getInvitations();
}
public function runCommands()
{
if($this->clan->clan_id)
return $this->runCommandsMember();
else return $this->runCommandsSolo();
}
private function runCommandsMember()
{
if($this->isLeader)
{
if(isset($_POST['accept']))
{
$forum = new Daemon_Forum($this->dbClient);
$this->clan->acceptCharacter($_POST['accept'], $forum);
return true;
}
if(isset($_POST['kick']))
{
$forum = new Daemon_Forum($this->dbClient);
$this->clan->kickMember($_POST['kick'], $forum);
return true;
}
if(isset($_POST['setLeader'], $_POST['desc']))
{
if($_POST['setLeader'] && ($_POST['setLeader'] != $this->clan->leader_id))
{
$this->clan->leader_id = $_POST['setLeader'];
$this->isLeader = false;
}
$this->clan->description = $_POST['desc'];
$this->clan->put();
return true;
}
if(isset($_POST['disband']))
{
$this->clan->delete();
$this->clan = new Daemon_DbObject_Clan;
$this->activeCharacter->clan_id = null;
return true;
}
}
if(isset($_POST['leave']))
{
$this->activeCharacter->clan_id = null;
$this->activeCharacter->put();
return true;
}
}
private function runCommandsSolo()
{
if(isset($_POST['join'], $_POST['desc']))
{
$forum = new Daemon_Forum($this->dbClient);
$this->activeCharacter->inviteClan($_POST['join'], $_POST['desc'], $forum);
return true;
}
if(isset($_POST['create'], $_POST['id'], $_POST['name']))
{
$this->activeCharacter->createClan($_POST['id'], $_POST['name']);
$this->prepareModel();
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Page($cfg);
$ctrl->execute();

View file

@ -0,0 +1,39 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Duel extends Daemon_Controller
{
protected $pageSubtitle = 'Pojedynek';
protected $pageTemplatePath = 'duel.xml';
protected $requireActiveChar = true;
protected $requireAuthentication = true;
protected $requireLocation = true;
protected $requireNoEvents = true;
private $combatLog = null;
public function prepareView()
{
$this->view->combatLog = $this->combatLog;
}
public function runCommands()
{
//attack
if(isset($_POST['attack']))
{
$this->view->setGameHeader($this->player->getPlayerId(),
$this->activeCharacter, $this->characterData, $this->location);
$this->combatLog = $this->characterData->attack($this->view, $_POST['attack'], $this->location->type);
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Duel($cfg);
$ctrl->execute();

View file

@ -0,0 +1,41 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Page extends Daemon_Controller
{
protected $pageSubtitle = 'Ustawienia';
protected $pageSubtitleDetails = 'konto';
protected $pageTemplatePath = 'edit-account.xml';
protected $requireAuthentication = true;
public function prepareView()
{
$this->view->player = $this->player;
$this->view->skins = array_keys(Daemon_Dictionary::$skinDirUrls);
}
protected function runCommands()
{
//update player data
if(isset($_POST['name'], $_POST['pass1'], $_POST['pass2'], $_POST['skin'], $_POST['email']))
{
if($_POST['pass1'] || $_POST['pass2'])
$this->player->setPassword($_POST['pass1'], $_POST['pass2']);
$_POST['name'] = Daemon::normalizeString($_POST['name']);
$this->player->name = $_POST['name'] ? $_POST['name'] : null;
$this->player->skin = $_POST['skin'] ? $_POST['skin'] : null;
$this->player->email = $_POST['email'] ? $_POST['email'] : null;
$this->player->put();
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Page($cfg);
$ctrl->execute();

View file

@ -0,0 +1,60 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Account extends Daemon_Controller
{
protected $pageSubtitle = 'Ustawienia';
protected $pageSubtitleDetails = 'postać';
protected $pageTemplatePath = 'edit-character.xml';
protected $requireAuthentication = true;
private $character;
public function prepareModel()
{
$id = isset($_GET['id']) ? (int) $_GET['id'] : null;
$this->character = new Daemon_DbObject_Character;
if($id)
{
$this->character->attachDbClient($this->dbClient);
$params = array('character_id' => $id, 'player_id' => $this->player->getPlayerId());
$this->character->get($params);
}
if(!$this->character->character_id)
{
Daemon_MsgQueue::add('Wybrana postać nie istnieje.');
Daemon::redirect($this->cfg->getUrl('map'));
exit;
}
}
public function prepareView()
{
$this->pageSubtitleDetails = $this->character->name;
$this->view->character = $this->character;
$this->view->preview = Daemon::formatMessage($this->character->description, true);
}
protected function runCommands()
{
//update character
if(isset($_POST['avatar'], $_POST['quote'], $_POST['desc']))
{
$this->character->show_player = !empty($_POST['player']);
$this->character->avatar_url = $_POST['avatar'];
$this->character->quote = $_POST['quote'];
$this->character->description = $_POST['desc'];
$this->character->put();
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Account($cfg);
$ctrl->execute();

View file

@ -0,0 +1,27 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Index extends Daemon_Controller
{
protected $pageTemplatePath = 'index.xml';
private $news;
public function prepareModel()
{
$this->news = new Daemon_News($this->dbClient);
}
public function prepareView()
{
$this->view->loginEnabled = (bool) $this->dbCfg->loginEnabled;
$this->view->news = $this->news->getEntries(3, false);
}
}
$ctrl = new Daemon_Controller_Index($cfg);
$ctrl->execute();

View file

@ -0,0 +1,106 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Inventory extends Daemon_Controller
{
protected $pageSubtitle = 'Ekwipunek';
protected $pageTemplatePath = 'inventory.xml';
protected $requireActiveChar = true;
protected $requireAuthentication = true;
protected $requireNoEvents = true;
private $inventory;
private $eventLog;
public function prepareModel()
{
$this->inventory = new Daemon_Inventory($this->dbClient, $this->characterData);
}
public function prepareView()
{
if($this->eventLog)
{
$this->pageSubtitle = 'Użycie przedmiotu';
$this->view->eventLog = $this->eventLog;
}
else $this->view->eventLog = null;
$items = $this->inventory->getItems('inventory');
$this->view->equipmentSlots = Daemon_Dictionary::$equipmentSlots;
//prepare items
$cmdNames = Daemon_Dictionary::$equipmentButtons;
foreach($items as &$row)
{
if($row['equipped'])
$cmd = 'unequip';
elseif('item' != $row['item']->type)
$cmd = 'equip';
else $cmd = 'use';
if(isset($cmdNames[$cmd]))
{
$row['item']->_cmdType = $cmd;
$row['item']->_cmdName = $cmdNames[$cmd];
}
else
{
$row['item']->_cmdType = null;
$row['item']->_cmdName = null;
}
$row['_showSlots'] = ('equip' == $cmd);
$row['_slots'] = $row['item']->getSlots();
$row['_multiSlots'] = (count($row['_slots']) > 1);
}
$this->view->items = $this->inventory->groupItemsByType($items);
$this->view->equipment = $this->inventory->getEquipment($items);
//prepare combat stats
$unit = (array) $this->characterData->getCombatUnit();
$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;
$this->view->combatStats = $unit;
}
protected function runCommands()
{
//equip item
if(isset($_POST['equip'], $_POST['slot']))
{
$this->inventory->equip($_POST['equip'], $_POST['slot']);
$this->characterData->resetCombatStats();
$this->characterData->put();
return true;
}
//unequip item
if(isset($_POST['unequip']))
{
$this->inventory->unequip($_POST['unequip']);
$this->characterData->resetCombatStats();
$this->characterData->put();
return true;
}
//use item
if(isset($_POST['use']))
{
$handler = new Daemon_Item();
$handler->attachCharacterData($this->characterData);
$handler->attachDbClient($this->dbClient);
$handler->execute($this->view, $_POST['use']);
$this->eventLog = $handler->getUsageLog();
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Inventory($cfg);
$ctrl->execute();

View file

@ -0,0 +1,67 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Chat extends Daemon_Controller
{
protected $pageSubtitle = 'Poczta';
protected $pageTemplatePath = 'mail.xml';
protected $requireAuthentication = true;
private $forum;
public function prepareModel()
{
$this->forum = new Daemon_Forum($this->dbClient);
if(isset($_GET['to']))
$_POST['to'] = $_GET['to'];
}
public function prepareView()
{
//fetch mail
$listLimit = max(1, (int) $this->dbCfg->listLimitMessages);
$listOffset = isset($_GET['n']) ? (int) $_GET['n'] : 0;
$characterId = (int) $this->activeCharacter->character_id;
$this->pageSubtitleUseQuery = true;
$from = isset($_GET['from']) ? (int) $_GET['from'] : 0;
$data = $this->forum->getMail($listLimit, $from, $characterId);
foreach($data['list'] as &$row)
$row['content'] = Daemon::formatMessage($row['content'], true);
//mark as read
if($data['list'])
{
$messageId = $data['list'][0]['message_id'];
$sql = "UPDATE characters SET last_mail_id = :messageId WHERE character_id = :characterId";
$params = array('characterId' => $characterId, 'messageId' => $messageId);
$this->dbClient->query($sql, $params);
$this->activeCharacter->last_mail_id = $messageId;
}
//display page
$this->view->inputTo = isset($_POST['to']) ? $_POST['to'] : null;
$this->view->inputMsg = isset($_POST['msg']) ? $_POST['msg'] : null;
$this->view->list = $data['list'];
$nextUrl = $data['next'] ? '?from='.urlencode($data['next']) : null;
$this->view->nextUrl = $nextUrl;
}
protected function runCommands()
{
if(isset($_POST['to'], $_POST['msg']))
{
$this->forum->addMail($this->player->getCharacterId(), $_POST['to'], $_POST['msg']);
return true;
}
return false;
}
}
$ctrl = new Daemon_Controller_Chat($cfg);
$ctrl->execute();

View file

@ -0,0 +1,86 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Map extends Daemon_Controller
{
protected $pageSubtitle = 'Otoczenie';
protected $pageTemplatePath = 'map.xml';
protected $requireActiveChar = true;
protected $requireAuthentication = true;
protected $requireLocation = true;
private $eventLog = null;
public function prepareView()
{
if($this->eventLog)
{
$this->pageSubtitle = 'Zdarzenie';
$this->pageTemplatePath = 'event.xml';
$this->view->eventLog = $this->eventLog;
return;
}
$bossStatuses = Daemon_Dictionary::$bossStatuses;
if(isset($bossStatuses[$this->location->boss_status]))
$this->location->boss_status_name = $bossStatuses[$this->location->boss_status];
else
$this->location->boss_status_name = null;
$this->pageSubtitleDetails = $this->location->name;
$this->view->locationDesc = nl2br(htmlspecialchars($this->location->description));
$this->view->pictureUrl = $this->location->getPictureUrl();
$this->view->region = $this->location->getRegionName();
$this->view->faction = $this->location->getFactionName();
$this->view->maps = $this->location->getMaps();
$this->view->paths = $this->location->getPaths();
$this->view->services = $this->location->getServices();
$this->view->lastMission = $this->characterData->getLastMission('completed');
//prepare character list
$showAll = isset($_GET['more']);
$halfLimit = $showAll ? null : (int) ceil($this->dbCfg->listLimitCharacters/2);
$n = $this->location->getCharacterCount(2*$halfLimit+1);
$characters = $this->location->getCharacters($this->characterData, $halfLimit);
$this->view->characters = $characters;
$this->view->showMoreLink = !$showAll && ($n > 2*$halfLimit);
}
public function runCommands()
{
$isCommand = false;
//actions
if(isset($_POST['act']))
{
switch($_POST['act'])
{
case'train':
$this->location->actionTrain();
break;
case'rest':
$this->location->actionRest();
break;
case'hunt':
$this->location->actionHunt();
break;
}
$isCommand = true;
}
//travel
if(isset($_POST['travel']))
{
$this->location->actionTravel($_POST['travel']);
$isCommand = true;
}
//run events
$this->view->setGameHeader($this->player->getPlayerId(),
$this->activeCharacter, $this->characterData, $this->location);
$this->eventLog = $this->characterData->runEvent($this->view);
//set "isCommand" flag
return $isCommand;
}
}
$ctrl = new Daemon_Controller_Map($cfg);
$ctrl->execute();

View file

@ -0,0 +1,33 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_News extends Daemon_Controller
{
protected $disablePlayer = true;
protected $pageOutputMode = Daemon_View::MODE_ATOM;
protected $pageSubtitle = 'Regulamin';
protected $pageTemplatePath = 'news.xml';
private $news;
public function prepareModel()
{
$this->news = new Daemon_News($this->dbClient);
}
public function prepareView()
{
$this->view->feedId = $this->cfg->applicationUrl;
$this->view->feedUrl = "{$this->cfg->applicationUrl}news";
$this->view->feedTitle = $this->cfg->applicationTitle;
$this->view->feedUpdated = $this->news->getLastUpdated();
$this->view->entries = $this->news->getEntries(10, true);
}
}
$ctrl = new Daemon_Controller_News($cfg);
$ctrl->execute();

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<title>Błąd</title>
<p>Wybrana strona jeszcze nie istnieje.</p>
</html>

View file

@ -0,0 +1,40 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Register extends Daemon_Controller
{
protected $pageSubtitle = 'Rejestracja';
protected $pageTemplatePath = 'register.xml';
private $registerEnabled;
public function prepareModel()
{
$this->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();

View file

@ -0,0 +1,38 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Page extends Daemon_Controller
{
protected $pageSubtitle = 'Reset hasła';
protected $pageTemplatePath = 'reset-password.xml';
private $registerEnabled;
public function prepareModel()
{
$this->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();

View file

@ -0,0 +1,59 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Respawn extends Daemon_Controller
{
protected $pageSubtitle = 'Otchłań Narodzin';
protected $pageTemplatePath = 'respawn.xml';
protected $requireActiveChar = true;
protected $requireAuthentication = true;
private $defaultRespawn;
private $respawns;
public function prepareModel()
{
if (empty($this->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();

View file

@ -0,0 +1,2 @@
User-Agent: *
Allow: *

View file

@ -0,0 +1,21 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'./_init.php';
class Daemon_Controller_Rules extends Daemon_Controller
{
protected $disablePlayer = true;
protected $pageSubtitle = 'Regulamin';
protected $pageTemplatePath = 'rules.xml';
public function prepareView()
{
$this->view->lastModified = date(DATE_RFC1123, filemtime($this->cfg->getFilePath('tpl', 'rules.xml')));
}
}
$ctrl = new Daemon_Controller_Rules($cfg);
$ctrl->execute();

View file

@ -0,0 +1,126 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'../_init.php';
class Daemon_Scyzoryk_Controller_Page extends Daemon_Scyzoryk_Controller
{
protected $pageSubtitle = 'Arena';
protected $pageTemplatePath = 'scyzoryk/arena.xml';
private $selectMode;
private $combatCount;
private $unitIdA;
private $unitIdB;
private $winsA;
private $winsB;
private $draws;
private $doubleKOs;
private $healthSumA;
private $healthSumB;
private $healthMinA;
private $healthMinB;
private $healthMaxA;
private $healthMaxB;
public function prepareModel()
{
$this->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();

View file

@ -0,0 +1,98 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'../_init.php';
class Daemon_Scyzoryk_Controller_Page extends Daemon_Scyzoryk_Controller
{
protected $pageSubtitle = 'Postacie';
protected $pageTemplatePath = 'scyzoryk/character-edit.xml';
private $character;
private $characterData;
protected function prepareModel()
{
$this->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();

View file

@ -0,0 +1,30 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'../_init.php';
class Daemon_Scyzoryk_Controller_Page extends Daemon_Scyzoryk_Controller
{
protected $pageSubtitle = 'Postacie';
protected $pageTemplatePath = 'scyzoryk/characters.xml';
protected function prepareView()
{
$sql = "SELECT c.character_id, c.player_id, p.login AS player_login, c.name, c.last_action
FROM characters c
JOIN character_data cd USING(character_id)
LEFT JOIN players p USING(player_id)
ORDER BY c.character_id";
$data = $this->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();

View file

@ -0,0 +1,51 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'../_init.php';
class Daemon_Controller_Page extends Daemon_Scyzoryk_Controller
{
protected $pageSubtitle = 'Jednostki bojowe';
protected $pageTemplatePath = 'scyzoryk/combat-unit-edit.xml';
private $unit;
protected function prepareModel()
{
$this->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();

View file

@ -0,0 +1,61 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'../_init.php';
class Daemon_Controller_Page extends Daemon_Scyzoryk_Controller
{
protected $pageSubtitle = 'Jednostki bojowe';
protected $pageTemplatePath = 'scyzoryk/combat-units.xml';
private $filter;
protected function prepareModel()
{
$this->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();

View file

@ -0,0 +1,51 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'../_init.php';
class Daemon_Scyzoryk_Controller_Config extends Daemon_Scyzoryk_Controller
{
protected $pageSubtitle = 'Ustawienia generatora';
protected $pageTemplatePath = 'scyzoryk/config-generator.xml';
private $dbCfg;
private $itemTypes;
protected function prepareModel()
{
$this->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();

View file

@ -0,0 +1,49 @@
<?php
//@author Krzysztof Sikorski
$cfg = require_once'../_init.php';
class Daemon_Scyzoryk_Controller_Config extends Daemon_Scyzoryk_Controller
{
protected $pageSubtitle = 'Ustawienia';
protected $pageTemplatePath = 'scyzoryk/config.xml';
private $dbCfg;
protected function prepareModel()
{
$this->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();

View file

@ -0,0 +1,20 @@
<?php
//@author Krzysztof Sikorski
ini_set('display_errors', '1');
$whitelist = array('127.0.0.1', '::1');
if(isset($_POST['doit']) && in_array(getenv('REMOTE_ADDR'), $whitelist))
{
header('Content-Type:text/plain;charset=UTF-8');
require implode(DIRECTORY_SEPARATOR, array(dirname(__FILE__), '..', '..', 'cron.php'));
exit;
}
header('Content-Type:text/html;charset=UTF-8');
?>
<!doctype HTML>
<html lang=pl>
<meta charset="UTF-8">
<title>Cron</title>
<form action="" method=post>
<p><input type=submit name=doit value=wykonaj></p>
</form>
</html>

Some files were not shown because too many files have changed in this diff Show more