/**
  * Creates a new local user in the application data base.
  * 
  * @param WebSoccer $websoccer Application context.
  * @param DbConnection $db DB Connection.
  * @param string $nick User name of new user. Optional if e-mail address is provided. Must be unique in local data base. Case sensitive.
  * @param string $email E-mail address of new user. Optional if nick is provided. Must be unique in local data base. Case insensitive (will be stored with lower letters).
  * @throws Exception if both nick and e-mail are blank, or if nick name or e-mail address is already in use. Messages are not internationalized. Method assumes appropriate checks before calling it.
  * @return int ID of newly created user.
  */
 public static function createLocalUser(WebSoccer $websoccer, DbConnection $db, $nick = null, $email = null)
 {
     $username = trim($nick);
     $emailAddress = strtolower(trim($email));
     // check if either nick or e-mail is provided. If not, it most probably is a wrong API call,
     // hence message is not required to be translated.
     if (!strlen($username) && !strlen($emailAddress)) {
         throw new Exception("UsersDataService::createBlankUser(): Either user name or e-mail must be provided in order to create a new internal user.");
     }
     // verify that there is not already such a user. If so, the calling function is wrongly implemented, hence
     // no translation of message.
     if (strlen($username) && self::getUserIdByNick($websoccer, $db, $username) > 0) {
         throw new Exception("Nick name is already in use.");
     }
     if (strlen($emailAddress) && self::getUserIdByEmail($websoccer, $db, $emailAddress) > 0) {
         throw new Exception("E-Mail address is already in use.");
     }
     // creates user.
     $i18n = I18n::getInstance($websoccer->getConfig("supported_languages"));
     $columns = array("nick" => $username, "email" => $emailAddress, "status" => "1", "datum_anmeldung" => $websoccer->getNowAsTimestamp(), "lang" => $i18n->getCurrentLanguage());
     if ($websoccer->getConfig("premium_initial_credit")) {
         $columns["premium_balance"] = $websoccer->getConfig("premium_initial_credit");
     }
     $db->queryInsert($columns, $websoccer->getConfig("db_prefix") . "_user");
     // provide ID of created user.
     if (strlen($username)) {
         $userId = self::getUserIdByNick($websoccer, $db, $username);
     } else {
         $userId = self::getUserIdByEmail($websoccer, $db, $emailAddress);
     }
     // trigger plug-ins
     $event = new UserRegisteredEvent($websoccer, $db, I18n::getInstance($websoccer->getConfig("supported_languages")), $userId, $username, $emailAddress);
     PluginMediator::dispatchEvent($event);
     return $userId;
 }
 /**
  * Notifies listeners about the specified event.
  * 
  * @param AbstractEvent $event event instance holding data for listeners.
  * @throws Exception if the configured listener function does not exist or if this function throws an Exception.
  */
 public static function dispatchEvent(AbstractEvent $event)
 {
     if (self::$_eventlistenerConfigs == null) {
         include CONFIGCACHE_EVENTS;
         if (isset($eventlistener)) {
             self::$_eventlistenerConfigs = $eventlistener;
         } else {
             self::$_eventlistenerConfigs = array();
         }
     }
     // any event listener configured?
     if (!count(self::$_eventlistenerConfigs)) {
         return;
     }
     // get available configurations for this event.
     $eventName = get_class($event);
     if (!isset(self::$_eventlistenerConfigs[$eventName])) {
         return;
     }
     // call listeners
     $eventListeners = self::$_eventlistenerConfigs[$eventName];
     foreach ($eventListeners as $listenerConfigStr) {
         $listenerConfig = json_decode($listenerConfigStr, TRUE);
         if (method_exists($listenerConfig['class'], $listenerConfig['method'])) {
             call_user_func($listenerConfig['class'] . '::' . $listenerConfig['method'], $event);
         } else {
             throw new Exception('Configured event listener must have function: ' . $listenerConfig['class'] . '::' . $listenerConfig['method']);
         }
     }
 }
 private function createYouthPlayer($clubId, $scout, $country)
 {
     $firstName = $this->getItemFromFile(NAMES_DIRECTORY . "/" . $country . "/firstnames.txt");
     $lastName = $this->getItemFromFile(NAMES_DIRECTORY . "/" . $country . "/lastnames.txt");
     // strength computation (always compute since plug-ins might override scouting-success-flag)
     $minStrength = (int) $this->_websoccer->getConfig("youth_scouting_min_strength");
     $maxStrength = (int) $this->_websoccer->getConfig("youth_scouting_max_strength");
     $scoutFactor = $scout["expertise"] / 100;
     $strength = $minStrength + round(($maxStrength - $minStrength) * $scoutFactor);
     // consider random deviation
     $deviation = (int) $this->_websoccer->getConfig("youth_scouting_standard_deviation");
     $strength = $strength + SimulationHelper::getMagicNumber(0 - $deviation, $deviation);
     $strength = max($minStrength, min($maxStrength, $strength));
     // make sure that condigured boundaries are not violated
     // determine position
     if ($scout["speciality"] == "Torwart") {
         $positionProbabilities = array("Torwart" => 40, "Abwehr" => 30, "Mittelfeld" => 25, "Sturm" => 5);
     } elseif ($scout["speciality"] == "Abwehr") {
         $positionProbabilities = array("Torwart" => 10, "Abwehr" => 50, "Mittelfeld" => 30, "Sturm" => 10);
     } elseif ($scout["speciality"] == "Mittelfeld") {
         $positionProbabilities = array("Torwart" => 10, "Abwehr" => 15, "Mittelfeld" => 60, "Sturm" => 15);
     } elseif ($scout["speciality"] == "Sturm") {
         $positionProbabilities = array("Torwart" => 5, "Abwehr" => 15, "Mittelfeld" => 40, "Sturm" => 40);
     } else {
         $positionProbabilities = array("Torwart" => 15, "Abwehr" => 30, "Mittelfeld" => 35, "Sturm" => 20);
     }
     $position = SimulationHelper::selectItemFromProbabilities($positionProbabilities);
     $minAge = $this->_websoccer->getConfig("youth_scouting_min_age");
     $maxAge = $this->_websoccer->getConfig("youth_min_age_professional");
     $age = $minAge + SimulationHelper::getMagicNumber(0, abs($maxAge - $minAge));
     // create player
     $this->_db->queryInsert(array("team_id" => $clubId, "firstname" => $firstName, "lastname" => $lastName, "age" => $age, "position" => $position, "nation" => $country, "strength" => $strength), $this->_websoccer->getConfig("db_prefix") . "_youthplayer");
     // trigger event for plug-ins
     $event = new YouthPlayerScoutedEvent($this->_websoccer, $this->_db, $this->_i18n, $clubId, $scout["id"], $this->_db->getLastInsertedId());
     PluginMediator::dispatchEvent($event);
     // create success message
     $this->_websoccer->addFrontMessage(new FrontMessage(MESSAGE_TYPE_SUCCESS, $this->_i18n->getMessage("youthteam_scouting_success"), $this->_i18n->getMessage("youthteam_scouting_success_details", $firstName . " " . $lastName)));
 }
 /**
  * Computes and stores the audience, including crediting the sales revenue.
  * Considers following factors:
  * - Fan popularity
  * - Ticket prices (compared to league average prices, which are set by the admin)
  * - bonus if the match is attractive. It is attractive if it is a cup match or if teams are neighbors in the standings.
  * 
  * @param WebSoccer $websoccer request context.
  * @param DbConnection $db database connection.
  * @param SimulationMatch $match Simulation match for that the audience shall be computed.
  */
 public static function computeAndSaveAudience(WebSoccer $websoccer, DbConnection $db, SimulationMatch $match)
 {
     // get stadium, user and team info
     $homeInfo = self::getHomeInfo($websoccer, $db, $match->homeTeam->id);
     if (!$homeInfo) {
         return;
     }
     // is match in particular attractive?
     $isAttractiveMatch = FALSE;
     if ($match->type == 'Pokalspiel') {
         $isAttractiveMatch = TRUE;
     } else {
         if ($match->type == 'Ligaspiel') {
             // consider difference between points
             $tcolumns = 'sa_punkte';
             $fromTable = $websoccer->getConfig('db_prefix') . '_verein';
             $whereCondition = 'id = %d';
             $result = $db->querySelect($tcolumns, $fromTable, $whereCondition, $match->homeTeam->id);
             $home = $result->fetch_array();
             $result->free();
             $result = $db->querySelect($tcolumns, $fromTable, $whereCondition, $match->guestTeam->id);
             $guest = $result->fetch_array();
             $result->free();
             if (abs($home['sa_punkte'] - $guest['sa_punkte']) <= 9) {
                 $isAttractiveMatch = TRUE;
             }
         }
     }
     // consider stadium extras
     $maintenanceInfluence = $homeInfo['level_videowall'] * $websoccer->getConfig('stadium_videowall_effect');
     $maintenanceInfluenceSeats = (5 - $homeInfo['level_seatsquality']) * $websoccer->getConfig('stadium_seatsquality_effect');
     $maintenanceInfluenceVip = (5 - $homeInfo['level_vipquality']) * $websoccer->getConfig('stadium_vipquality_effect');
     // compute sold tickets
     $rateStands = self::computeRate($homeInfo['avg_price_stands'], $homeInfo['avg_sales_stands'], $homeInfo['price_stands'], $homeInfo['popularity'], $isAttractiveMatch, $maintenanceInfluence);
     $rateSeats = self::computeRate($homeInfo['avg_price_seats'], $homeInfo['avg_sales_seats'], $homeInfo['price_seats'], $homeInfo['popularity'], $isAttractiveMatch, $maintenanceInfluence - $maintenanceInfluenceSeats);
     $rateStandsGrand = self::computeRate($homeInfo['avg_price_stands'] * 1.2, $homeInfo['avg_sales_stands_grand'], $homeInfo['price_stands_grand'], $homeInfo['popularity'], $isAttractiveMatch, $maintenanceInfluence);
     $rateSeatsGrand = self::computeRate($homeInfo['avg_price_seats'] * 1.2, $homeInfo['avg_sales_seats_grand'], $homeInfo['price_seats_grand'], $homeInfo['popularity'], $isAttractiveMatch, $maintenanceInfluence - $maintenanceInfluenceSeats);
     $rateVip = self::computeRate($homeInfo['avg_price_vip'], $homeInfo['avg_sales_vip'], $homeInfo['price_vip'], $homeInfo['popularity'], $isAttractiveMatch, $maintenanceInfluence - $maintenanceInfluenceVip);
     // call plug-ins
     $event = new TicketsComputedEvent($websoccer, $db, I18n::getInstance($websoccer->getConfig('supported_languages')), $match, $homeInfo['stadium_id'], $rateStands, $rateSeats, $rateStandsGrand, $rateSeatsGrand, $rateVip);
     PluginMediator::dispatchEvent($event);
     // is sold out?
     if ($rateStands == 1 && $rateSeats == 1 && $rateStandsGrand == 1 && $rateSeatsGrand == 1 && $rateVip == 1) {
         $match->isSoldOut = TRUE;
     }
     $tickets_stands = min(1, max(0, $rateStands)) * $homeInfo['places_stands'];
     $tickets_seats = min(1, max(0, $rateSeats)) * $homeInfo['places_seats'];
     $tickets_stands_grand = min(1, max(0, $rateStandsGrand)) * $homeInfo['places_stands_grand'];
     $tickets_seats_grand = min(1, max(0, $rateSeatsGrand)) * $homeInfo['places_seats_grand'];
     $tickets_vip = min(1, max(0, $rateVip)) * $homeInfo['places_vip'];
     // update team statistic
     $columns['last_steh'] = $tickets_stands;
     $columns['last_sitz'] = $tickets_seats;
     $columns['last_haupt_steh'] = $tickets_stands_grand;
     $columns['last_haupt_sitz'] = $tickets_seats_grand;
     $columns['last_vip'] = $tickets_vip;
     $fromTable = $websoccer->getConfig('db_prefix') . '_verein';
     $whereCondition = 'id = %d';
     $db->queryUpdate($columns, $fromTable, $whereCondition, $match->homeTeam->id);
     // update match field
     $mcolumns['zuschauer'] = $tickets_stands + $tickets_seats + $tickets_stands_grand + $tickets_seats_grand + $tickets_vip;
     $fromTable = $websoccer->getConfig('db_prefix') . '_spiel';
     $db->queryUpdate($mcolumns, $fromTable, $whereCondition, $match->id);
     // compute and credit income
     $revenue = $tickets_stands * $homeInfo['price_stands'];
     $revenue += $tickets_seats * $homeInfo['price_seats'];
     $revenue += $tickets_stands_grand * $homeInfo['price_stands_grand'];
     $revenue += $tickets_seats_grand * $homeInfo['price_seats_grand'];
     $revenue += $tickets_vip * $homeInfo['price_vip'];
     BankAccountDataService::creditAmount($websoccer, $db, $match->homeTeam->id, $revenue, 'match_ticketrevenue_subject', 'match_ticketrevenue_sender');
     self::weakenPlayersDueToGrassQuality($websoccer, $homeInfo, $match);
     self::updateMaintenanceStatus($websoccer, $db, $homeInfo);
 }
     // increase age of youth players
     $youthresult = $db->querySelect('id,age', $conf['db_prefix'] . '_youthplayer', 'team_id = %d', $team['id']);
     while ($youthplayer = $youthresult->fetch_array()) {
         $playerage = $youthplayer['age'] + 1;
         // delete youth player
         if ($maxYouthAge > 0 && $maxYouthAge <= $playerage) {
             $db->queryDelete($conf['db_prefix'] . '_youthplayer', 'id = %d', $youthplayer['id']);
             // update youth player
         } else {
             $db->queryUpdate(array('age' => $playerage), $conf['db_prefix'] . '_youthplayer', 'id = %d', $youthplayer['id']);
         }
     }
     $youthresult->free();
     // dispatch event
     $event = new SeasonOfTeamCompletedEvent($website, $db, $i18n, $team['id'], $season['id'], $rank);
     PluginMediator::dispatchEvent($event);
     $rank++;
 }
 $result->free();
 // reset clubs statistics of teams which have not been moved
 $teamcolumns = array();
 $teamcolumns['sa_tore'] = 0;
 $teamcolumns['sa_gegentore'] = 0;
 $teamcolumns['sa_spiele'] = 0;
 $teamcolumns['sa_siege'] = 0;
 $teamcolumns['sa_niederlagen'] = 0;
 $teamcolumns['sa_unentschieden'] = 0;
 $teamcolumns['sa_punkte'] = 0;
 $db->queryUpdate($teamcolumns, $conf['db_prefix'] . '_verein', 'liga_id = %d', $season['liga_id']);
 // update season
 $db->queryUpdate($seasoncolumns, $conf['db_prefix'] . '_saison', 'id = %d', $season['id']);
 /**
  * called when match is considered as completed.
  * 
  * @param SimulationMatch $match
  */
 private function completeMatch($match)
 {
     $match->isCompleted = TRUE;
     foreach ($this->_observers as $observer) {
         $observer->onMatchCompleted($match);
     }
     // trigger plug-ins
     $event = new MatchCompletedEvent($this->_websoccer, $this->_db, I18n::getInstance($this->_websoccer->getConfig('supported_languages')), $match);
     PluginMediator::dispatchEvent($event);
 }
 private function trainPlayers($teamId, $trainer, $unit)
 {
     // compute effect on every player
     $players = PlayersDataService::getPlayersOfTeamById($this->_websoccer, $this->_db, $teamId);
     // freshness decrease for stamina and technique training
     $freshnessDecrease = round(1 + $unit["intensity"] / 100 * 5);
     $fromTable = $this->_websoccer->getConfig("db_prefix") . "_spieler";
     $whereCondition = "id = %d";
     $trainingEffects = array();
     foreach ($players as $player) {
         // injured player only refreshes and looses stamina
         $effectFreshness = 0;
         $effectStamina = 0;
         $effectTechnique = 0;
         $effectSatisfaction = 0;
         if ($player["matches_injured"]) {
             $effectFreshness = 1;
             $effectStamina = -1;
         } else {
             // regeneration training
             if ($unit["focus"] == "FR") {
                 $effectFreshness = 5;
                 $effectStamina = -2;
                 $effectSatisfaction = 1;
                 // motivation training
             } else {
                 if ($unit["focus"] == "MOT") {
                     $effectFreshness = 1;
                     $effectStamina = -1;
                     $effectSatisfaction = 5;
                     // stamina training
                 } else {
                     if ($unit["focus"] == "STA") {
                         $effectSatisfaction = -1;
                         // freshness depends on intensity
                         $effectFreshness = -$freshnessDecrease;
                         // success depends on trainer skills and intensity
                         $staminaIncrease = 1;
                         if ($unit["intensity"] > 50) {
                             $successFactor = $unit["intensity"] * $trainer["p_stamina"] / 100;
                             $pStamina[5] = $successFactor;
                             $pStamina[1] = 100 - $successFactor;
                             $staminaIncrease += SimulationHelper::selectItemFromProbabilities($pStamina);
                         }
                         $effectStamina = $staminaIncrease;
                         // technique
                     } else {
                         $effectFreshness = -$freshnessDecrease;
                         if ($unit["intensity"] > 20) {
                             $effectStamina = 1;
                         }
                         $techIncrease = 0;
                         if ($unit["intensity"] > 75) {
                             $successFactor = $unit["intensity"] * $trainer["p_technique"] / 100;
                             $pTech[2] = $successFactor;
                             $pTech[0] = 100 - $successFactor;
                             $techIncrease += SimulationHelper::selectItemFromProbabilities($pTech);
                         }
                         $effectTechnique = $techIncrease;
                     }
                 }
             }
         }
         // call plugins
         $event = new PlayerTrainedEvent($this->_websoccer, $this->_db, $this->_i18n, $player["id"], $teamId, $trainer["id"], $effectFreshness, $effectTechnique, $effectStamina, $effectSatisfaction);
         PluginMediator::dispatchEvent($event);
         // update player
         $columns = array("w_frische" => min(100, max(1, $player["strength_freshness"] + $effectFreshness)), "w_technik" => min(100, max(1, $player["strength_technic"] + $effectTechnique)), "w_kondition" => min(100, max(1, $player["strength_stamina"] + $effectStamina)), "w_zufriedenheit" => min(100, max(1, $player["strength_satisfaction"] + $effectSatisfaction)));
         $this->_db->queryUpdate($columns, $fromTable, $whereCondition, $player["id"]);
         // add effect
         $trainingEffects[$player["id"]] = array("name" => $player["pseudonym"] ? $player["pseudonym"] : $player["firstname"] . " " . $player["lastname"], "freshness" => $effectFreshness, "technique" => $effectTechnique, "stamina" => $effectStamina, "satisfaction" => $effectSatisfaction);
     }
     $this->_websoccer->addContextParameter("trainingEffects", $trainingEffects);
 }
 private function _createUser($parameters, $fromTable)
 {
     $dbcolumns = array();
     $dbcolumns["nick"] = $parameters["nick"];
     $dbcolumns["email"] = strtolower($parameters["email"]);
     $dbcolumns["passwort_salt"] = SecurityUtil::generatePasswordSalt();
     $dbcolumns["passwort"] = SecurityUtil::hashPassword($parameters["pswd"], $dbcolumns["passwort_salt"]);
     $dbcolumns["datum_anmeldung"] = $this->_websoccer->getNowAsTimestamp();
     $dbcolumns["schluessel"] = str_replace("&", "_", SecurityUtil::generatePassword());
     $dbcolumns["status"] = 2;
     $dbcolumns["lang"] = $this->_i18n->getCurrentLanguage();
     if ($this->_websoccer->getConfig("premium_initial_credit")) {
         $dbcolumns["premium_balance"] = $this->_websoccer->getConfig("premium_initial_credit");
     }
     $this->_db->queryInsert($dbcolumns, $fromTable);
     // get user id
     $columns = "id";
     $wherePart = "email = '%s'";
     $result = $this->_db->querySelect($columns, $fromTable, $wherePart, $dbcolumns["email"]);
     $newuser = $result->fetch_array();
     $result->free();
     $querystr = "key=" . $dbcolumns["schluessel"] . "&userid=" . $newuser["id"];
     $tplparameters["activationlink"] = $this->_websoccer->getInternalActionUrl("activate", $querystr, "activate-user", TRUE);
     // send e-mail
     EmailHelper::sendSystemEmailFromTemplate($this->_websoccer, $this->_i18n, $dbcolumns["email"], $this->_i18n->getMessage("activation_email_subject"), "useractivation", $tplparameters);
     // trigger plug-ins
     $event = new UserRegisteredEvent($this->_websoccer, $this->_db, $this->_i18n, $newuser["id"], $dbcolumns["nick"], $dbcolumns["email"]);
     PluginMediator::dispatchEvent($event);
 }
 /**
  * Update overall player statistics and match records of player.
  * 
  * @param SimulationMatch $match
  * @param SimulationPlayer $player
  * @param $isOnPitch TRUE if player is on pitch in the end, FALSE if got removed (substitution or red card or injury).
  */
 private function _updatePlayer(SimulationMatch $match, SimulationPlayer $player, $isOnPitch)
 {
     // update match statistics
     $columns = array('name' => $player->name, 'position_main' => $player->mainPosition, 'grade' => $player->getMark(), 'minutes_played' => $player->getMinutesPlayed(), 'card_yellow' => $player->yellowCards, 'card_red' => $player->redCard, 'goals' => $player->getGoals(), 'strength' => $player->strength, 'ballcontacts' => $player->getBallContacts(), 'wontackles' => $player->getWonTackles(), 'shoots' => $player->getShoots(), 'passes_successed' => $player->getPassesSuccessed(), 'passes_failed' => $player->getPassesFailed(), 'assists' => $player->getAssists(), 'state' => $isOnPitch ? '1' : 'Ausgewechselt');
     $this->_db->queryUpdate($columns, $this->_websoccer->getConfig('db_prefix') . '_youthmatch_player', 'match_id = %d AND player_id = %d', array($match->id, $player->id));
     // update player record, if actually played
     if ($this->_websoccer->getConfig('sim_played_min_minutes') <= $player->getMinutesPlayed()) {
         // query existing statistics
         $result = $this->_db->querySelect('*', $this->_websoccer->getConfig('db_prefix') . '_youthplayer', 'id = %d', $player->id);
         $playerinfo = $result->fetch_array();
         $result->free();
         $strengthChange = $this->_computeStrengthChange($player);
         // trigger plug-ins
         $event = new YouthPlayerPlayedEvent($this->_websoccer, $this->_db, I18n::getInstance($this->_websoccer->getConfig('supported_languages')), $player, $strengthChange);
         PluginMediator::dispatchEvent($event);
         $yellowRedCards = 0;
         if ($player->yellowCards == 2) {
             $yellowCards = 1;
             $yellowRedCards = 1;
         } else {
             $yellowCards = $player->yellowCards;
         }
         // ensure that new strength does not exceed boundaries (max/min strength)
         $strength = $playerinfo['strength'] + $strengthChange;
         $maxStrength = $this->_websoccer->getConfig('youth_scouting_max_strength');
         $minStrength = $this->_websoccer->getConfig('youth_scouting_min_strength');
         if ($strength > $maxStrength) {
             $strengthChange = 0;
             $strength = $maxStrength;
         } else {
             if ($strength < $minStrength) {
                 $strengthChange = 0;
                 $strength = $minStrength;
             }
         }
         // save
         $columns = array('strength' => $strength, 'strength_last_change' => $strengthChange, 'st_goals' => $playerinfo['st_goals'] + $player->getGoals(), 'st_matches' => $playerinfo['st_matches'] + 1, 'st_assists' => $playerinfo['st_assists'] + $player->getAssists(), 'st_cards_yellow' => $playerinfo['st_cards_yellow'] + $yellowCards, 'st_cards_yellow_red' => $playerinfo['st_cards_yellow_red'] + $yellowRedCards, 'st_cards_red' => $playerinfo['st_cards_red'] + $player->redCard);
         $this->_db->queryUpdate($columns, $this->_websoccer->getConfig('db_prefix') . '_youthplayer', 'id = %d', $player->id);
     }
 }