479 lines
14 KiB
PHP
479 lines
14 KiB
PHP
<?php
|
|
|
|
//------------------------------------------------------
|
|
// "Day tick": Research, population increase, rankings,
|
|
// protection levels
|
|
//------------------------------------------------------
|
|
|
|
class beta5_ticks_day_library {
|
|
|
|
public function __construct($lib) {
|
|
$this->lib = $lib;
|
|
$this->game = $this->lib->game;
|
|
$this->db = $this->game->db;
|
|
$this->main = $this->game->getLib();
|
|
$this->planets = $this->game->getLib('beta5/planet');
|
|
$this->players = $this->game->getLib('beta5/player');
|
|
$this->msgs = $this->game->getLib('beta5/msgs');
|
|
$this->fleets = $this->game->getLib('beta5/fleet');
|
|
$this->rankings = $this->game->getLib('main/rankings');
|
|
$this->rules = $this->game->getLib('beta5/rules');
|
|
$this->tech = $this->game->getLib('beta5/tech');
|
|
}
|
|
|
|
|
|
public function runTick() {
|
|
// Cache the whole research tree, we'll need that later
|
|
$this->tech->call('getTree');
|
|
|
|
// Increase neutral population
|
|
l::debug("Increasing neutral population");
|
|
$this->db->safeTransaction(array($this, 'increasePopulation'), array(null, false));
|
|
|
|
// Increase population and research
|
|
$players = $this->db->safeTransaction(array($this, 'getPlayers'));
|
|
$this->playerPopulation($players);
|
|
$this->playerResearch($players);
|
|
|
|
// Mass-update status and timers
|
|
$this->db->safeTransaction(array($this, 'statusUpdates'));
|
|
|
|
// Dry run of the rankings computation
|
|
list($players, $playersById) = $this->db->safeTransaction(
|
|
array($this->main, 'call'), array('updateRankings', true)
|
|
);
|
|
|
|
// Restore neutral planets and decrease protection levels
|
|
if ((int) $this->game->params['victory'] == 0) {
|
|
$this->db->safeTransaction(array($this, 'restoreNeutrals'));
|
|
$this->db->safeTransaction(array($this, 'decreaseProtection'));
|
|
}
|
|
|
|
// Update the data warehouse
|
|
$this->db->safeTransaction(array($this, 'updateWarehouse'), array($players, $playersById));
|
|
}
|
|
|
|
|
|
public function restoreNeutrals() {
|
|
$q = $this->db->query("SELECT p.id FROM planet p, system s "
|
|
. "WHERE p.owner IS NULL AND p.status = 0 AND s.id = p.system "
|
|
. "AND s.nebula = 0"
|
|
);
|
|
|
|
while ($r = dbFetchArray($q)) {
|
|
$this->planets->call('restoreNeutral', $r[0]);
|
|
}
|
|
}
|
|
|
|
|
|
public function decreaseProtection() {
|
|
$q = $this->db->query(
|
|
"SELECT s.id, s.prot, y.id FROM system s, player y "
|
|
. "WHERE s.prot > 0 AND s.nebula = 0 "
|
|
. "AND y.id = (SELECT DISTINCT p.owner FROM planet p "
|
|
. "WHERE p.system = s.id AND owner IS NOT NULL) "
|
|
. "FOR UPDATE OF s, y");
|
|
$removeProtection = array();
|
|
while ($r = dbFetchArray($q)) {
|
|
if ($r[1] == 1) {
|
|
$removeProtection[$r[0]] = $r[2];
|
|
}
|
|
}
|
|
|
|
$this->db->query("UPDATE system SET prot = prot - 1 WHERE prot > 1 AND nebula = 0");
|
|
foreach ($removeProtection as $system => $player) {
|
|
$this->players->call('breakProtection', $player, 'EXP');
|
|
}
|
|
}
|
|
|
|
|
|
public function getPlayers() {
|
|
$q = $this->db->query(
|
|
"SELECT p.id, (a.status <> 'STD') FROM player p, account a "
|
|
. "WHERE (p.quit IS NULL OR UNIX_TIMESTAMP(NOW()) - p.quit < 86400) "
|
|
. "AND a.id = p.userid"
|
|
);
|
|
|
|
$players = array();
|
|
while ($r = dbFetchArray($q)) {
|
|
$players[$r[0]] = ($r[1] == 't');
|
|
}
|
|
|
|
return $players;
|
|
}
|
|
|
|
|
|
private function playerResearch($players) {
|
|
l::debug("Increasing player research");
|
|
foreach ($players as $playerID => $vacation) {
|
|
$this->db->safeTransaction(array($this, 'research'), array($playerID, $vacation));
|
|
}
|
|
}
|
|
|
|
|
|
private function playerPopulation($players) {
|
|
l::debug("Increasing player population");
|
|
foreach ($players as $playerID => $vacation) {
|
|
l::trace("Increasing population for player #$playerID");
|
|
$this->db->safeTransaction(array($this, 'increasePopulation'), array($playerID, $vacation));
|
|
}
|
|
}
|
|
|
|
|
|
public function statusUpdates() {
|
|
// Laws: reduce delays to enact/revoke laws
|
|
$this->db->query("UPDATE research_player SET in_effect=in_effect-1 WHERE in_effect>1");
|
|
$this->db->query("UPDATE research_player SET in_effect=in_effect+1 WHERE in_effect<0");
|
|
|
|
// Remove research assistance
|
|
$this->db->query("UPDATE player SET res_assistance=NULL WHERE res_assistance IS NOT NULL");
|
|
|
|
// Restrictions on new players
|
|
$this->db->query("UPDATE player SET restrain=restrain-1 WHERE restrain >= 1");
|
|
|
|
// Decrease unhappiness because of Wormhole Super Novas and sales
|
|
$this->db->query("UPDATE player SET bh_unhappiness=bh_unhappiness-1 WHERE bh_unhappiness>0");
|
|
$this->db->query("UPDATE planet SET bh_unhappiness=bh_unhappiness-1 WHERE bh_unhappiness>0 AND status=0");
|
|
|
|
// Probe building
|
|
$this->db->query("UPDATE planet SET built_probe=FALSE WHERE built_probe");
|
|
}
|
|
|
|
|
|
// Advance a player's research status.
|
|
public function research($id, $onVacation) {
|
|
$q = $this->db->query("SELECT COUNT(*) FROM research_player WHERE player=$id");
|
|
list($c) = dbFetchArray($q);
|
|
if (!$c) {
|
|
if ($this->lib->game->params['partialtechs'] == 1) {
|
|
$this->tech->call('createTree', $id);
|
|
} else {
|
|
$q = $this->db->query("SELECT id FROM research");
|
|
while ($r = dbFetchArray($q)) {
|
|
$this->db->query("INSERT INTO research_player(player,research,possible) "
|
|
. "VALUES($id,{$r[0]},TRUE)");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the whole tech tree and the player's research points
|
|
$rp = $this->tech->call('getPoints',$id);
|
|
list($res, $rLeaf) = $this->tech->call('getTree', $id);
|
|
|
|
// Reduce by half if we're giving assistance
|
|
$q = $this->db->query("SELECT id FROM player WHERE res_assistance=$id");
|
|
if (dbCount($q)) {
|
|
$rp = round($rp / 2);
|
|
}
|
|
|
|
// Are we receiving assistance?
|
|
$q = $this->db->query("SELECT research,res_assistance FROM player WHERE id=$id");
|
|
list($rb, $ra) = dbFetchArray($q);
|
|
if (!is_null($ra)) {
|
|
$rp += round(min($this->tech->call('getPoints', $ra), $rp) / 2);
|
|
}
|
|
|
|
// If the player's on vacation, he gets a fourth of the RPs
|
|
if ($onVacation) {
|
|
$rp = round($rp / 4);
|
|
}
|
|
|
|
// If the player is under protection, he gets a +25% bonus
|
|
if ($this->players->call('getProtectionLevel', $id)) {
|
|
$rp = round($rp * 1.25);
|
|
}
|
|
|
|
// Divide research points between categories
|
|
$rbl = explode('!',$rb);
|
|
$rbp = array();
|
|
for ($i=$s=0;$i<3;$i++) {
|
|
$rbp[$i] = floor($rbl[$i] * $rp / 100);
|
|
$s += $rbp[$i];
|
|
}
|
|
for ($i=0;$s<$rb;$i=($i+1)%3) {
|
|
$rbp[$i] ++; $s ++;
|
|
}
|
|
$possible = array(array(), array(), array());
|
|
|
|
// Get current research topics
|
|
$q = $this->db->query("SELECT p.research,p.points FROM research_player p,research r "
|
|
. "WHERE p.research=r.id AND p.points>0 AND p.points<r.points AND p.player=$id");
|
|
$npc = array(0, 0, 0);
|
|
while ($r = dbFetchArray($q)) {
|
|
$rp = array(
|
|
'id' => $r[0],
|
|
'pts' => $r[1],
|
|
'max' => $res[$r[0]]['points']
|
|
);
|
|
$cat = $res[$r[0]]['category'];
|
|
array_push($possible[$cat], $rp);
|
|
$npc[$cat] ++;
|
|
}
|
|
|
|
// Get pending research topics
|
|
$q = $this->db->query("SELECT research FROM research_player WHERE player=$id AND points=0 AND possible");
|
|
$pending = array(array(),array(),array());
|
|
while ($r = dbFetchArray($q)) {
|
|
array_push($pending[$res[$r[0]]['category']], $r[0]);
|
|
}
|
|
|
|
// Get completed research
|
|
$q = $this->db->query("SELECT p.research FROM research_player p,research r "
|
|
. "WHERE p.research=r.id AND p.points=r.points AND (r.is_law OR p.in_effect <> 0) AND p.player=$id");
|
|
$pRes = array();
|
|
while ($r = dbFetchArray($q)) {
|
|
$pRes[$r[0]] = 1;
|
|
}
|
|
|
|
// Find topics which have their dependencies satisfied
|
|
for ($i=0;$i<3;$i++) {
|
|
foreach ($pending[$i] as $p) {
|
|
$ok = true;
|
|
foreach ($res[$p]['depends_on'] as $dep) {
|
|
$ok = $ok && $pRes[$dep];
|
|
}
|
|
if ($ok) {
|
|
array_push($possible[$i], array(
|
|
'id' => $p,
|
|
'pts' => 0,
|
|
'max' => $res[$p]['points']
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find points left for each category
|
|
$left = array(); $nrec = 0; $points = 0;
|
|
for ($i=0;$i<3;$i++) {
|
|
$max = 0;
|
|
foreach ($possible[$i] as $rt) {
|
|
$max += $rt['max'] - $rt['pts'];
|
|
}
|
|
$left[$i] = max(0, $rbp[$i] - $max);
|
|
if ($left[$i] == 0) {
|
|
$nrec ++;
|
|
} else {
|
|
$points += $left[$i];
|
|
$rbp[$i] -= $left[$i];
|
|
}
|
|
}
|
|
|
|
// Reassign extra points if possible
|
|
if ($points) {
|
|
for ($i=0;$i<3;$i++) {
|
|
if ($left[$i] == 0) {
|
|
$rbp[$i] += floor($points / $nrec);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Increment research points for existing research
|
|
for ($i=0;$i<3;$i++) {
|
|
while ($possible[$i][0]['pts'] && $npc[$i]) {
|
|
$inc = min(floor($rbp[$i] / $npc[$i]), $possible[$i][0]['max'] - $possible[$i][0]['pts']);
|
|
l::trace("Adding $inc points to research #{$possible[$i][0]['id']} for player #$id");
|
|
$this->db->query(
|
|
"UPDATE research_player SET points = points + $inc "
|
|
. "WHERE research = {$possible[$i][0]['id']} AND player = $id"
|
|
);
|
|
array_shift($possible[$i]);
|
|
$rbp[$i] -= $inc;
|
|
$npc[$i] --;
|
|
}
|
|
}
|
|
|
|
// Generate probabilities for new research: the lower a tech's required points,
|
|
// the higher its probabillity
|
|
$probabilities = array(array(), array(), array());
|
|
for ($i=0;$i<3;$i++) {
|
|
if ($rbp[$i] == 0) {
|
|
continue;
|
|
}
|
|
|
|
$sum = 0;
|
|
foreach ($possible[$i] as $idx => $t) {
|
|
$sum += $t['max'];
|
|
}
|
|
|
|
foreach ($possible[$i] as $idx => $t) {
|
|
$raw = $sum / $t['max'];
|
|
$n = ceil($raw * $raw);
|
|
for ($j=0;$j<$n;$j++) {
|
|
array_push($probabilities[$i], $idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start new research
|
|
$doneSomething = true;
|
|
$addedTechs = array(array(), array(), array());
|
|
for ($i=0;$i%3||$doneSomething;$i=($i+1)%3) {
|
|
if ($i%3==0) {
|
|
$doneSomething = false;
|
|
}
|
|
if (!$rbp[$i] || count($possible[$i]) == count($addedTechs[$i])) {
|
|
continue;
|
|
}
|
|
|
|
$doneSomething = true;
|
|
do {
|
|
$ridx = $probabilities[$i][rand(0, count($probabilities[$i]) - 1)];
|
|
} while (in_array($ridx, $addedTechs[$i]));
|
|
array_push($addedTechs[$i], $ridx);
|
|
|
|
$r = $possible[$i][$ridx];
|
|
$inc = min($rbp[$i], $r['max']);
|
|
$this->db->query(
|
|
"UPDATE research_player SET points = points + $inc "
|
|
. "WHERE research = {$r['id']} AND player = $id"
|
|
);
|
|
l::trace("Starting research {$r['id']} with $inc points for player $id");
|
|
$rbp[$i] -= $inc;
|
|
}
|
|
}
|
|
|
|
|
|
public function increasePopulation($id, $onVacation = false) {
|
|
$rules = $this->rules->call('get', $id);
|
|
if (!is_null($id) && $this->players->call('getProtectionLevel', $id)) {
|
|
$rules['pop_growth_factor'] = min($rules['pop_growth_factor'] + 1, 5);
|
|
}
|
|
$pow = 1.45 - (0.05 * $rules['pop_growth_factor']);
|
|
$vacFact = $onVacation ? 4 : 1;
|
|
|
|
$q = $this->db->query("SELECT id,pop,max_pop,happiness,corruption,mfact+ifact FROM planet "
|
|
. "WHERE owner" . (is_null($id) ? " IS NULL AND status=0" : "=$id") . " FOR UPDATE");
|
|
while ($r = dbFetchArray($q)) {
|
|
$hapVal = ($r[3] - 50) / 12;
|
|
$hapFct = exp($hapVal) / ((exp($hapVal)+1)*9);
|
|
|
|
// Population growth for players that are on vacation is 1/4th of normal
|
|
$p = round($r[1] + pow($hapFct, $pow) * $r[1] * ($r[2] - $r[1]) / ($vacFact * $r[2]));
|
|
$growthRatio = $r[1] / $p;
|
|
|
|
// Increase corruption
|
|
$cDec = 5 + 3 * $r[3] / 10;
|
|
if (!is_null($id)) {
|
|
$ncor = max(0, $r[4] - $cDec);
|
|
$x = ($r[1] - 2000) / 48000;
|
|
$optFact = ($r[1] / 30) - 754 * $x * $x;
|
|
$mCorInc = $r[5] / 3;
|
|
$nf = ($r[5] > $optFact * 3) ? ($optFact * 3) : $r[5];
|
|
$x = abs($nf / $optFact - 1);
|
|
$v = 10 + $x * $x * $x * 50;
|
|
|
|
$ncor = min(32000, $ncor + $v);
|
|
} else {
|
|
$ncor = max(0, $r[4] - $cDec * 3);
|
|
}
|
|
$ncor = round($ncor * $growthRatio * $growthRatio * $growthRatio);
|
|
|
|
$this->db->query("UPDATE planet SET pop=$p,corruption=$ncor WHERE id={$r[0]}");
|
|
$this->planets->call('updateHappiness', $r[0]);
|
|
}
|
|
}
|
|
|
|
|
|
public function updateWarehouse(&$players, &$playersById) {
|
|
// Get missing data for the warehouse:
|
|
$this->at = time();
|
|
|
|
// -> IDR:
|
|
$o = $this->rankings->call('getAll', $this->rankings->call('getType', 'p_idr'));
|
|
foreach ($o as $r) {
|
|
$players[$r['id']]['d_rank'] = $r['points'];
|
|
}
|
|
|
|
// -> Alliance tag:
|
|
$q = $this->db->query("SELECT p.id,a.tag FROM alliance a,player p "
|
|
. "WHERE (p.quit IS NULL OR UNIX_TIMESTAMP(NOW())-p.quit<86400)"
|
|
. " AND p.a_status='IN' AND p.alliance IS NOT NULL AND NOT p.hidden"
|
|
. " AND a.id=p.alliance");
|
|
while ($r = dbFetchArray($q)) {
|
|
$players[$playersById[$r[0]]]['a_tag'] = $r[1];
|
|
}
|
|
|
|
// -> Tech lists
|
|
$lsts = array();
|
|
$q = $this->db->query("SELECT p.id,r.id FROM research r,research_player j,player p "
|
|
. "WHERE (p.quit IS NULL OR UNIX_TIMESTAMP(NOW())-p.quit<86400) AND NOT p.hidden"
|
|
. " AND j.player=p.id AND j.research=r.id AND j.points=r.points");
|
|
while ($r = dbFetchArray($q)) {
|
|
if (!is_array($lsts[$r[0]])) {
|
|
$lsts[$r[0]] = array();
|
|
}
|
|
array_push($lsts[$r[0]], $r[1]);
|
|
}
|
|
foreach ($lsts as $pid => $tl) {
|
|
$players[$playersById[$pid]]['tech_list'] = join(',', $tl);
|
|
}
|
|
|
|
// -> Planet lists and details
|
|
$q = $this->db->query("SELECT p.id,r.id,r.name,r.pop,r.max_pop,r.ifact,r.mfact,r.turrets,"
|
|
. "r.happiness,r.beacon,r.abandon,r.corruption "
|
|
. "FROM planet r,player p "
|
|
. "WHERE (p.quit IS NULL OR UNIX_TIMESTAMP(NOW())-p.quit<86400) AND NOT p.hidden"
|
|
. " AND r.owner=p.id");
|
|
$plPlanets = array();
|
|
while ($r = dbFetchArray($q)) {
|
|
if (!is_array($plPlanets[$r[0]])) {
|
|
$plPlanets[$r[0]] = array();
|
|
}
|
|
array_push($plPlanets[$r[0]], array(
|
|
"planet" => $r[1],
|
|
"name" => $r[2],
|
|
"pop" => $r[3],
|
|
"max_pop" => $r[4],
|
|
"ifact" => $r[5],
|
|
"mfact" => $r[6],
|
|
"turrets" => $r[7],
|
|
"happiness" => $r[8],
|
|
"beacon" => $r[9],
|
|
"abandon" => $r[10],
|
|
"corruption" => $r[11]
|
|
));
|
|
}
|
|
|
|
|
|
// Update the warehouse
|
|
foreach ($players as $name => $data) {
|
|
$pid = $data['player'];
|
|
if (is_null($pid)) {
|
|
l::warn("****** BUG: null player ID in the warehouse update loop");
|
|
continue;
|
|
}
|
|
foreach ($data as $k => $v) {
|
|
$data[$k] = "'" . addslashes($v) . "'";
|
|
}
|
|
if (is_null( $data['tech_list'] )) {
|
|
$data['tech_list'] = "''";
|
|
}
|
|
|
|
$qs = "INSERT INTO warehouse(" . join(',', array_keys($data))
|
|
. ") VALUES (" . join(',', array_values($data)) . ")";
|
|
$this->db->query($qs);
|
|
|
|
$q = $this->db->query("SELECT id FROM warehouse WHERE tick_at={$this->at} AND player=$pid");
|
|
list($id) = dbFetchArray($q);
|
|
if (!$id || is_null($plPlanets[$pid])) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($plPlanets[$pid] as $pdata) {
|
|
$pdata['id'] = $id;
|
|
|
|
foreach ($pdata as $k => $v) {
|
|
$pdata[$k] = "'" . addslashes($v == '' ? '0' : $v) . "'";
|
|
}
|
|
|
|
$qs = "INSERT INTO wh_planet(" . join(',', array_keys($pdata))
|
|
. ") VALUES (" . join(',', array_values($pdata)) . ")";
|
|
$this->db->query($qs);
|
|
}
|
|
}
|
|
$this->db->query("DELETE FROM warehouse WHERE UNIX_TIMESTAMP(NOW())-tick_at>86400*60");
|
|
}
|
|
}
|
|
|
|
|
|
?>
|