Location: PHPKode > projects > Tactical Information Center > ticng/Taktik/Taktik.class.php
<?php
/*************************************************************************************
 *                                                                                   *
 *  T.I.C. NG - Next Generation of Tactical Information Center                       *
 *  Copyright (C) 2006  Andreas Hemel  <hide@address.com>                            *
 *                                                                                   *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                   *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                   *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.  *
 *                                                                                   *
 *************************************************************************************/

require_once("Flottenbewegung.class.php");
require_once("frontend.php");

class Taktik extends TICModule
{
    function Taktik()
    {
	parent::__construct(
	array(new Author("Andreas Hemel", "daishan", "hide@address.com"),
	new Author("AlbertLast","AlbertLast","#tic-hide@address.com")),
	"10",
	"Taktik",
	"Taktik Modul",
	array(
            "Core" => "10",
            "Design" => "10",
            "ADOdb" => "10",
            "UserMan" => "10"
        ));
    
    }

    public function onLoad() {
        global $tic;
        $tic->mod['JSON']->registerAjaxCall('taktikhud', 'Taktik', 'taktikHUD');
    }

    public function createMenuEntries($menuroot)
    {
        $taktik = new MenuEntry("Taktik", 0, $this->getName(), "Taktik");
        $taktik->addChild(new MenuEntry("Incomings", 0, $this->getName(), "Incs"));
        $taktik->addChild(new MenuEntry("Meta", 1, $this->getName(), "Meta"));
        $taktik->addChild(new MenuEntry("Allianz", 2, $this->getName(), "Ally"));
        $taktik->addChild(new MenuEntry("Galaxie", 3, $this->getName(), "Gala"));
        $main = $menuroot->getChildByName('Main');
        $main->addChild($taktik);
    }

    public function onExecute($menuentry)
    {
        switch ($menuentry) {
        case 'Taktik':
        case 'Incs':
        case 'Meta':
            showTaktikFlotten();
            break;
        case 'Ally':
            showAllianz();
            break;
        case 'Gala':
            showGalaxie();
            break;
        default:
            showTaktikFlotten();
            break;
        }
    }

    public function getInstallQueriesMySQL()
    {
    	global $tic;
        return array_merge($tic->mod['UserMan']->getInstallQueriesMySQL(),
        array(
            'DROP TABLE IF EXISTS flotten',
            'DROP TABLE IF EXISTS taktik_update',
            "CREATE  TABLE IF NOT EXISTS `flotten` (
			  `id` INT(11) NOT NULL AUTO_INCREMENT ,
			  `start_gala` INT(11) NOT NULL ,
			  `start_planet` INT(11) NOT NULL ,
			  `flotte` TINYINT(4) NOT NULL ,
			  `ziel_gala` INT(11) NOT NULL ,
			  `ziel_planet` INT(11) NOT NULL ,
			  `angriff` TINYINT(1) NOT NULL ,
			  `rueckflug` TINYINT(1) NOT NULL ,
			  `flugdauer` INT(11) NOT NULL ,
			  `bleibedauer` INT(11) NOT NULL ,
			  `eta` INT(11) NOT NULL ,
			  `safe` TINYINT(1) NOT NULL ,
			  `user_gala` INT(11) NULL DEFAULT NULL ,
			  `user_planet` INT(11) NULL DEFAULT NULL ,
			  PRIMARY KEY (`id`) ,
			  INDEX `fk_Flotten_GNPlayer` (`start_planet` ASC, `start_gala` ASC) ,
			  INDEX `fk_Flotten_GNPlayer1` (`ziel_gala` ASC, `ziel_planet` ASC) ,
			  INDEX `fk_flotten_tic_user` (`user_planet` ASC, `user_gala` ASC) ,
 			 CONSTRAINT `fk_Flotten_GNPlayer`
			    FOREIGN KEY (`start_planet` , `start_gala` )
			    REFERENCES `gnplayer` (`planet` , `gala` )
			    ON DELETE NO ACTION
			    ON UPDATE NO ACTION,
 			 CONSTRAINT `fk_Flotten_GNPlayer1`
			    FOREIGN KEY (`ziel_gala` , `ziel_planet` )
			    REFERENCES `gnplayer` (`gala` , `planet` )
			    ON DELETE NO ACTION
			    ON UPDATE NO ACTION,
			  CONSTRAINT `fk_flotten_tic_user`
			    FOREIGN KEY (`user_planet` , `user_gala` )
			    REFERENCES `tic_user` (`planet` , `gala` )
			    ON DELETE NO ACTION
			    ON UPDATE NO ACTION)
			ENGINE = InnoDB
			AUTO_INCREMENT = 1
			DEFAULT CHARACTER SET = latin1
			COLLATE = latin1_german1_ci;",
            "CREATE  TABLE IF NOT EXISTS `taktik_update` (
			  `id` INT(11) NOT NULL AUTO_INCREMENT ,
			  `user_planet` INT(11) NULL ,
			  `user_gala` INT(11) NULL ,
			  `galaxie` INT(11) NOT NULL ,
			  `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP ,
			  PRIMARY KEY (`id`) ,
			  INDEX `fk_TaktikUpdate_Galaxie` (`galaxie` ASC) ,
			  INDEX `fk_TaktikUpdate_TICUser` (`user_planet` ASC, `user_gala` ASC) ,
			  CONSTRAINT `fk_TaktikUpdate_Galaxie`
			    FOREIGN KEY (`galaxie` )
			    REFERENCES `galaxie` (`gala` )
			    ON DELETE NO ACTION
			    ON UPDATE NO ACTION,
			  CONSTRAINT `fk_TaktikUpdate_TICUser`
			    FOREIGN KEY (`user_planet` , `user_gala` )
			    REFERENCES `tic_user` (`planet` , `gala` )
			    ON DELETE NO ACTION
			    ON UPDATE NO ACTION)
			ENGINE = InnoDB
			AUTO_INCREMENT = 1
			DEFAULT CHARACTER SET = latin1
			COLLATE = latin1_german1_ci;"
        ));
    }

	public function getInstallQueriesPostgreSQL()
	{
        return array(
            'DROP TABLE flotten CASCADE',
            'DROP TABLE taktik_update CASCADE',
            'CREATE TABLE flotten (
                id serial NOT NULL PRIMARY KEY,
                start_gala int NOT NULL,
                start_planet int NOT NULL,
                flotte smallint NOT NULL,
                ziel_gala int NOT NULL,
                ziel_planet int NOT NULL,
                angriff smallint NOT NULL,
                rueckflug smallint NOT NULL,
                flugdauer int NOT NULL,
                bleibedauer int NOT NULL,
                eta int NOT NULL,
                safe smallint NOT NULL,
                ticuser int
                --UNIQUE(start_gala, start_planet, flotte)
                --FOREIGN KEY (start_gala, start_planet) REFERENCES GNPlayer(planet, gala),
                --FOREIGN KEY (ziel_gala, ziel_planet) REFERENCES GNPlayer(planet, gala)
            );',
            'CREATE TABLE taktik_update (
                id serial NOT NULL PRIMARY KEY,
                ticuser int,
                galaxie int NOT NULL UNIQUE,
                time timestamp(0) NOT NULL DEFAULT now(),
                FOREIGN KEY (ticuser) REFERENCES TICUser(ticuser)
            );'
        );
    }

    /* erwartet ein assoziatives array das als key die Namen der
     * gesuchten Eigenschaften und als value die gesuchten Werte enthaelt
     *
     * erlaubte keys sind:
     *  - start_gala
     *  - start_planet
     *  - flotte
     *  - ziel_gala
     *  - ziel_planet
     *  - angriff
     *  - flugdauer
     *  - bleibedauer
     *  - eta
     *  - safe
     * */
    public function findFlotten($param) {
        global $tic;

        $qry = "SELECT start_gala, start_planet, flotte, ziel_gala, ziel_planet, ".
            "angriff, rueckflug, flugdauer, bleibedauer, eta, safe, id ".
            "FROM flotten ";

        $args = array();
        $first = true;
        foreach ($param as $key => $value) {
            if ($first) {
                $qry .= "WHERE "; $first = false;
            } else {
                $qry .= "AND ";
            }
            $qry .= "$key = %s ";
            array_push($args, $value);
        }
        $qry .= "ORDER BY ziel_gala, ziel_planet, angriff, eta, start_gala, start_planet, flotte";
        $rs = $tic->db->Execute('Taktik', $qry, $args);
        $fleets = array();
        for (; !$rs->EOF; $rs->MoveNext()) {
            $fleet = new Flottenbewegung($rs->fields[0],
                                         $rs->fields[1],
                                         $rs->fields[2],
                                         $rs->fields[3],
                                         $rs->fields[4],
                                         ($rs->fields[5] == 0) ? false : true,
                                         ($rs->fields[6] == 0) ? false : true,
                                         $rs->fields[7],
                                         $rs->fields[8],
                                         $rs->fields[9],
                                         ($rs->fields[10] == 0) ? false : true);
            $fleet->_id = $rs->fields[11];
            array_push($fleets, $fleet);
        }
        return $fleets;
    }

    public function onTick($id)
    {
        $this->calcTicks(1);
    }

    /* berechne n Ticks */
    public function calcTicks($n)
    {
        global $tic;
        $backup = $tic->disableSecurity;
        $tic->disableSecurity = true;
        $flotten = $this->findFlotten(array());
        foreach ($flotten as $flotte) {
            $flotte->calcTicks($n);
            $flotte->save();
        }
        $tic->disableSecurity = $backup;
    }

    public function getAllianzIncCount($alli, $type = 'open')
    {
        global $tic;
        switch ($type) {
        case 'open':
            $qry = "SELECT count(*) FROM flotten JOIN tic_user ".
                   "ON(gala = ziel_gala AND planet = ziel_planet) ".
                   "NATURAL JOIN galaxie ".
                   "WHERE rueckflug = '0' AND angriff = '1' AND safe = '0' AND eta >= 14 ".
                   "AND galaxie.allianz = %s";
            break;
        case 'undertime':
            $qry = "SELECT count(*) FROM flotten JOIN tic_user ".
                   "ON(gala = ziel_gala AND planet = ziel_planet) ".
                   "NATURAL JOIN galaxie ".
                   "WHERE rueckflug = '0' AND angriff = '1' AND safe = '0' AND eta < 14 ".
                   "AND galaxie.allianz = %s";
            break;
        case 'safe':
            $qry = "SELECT count(*) FROM flotten JOIN tic_user ".
                   "ON(gala = ziel_gala AND planet = ziel_planet) ".
                   "NATURAL JOIN galaxie ".
                   "WHERE rueckflug = '0' AND angriff = '1' AND safe = '1' AND eta >= 14 ".
                   "AND galaxie.allianz = %s";
            break;
        default:
            assert(false);
        }
        $rs = $tic->db->Execute($this->getName(), $qry, array($alli));
        return $rs->fields[0];
    }

    // bekommt ein Array mit nicht ge-save()-ten Flotten uebergeben
    public function updateGalaTaktik($new)
    {
        global $tic;

        $user = $tic->mod['Auth']->getActiveUser();
        $gala = $user->getGalaxie();

        $tic->db->StartTrans();

        $qry = "DELETE FROM taktik_update WHERE galaxie = %s";
        $tic->db->Execute($this->getName(), $qry, array($gala));

        $qry = "INSERT INTO taktik_update (user_gala,user_planet, galaxie) VALUES (%s, %s, %s)";
        $tic->db->Execute($this->getName(), $qry, array_merge($user->getId(),array($gala)));

        $arr1 = $this->findFlotten(array('start_gala' => $gala));
        $arr2 = $this->findFlotten(array('ziel_gala' => $gala));
        $old = array_merge($arr1, $arr2);

        $fleetsByStart = array();

        foreach ($old as $oldFleet) {
            $koords = $oldFleet->getStartKoords();
            if (!array_key_exists($koords, $fleetsByStart))
                $fleetsByStart[$koords] = array('old' => array(), 'new' => array());
            $fleetsByStart[$koords]['old'][] = $oldFleet;
        }

        foreach ($new as $newFleet) {
            $koords = $newFleet->getStartKoords();
            if (!array_key_exists($koords, $fleetsByStart))
                $fleetsByStart[$koords] = array('old' => array(), 'new' => array());
            $fleetsByStart[$koords]['new'][] = $newFleet;
        }

        echo '<pre>';
        print_r($fleetsByStart);
        echo '</pre>';

        foreach ($fleetsByStart as $startKey => $start) {
            $this->matchOldAndNew($start['old'], $start['new']);
        }

        if ($tic->db->HasFailedTrans())
            $tic->error($this->getName(), 'Datenbank-Fehler beim Abspeichern der Taktik -> Abbruch');
        $tic->db->CompleteTrans();
    }


    private function matchOldAndNew($old, $new)
    {
        assert(count($old) >= 0 || count($old) <= 2);
        assert(count($new) >= 0 || count($new) <= 2);

        $done = array();

        // alle flotten die auch mit flotten nr. übereinstimmen updaten
        foreach ($old as $oKey => $oFleet) {
            foreach ($new as $nKey => $nFleet) {
                if ($oFleet->match($nFleet, true)) {
                    $oFleet->updateWith($nFleet);
                    $this->fleetsMatched($new, $old, $done, $nKey, $oKey);
                }
            }
        }

        // automatische flottennr vervollständigung
        if (count($done) == 1) {
            foreach ($old as $oKey => $oFleet) {
                foreach ($new as $nKey => $nFleet) {

                    // wir haben schon eine Flotten-Nr.
                    // also kennen wir jetzt auch die andere
                    if ($oFleet->match($nFleet, false) && count($done) == 1) {

                        $doneNr = $done[0]->getFlotte();
                        switch ($doneNr) {
                        case 1:
                            $nFleet->setFlotte(2);
                            break;
                        case 2:
                            $nFleet->setFlotte(1);
                            break;
                        default:
                            assert(false);
                        }
                        $oFleet->updateWith($nFleet);
                        $this->fleetsMatched($new, $old, $done, $nKey, $oKey);
                    }
                }
            }
        }

        echo '<pre>';
        echo "vor machtUnknownFleet()\n";
        print_r(array('old' => $old, 'new' => $new, 'done' => $done));
        echo '</pre>';

        // alte flotten koennen anhand des etas und rueckflugs status
        // den neuen zugeordnet werden
        foreach ($old as $oKey => $oFleet) {
            foreach ($new as $nKey => $nFleet) {
                if ($oFleet->matchUnknownFleet($nFleet)) {
                    $oFleet->updateWith($nFleet);
                    $this->fleetsMatched($new, $old, $done, $nKey, $oKey);
                }
            }
        }

        // =============================================================
        // alles was hier noch übrig ist hat unbekannte oder nicht 
        // übereinstimmende flottennummern und nicht übereinstimmende etas
        // =============================================================

        // überprüfen ob sich die floten alle das gleiche tun 
        // (gleiches ziel, angriff/deff gleich
        /*$allMatch = true;
        foreach ($old as $oKey => $oFleet) {
            foreach ($new as $nKey => $nFleet) {
                if (!$oFleet->match($nFleet))
                    $allMatch = false;
            }
        }

        // alle flotten haben das gleiche ziel und sind im gleichen modus
        if ($allMatch) {
            // wir haben schon bei den alten flotten die flotten nummern
            if (count($old) == count($new) && count($old) > 0) {
                $allKnown = true;
                foreach ($old as $oKey => $oFleet) {
                    if ($oFleet->getFlotte() == -1)
                        $allKnown = false;
                }
                if ($allKnown) {
                    if ($count($old) == 1) {
                        // wir habe nur eine neue und eine alte flotte
                        $old[0]->updateWith($new[0]);
                        $done[] = $old[0];
                        unset($old[0]);
                        unset($new[0]);
                        $old = array_values($old);
                        $new = array_values($new);
                    } else { // FIXME FIXME FIXME eta vergleichen nicht flugdauer
                        // wir haben 2 alte und 2 neue fleets, aber wissen
                        // nich welche neue zu welcher alten gehört, also
                        // gucken wir nach den flugzeiten um das verhältnis
                        // möglichst zu erhalten
                        if ($old[0]->getFlugdauer() < $old[1]->getFlugdauer()) {
                            $oLower = 0;
                            $oHigher = 1;
                        } else {
                            $oLower = 1;
                            $oHigher = 0;
                        }
                        if ($new[0]->getFlugdauer() < $new[1]->getFlugdauer()) {
                            $nLower = 0;
                            $nHigher = 1;
                        } else {
                            $nLower = 1;
                            $nHigher = 0;
                        }
                        $old[$oLower]->updateWith($new[$nLower]);
                        $old[$oHigher]->updateWith($new[$nHigher]);
                        $this->fleetsMatched($new, $old, $done, $nLower, $oLower);
                        $this->fleetsMatched($new, $old, $done, $nHigher, $oHigher);
                    }
                }
            }

            if (count($new) > 0 && count($old) > 0) {
                if (count($old) > count($new)) {
                    // mehr alte als neue flotten, suche die die besser passt
                    $oFleetKey = $this->findBestMatch($new[0], $old);
                    $oFleet = $old[$oFleetKey];
                    $oFleet->updateWith($new[0]);
                    $this->fleetsMatched($new, $old, $done, 0, $oFleetKey);
                } else {
                    // mehr neue als alte flotten
                    $nFleetKey = $this->findBestMatch($old[0], $new);
                    $nFleet = $new[$nFleetKey];
                    $old[0]->updateWith($nFleet);
                    $this->fleetsMatched($new, $old, $done, $nFleetKey, 0);
                }
            }
        }*/

        // beim rest wurden keine übereinstimmungen gefunden
        foreach ($old as $oFleet) {
            $oFleet->delete();
        }
        foreach ($new as $nFleet) {
            $nFleet->save();
        }
    }

    // verschiebt 2 flotten aus $old und $new, die scheinbar 
    // die gleiche sind, in das $done array und sorgt dafür
    // dass die array indices wieder fortlaufend sind
    private function fleetsMatched(&$new, &$old, &$done, $nKey, $oKey)
    {
        $done[] = $old[$oKey];
        unset($old[$oKey]);
        unset($new[$nKey]);
        $new = array_values($new);
        $old = array_values($old);
    }

    // suche die beste flugzeiten übereinstimmung
    private function findBestMatch($fleet, $fleets)
    {
        $diffs = array();
        foreach ($fleets as $key => $val) {
            $diff[$key] = abs($val->getFlugdauer() - $fleet->getFlugdauer());
        }
        $smallest = 0;
        foreach ($diffs as $key => $diff) {
            if ($diffs[$smallest] > $diff) {
                $smallest = $key;
            }
        }
        return $smallest;
    }

    public function updateFleet($newFleet)
    {
        $search = array();
        $search['start_gala'] = $newFleet->getStartGala();
        $search['start_planet'] = $newFleet->getStartPlanet();

        if ($newFleet->getFlotte() != -1) {
            // wir haben eine flotten nummer
            $search['flotte'] = $newFleet->getFlotte();
            $fleets = $this->findFlotten($search);
            assert(count($fleets) <= 1);
            if (count($fleets) == 1) {
                // flotte existiert bereits -> update
                $fleets[0].updateWith($newFleet);
            } else {
                // flotte ist neu
                $fleet->save();
            }
        } else {
            // flotte ist unbekannt
            $search['ziel_gala'] = $newFleet->getZielGala();
            $search['ziel_planet'] = $newFleet->getZielPlanet();
            $search['angriff'] = $newFleet->getZielPlanet();
            $fleets = $this->findFlotten($search);
            $matchFound = false;
            foreach ($fleets as $fleet) {
                if ($fleet->matchUnknownFleet($newFleet)) {
                    $fleet->updateWith($newFleet);
                    $matchFound = true;
                }
            }
            if (!$matchFound)
                $newFleet->save();
        }
    }

    public function updateFlotte($flotte)
    {
        if (!is_object($flotte)) { $tic->error = "Uebergabe muss als Objekt erfolgen!!!"; }
        $flotte->save();
        return true;
    }

    // ============================ callbacks for json generation =============================

    public function taktikHUD()
    {
        global $tic;

        // allianzen mit meta
        $meten = $tic->mod['UserMan']->getAllMeten();
        $metenArr = array();
        foreach ($meten as $meta) {
            $allianzen = $meta->getAllianzen();
            $metaArr = array();
            $metaArr['name'] = $meta->getName();
            $metaArr['tag'] = $meta->getTag();
            $metaArr['allianzen'] = array();
            foreach ($allianzen as $alli) {
                $metaArr['allianzen'][] = $this->taktikHUDHelperCreateAlli($alli);
            }
            $metenArr[] = $metaArr;
        }

        // allianzen ohne meta
        $frei = $tic->mod['UserMan']->getFreieAllianzen();
        $metaArr = array('name' => '[metalose Allianzen]', 'tag' => '[metalos]', 'allianzen' => array());
        foreach ($frei as $alli) {
            $metaArr['allianzen'][] = $this->taktikHUDHelperCreateAlli($alli);
        }
        $metenArr[] = $metaArr;

        return $metenArr;
    }

    private function taktikHUDHelperCreateAlli($alli)
    {
        global $tic;
        $alliArr = array();
        $alliArr['name'] = $alli->getName();
        $alliArr['tag'] = $alli->getTag();
        $alliArr['online'] = (int) $alli->getOnlineUserCount();
        $alliArr['member'] = (int) $alli->getMemberCount();
        $alliArr['open'] = (int) $tic->mod['Taktik']->getAllianzIncCount($alli->getId(), 'open');
        $alliArr['undertime'] = (int) $tic->mod['Taktik']->getAllianzIncCount($alli->getId(), 'undertime');
        $alliArr['safe'] = (int) $tic->mod['Taktik']->getAllianzIncCount($alli->getId(), 'safe');
        $alliArr['online_users'] = array(array('nick' => 'foo', 'koords' => '123:13'), //FIXME
                                         array('nick' => 'bar', 'koords' => '123:14'));
        return $alliArr;
    }


}

?>
Return current item: Tactical Information Center