This repository has been archived on 2024-07-18. You can view files and clone it, but cannot push or open issues or pull requests.
lwb5/scripts/game/beta5/ticks/universe/library.inc

483 lines
13 KiB
PHP

<?php
//---------------------------------------------------------------
// "Universe tick": generates sections of the universe
// Executed once every 5 minutes, starting from 7:02:00 ST
//---------------------------------------------------------------
class beta5_ticks_universe_library {
public function __construct($lib) {
$this->lib = $lib;
$this->game = $this->lib->game;
$this->db = $this->game->db;
$this->main = $this->game->getLib('main');
$this->planets = $this->game->getLib('beta5/planet');
}
public function runTick() {
$this->db->safeTransaction(array($this, 'genUniverse'));
}
public function genUniverse() {
// Run a special version of the tick if we are using a CTF map
$map = (int) $this->game->params['usemap'];
if ($map > 0) {
$this->handleCTFMap($map);
return;
}
// Read the game's parameters
$np = $this->game->params['nebulaprob'];
$this->nebulaProb = ($np > -1 && $np < 21) ? $np : 2;
$this->minSystems = $this->game->params['minsystems'];
$this->maxSystems = $this->game->params['maxsystems'];
$zs = $this->game->params['zonesize'];
$this->zoneSize = ($zs <= 0 || $zs > 10) ? 2 : $zs;
// If there is a maximum value, check it
if ($this->maxSystems) {
$ns = $this->getAllSystems();
if ($ns >= $this->maxSystems) {
return;
}
}
// Get the amount of free systems
$this->reassignEmpty();
$ns = $this->getFreeSystems();
if ($ns >= $this->minSystems) {
return;
}
// Get the latest system ID
$q = $this->db->query("SELECT MAX(id) FROM system");
list($id1) = dbFetchArray($q);
// Read the universe generator's parameters
list($dir, $size) = $this->getSystemGenParm();
$ldir = $dir; $lsize = $lsize;
// Generate zones
while ($ns < $this->minSystems) {
list($x1, $y1, $x2, $y2) = $this->getNextRectangle($dir, $size);
$this->makeZone($x1, $y1, $x2, $y2);
$ns = $this->getFreeSystems();
}
if ($ldir != $dir || $lsize != $size) {
// Update the universe generator's parameters
if ($ldir != $dir) {
$this->db->query("UPDATE gdata SET value='$dir' WHERE id='sg_dir'");
}
if ($lsize != $size) {
$this->db->query("UPDATE gdata SET value='$size' WHERE id='sg_len'");
}
// Make sure we have a first ID
if (is_null($id1)) {
$q = $this->db->query("SELECT MIN(id) FROM system");
list($id1) = dbFetchArray($q);
} else {
$id1++;
}
// Read the last ID
$q = $this->db->query("SELECT MAX(id) FROM system");
list($id2) = dbFetchArray($q);
// Generate the systems' planets
for ($i=$id1;$i<=$id2;$i++) {
$this->genSystem($i);
}
// Get the size of the next area
list($x1,$y1,$x2,$y2) = $this->getNextRectangle($dir, $size);
$w = abs($x2 - $x1) + 1;
$h = abs($y2 - $y1) + 1;
$q = $this->db->query("SELECT MAX(id) FROM planet");
list($idp) = dbFetchArray($q);
// Generate new planets
$this->main->call('requestGenPlanets', $idp + 1, ($w + 5) * $h * 6);
}
}
private function handleCTFMap($mapID) {
// Check if there are systems
$q = $this->db->query("SELECT COUNT(*) FROM system");
if (!($q && dbCount($q))) {
return;
}
list($nSys) = dbFetchArray($q);
if ($nSys > 0) {
return;
}
// No systems -> we must generate the map
// Start by loading the template's definition
$q = $this->db->query("SELECT width,height FROM ctf_map_def WHERE id=$mapID");
if (!($q && dbCount($q))) {
logText("Could not load map definition #$mapID", LOG_ERR);
return;
}
list($width, $height) = dbFetchArray($q);
// Load the template's layout
$q = $this->db->query("SELECT * FROM ctf_map_layout WHERE map=$mapID");
if (!($q && dbCount($q) == $width * $height)) {
logText("Could not load map layout for template #$mapID", LOG_ERR);
return;
}
// Create systems and generate CTF-specific data along the way
$ids = array();
$targets = array();
while ($row = dbFetchHash($q)) {
// Insert the map system
$qStr = "INSERT INTO system(x,y,prot,assigned,nebula) VALUES ("
. "{$row['sys_x']},{$row['sys_y']},0,FALSE,"
. (($row['sys_type'] == 'S') ? "0" : $row['sys_type']) . ")";
$sys = $this->db->query($qStr);
if (!$sys) {
logText("Error inserting system at ({$row['sys_x']},{$row['sys_y']})", LOG_ERR);
return;
}
array_push($ids, $sys);
if ($row['sys_type'] != 'S') {
continue;
}
if ($row['alloc_for'] == 0) {
// Insert a target record
$this->db->query("INSERT INTO ctf_target(system) VALUES ($sys)");
array_push($targets, $sys);
} else {
// Insert an alliance area record
$this->db->query("INSERT INTO ctf_alloc(system,alliance,spawn_point) VALUES ("
. "$sys,{$row['alloc_for']},'{$row['spawn_here']}')");
}
}
// Generate system contents
foreach ($ids as $sys) {
$this->genSystem($sys);
}
// Upgrade target planets
$q = $this->db->query("SELECT id FROM planet WHERE system IN (" . join(',', $targets) . ")");
while ($r = dbFetchArray($q)) {
$q = $this->db->query("UPDATE planet SET ifact=33, mfact=33, turrets=90 WHERE id = {$r[0]}");
$this->planets->call('updateHappiness', $r[0]);
}
}
private function reassignEmpty() {
$q = $this->db->query("SELECT s.id,COUNT(p.*) AS cnt FROM system s,planet p "
. "WHERE s.assigned AND s.nebula=0 AND p.owner IS NULL AND p.status=0 AND p.system=s.id "
. "GROUP BY s.id HAVING COUNT(p.*)=6");
if (!($q && dbCount($q))) {
return;
}
$sids = array();
while ($r = dbFetchArray($q)) {
$allocate = true;
$q2 = $this->db->query("SELECT id FROM planet WHERE system = {$r[0]}");
while ($allocate && $r2 = dbFetchArray($q2)) {
$allocate = $this->planets->call('canAllocate', $r2[0]);
}
if ($allocate) {
array_push($sids, $r[0]);
}
}
if (empty($sids)) {
return;
}
$q = $this->db->query("SELECT DISTINCT p.system FROM fleet f,planet p "
. "WHERE f.location IS NOT NULL AND f.location=p.id AND p.system IN (" . join(',', $sids) . ")");
if (!$q) {
return;
}
$flids = array();
while ($r = dbFetchArray($q)) {
array_push($flids, $r[0]);
}
$rsids = array();
foreach ($sids as $sid) {
if (!in_array($sid, $flids)) {
array_push($rsids, $sid);
logText("beta5/universe: system $sid will be reassigned");
}
}
if (empty($rsids)) {
return;
}
$this->db->query("UPDATE system SET assigned=FALSE WHERE id IN (" . join(',', $rsids) . ")");
}
private function getAllSystems() {
$q = $this->db->query("SELECT COUNT(*) FROM system WHERE nebula = 0");
list($n) = dbFetchArray($q);
return $n;
}
private function getFreeSystems() {
$q = $this->db->query("SELECT COUNT(*) FROM system WHERE NOT assigned AND nebula = 0");
list($n) = dbFetchArray($q);
return $n;
}
private function getSystemGenParm() {
$q = $this->db->query("SELECT id,value FROM gdata WHERE id IN ('sg_dir','sg_len')");
$rs = array();
while ($r = dbFetchArray($q)) {
$rs[$r[0] == "sg_dir" ? 0 : 1] = $r[1];
}
return $rs;
}
private function getNextRectangle(&$dir, &$size) {
$zs = $this->zoneSize;
$zsT = 1 + 2*$zs;
$zsI = $zsT - 1;
if ($dir < 2) {
//$x1 = -2 - 5*$size/2;
$x1 = -$zs - $zsT * $size / 2;
// $y1 = ($dir == 0) ? (-2 - 5*$size/2) : (3 + 5*$size/2);
$y1 = ($dir == 0) ? (-$zs - $zsT * $size / 2) : ($zs + 1 + $zsT * $size / 2);
//$x2 = $x1 + 4 + (($dir == 1) ? $size * 5 : 0);
$x2 = $x1 + $zs * 2 + (($dir == 1) ? $size * $zsT : 0);
//$y2 = $y1 + 4 + (($dir == 0) ? $size * 5 : 0);
$y2 = $y1 + $zs * 2 + (($dir == 0) ? $size * $zsT : 0);
} else {
$m = $size % 2;
//$x2 = 7 + 5*($size-$m)/2;
$x2 = $zsT + $zs + $zsT * ($size-$m) / 2;
//$y2 = ($dir == 2) ? (7 + 5*($size-$m)/2) : (-3 - 5*($size-$m)/2);
$y2 = ($dir == 2) ? ($zsT + $zs + $zsT * ($size - $m) / 2) : (- $zs - 1 - $zsT * ($size - $m) /2);
//$x1 = $x2 - 4 - (($dir == 3) ? $size * 5 : 0);
$x1 = $x2 - $zsT + 1 - (($dir == 3) ? $size * $zsT : 0);
//$y1 = $y2 - 4 - (($dir == 2) ? $size * 5 : 0);
$y1 = $y2 - $zsT + 1 - (($dir == 2) ? $size * $zsT : 0);
}
$dir = ($dir + 1) % 4;
if ($dir % 2 == 0) {
$size ++;
}
return array($x1,$y1,$x2,$y2);
}
private function findBorderNebulas($x1, $y1, $x2, $y2) {
$x1--;$y1--;$x2++;$y2--;
$q = $this->db->query(
"SELECT COUNT(*) FROM system WHERE nebula > 0 AND ("
. "((x=$x1 OR x=$x2) AND y>=$y1 AND y<=$y2) OR ((y=$y1 OR y=$y2) AND x>$x1 AND x<$y2))"
);
list($c) = dbFetchArray($q);
return $c;
}
private function getAdjacentNebula($x, $y) {
list($x1,$x2,$y1,$y2) = array($x+1, $x-1, $y+1, $y-1);
$q = $this->db->query(
"SELECT SUM(nebula) FROM system "
. "WHERE (x=$x AND y=$y1) OR (x=$x AND y=$y2) OR (x=$x1 AND y=$y) OR (x=$x2 AND y=$y)"
);
list($r) = dbFetchArray($q);
return min((int)$r, 4);
}
private function nebulaPass($x1, $y1) {
$ds = array(0,1,1,1,2,2,2,2,2,3);
$c = 0;
$zsT = $this->zoneSize * 2 + 1;
for ($x = $x1; $x < $x1 + $zsT; $x++) {
for ($y = $y1; $y < $y1 + $zsT; $y++) {
$q = $this->db->query("SELECT assigned,nebula FROM system WHERE x=$x AND y=$y");
list($as,$nb) = dbFetchArray($q);
if ($as == 'f' || $nb > 0)
continue;
$ap = $this->getAdjacentNebula($x,$y);
if ($ap == 0) {
continue;
}
$ap -= $ds[rand(0,9)];
if ($ap <= 0) {
$this->db->query("UPDATE system SET assigned=FALSE,nebula=0 WHERE x=$x AND y=$y");
} else {
$this->db->query("UPDATE system SET nebula=$ap WHERE x=$x AND y=$y");
}
$c = 1;
}
}
return $c;
}
private function makeNebulas($x1,$y1) {
do {
$c = $this->nebulaPass($x1,$y1);
} while ($c);
}
private function makeCluster($x1,$y1) {
$np = array(1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,4);
$zs = $this->zoneSize * 2;
$x = $x1 + $zs; $y = $y1 + $zs;
$nl = $this->findBorderNebulas($x1,$y1,$x,$y);
$zsT = $zs + 1;
for ($x = $x1; $x < $x1 + $zsT; $x++) {
for ($y = $y1; $y < $y1 + $zsT; $y++) {
if (rand(0,99) <= $this->nebulaProb && $nl == 0) {
$v = $np[rand(0,19)];
$nl ++;
} else {
$v = 0;
}
$this->db->query("INSERT INTO system(x,y,prot,assigned,nebula) VALUES($x,$y,0,TRUE,$v)");
}
}
if ($nl > 0) {
$this->makeNebulas($x1,$y1);
}
$this->db->query("UPDATE system SET assigned=FALSE WHERE x>=$x1 AND y>=$y1 AND x<$x AND y<$y AND nebula=0");
}
private function makeZone($x1,$y1,$x2,$y2) {
$zsT = $this->zoneSize * 2 + 1;
for ($x=$x1;$x<$x2;$x+=$zsT) {
for ($y=$y1;$y<$y2;$y+=$zsT) {
$this->makeCluster($x,$y);
}
}
}
private function genPlanets($system) {
$npl = 6;
// Generate the turrets
$ttn = 17;
$i = -1;
$tna = array();
for ($i=0;$i<$npl;$i++) {
$tna[$i] = 3;
}
while ($ttn) {
$i = ($i + 1) % $npl;
if ($tna[$i] == 8) {
continue;
}
$a = rand(0,1);
$ttn -= $a; $tna[$i] += $a;
}
// Generate the planets' maximum populations for each tech level
$mpa = array();
$levels = 4;
for ($tl=0;$tl<$levels;$tl++) {
$minPPop = 8500 + $tl * 10000;
$maxPPop = 11500 + $tl * 10000;
$ttp = rand(8800,9200);
$mpa[$tl] = array();
for ($i=0;$i<$npl;$i++) {
$mpa[$tl][$i] = $minPPop;
}
while ($ttp) {
$i = ($i + 1) % $npl;
if ($mpa[$tl][$i] >= $maxPPop) {
continue;
}
$a = rand(1, min($maxPPop - $mpa[$tl][$i], $ttp));
$ttp -= $a;
$mpa[$tl][$i] += $a;
}
}
// Create the planets
for ($i=0;$i<$npl;$i++) {
$qs = "INSERT INTO planet(system,orbit,name,turrets,max_pop) VALUES($system,$i,";
do {
$rn = strtoupper(substr(md5(uniqid(rand())), 0, 7));
$q = $this->db->query("SELECT id FROM planet WHERE name='P-[$rn]'");
} while(dbCount($q));
$qs .= "'P-[$rn]',{$tna[$i]},{$mpa[0][$i]})";
$this->db->query($qs);
}
// Compute the new planets' happiness and store their maximal populations
$q = $this->db->query("SELECT id FROM planet WHERE system=$system");
$np = 0;
while ($r = dbFetchArray($q)) {
for ($i=0;$i<$levels;$i++) {
$this->db->query("INSERT INTO planet_max_pop(planet,tech_level,max_pop) VALUES ({$r[0]},$i,{$mpa[$i][$np]})");
}
$this->planets->call('updateHappiness', $r[0]);
$np ++;
}
}
private function genNebulaOrbit($system, $n) {
$npl = 6;
for ($i=0;$i<$npl;$i++) {
$qs = "INSERT INTO planet(system,orbit,name,status,pop,max_pop,ifact,mfact,turrets,happiness) VALUES($system,$i,";
do {
$rn = strtoupper(substr(md5(uniqid(rand())), 0, 10));
$q = $this->db->query("SELECT id FROM planet WHERE name='NB-[$rn]'");
} while(dbCount($q));
$md = min(4, max($n + rand(0,2) - 1, 1)) + 1;
$qs .= "'NB-[$rn]',$md,0,0,0,0,0,0)";
$this->db->query($qs);
}
}
private function genSystem($i) {
$q = $this->db->query("SELECT nebula FROM system WHERE id=$i");
list($n) = dbFetchArray($q);
if ($n == 0) {
$this->genPlanets($i);
} else {
$this->genNebulaOrbit($i, $n);
}
}
}
?>