Added full source code

This commit is contained in:
Emmanuel BENOîT 2016-01-10 11:01:49 +01:00
commit 33f8586698
1377 changed files with 123808 additions and 0 deletions
scripts/game/beta5/ticks

View file

@ -0,0 +1,521 @@
<?php
//-------------------------------------------------------------------
// "Battle tick": computes battle outcomes
//-------------------------------------------------------------------
class beta5_ticks_battle_library {
function beta5_ticks_battle_library($lib) {
$this->lib = $lib;
$this->db = $lib->game->db;
$this->fleets = $lib->game->getLib('beta5/fleet');
$this->msgs = $lib->game->getLib('beta5/msg');
$this->planets = $lib->game->getLib('beta5/planet');
$this->players = $lib->game->getLib('beta5/player');
$this->rules = $lib->game->getLib('beta5/rules');
$this->sales = $lib->game->getLib('beta5/sale');
$this->rankings = $lib->game->getLib('main/rankings');
}
function runTick() {
$this->idrInc = array();
$toThrow = null;
try {
$locations = $this->db->safeTransaction(array($this, 'getBattleLocations'));
foreach ($locations as $lId) {
$this->db->safeTransaction(array($this, 'battleAt'), array($lId), 5);
}
} catch (Exception $e) {
$toThrow = $e;
}
$this->db->safeTransaction(array($this, 'updateStatus'));
if (!is_null($toThrow)) {
throw $toThrow;
}
$this->db->safeTransaction(array($this, 'updateRankings'));
}
public function getBattleLocations() {
$q = $this->btQuery(
"SELECT DISTINCT location FROM fleet WHERE location IS NOT NULL AND attacking"
);
$locations = array();
while ($r = dbFetchArray($q)) {
array_push($locations, $r[0]);
}
return $locations;
}
public function updateStatus() {
// Mark fleets that were in battle as available and commit
$this->btQuery("UPDATE fleet SET can_move='Y' WHERE can_move='B'");
}
public function updateRankings() {
// Increase inflicted damage points
$rt = $this->rankings->call('getType', 'p_idr');
$rl = $this->rankings->call('getAll', $rt);
$idr = array();
foreach ($rl as $r) {
$idr[$r['id']] = $r['points'];
}
foreach ($this->idrInc as $n => $inc) {
$idr[$n] += $inc;
}
$idrR = array();
foreach ($idr as $n => $p) {
if (!is_array($idrR[$p])) {
$idrR[$p] = array();
}
array_push($idrR[$p], $n);
}
$this->rankings->call('update', $rt, $idrR);
}
public function battleAt($lId) {
// Execute computations
$idrInc = $this->battleComputation($lId);
// Increase stored IDR
foreach ($idrInc as $n => $ii) {
if (is_null($this->idrInc[$n])) {
$this->idrInc[$n] = $ii;
} else {
$this->idrInc[$n] += $ii;
}
}
}
private function battleComputation($lId) {
static $rnames = array('turret', 'gaship', 'fighter', 'cruiser', 'bcruiser');
static $addLosses = array(.5, .25, 1, .25, .125);
// Get the ID of the planet's owner and the amount of turrets
$q = $this->btQuery("SELECT name,owner,turrets,vacation FROM planet WHERE id=$lId "
. "FOR UPDATE");
list($pName,$pOwner,$pTurrets,$vacation) = dbFetchArray($q);
$poFake = is_null($pOwner) ? 0 : $pOwner;
// Compute the power of the planet's turrets
if ($vacation == 'YES ') {
$defPower = 0;
$pTurrets = 0;
} else {
$defPower = $this->planets->call('getPower', $lId);
}
$turretPower = $defPower;
// Get all fleets at that location
$q = $this->btQuery("SELECT * FROM fleet WHERE location=$lId FOR UPDATE");
$attFleets = array(0,0,0,0,0,0,0,0);
$defFleets = array(0,0,0,0,0,0,0,0);
$attPlayers = $defPlayers = 0;
$fleets = array();
$players = array(
$poFake => array(false, $turretPower, $pTurrets, 0, 0, 0, 0, array())
);
$attPower = 0;
$cSales = null;
// Extract fleet data
while ($r = dbFetchHash($q)) {
if ( ($r['owner'] == $pOwner && $vacation == 'YES ')
|| ($r['attacking'] == 't' && $r['time_spent'] < 15) ) {
continue;
}
$r['power'] = $this->fleets->call('getPower', $r['owner'], $r['gaships'],
$r['fighters'], $r['cruisers'], $r['bcruisers']);
if ($r['attacking'] == 't') {
$attFleets[0] += $r['gaships'];
$attFleets[1] += $r['fighters'];
$attFleets[2] += $r['cruisers'];
$attFleets[3] += $r['bcruisers'];
$attPower += $r['power'];
} else {
$defFleets[0] += $r['gaships'];
$defFleets[1] += $r['fighters'];
$defFleets[2] += $r['cruisers'];
$defFleets[3] += $r['bcruisers'];
$defPower += $r['power'];
}
if (is_null($players[$r['owner']])) {
$players[$r['owner']] = array(
$r['attacking'] == 't', $r['power'], 0, $r['gaships'],
$r['fighters'], $r['cruisers'], $r['bcruisers'], array()
);
if ($r['attacking'] == 't') {
$attPlayers ++;
} else {
$defPlayers ++;
}
} else {
$players[$r['owner']][1] += $r['power'];
$players[$r['owner']][3] += $r['gaships'];
$players[$r['owner']][4] += $r['fighters'];
$players[$r['owner']][5] += $r['cruisers'];
$players[$r['owner']][6] += $r['bcruisers'];
}
array_push($players[$r['owner']][7], $r['id']);
$fleets[$r['id']] = $r;
}
// If there is no defense at all, forget it
// (that can also mean that the local defense fleet is on vacation)
// Also skip if there are no attack fleets (could happen because of separate transactions)
if ($defPower == 0 || $attPower == 0) {
l::debug("Skipped battle on $pName, no fleets");
return array();
}
l::debug("Starting battle on $pName (owner: $poFake); $pTurrets turrets, vacation=$vacation");
// Compute the damage index, which is the proportion of damage inflicted by the biggest fleet
$dRandom = $defPower;
while ($dRandom > 1000) {
$dRandom = $dRandom / 10;
}
$rdPower = $defPower + rand(0, round($dRandom * 0.05));
l::debug("Defense: power= $defPower ; random= $dRandom; rPower= $rdPower");
$aRandom = $attPower;
while ($aRandom > 1000) {
$aRandom = $aRandom / 10;
}
$raPower = $attPower + rand(0, round($aRandom * 0.05));
l::debug("Attack: power= $attPower ; random= $aRandom; rPower= $raPower");
$bigDef = ($rdPower > $raPower);
$bigPower = $bigDef ? $rdPower : $raPower;
$smallPower = $bigDef ? $raPower : $rdPower;
$ratio = $bigPower / $smallPower;
$damageIndex = ($ratio > 10 ? 1 : ((exp($ratio - 1) / (exp($ratio - 1) + 1))));
$attDamage = round(($bigDef ? $damageIndex : (1 - $damageIndex)) * $attPower);
$defDamage = round(($bigDef ? (1-$damageIndex) : $damageIndex) * $defPower);
l::debug("Damage index: $damageIndex (attDamage: $attDamage ; defDamage: $defDamage)");
// Handle heroic defense
if (! $bigDef && $ratio >= 5 && rand(0, 10000) <= 200) {
$heroicDefense = true;
$addDamage = ceil($smallPower * rand(300, 400) / 100);
if ($addDamage > $bigPower / 5) {
$addDamage = ceil($bigPower / 5);
}
$attDamage += $addDamage;
l::debug("Heroic defense! Damage increased by $addDamage (-> $attDamage)");
} else {
$heroicDefense = false;
}
// Compute the amount of damage to each player
$defLosses = $attLosses = 0;
$plist = array_keys($players);
$turretLoss = $tPowerLoss = 0;
foreach ($plist as $id) {
l::debug(" -> Player $id");
if ($players[$id][0])
$losses = ($players[$id][1] / $attPower) * $attDamage;
else
$losses = ($players[$id][1] / $defPower) * $defDamage;
l::debug(" * losses = $losses");
$rules = $this->rules->call('get', ($id == 0) ? null : $id);
if ($damageIndex < 1 || ($players[$id][0] && !$bigDef || !$players[$id][0] && $bigDef)) {
$losses = ($losses / 100) * $rules['battle_losses'];
l::debug(" * losses = $losses after adjustment ({$rules['battle_losses']}%)");
} else {
l::debug(" * losses not adjusted");
}
// Compute damage for each type of ship
$probLoss = array(0, 0, 0, 0, 0); $oriFleet = array();
if ($players[$id][1] > 0) {
$sPowers = array(); $power = array();
$tLoss = 0;
$lossRatio = $losses / $players[$id][1];
for ($i=0;$i<5;$i++) {
$sPowers[$i] = ($rules[$rnames[$i] . "_power"] / 100)
* $rules['effective_fleet_power'];
$oriFleet[$i] = $players[$id][$i+2];
$power[$i] = $sPowers[$i] * $players[$id][$i+2];
$shipRatio = $power[$i] / $players[$id][1];
$pLoss = $shipRatio * $losses;
$probLoss[$i] = min($players[$id][$i+2], round($pLoss / $sPowers[$i]));
$tLoss += $probLoss[$i] * $sPowers[$i];
}
$i = $n = 0;
while ($tLoss < $losses && $n < 5) {
if ($probLoss[$i] < $players[$id][$i+2]) {
$probLoss[$i] ++;
$tLoss += $sPowers[$i];
$n = 0;
} else {
$n++;
}
$i = ($i + 1) % 5;
}
}
l::debug(" * ship losses (T/G/F/C/B) = " . join(', ', $probLoss)
. " out of " . join(', ', $oriFleet));
// If there are turret losses, remove turrets
if ($probLoss[0] > 0) {
$turretLoss = $probLoss[0];
$tPowerLoss = $this->planets->call('getPower', $pOwner, $turretLoss);
$this->btQuery("UPDATE planet SET turrets=turrets-$turretLoss WHERE id=$lId");
$tm = time();
$this->btQuery("DELETE FROM turhist WHERE $tm-moment>86400");
$this->btQuery("INSERT INTO turhist VALUES ($lId,$tm,-$turretLoss)");
// Mark the planet's sale to be cancelled if that applies
$q = $this->btQuery("SELECT id,player,finalized,sold_to FROM sale WHERE planet=$lId");
if (($r = dbFetchArray($q)) && is_null($cSales)) {
$cSales = $r;
}
}
// Apply losses to the player's individual fleets
$removed = array(0, 0, 0, 0);
foreach ($players[$id][7] as $fid) {
$rff = $tif = 0;
$rem = array();
for ($i=0;$i<4;$i++) {
// Ships that must be destroyed
$toRemove = $probLoss[$i+1] - $removed[$i];
// Ships in the fleet
$inFleet = $fleets[$fid][$rnames[$i+1]."s"];
$tif += $inFleet;
// Remove ships
$fromFleet = min($toRemove, $inFleet);
$removed[$i] += $fromFleet;
$rff += $fromFleet;
$rem[$i] = $fromFleet;
}
l::debug(" * fleet $fid losses (G/F/C/B) = " . join(', ', $rem));
// Mark the fleet's sale to be cancelled if that applies
if ($rff) {
$q = $this->btQuery("SELECT id,player,finalized,sold_to FROM sale WHERE fleet=$fid");
if ($r = dbFetchArray($q)) {
if (is_null($r[2])) {
$ga = 'cancel';
} else {
$ga = 'cancelTransfer';
}
$this->sales->call($ga, $r[1], $r[0]);
// FIXME: send messages
if (!is_null($cSales) && $cSales == $r[0]) {
$cSales = null;
}
}
}
if ($rff == $tif) {
// The whole fleet has been lost
$this->btQuery("DELETE FROM fleet WHERE id=$fid");
} elseif ($rff) {
// Fleet has suffered some losses
$qs = "UPDATE fleet SET ";
$qsi = false;
for ($i=0;$i<4;$i++) {
if ($rem[$i] == 0) {
continue;
}
if ($qsi) {
$qs .= ",";
} else {
$qsi = true;
}
$qs .= $rnames[$i+1]."s=".$rnames[$i+1]."s-".$rem[$i];
}
$qs .= " WHERE id=$fid";
$this->btQuery($qs);
} else {
// No losses, we're done
break;
}
}
// Add losses to the correct array
if ($players[$id][0]) {
for ($i=0;$i<4;$i++) {
$attFleets[$i+4] += $probLoss[$i+1];
}
} else {
for ($i=0;$i<4;$i++) {
$defFleets[$i+4] += $probLoss[$i+1];
}
}
// Store the player's losses
$lostPower = $this->fleets->call('getPower', ($id==0 ? null : $id), $probLoss[1], $probLoss[2], $probLoss[3], $probLoss[4]);
$lostPower += $this->planets->call('getPower', ($id==0 ? null : $id), $probLoss[0]);
array_shift($probLoss);
$players[$id][8] = $probLoss;
$players[$id][9] = $lostPower;
if ($players[$id][0]) {
$attLosses += $lostPower;
} else {
$defLosses += $lostPower;
}
}
// Cancel the planet's sale if it suffered damage
if (!is_null($cSales)) {
if (is_null($cSales[2])) {
$ga = 'cancel';
} else {
$ga = 'cancelTransfer';
}
$this->sales->call($ga, $cSales[1], $cSales[0]);
// FIXME: send messages
}
// Give the players "inflicted damage" points
$idrInc = array();
foreach ($plist as $id) {
if ($id == 0) {
continue;
}
$q = $this->btQuery("SELECT hidden FROM player WHERE id = $id");
list($hidden) = dbFetchArray($q);
if ($hidden == 't') {
continue;
}
if ($players[$id][0]) {
$tPower = $attPower;
$eLoss = $defLosses;
} else {
$tPower = $defPower;
$eLoss = $attLosses;
}
$ii = round(($players[$id][1] / $tPower) * $eLoss);
$n = $this->players->call('getName', $id);
if (is_null($idrInc[$n])) {
$idrInc[$n] = $ii;
} else {
$idrInc[$n] += $ii;
}
}
// Send battle reports
$tm = time();
foreach ($plist as $id) {
// Avoid the fake "neutral" player
if ($id == 0) {
continue;
}
$p = $players[$id];
// Get friendly/hostile data
if ($players[$id][0]) {
$fPower = $attPower;
$ePower = $defPower;
$fLosses = $attLosses;
$eLosses = $defLosses;
$fFleets = $attFleets;
$eFleets = $defFleets;
$tMode = ($pTurrets != 0 ? 3 : 0);
} else {
$fPower = $defPower;
$ePower = $attPower;
$fLosses = $defLosses;
$eLosses = $attLosses;
$fFleets = $defFleets;
$eFleets = $attFleets;
$tMode = ($pTurrets != 0 ? ($pOwner == $id ? 1 : 2) : 0);
}
// Remove the player's own statistics from the list of friendlies
$fPower -= $p[1]; $fLosses -= $p[9];
for ($i=0;$i<4;$i++) {
$fFleets[$i] -= $p[$i+3];
$fFleets[$i+4] -= $p[8][$i];
}
// Send battle report
$this->msgs->call('send', $id, "battle", array(
"planet_id" => $lId,
"planet" => $pName,
"o_gaships" => $p[3],
"o_fighters" => $p[4],
"o_cruisers" => $p[5],
"o_bcruisers" => $p[6],
"o_power" => $p[1],
"ol_gaships" => $p[8][0],
"ol_fighters" => $p[8][1],
"ol_cruisers" => $p[8][2],
"ol_bcruisers" => $p[8][3],
"ol_power" => $p[9],
"a_gaships" => $fFleets[0],
"a_fighters" => $fFleets[1],
"a_cruisers" => $fFleets[2],
"a_bcruisers" => $fFleets[3],
"a_power" => $fPower,
"al_gaships" => $fFleets[4],
"al_fighters" => $fFleets[5],
"al_cruisers" => $fFleets[6],
"al_bcruisers" => $fFleets[7],
"al_power" => $fLosses,
"e_gaships" => $eFleets[0],
"e_fighters" => $eFleets[1],
"e_cruisers" => $eFleets[2],
"e_bcruisers" => $eFleets[3],
"e_power" => $ePower,
"el_gaships" => $eFleets[4],
"el_fighters" => $eFleets[5],
"el_cruisers" => $eFleets[6],
"el_bcruisers" => $eFleets[7],
"el_power" => $eLosses,
"turrets" => $pTurrets,
"tpower " => $turretPower,
"l_turrets" => $turretLoss,
"l_tpower" => $tPowerLoss,
"tmode" => $tMode,
"heroic_def" => $heroicDefense ? ($players[$id][0] ? -1 : 1) : 0
));
}
// Update happiness and attack status
$this->planets->call('updateHappiness', $lId);
$this->planets->call('updateMilStatus', $lId);
// If the planet was pending entrance in vacation mode and all enemy
// fleet is dead, set it to vacation mode.
if ($vacation == 'PEND') {
$q = $this->btQuery("SELECT COUNT(*) FROM fleet WHERE location=$lId AND attacking");
if ($q && dbCount($q) == 1) {
list($c) = dbFetchArray($q);
if ($c == 0) {
$this->btQuery("UPDATE planet SET vacation='YES' WHERE id=$lId");
}
}
}
return $idrInc;
}
function btQuery($q) {
$r = $this->db->query($q);
l::trace("Result '$r' for query: $q");
return $r;
}
}
?>

View file

@ -0,0 +1,250 @@
<?php
//------------------------------------------------------
// "Cash tick": Income computations & fleet destruction
//------------------------------------------------------
class beta5_ticks_cash_library {
public function __construct($lib) {
$this->lib = $lib;
$this->game = $this->lib->game;
$this->db = $this->game->db;
$this->fleets = $this->game->getLib('beta5/fleet');
$this->msgs = $this->game->getLib('beta5/msg');
$this->planets = $this->game->getLib('beta5/planet');
$this->players = $this->game->getLib('beta5/player');
$this->rules = $this->game->getLib('beta5/rules');
}
public function runTick() {
// Update time required to abandon planets
$this->db->safeTransaction(array($this, "handleAbandon"));
// Handle the players' income
$players = $this->db->safeTransaction(array($this, "getPlayers"));
foreach ($players as $player) {
$this->db->safeTransaction(array($this, "playerIncome"), $player);
}
// Handle CTF victory
$this->db->safeTransaction(array($this, "checkCTF"));
}
public function checkCTF() {
// Check victory status on CTF games
if ($this->game->params['victory'] == 2 && ! $this->game->getLib()->call('isFinished')) {
$this->game->getLib('beta5/ctf')->call('checkTargets');
}
}
public function getPlayers() {
$q = $this->db->query(
"SELECT id FROM player "
. "WHERE quit IS NULL "
. "OR UNIX_TIMESTAMP(NOW()) - quit < 86400"
);
$players = array();
while ($r = dbFetchArray($q)) {
array_push($players, $r[0]);
}
return $players;
}
public function playerIncome($id) {
$q = $this->db->query(
"SELECT p.cash, (a.status = 'VAC') FROM player p,account a "
. "WHERE p.id = $id AND a.id = p.userid "
. "FOR UPDATE OF p"
);
list($cash, $vac) = dbFetchArray($q);
$protected = $this->players->call('getProtectionLevel', $id);
$div = ($vac == 't' ? 4 : 1);
$mult = ($protected ? 1.15 : 1);
$income = round($this->computeIncome($id) * $mult / $div);
$upkeep = round($this->computeUpkeep($id) / $div);
$profit = ceil($income - $upkeep) / 2;
$cash += $profit;
if ($cash < 0) {
$this->debt($id, $income, $upkeep, $cash);
$cash = 0;
}
$this->db->query("UPDATE player SET cash=$cash WHERE id=$id");
}
private function computeIncome($player) {
$income = 0;
$ppl = $this->players->call('getPlanets', $player);
foreach ($ppl as $id => $name) {
$info = $this->planets->call('byId', $id);
$m = $this->planets->call('getIncome', $player, $info['pop'], $info['happiness'],
$info['ifact'], $info['mfact'], $info['turrets'], $info['corruption']);
$income += $m[0];
}
return $income;
}
private function computeUpkeep($player) {
$pfl = $this->players->call('getFleets', $player);
$upkeep = 0;
foreach ($pfl as $id => $name) {
$info = $this->fleets->call('get', $id);
$u = $this->fleets->call('getUpkeep', $player, $info['gaships'], $info['fighters'],
$info['cruisers'], $info['bcruisers']);
$upkeep += $u;
}
return $upkeep;
}
private function killFleets($id, $ratio) {
$fl = array_keys($this->players->call('getFleets', $id));
$rules = $this->rules->call('get', $id);
$kills = array(0, 0, 0, 0);
foreach ($fl as $fid) {
$f = $this->fleets->call('get', $fid);
if (is_null($f['waiting']) && (is_null($f['moving'])
|| !is_null($f['moving']) && $f['move']['hyperspace'] == 'f')) {
// Fleet is idle or not in HyperSpace
$kg = ceil($ratio * $f['gaships']);
$kf = ceil($ratio * $f['fighters']);
$kc = ceil($ratio * $f['cruisers']);
$kb = ceil($ratio * $f['bcruisers']);
} else {
// Fleet is in Hyperspace
$kc = ceil($f['cruisers'] * $ratio);
$kb = ceil($f['bcruisers'] * $ratio);
// Compute the amount of transported ships to be destroyed
// FIXME: computeHyperspaceDamage() ?
$gaSpace = $f['gaships'] * $rules['gaship_space'];
$fSpace = $f['fighters'] * $rules['fighter_space'];
$tSpace = $gaSpace + $fSpace;
if ($tSpace > 0) {
$haul = $f['cruisers'] * $rules['cruiser_haul']
+ $f['bcruisers'] * $rules['bcruiser_haul'];
$haulUsed = $tSpace / $haul;
$lHaul = $kc * $rules['cruiser_haul'] + $kb * $rules['bcruiser_haul'];
$lSpace = $haulUsed * $lHaul;
$gaLSpace = ($lSpace / $tSpace) * $gaSpace;
$fLSpace = ($lSpace / $tSpace) * $fSpace;
$kg = min($f['gaships'], ceil($gaLSpace / $rules['gaship_space']));
$kf = min($f['fighters'], ceil($fLSpace / $rules['fighter_space']));
} else {
$kg = $kf = 0;
}
}
if ($kc == $f['cruisers'] && $kb == $f['bcruisers'] && $kf == $f['fighters']
&& $kg == $f['gaships']) {
$this->fleets->call('disband', $fid, true);
} elseif ($kc + $kb + $kf + $kg > 0) {
$this->db->query(
"UPDATE fleet SET gaships=gaships - $kg, fighters = fighters - $kf, "
. "cruisers=cruisers - $kc,bcruisers = bcruisers - $kb "
. "WHERE id=$fid"
);
$this->fleets->call('invCache', $fid);
}
$kills[0] += $kg; $kills[1] += $kf; $kills[2] += $kc; $kills[3] += $kb;
}
// No kills?
if ($kills[0] + $kills[1] + $kills[2] + $kills[3] == 0) {
return;
}
// Send the message
$this->msgs->call('send', $id, 'kfleet', array(
"gaships" => $kills[0],
"fighters" => $kills[1],
"cruisers" => $kills[2],
"bcruisers" => $kills[3],
));
$this->players->call('updateHappiness', $id);
}
private function debt($id, $income, $upkeep, $cash) {
if ($upkeep > 0) {
$ratio = min(-$cash * 2 / $upkeep, 1) / 5;
$this->killFleets($id, $ratio);
return;
}
$rules = $this->rules->call('get', $id);
$ppl = $this->players->call('getPlanets', $id);
$pinf = array();
foreach ($ppl as $pid => $name) {
$info = $this->planets->call('byId', $pid);
$ic = $this->planets->call('getIncome', $id, $info['pop'], $info['ifact'],
$info['mfact'], $info['turrets'], $info['corruption']);
$info['income'] = $ic[0];
if ($info['income'] >= 0) {
$income -= $info['income'];
continue;
} else {
$income -= $ic[1] + $ic[2];
$info['income'] -= $ic[1] + $ic[2];
}
$pinf[$pid] = $info;
}
$kill = array(0, 0);
foreach ($pinf as $pid => $p) {
$ratio = $p['income'] / $income;
$tCost = $p['turrets'] * $rules['turret_cost'];
$mCost = $p['mfact'] * $rules['factory_cost'];
$tRatio = (rand(10,20)/100) * $ratio * $tCost / (-$p['income']);
$mRatio = (rand(10,20)/100) * $ratio * $mCost / (-$p['income']);
$tLoss = ceil($p['turrets'] * $tRatio);
$mLoss = ceil($p['mfact'] * $mRatio);
if ($tLoss + $mLoss == 0) {
continue;
}
$this->db->query("UPDATE planet SET turrets=turrets-$tLoss,mfact=mfact-$mLoss WHERE id=$pid");
$kill[0] += $tLoss; $kill[1] += $mLoss;
$this->planets->call('updateHappiness', $pid);
}
if ($kill[0] + $kill[1] == 0) {
return;
}
$this->msgs->call('send', $id, 'kimpr', array("turrets" => $kill[0], "factories" => $kill[1]));
}
public function handleAbandon() {
$this->db->query(
"UPDATE planet_abandon_time "
. "SET time_required = time_required + 1 "
. "WHERE time_required < 24"
);
}
}
?>

View file

@ -0,0 +1,479 @@
<?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");
}
}
?>

View file

@ -0,0 +1,911 @@
<?php
//-------------------------------------------------------------------
// "Hour tick": owner change, production, stuck fleets, HS stand-by,
// revolts, wormholes, sales
//-------------------------------------------------------------------
class beta5_ticks_hour_library {
var $idrInc = array();
var $oChange = array();
public function __construct($lib) {
$this->lib = $lib;
$this->game = $this->lib->game;
$this->db = $this->game->db;
$this->main = $this->game->getLib();
$this->bq = $this->game->getLib('beta5/bq');
$this->fleets = $this->game->getLib('beta5/fleet');
$this->msg = $this->game->getLib('beta5/msg');
$this->planets = $this->game->getLib('beta5/planet');
$this->players = $this->game->getLib('beta5/player');
$this->rankings = $this->game->getLib('main/rankings');
$this->rules = $this->game->getLib('beta5/rules');
$this->sales = $this->game->getLib('beta5/sale');
$this->standby = $this->game->getLib('beta5/standby');
}
public function runTick() {
// Planetary events
l::debug("Handling planetary events");
$handledSystems = 0;
do {
$increase = $this->db->safeTransaction(
array($this, 'handleSystemGroup'), $handledSystems
);
$handledSystems += $increase;
} while ($increase > 0);
// Abandon planets
$this->db->safeTransaction(array($this, "handleAbandon"));
// Stuck fleets
$this->db->safeTransaction(array($this, 'unstickFleets'));
// Fleets waiting in hyperspace
$this->db->safeTransaction(array($this, 'hyperspaceStandby'));
// Fleet sales
$this->db->safeTransaction(array($this, 'fleetSales'));
// Update happiness
$this->db->safeTransaction(array($this, 'updateHappiness'));
// Increase inflicted damage points for WHSN'd fleets
if (count($this->idrInc)) {
$this->db->safeTransaction(array($this, 'updateIDR'));
}
// Check victory status on CTF games
if ($this->game->params['victory'] == 2) {
$this->db->safeTransaction(array($this, 'updateCTF'));
}
// Update rankings
$this->db->safeTransaction(array($this->main, 'call'), 'updateRankings');
}
public function handleSystemGroup($startAt) {
$q = $this->db->query("SELECT id,nebula FROM system ORDER BY id ASC LIMIT 5 OFFSET $startAt");
$n = dbCount($q);
while ($r = dbFetchArray($q)) {
$this->handleSystem($r[0], $r[1]);
}
return $n;
}
public function unstickFleets() {
l::debug("Unsticking fleets");
$this->db->query("UPDATE fleet SET can_move = 'Y' WHERE can_move = 'H'");
}
public function hyperspaceStandby() {
$q = $this->db->query("SELECT id,owner,waiting FROM fleet WHERE waiting IS NOT NULL");
$fWait = array();
while ($r = dbFetchArray($q)) {
$fWait[$r[2]] = array($r[0],$r[1]);
}
if (!count($fWait)) {
return;
}
l::debug("Handling hyperspace stand-by");
$q = $this->db->query(
"SELECT * FROM hs_wait WHERE id IN (" . join(',', array_keys($fWait)) . ")"
);
while ($r = dbFetchHash($q)) {
$r['fleet'] = $fWait[$r['id']][0];
$r['player'] = $fWait[$r['id']][1];
$fWait[$r['id']] = $r;
}
$losses = array();
$planets = array();
foreach ($fWait as $fw) {
if ($this->handleWait($fw, &$losses)) {
array_push($planets, $fw['drop_point']);
}
}
foreach ($planets as $planetID) {
$this->planets->call('detectFleets', $planetID);
}
$tm = time();
foreach ($losses as $pid => $locs) {
foreach ($locs as $plid => $fl) {
$pinf = $this->planets->call('byId', $plid);
$hsLosses = array();
foreach ($fl as $f) {
array_push($hsLosses, array(
"p_id" => $plid,
"p_name" => $pinf['name'],
"f_name" => $f[0],
"gaships" => $f[1],
"fighters" => $f[2],
"cruisers" => $f[3],
"bcruisers" => $f[4],
"power" => $f[5]
));
}
$this->msg->call('send', $pid, 'hsloss', $hsLosses);
}
}
$this->fleets->call('sendMoveMessages');
}
public function fleetSales() {
$q = $this->db->query("SELECT id FROM fleet WHERE sale IS NOT NULL");
if (! dbCount($q)) {
return;
}
l::debug("Handling fleet sales");
while ($r = dbFetchArray($q)) {
$f = $this->fleets->call('get', $r[0]);
l::trace("FSALE: found fleet {$r[0]}");
if (!is_null($f['sale_info']['sale']['planet'])) {
l::trace("FSALE({$r[0]}): fleet is bundled with a planet, skipping");
continue;
}
if ($f['sale'] > 0) {
l::debug("FSALE({$f['id']}): fleet isn't ready yet ({$f['sale']}h remaining)");
$this->db->query("UPDATE fleet SET sale=sale-1 WHERE id={$f['id']}");
continue;
}
$q2 = $this->db->query(
"SELECT id,sold_to FROM sale WHERE player={$f['owner']} AND fleet={$f['id']}"
. " AND finalized IS NOT NULL ORDER BY finalized DESC LIMIT 1"
);
list($oid,$to) = dbFetchArray($q2);
l::trace("FSALE({$f['id']}): offer #$oid, buyer #$to");
// Read data required to send the messages
$fps = $this->fleets->call('getPower', $f['owner'], $f['gaships'], $f['fighters'],
$f['cruisers'], $f['bcruisers']);
$fpb = $this->fleets->call('getPower', $to, $f['gaships'], $f['fighters'], $f['cruisers'],
$f['bcruisers']);
l::trace("FSALE({$f['id']}): fps= $fps, fpb= $fpb");
// Send messages
$this->msg->call('send', $f['owner'], 'sale', array(
'f_name' => $f['name'],
'f_power' => $fps,
'pl_id' => $to,
'pl_name' => $this->players->call('getName', $to),
'is_sale' => 'TRUE'
));
$this->msg->call('send', $to, 'sale', array(
'f_name' => $f['name'],
'f_power' => $fpb,
'pl_id' => $f['owner'],
'pl_name' => $this->players->call('getName', $f['owner']),
'is_sale' => 'FALSE'
));
l::trace("FSALE({$f['id']}): messages sent");
// Transfer control
$this->db->query("UPDATE fleet SET sale=NULL,owner=$to WHERE id={$f['id']}");
$this->fleets->call('invCache', $f['id']);
// FIXME: add history
$this->db->query("DELETE FROM sale WHERE id=$oid");
l::debug("FSALE({$f['id']}): control transfered");
}
}
public function updateIDR() {
l::debug("Updating IDR");
$rt = $this->rankings->call('getType', 'p_idr');
$rl = $this->rankings->call('getAll', $rt);
$idr = array();
foreach ($rl as $r) {
$idr[$r['id']] = $r['points'];
}
foreach ($this->idrInc as $n => $inc) {
$idr[$n] += $inc;
}
$idrR = array();
foreach ($idr as $n => $p) {
if (!is_array($idrR[$p])) {
$idrR[$p] = array();
}
array_push($idrR[$p], $n);
}
$this->rankings->call('update', $rt, $idrR);
}
public function updateCTF() {
if (! $this->game->getLib()->call('isFinished')) {
$this->game->getLib('beta5/ctf')->call('checkTargets');
}
}
public function updateHappiness() {
l::debug("Updating planetary happiness");
$q = $this->db->query("SELECT id,owner FROM planet WHERE status=0");
$destroy = array();
while ($r = dbFetchArray($q)) {
$nHap = $this->planets->call('updateHappiness', $r[0]);
// Handle revolts
if ($nHap < 20 && !in_array($r[0], $this->oChange)) {
if (!is_null($r[1]) && is_null($destroy[$r[1]])) {
$destroy[$r[1]] = array();
}
$pDestr = $this->planetRevolt($r[0], $nHap);
if (!is_null($r[1])) {
array_push($destroy[$r[1]], $pDestr);
}
$this->planets->call('updateHappiness', $r[0]);
}
}
// Send damage reports for revolts
$tm = time() + 1;
foreach ($destroy as $player => $plist) {
$report = array();
foreach ($plist as $data) {
array_push($report, array(
"planet" => $data[0],
"pname" => $data[1],
"ifact" => $data[2],
"mfact" => $data[3],
"turrets" => $data[4]
));
}
$this->msg->call('send', $player, 'revdmg', $report);
}
}
private function buildFleets($plId, $ownId, $g, $f, $c, $b) {
if ($g + $f + $c + $b == 0) {
return;
}
$q = $this->db->query("SELECT id FROM fleet WHERE can_move='Y' AND LOWER(name)='no name' AND location=$plId AND owner=$ownId AND sale IS NULL LIMIT 1");
if (dbCount($q)) {
list($id) = dbFetchArray($q);
$this->db->query("UPDATE fleet SET gaships=gaships+$g,fighters=fighters+$f,cruisers=cruisers+$c,bcruisers=bcruisers+$b WHERE id=$id");
} else {
$qs = "INSERT INTO fleet(owner,location,gaships,fighters,cruisers,bcruisers)VALUES($ownId,$plId,$g,$f,$c,$b)";
$this->db->query($qs);
}
}
private function planetBuildQueue($planet, $rules) {
static $types = array('turret', 'gaship', 'fighter', 'cruiser', 'bcruiser');
$units = $rules['mf_productivity'] * $rules['mf_productivity_factor'] * $planet['mfact'];
$cl = $planet['corruption'] / 32000;
if ($cl > .1) {
$units = $units * (1.1 - $cl);
}
$vac = $this->players->call('isOnVacation', $planet['owner']);
if ($vac) {
$units = $units / 4;
}
$prot = $this->players->call('getProtectionLevel', $planet['owner']);
if ($prot) {
$units = $units * 1.15;
}
$units = round($units);
$bq = $this->bq->call('get', $planet['id']);
$prod = array(0, 0, 0, 0, 0);
$move = 0;
while (count($bq) && $units > 0) {
if ($bq[0]['units'] <= $units) {
$units -= $bq[0]['units'];
$prod[$bq[0]['type']] += $bq[0]['quantity'];
$this->db->query("DELETE FROM buildqueue WHERE planet=".$planet['id']." AND bq_order=$move");
$move ++;
array_shift($bq);
} else {
$units += $bq[0]['workunits'];
$wuCost = $rules['workunits_'.$types[$bq[0]['type']]];
$mod = $units % $wuCost;
$nBuild = ($units - $mod) / $wuCost;
$prod[$bq[0]['type']] += $nBuild;
$nQt = $bq[0]['quantity'] - $nBuild;
$this->db->query("UPDATE buildqueue SET quantity=$nQt,workunits=$mod WHERE planet=".$planet['id']." AND bq_order=$move");
$units = 0;
}
}
if ($move) {
$this->bq->call('reorder', $planet['id']);
}
if ($prod[0] > 0) {
$this->db->query("UPDATE planet SET turrets=turrets+".$prod[0]." WHERE id=".$planet['id']);
$tm = time();
$this->db->query("DELETE FROM turhist WHERE $tm-moment>86400");
$this->db->query("INSERT INTO turhist VALUES({$planet['id']},$tm,$prod[0])");
}
$this->buildFleets($planet['id'], $planet['owner'], $prod[1], $prod[2], $prod[3], $prod[4]);
$this->planets->call('updateMilStatus', $planet['id']);
}
private function planetSale(&$planet) {
l::trace("SALE: handling planet {$planet['id']}");
if ($planet['sale'] > 0) {
l::debug("SALE({$planet['id']}): not ready for sale yet ({$planet['sale']}h remaining)");
$this->db->query("UPDATE planet SET sale=sale-1 WHERE id=".$planet['id']);
$q = $this->db->query(
"SELECT fleet FROM sale WHERE player=".$planet['owner']." AND planet=".$planet['id']
. " AND finalized IS NOT NULL ORDER BY finalized DESC LIMIT 1"
);
list($fleet) = dbFetchArray($q);
if (!is_null($fleet)) {
l::trace("SALE({$planet['id']}): fleet $fleet bundled");
$this->db->query("UPDATE fleet SET sale=sale-1 WHERE id=$fleet");
}
} else {
l::trace("SALE({$planet['id']}): running sale code");
$q = $this->db->query(
"SELECT id,sold_to,fleet FROM sale WHERE player=".$planet['owner']." AND planet=".$planet['id']
. " AND finalized IS NOT NULL ORDER BY finalized DESC LIMIT 1"
);
if (!dbCount($q)) {
l::warn("***** BUG: planet {$planet['id']} has sale field set to 0, but no "
. "sales were found");
$this->db->query("UPDATE planet SET sale=NULL WHERE id={$planet['id']}");
return;
}
list($oid,$to,$fleet) = dbFetchArray($q);
l::trace("SALE({$planet['id']}): offer #$oid, sold to player #$to (bundled fleet: "
. (is_null($fleet) ? "none" : "#$fleet") . ")");
// Read data required to send the messages
$tn = addslashes($this->players->call('getName', $to));
$fn = addslashes($this->players->call('getName', $planet['owner']));
l::trace("SALE({$planet['id']}): original owner '$tn', new owner '$fn'");
if (is_null($fleet)) {
$fqs = $fqb = "NULL,NULL";
} else {
$q = $this->db->query("SELECT * FROM fleet WHERE id=$fleet");
$fd = dbFetchHash($q);
$fps = $this->fleets->call('getPower', $fd['owner'], $fd['gaships'], $fd['fighters'], $fd['cruisers'], $fd['bcruisers']);
$fpb = $this->fleets->call('getPower', $to, $fd['gaships'], $fd['fighters'], $fd['cruisers'], $fd['bcruisers']);
$fqs = "'" . addslashes($fd['name']) . "',$fps";
$fqb = "'" . addslashes($fd['name']) . "',$fpb";
}
l::trace("SALE({$planet['id']}): fqs: $fqs");
l::trace("SALE({$planet['id']}): fqb: $fqb");
// Send messages
// FIXME: message API
$tm = time();
$this->db->query("INSERT INTO message(player,sent_on,mtype,ftype,is_new) VALUES({$planet['owner']},$tm,'sale','INT',TRUE)");
$q = $this->db->query("SELECT id FROM message WHERE player={$planet['owner']} AND sent_on=$tm AND mtype='sale' ORDER BY id DESC LIMIT 1");
list($mid) = dbFetchArray($q);
$this->db->query("INSERT INTO msg_sale VALUES($mid,{$planet['id']},'".addslashes($planet['name'])."',$fqs,$to,'$tn',TRUE)");
$this->db->query("INSERT INTO message(player,sent_on,mtype,ftype,is_new) VALUES($to,$tm,'sale','INT',TRUE)");
$q = $this->db->query("SELECT id FROM message WHERE player=$to AND sent_on=$tm AND mtype='sale' ORDER BY id DESC LIMIT 1");
list($mid) = dbFetchArray($q);
$this->db->query("INSERT INTO msg_sale VALUES($mid,{$planet['id']},'".addslashes($planet['name'])."',$fqb,{$planet['owner']},'$fn',FALSE)");
l::trace("SALE({$planet['id']}): messages sent");
// Check for vacation mode
$vac = $this->players->call('isOnVacation', $to);
if ($vac) {
$q = $this->db->query("SELECT COUNT(*) FROM fleet WHERE location={$planet['id']} AND attacking");
list($c) = dbFetchArray();
if ($c) {
$vm = 'PEND';
} else {
$vm = 'YES';
}
} else {
$vm = 'NO';
}
l::trace("SALE({$planet['id']}): vacation mode for target player: $vm");
// Sell the planet
$this->players->call('losePlanet', $planet['owner'], $planet['id']);
$this->players->call('takePlanet', $to, $planet['id']);
$nVal = $this->planets->call('ownerChange', $planet['id'], $to, $vm);
if ($nVal > 0) {
$planet['max_pop'] = $nVal;
}
$this->db->query("UPDATE player SET bh_unhappiness=bh_unhappiness+1 WHERE id={$planet['owner']} AND bh_unhappiness<15");
if (!is_null($fleet)) {
$this->db->query("UPDATE fleet SET sale=NULL,owner=$to WHERE id=$fleet");
}
$planet['owner'] = $to;
// FIXME: add history
$this->db->query("DELETE FROM sale WHERE id=$oid");
l::debug("SALE({$planet['id']}) completed");
}
}
private function whsnPreparation(&$planet) {
if ($planet['bh_prep'] > 0) {
// WHSN is not ready yet
$this->db->query("UPDATE planet SET bh_prep=bh_prep-1 WHERE id=".$planet['id']);
$this->db->query("UPDATE player SET bh_unhappiness=bh_unhappiness+1 WHERE id={$planet['owner']} AND bh_unhappiness<15");
return false;
}
// Get fleet statistics to send messages
$players = array(
$planet['owner'] => array(
"was_owner" => dbBool(true),
"attacking" => false,
"power" => 0,
"fleets" => array(),
)
);
$q = $this->db->query("SELECT * FROM fleet WHERE location=".$planet['id']);
$aPower = $dPower = 0;
while ($r = dbFetchHash($q)) {
if (is_null($players[$r['owner']])) {
$players[$r['owner']] = array(
"was_owner" => dbBool(false),
"attacking" => ($r['attacking'] == 't'),
"power" => 0,
"fleets" => array(),
);
}
$f = array(
"name" => $r['name'],
"gaships" => $r['gaships'],
"fighters" => $r['fighters'],
"cruisers" => $r['cruisers'],
"bcruisers" => $r['bcruisers'],
);
$power = $this->fleets->call('getPower', $r['owner'], $r['gaships'], $r['fighters'], $r['cruisers'], $r['bcruisers']);
$players[$r['owner']]["power"] += ($f['power'] = $power);
array_push($players[$r['owner']]['fleets'], $f);
if ($players[$r['owner']]['attacking']) {
$aPower += $power;
} else {
$dPower += $power;
}
// Cancel fleet sales
$qfs = $this->db->query("SELECT id,player,finalized FROM sale WHERE fleet={$r['id']}");
if (dbCount($qfs)) {
list($saleId, $seller, $saleFin) = dbFetchArray($qfs);
if (is_null($saleFin)) {
$ga = "cancel";
} else {
$ga = "cancelTransfer";
}
$this->sales->call($ga, $seller, $saleId);
}
}
// Send the messages
// FIXME: message API
$tm = time();
foreach ($players as $pId => $data) {
$this->db->query("INSERT INTO message(player,sent_on,mtype,ftype,is_new) VALUES($pId,$tm,'whsn','INT',TRUE)");
$q = $this->db->query("SELECT id FROM message WHERE player=$pId AND sent_on=$tm AND mtype='whsn' ORDER BY id DESC LIMIT 1");
list($mid) = dbFetchArray($q);
if ($data['attacking']) {
$pow = $aPower - $data['power'];
$fPower = $pow; $ePower = $dPower;
} else {
$pow = $dPower - $data['power'];
$fPower = $pow; $ePower = $aPower;
}
$this->db->query("INSERT INTO msg_whsn VALUES($mid,{$planet['id']},'"
. addslashes($planet['name']) . "'," . $data['was_owner']
. ",$fPower,$ePower)");
foreach ($data['fleets'] as $f)
$this->db->query("INSERT INTO whsn_fleet VALUES($mid,'" . addslashes($f['name'])
. "',{$f['gaships']},{$f['fighters']},{$f['cruisers']},"
. "{$f['bcruisers']},{$f['power']})");
}
// Check for IDR points
$pow = $aPower - $dPower;
if ($pow > 0) {
$pn = $this->players->call('getName', $planet['owner']);
if (is_null($this->idrInc[$pn])) {
$this->idrInc[$pn] = $pow;
} else {
$this->idrInc[$pn] += $pow;
}
}
// Destroy the planet and anything around it
$this->players->call('losePlanet', $planet['owner'], $planet['id']);
$this->bq->call('flush', $planet['id']);
$this->db->query("DELETE FROM fleet WHERE location=".$planet['id']);
do {
$rn = strtoupper(substr(md5(uniqid(rand())), 0, 10));
$q = $this->db->query("SELECT id FROM planet WHERE name='PR-[$rn]'");
} while(dbCount($q));
$this->db->query(
"UPDATE planet "
. "SET pop = 0, max_pop = 0, happiness = 0, mfact = 0, ifact = 0, beacon = 0, "
. "built_probe = FALSE, probe_policy = NULL, bh_prep = NULL, owner = NULL, "
. "sale = NULL, status = 1, bh_unhappiness = 0, vacation = 'NO', "
. "name = 'PR-[$rn]', abandon = NULL "
. "WHERE id = {$planet['id']}"
);
$this->db->query(
"UPDATE planet "
. "SET bh_unhappiness = bh_unhappiness + 4 "
. "WHERE system={$planet['system']} AND id <> {$planet['id']} "
. "AND status = 0 AND bh_unhappiness < 12"
);
$this->db->query("DELETE FROM planet_abandon_time WHERE id = {$planet['id']}");
// Raise the empire-wide unhappiness for the planet's owner
$q = $this->db->query("SELECT bh_unhappiness FROM player WHERE id={$planet['owner']}");
list($bhu) = dbFetchArray($q);
$nuh = max($bhu + 4, 15);
$this->db->query("UPDATE player SET bh_unhappiness=$nuh WHERE id={$planet['owner']}");
return true;
}
private function planetTaken(&$planet) {
if ($planet['turrets'] > 0) {
return false;
}
// Check if the planet was in vacation mode before the attacking ships arrived
// If it was, it can't be taken
if ($planet['vacation'] == 'YES ') {
return false;
}
// List fleets at location
$q = $this->db->query("SELECT owner,gaships,attacking,time_spent FROM fleet WHERE location={$planet['id']}");
$players = array();
while ($r = dbFetchArray($q)) {
// If there are defending fleets, forget it
if ($r[2] == 'f') {
if ($r[3] > 5) {
return false;
}
continue;
} elseif ($r[3] < 15) {
continue;
}
// Count GA Ships per player
$players[$r[0]] += $r[1];
}
// Get the highest GA efficiency
$maxGAPower = $maxGAPlayer = -1;
foreach ($players as $id => $gas) {
$rules = $this->rules->call('get', $id);
$gaPower = $gas * $rules['gaship_pop'];
if ($gaPower > $maxGAPower) {
$maxGAPower = $gaPower;
$maxGAPlayer = $id;
}
}
// Check whether the player can actually take the planet
if ($maxGAPlayer == -1 || $maxGAPower < $planet['pop']) {
return false;
}
// A player has taken control on the planet
if (!is_null($planet['owner'])) {
// If the planet had an owner, flush build queue and cancel sales
$this->bq->call('flush', $planet['id']);
$q = $this->db->query("SELECT id,finalized,sold_to FROM sale WHERE planet={$planet['id']}");
if (dbCount($q)) {
// Cancel sale
list($sId,$fin,$to) = dbFetchArray($q);
if (is_null($fin)) {
$ga = 'cancel';
} else {
$ga = 'cancelTransfer';
// Send message
// FIXME ...
$tm = time();
$this->db->query("INSERT INTO message(player,sent_on,mtype,ftype,is_new) VALUES($to,$tm,'plsc','INT',TRUE)");
$q = $this->db->query("SELECT id FROM message WHERE player=$to AND sent_on=$tm AND mtype='plsc' ORDER BY id DESC LIMIT 1");
list($mid) = dbFetchArray($q);
$this->db->query("INSERT INTO msg_plsc VALUES($mid,{$planet['owner']},$maxGAPlayer,{$planet['id']},'"
. addslashes($planet['name']) . "')");
}
$this->sales->call($ga, $planet['owner'], $sId);
}
$rqs = ",renamed=".time();
} else {
$rqs = "";
}
// Check for vacation mode
$vm = $this->players->call('isOnVacation', $maxGAPlayer) ? 'YES' : 'NO';
// Transfer control
$this->players->call('losePlanet', $planet['owner'], $planet['id']);
$this->players->call('takePlanet', $maxGAPlayer, $planet['id']);
$nVal = $this->planets->call('ownerChange', $planet['id'], $maxGAPlayer, $vm);
if ($nVal > 0) {
$planet['max_pop'] = $nVal;
}
// Update a potential abandon log entry
$tm = time();
$this->db->query("UPDATE abandon_log SET retake_owner=$maxGAPlayer,retake_time=$tm WHERE planet={$planet['id']} AND retake_time IS NULL");
// Get the list of the new owner's enemies
$q = $this->db->query("SELECT enemy FROM enemy_player WHERE player=$maxGAPlayer");
$enemies = array();
while ($r = dbFetchArray($q)) {
array_push($enemies, $r[0]);
}
$q = $this->db->query("SELECT p.id FROM player p,enemy_alliance e WHERE p.alliance=e.alliance AND p.a_status='IN' AND e.player=$maxGAPlayer");
while ($r = dbFetchArray($q)) {
array_push($enemies, $r[0]);
}
$enemies = array_unique($enemies);
// Switch attacking fleets to defense unless they belong to an enemy
$qs = "UPDATE fleet SET attacking=FALSE,can_move='Y' WHERE location={$planet['id']}";
if (count($enemies)) {
$qs .= " AND owner NOT IN (".join(',',$enemies).")";
}
$this->db->query($qs);
// Send messages to the former owner and the new owner
$pn = addslashes($planet['name']);
$noMsg = array($maxGAPlayer);
$mData = array(
'p_id' => $planet['id'],
'p_name' => $planet['name'],
'owner' => $planet['owner'],
'status' => 'TAKE'
);
$this->msg->call('send', $maxGAPlayer, 'ownerch', $mData);
$mData['owner'] = $maxGAPlayer;
if (!is_null($planet['owner'])) {
$mData['status'] = 'LOSE';
array_push($noMsg, $pid = $planet['owner']);
$this->msg->call('send', $pid, 'ownerch', $mData);
}
// Get the list of other players and send them a message as well
$q2 = $this->db->query("SELECT DISTINCT owner FROM fleet WHERE location={$planet['id']} AND owner NOT IN (" . join(',', $noMsg) . ")");
$mData['status'] = 'VIEW';
while ($r = dbFetchArray($q2)) {
$this->msg->call('send', $r[0], 'ownerch', $mData);
}
return true;
}
private function handlePlanet($planet) {
// WHSN preparation
if (!is_null($planet['bh_prep']) && $this->whsnPreparation($planet)) {
return false;
}
// Check for owner change
if ($this->planetTaken($planet)) {
$this->planets->call('updateMilStatus', $planet['id']);
return true;
}
// Planet sale
if (!is_null($planet['sale'])) {
$this->planetSale($planet);
}
// No build queues or revolts on neutral planets
if (is_null($planet['owner'])) {
return false;
}
// Load owner rules
$rules = $this->rules->call('get', $planet['owner']);
// Build queue
$this->planetBuildQueue($planet, $rules);
return false;
}
private function handleSystem($sid, $neb) {
if ($neb) {
return;
}
l::trace("Handling system #$sid");
$q = $this->db->query("SELECT * FROM planet WHERE system=$sid");
while ($planet = dbFetchHash($q)) {
if ($neb || $planet['status'] == 1) {
continue;
}
if ($this->handlePlanet($planet)) {
array_push($this->oChange, $planet['id']);
}
}
}
private function handleWait($fw, &$losses) {
$canDestroy = $this->standby->call('canDestroy', $fw['drop_point'], $fw['player']);
// If we can destroy fleets, find out whether we must do so
if ($canDestroy) {
$prob = $this->standby->call('getLossProb', $fw['time_spent']);
l::debug("HSSB({$fw['fleet']}): at planet #{$fw['drop_point']}, loss probability = "
. "$prob (time spent: {$fw['time_spent']}h)");
if (rand(1,100) <= $prob) {
l::debug("HSSB({$fw['fleet']}): applying fleet losses");
// Destroy a part of the fleet
$amount = rand(5,round(10+$prob/10));
$fleet = $this->fleets->call('get', $fw['fleet']);
$rules = $this->rules->call('get', $fw['player']);
$fcLoss = ceil(($fleet['cruisers'] / 100) * $amount);
$fbLoss = ceil(($fleet['bcruisers'] / 100) * $amount);
l::trace("HSSB({$fw['fleet']}): cruisers lost: $fcLoss; battle cruisers lost: $fbLoss");
// Compute the amount of transported ships to be destroyed
$gaSpace = $fleet['gaships'] * $rules['gaship_space'];
$fSpace = $fleet['fighters'] * $rules['fighter_space'];
$tSpace = $gaSpace + $fSpace;
if ($tSpace > 0) {
$haul = $fleet['cruisers'] * $rules['cruiser_haul']
+ $fleet['bcruisers'] * $rules['bcruiser_haul'];
$haulUsed = $tSpace / $haul;
$lHaul = $fcLoss * $rules['cruiser_haul'] + $fbLoss * $rules['bcruiser_haul'];
$lSpace = $haulUsed * $lHaul;
$gaLSpace = ($lSpace / $tSpace) * $gaSpace;
$fLSpace = ($lSpace / $tSpace) * $fSpace;
$fgLoss = min($fleet['gaships'], ceil($gaLSpace / $rules['gaship_space']));
$ffLoss = min($fleet['fighters'], ceil($fLSpace / $rules['fighter_space']));
} else {
$fgLoss = $ffLoss = 0;
}
l::trace("HSSB({$fw['fleet']}): fighters lost: $ffLoss; GA ships lost: $fgLoss");
// Add details to the $losses array
if (!is_array($losses[$fleet['owner']])) {
$losses[$fleet['owner']] = array();
}
if (!is_array($losses[$fleet['owner']][$fw['drop_point']])) {
$losses[$fleet['owner']][$fw['drop_point']] = array();
}
array_push($losses[$fleet['owner']][$fw['drop_point']], array(
addslashes($fleet['name']), $fgLoss, $ffLoss, $fcLoss, $fbLoss,
$this->fleets->call('getPower', $fleet['owner'], $fgLoss, $ffLoss,
$fcLoss, $fbLoss)
));
if ($fcLoss == $fleet['cruisers'] && $fbLoss == $fleet['bcruisers']
&& $ffLoss == $fleet['fighters'] && $fgLoss == $fleet['gaships']) {
$this->fleets->call('disband',$fw['fleet'],true);
l::trace("HSSB({$fw['fleet']}): fleet disbanded");
} elseif ($fcLoss + $fbLoss + $ffLoss + $fgLoss > 0) {
$this->db->query(
"UPDATE fleet SET gaships = gaships - $fgLoss, "
. "fighters = fighters - $ffLoss, "
. "cruisers = cruisers - $fcLoss, "
. "bcruisers = bcruisers - $fbLoss "
. "WHERE id = {$fw['fleet']}"
);
}
}
}
if ($fw['time_left'] == 1) {
// Fleet arrival
$this->fleets->call('arrival', $fw['fleet'], $fw['drop_point'], $fw['origin']);
// Destroy wait entry
$this->db->query("DELETE FROM hs_wait WHERE id={$fw['id']}");
} else {
// Update wait entry
$this->db->query(
"UPDATE hs_wait SET time_left = time_left - 1, time_spent = time_spent + 1 "
. "WHERE id={$fw['id']}"
);
}
return !($fw['time_left'] == 1);
}
private function planetRevolt($id, $hap) {
$q = $this->db->query("SELECT name,ifact,mfact,turrets FROM planet WHERE id=$id");
list($n, $if, $mf, $tr) = dbFetchArray($q);
$pdestr = 30 - $hap;
$pdestr += rand(-floor($pdestr/2),floor($pdestr/2));
$pdestr /= 150;
$dInd = round($if * $pdestr);
$dMil = round($mf * $pdestr);
$dTur = round($tr * $pdestr);
if ($dInd + $dMil + $dTur != 0) {
$this->db->query(
"UPDATE planet SET ifact = ifact - $dInd, mfact = mfact - $dMil, "
. "turrets = turrets - $dTur "
. "WHERE id = $id"
);
}
return array($id, $n, $dInd, $dMil, $dTur);
}
public function handleAbandon() {
l::debug("Handling planets being abandonned");
// Decrease time to abandon
$this->db->query("UPDATE planet SET abandon = abandon - 1 WHERE abandon IS NOT NULL");
// Get the list of planets to abandon at this tick
$q = $this->db->query("SELECT id, owner FROM planet WHERE abandon = 0");
$oList = array();
while ($r = dbFetchArray($q)) {
if (is_null($r[1])) {
l::warn("****** BUG: planet {$r[0]} is being abandoned and has no owner");
continue;
}
$this->abandonPlanet($r[0], $r[1]);
if (!is_array($oList[$r[1]])) {
$oList[$r[1]] = array();
}
array_push($oList[$r[1]], $r[0]);
}
foreach ($oList as $pid => $aPl) {
// Send the message
$message = array();
foreach ($aPl as $plId) {
$planet = $this->planets->call('byId', $plId);
array_push($message, array("p_id" => $plId, "p_name" => $planet['name']));
}
$this->msg->call('send', $pid, 'abandon', $message);
// Update other planets' happiness
$this->players->call('updateHappiness', $pid);
}
}
private function abandonPlanet($pid, $oid) {
// Insert log entry
$this->db->query(
"INSERT INTO abandon_log(planet,abandon_time,former_owner) "
. "VALUES ($pid," . time() . ",$oid)"
);
// Make the planet neutral
$this->planets->call('ownerChange', $pid);
$this->players->call('losePlanet', $oid, $pid);
}
}
?>

View file

@ -0,0 +1,121 @@
<?php
//-------------------------------------
// "Move tick": handles moving objects
//-------------------------------------
class beta5_ticks_move_library {
private $arrivals = array();
public function __construct($lib) {
$this->lib = $lib;
$this->db = $lib->game->db;
$this->fleets = $lib->game->getLib('beta5/fleet');
$this->planets = $lib->game->getLib('beta5/planet');
}
public function runTick() {
// Increase time spent for fleets
$this->db->safeTransaction(array($this, 'increaseTimeSpent'));
// Advance moving objects and get the list of arrivals
$this->db->safeTransaction(array($this, 'advanceMovingObjects'));
// Fleet arrivals
if (count($this->arrivals)) {
$this->db->safeTransaction(array($this, 'fleetArrivals'));
$this->db->safeTransaction(array($this, 'fleetArrivalUpdates'));
}
// Remove completed trajectories
$this->db->safeTransaction(array($this, 'deleteOldTrajectories'));
}
public function deleteOldTrajectories() {
$this->db->query("DELETE FROM moving_object WHERE time_left = 0");
}
public function increaseTimeSpent() {
// Increase time spent on planets for fleets that are not moving
$this->db->query("UPDATE fleet SET time_spent = time_spent + 1 WHERE location IS NOT NULL");
}
public function advanceMovingObjects() {
// Advances all moving objects that don't suffer a destination change penalty
$this->db->query("UPDATE moving_object SET time_left = time_left - 1 WHERE changed=0 AND time_left > 0");
// Decreases penalty
$this->db->query("UPDATE moving_object SET changed = 61 WHERE changed > 60");
$this->db->query("UPDATE moving_object SET changed = changed - 1 WHERE changed > 0");
// Get all moving objects arriving at destination
$q = $this->db->query("SELECT id, m_to, m_from, wait_order FROM moving_object WHERE time_left = 0");
if (!dbCount($q)) {
return;
}
$mobjs = array();
while ($r = dbFetchArray($q)) {
$mobjs[$r[0]] = array($r[1], $r[2], $r[3]);
}
$this->arrivals = $mobjs;
}
function fleetArrivals() {
// Find moving fleets
$q = $this->db->query(
"SELECT id, moving FROM fleet WHERE moving IN (" . join(',', array_keys($this->arrivals)) . ")"
);
$planets = array(); $plDetect = array();
while ($r = dbFetchArray($q)) {
list($fId,$mId) = $r;
// Does it have an HS stand-by order?
if (!is_null($this->arrivals[$mId][2])) {
$this->db->query(
"UPDATE hs_wait SET origin={$this->arrivals[$mId][1]}, "
. "drop_point={$this->arrivals[$mId][0]}"
. " WHERE id={$this->arrivals[$mId][2]}");
$this->db->query("UPDATE fleet SET moving=NULL,waiting="
. $this->arrivals[$mId][2] . " WHERE id=$fId");
// Add the planet to the list of planets where detection is to be run
if (!in_array($this->arrivals[$mId][0], $plDetect)) {
array_push($plDetect, $this->arrivals[$mId][0]);
}
} else {
// If there is no HS stand-by order, the fleet has arrived.
$this->fleets->call('arrival', $fId, $this->arrivals[$mId][0], $this->arrivals[$mId][1]);
// Add the planet to the list of planets to be updated
if (!in_array($this->arrivals[$mId][0], $planets)) {
array_push($planets, $this->arrivals[$mId][0]);
}
}
}
$this->plDetect = $plDetect;
$this->plUpdate = $planets;
}
public function fleetArrivalUpdates() {
// Update planet status
foreach ($this->plDetect as $pid) {
$this->planets->call('detectFleets', $pid);
}
foreach ($this->plUpdate as $pid) {
$this->planets->call('updateHappiness', $pid);
$this->planets->call('updateMilStatus', $pid);
}
$this->fleets->call('sendMoveMessages');
}
}
?>

View file

@ -0,0 +1,48 @@
<?php
//-------------------------------------------------------------
// "Punishment tick": contains the code to reset planets if
// they are not renamed in time.
//-------------------------------------------------------------
class beta5_ticks_punishment_library {
public function __construct($lib) {
$this->lib = $lib;
$this->db = $lib->game->db;
$this->b5adm = $lib->game->getLib('admin/beta5');
}
public function runTick() {
$this->db->safeTransaction(array($this, 'punish'));
}
public function punish() {
// Neutralize planets with funky names
$q = $this->db->query(
"SELECT id, name FROM planet "
. "WHERE mod_check AND force_rename IS NOT NULL "
. "AND UNIX_TIMESTAMP(NOW())-force_rename >= 86400"
);
while ($r = dbFetchArray($q)) {
$q2 = $this->db->query(
"SELECT moderator FROM msg_warnname "
. "WHERE planet={$r[0]} AND event='WARN' "
. "ORDER BY id DESC LIMIT 1"
);
list($mod) = dbFetchArray($q2);
if (is_null($mod)) {
l::error("****** BUG: moderator ID not found for planet {$r[0]}");
continue;
}
l::notice("resetting planet '{$r[1]}' (id: $r[0]) due to funky name");
$this->b5adm->call('resetPlanet', $r[0], $mod, false);
}
}
}
?>

View file

@ -0,0 +1,33 @@
<?php
//--------------------------------------------
// "Quit tick": account deletion for quitters
//--------------------------------------------
class beta5_ticks_quit_library {
public function __construct($lib) {
$this->lib = $lib;
$this->db = $lib->game->db;
$this->main = $lib->game->getLib();
}
public function runTick() {
$this->db->safeTransaction(array($this, 'quit'));
}
public function quit() {
$q = $this->db->query(
"SELECT id FROM player "
. "WHERE quit IS NOT NULL AND (UNIX_TIMESTAMP(NOW()) - quit) < 86400 "
. "AND (UNIX_TIMESTAMP(NOW()) - quit) > 86339"
);
while ($r = dbFetchArray($q)) {
$this->main->call('leaveGame', $r[0], 'QUIT');
}
}
}
?>

View file

@ -0,0 +1,127 @@
<?php
//-------------------------------------------------------------
// "Sales tick": makes sales expire and finalize auction sales
//-------------------------------------------------------------
class beta5_ticks_sales_library {
public function __construct($lib) {
$this->lib = $lib;
$this->game = $this->lib->game;
$this->db = $this->game->db;
}
public function runTick() {
// Update sales
$this->db->safeTransaction(array($this, 'expireSales'));
// Check CTF victory
if ($this->game->params['victory'] == 0) {
$this->db->safeTransaction(array($this, 'checkProtection'));
} elseif ($this->game->params['victory'] == 2) {
$this->db->safeTransaction(array($this, 'checkCTFVictory'));
}
}
public function expireSales() {
$q = $this->db->query("SELECT * FROM sale WHERE expires<UNIX_TIMESTAMP(NOW()) AND finalized IS NULL");
while ($r = dbFetchHash($q)) {
// Public offers
$q2 = $this->db->query("SELECT auction FROM public_offer WHERE offer=".$r['id']);
if ($q2 && dbCount($q2)) {
list($auction) = dbFetchArray($q2);
$this->expirePublicOffer($r, $auction == 't');
continue;
}
// Private offers
// FIXME
}
$this->db->query(
"DELETE FROM sale_history WHERE end_mode IS NOT NULL AND UNIX_TIMESTAMP(NOW()) - ended > 5184000"
);
}
public function checkCTFVictory() {
// Check victory status on CTF games
if (! $this->game->getLib()->call('isFinished')) {
$this->game->getLib('beta5/ctf')->call('checkTargets');
}
}
public function checkProtection() {
$q = $this->db->query(
"SELECT p.id, s.id FROM planet p, system s WHERE s.id = p.system AND s.prot > 0"
);
$checked = array();
$pLib = $this->game->getLib('beta5/prot');
while ($r = dbFetchArray($q)) {
if (! in_array($r[1], $checked)) {
array_push($checked, $r[1]);
$pLib->call('checkSystem', $r[0]);
}
}
$pLib->call('flushStatus');
}
private function expirePublicOffer($offer, $isAuction) {
$tm = time();
// If it isn't an auction sale, just delete it
if (!$isAuction) {
// Insert entry in player's history
$this->db->query("UPDATE sale_history SET ended=$tm,end_mode=3,sell_price=NULL WHERE offer=".$offer['id']);
// FIXME: send message
$this->db->query("DELETE FROM sale WHERE id=".$offer['id']);
return;
}
// Check for a buyer
$q = $this->db->query("SELECT player,price FROM auction WHERE offer=".$offer['id']." ORDER BY price DESC LIMIT 1");
if (!($q && dbCount($q))) {
// Insert entry in player's history
$this->db->query("UPDATE sale_history SET ended=$tm,end_mode=3,sell_price=NULL WHERE offer=".$offer['id']);
// FIXME: send message
$this->db->query("DELETE FROM sale WHERE id=".$offer['id']);
return;
}
list($buyer,$price) = dbFetchArray($q);
// Refund players who have failed to buy the item
$q = $this->db->query("SELECT player,MAX(price) FROM auction WHERE offer=".$offer['id']." AND player<>$buyer GROUP BY player");
while ($r = dbFetchArray($q)) {
// FIXME: send message
$this->db->query("UPDATE player SET cash=cash+".$r[1]." WHERE id=".$r[0]);
}
// Mark the transaction as finalized
$this->db->query("UPDATE sale SET finalized=$tm,sold_to=$buyer WHERE id=".$offer['id']);
$this->db->query("UPDATE public_offer SET price=$price WHERE offer=".$offer['id']);
$this->db->query("DELETE FROM auction WHERE offer=".$offer['id']);
$this->db->query("UPDATE player SET cash=cash+$price WHERE id=".$offer['player']);
// Inform both the current owner and the buyer
// FIXME
// Insert history entry
$this->db->query("UPDATE sale_history SET ended=$tm,end_mode=2,sell_price=$price,to_player=$buyer WHERE offer=".$offer['id']);
// Mark the items for sale
if (!is_null($offer['planet'])) {
$this->db->query("UPDATE planet SET sale=3 WHERE id=".$offer['planet']);
}
if (!is_null($offer['fleet'])) {
$this->db->query("UPDATE fleet SET sale=3 WHERE id=".$offer['fleet']);
}
}
}
?>

View file

@ -0,0 +1,483 @@
<?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);
}
}
}
?>