diff --git a/GameEngine/Alliance.php b/GameEngine/Alliance.php index 9afffff7..ccf401f3 100755 --- a/GameEngine/Alliance.php +++ b/GameEngine/Alliance.php @@ -437,7 +437,11 @@ class Alliance { } else { // check whether this is not the founder leaving and if he is, see whether // his replacement has been selected - if ($session->alliance && $database->isAllianceOwner($session->uid) == $session->alliance) { + if ( + $session->alliance && + $database->isAllianceOwner($session->uid) == $session->alliance && + $database->countAllianceMembers($session->alliance) > 1 + ) { // check that we have a valid new founder if (!isset($post['new_founder'])) { $form->addError("founder", 'founder was not selected'); @@ -461,13 +465,13 @@ class Alliance { return; } - $newleader = $post['new_founder']; - $q = "UPDATE " . TB_PREFIX . "alidata set leader = ".(int) $newleader." where id = ".(int) $session->alliance.""; + $newleader = (int) $post['new_founder']; + $q = "UPDATE " . TB_PREFIX . "alidata set leader = ".$newleader." where id = ".(int) $session->alliance; $_SESSION['alliance_user'] = 0; $database->query($q); - $database->updateAlliPermissions($newleader, 1, 1, 1, 1, 1, 1, 1, 1, 1); + $database->createAlliPermissions($newleader, $session->alliance, 'Alliance Leader', 1, 1, 1, 1, 1, 1, 1, 1); $this->updateMax($newleader); - + // send the new founder an in-game message, notifying them of their election $database->sendMessage( $newleader, diff --git a/GameEngine/Automation.php b/GameEngine/Automation.php index e65a2b4b..b89e5b8e 100755 --- a/GameEngine/Automation.php +++ b/GameEngine/Automation.php @@ -1996,6 +1996,7 @@ class Automation { } if ($tbgid==18){ $this->updateMax($database->getVillageField($data['to'],'owner')); + //$p_alliance = $database->getUserField($data['to'],"alliance",0); } if ($isoasis == 0) { $pop=$this->recountPop($data['to']); @@ -4750,7 +4751,7 @@ $wallimg = "dblink,$q); @@ -1825,13 +1825,28 @@ class MYSQLi_DB implements IDbConnection { function deleteAlliance($aid) { list($aid) = $this->escape_input((int) $aid); - $result = mysqli_query($this->dblink,"SELECT * FROM " . TB_PREFIX . "users where alliance = $aid"); - $num_rows = mysqli_num_rows($result); - if($num_rows == 0) { - $q = "DELETE FROM " . TB_PREFIX . "alidata WHERE id = $aid"; - mysqli_query($this->dblink,$q); - return mysqli_insert_id($this->dblink); - } + $result = mysqli_fetch_array(mysqli_query($this->dblink,"SELECT Count(*) as Total FROM " . TB_PREFIX . "users where alliance = $aid", MYSQLI_ASSOC)); + if ($result['Total'] == 0) { + // remove the alliance + $q = "DELETE FROM " . TB_PREFIX . "alidata WHERE id = $aid"; + mysqli_query($this->dblink,$q); + + // remove all permissions for that alliance + $q = "DELETE FROM " . TB_PREFIX . "ali_permission WHERE alliance = $aid"; + mysqli_query($this->dblink,$q); + + // remove all logs for that alliance + $q = "DELETE FROM " . TB_PREFIX . "ali_log WHERE aid = $aid"; + mysqli_query($this->dblink,$q); + + // remove all medals for that alliance + $q = "DELETE FROM " . TB_PREFIX . "allimedal WHERE allyid = $aid"; + mysqli_query($this->dblink,$q); + + // remove all invitations for that alliance + $q = "DELETE FROM " . TB_PREFIX . "ali_invite WHERE alliance = $aid"; + mysqli_query($this->dblink,$q); + } } /***************************************** @@ -2168,7 +2183,7 @@ class MYSQLi_DB implements IDbConnection { return $row["f" . $field]; } - function getSingleFieldTypeCount($uid, $field, $lvl = false, $lvlComparisonSign = '=') { + function getSingleFieldTypeCount($uid, $field, $lvlComparisonSign = '=', $lvl = false) { $uid = (int) $uid; $field = (int) $field; $lvl = ($lvl === false ? $lvl : (int) $lvl); @@ -2788,17 +2803,29 @@ class MYSQLi_DB implements IDbConnection { global $building, $village, $session; - $fLevel = $this->getFieldLevel($wid,$field); + // check if we're not demolishing an Embassy + if ($this->getFieldType($wid,$field) == 18) { + + // get field level, alliance members count and the minimum + // level of Embassy to be able to hold this number of people + $fLevel = $this->getFieldLevel($wid,$field); + $membersCount = $this->countAllianceMembers($session->alliance); + $minEmbassyLevel = $this->getMinEmbassyLevel($membersCount); + $isOwner = $this->isAllianceOwner($session->uid) == $session->alliance; + + // make sure minimum Embassy level is 3 of the player is alliance owner + if ($isOwner && $minEmbassyLevel < 3) { + $minEmbassyLevel = 3; + } - // check if we're not demolishing an Embassy at level 3 - if ($fLevel == 3 && $this->getFieldType($wid,$field) == 18) { // check if this user is the founder of the alliance - if ($session->alliance && $this->isAllianceOwner($session->uid) == $session->alliance) { + // and whether we're not trying to demolish under the lowest level + // which can hold current number of members + if ($fLevel == $minEmbassyLevel && $session->alliance && $isOwner) { // check if we have any other players in this alliance left - $membersCount = $this->countAllianceMembers($session->alliance); if ($membersCount > 1) { - // check if this player has only 1 last Embassy on level 3 - if ($this->getSingleFieldTypeCount($session->uid, 18, 3, '>=') == 1) { + // check if this player has only 1 last Embassy on a sufficient level + if ($this->getSingleFieldTypeCount($session->uid, 18, '>=', $minEmbassyLevel) == 1) { // cannot demolish Embassy further until the player quits the alliance, // as they are founder and there are still other players in the alliance, // thus destroying Embassy would evict this player from the alliance @@ -2849,12 +2876,12 @@ class MYSQLi_DB implements IDbConnection { if ($checkEmbassy) { // check if we've demolished an Embassy // and select the user it belonged to as well, - // so we can potentially disconnect them from the alliance + // so we can potentially evict them from the alliance // and remove it - if they don't have any more Embassies // or if the they are founder and they have no more lvl 3+ Embassies $q = ' SELECT - u.id, u.alliance, d.buildnumber, d.lvl + u.id, u.username, u.alliance, d.buildnumber, d.lvl FROM '.TB_PREFIX.'demolition d LEFT JOIN '.TB_PREFIX.'vdata v ON d.vref = v.wref @@ -2863,64 +2890,271 @@ class MYSQLi_DB implements IDbConnection { $res = mysqli_fetch_all(mysqli_query($this->dblink, $q), MYSQLI_ASSOC); foreach ($res as $key) { - // if this building was demolished completely, there is no record of what it was - // therefore, we need to check status of Embassies in case we've just demolished - // an Embassy and should disconnect a player from an alliance - if ($key['lvl'] == 0 && $key['alliance'] > 0 && $this->getSingleFieldTypeCount($key['id'], 18, 1, '>=') == 0) { - // if we have no more Embassies and this player is in an alliance, - // disconnect him from that alliance - mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id = '.$key['id']); - $_SESSION['alliance_user'] = 0; + // if this building being demolished is an Embassy or was demolished completely + // and the player is in an alliance, check and update their alliance status + if (($key['alliance'] > 0) && ($key['lvl'] == 0 || $this->getFieldType($wid, $key['buildnumber']) == 18)) { + $this->checkAllianceEmbassiesStatus($key, true); + } + } + } - // notify them via in-game messaging - $this->sendMessage( - $key['id'], - 2, - 'You left the alliance', - "Hi!\n\nThis is to inform you that due to a finished demolition of your last Embassy, you have now successfully left your alliance.\n\nYours sincerely,\nServer Robot :)", - 0, - 0, - 0, - 0, - 0, - true); + $q = "DELETE FROM " . TB_PREFIX . "demolition WHERE vref=" . $wid; + return mysqli_query($this->dblink,$q); + } + + /** + * Returns a minimum level for an Embassy in order to accomodate + * the given number of members. + * + * @param int $membersCount Number of members for an alliance to accomodate. + * Maximum = 60 + * + * @return number Returns the level of Embassy required to accomodate + * the given number of members. + */ + public function getMinEmbassyLevel($membersCount) { + $membersCount = (int) $membersCount; + + if ($membersCount > 60) { + $membersCount = 60; + } + + if ($membersCount < 0) { + $membersCount = 0; + } + + return ceil((20 / 60) * $membersCount); + } + + /*** + * Returns the number of members an alliance can hold + * with the current level of leader's Embassy. + * + * @param int $embassyLevel Level of leader's Embassy building. + * + * @return number Returns the number of members an alliance + * can hold with the current level of leader's Embassy. + */ + public function getAllianceCapacity($embassyLevel) { + $embassyLevel = (int) $embassyLevel; + + if ($embassyLevel > 20) { + $embassyLevel = 20; + } + + if ($embassyLevel < 0) { + $embassyLevel = 0; + } + + // ceil is not really necessary but to make sure + // decimals won't crack this up, it's here + return ceil((60 / 20) * $embassyLevel); + } + + /** + * Checks and potentially updates the status of a player-alliance + * relationship given the user input. + * + * @param array $userData Data of the user for which we want to check + * the player-alliance relationship. + * @param boolean $demolition Determines whether the request came from + * a buiding demolition (true) or from a battle + * report (false). + * + * @return boolean Returns TRUE if there was no change + * to the player-alliance relationship + * FALSE otherwise. + */ + public function checkAllianceEmbassiesStatus($userData, $demolition = false) { + // TODO: refactor this and break it into more smaler methods + global $session, $automation; + + // check whether this player is an alliance owner + $isOwner = ($userData['alliance'] && $this->isAllianceOwner($userData['id']) == $userData['alliance']); + + // for demolition, the Embassy was already destroyed, so we need at least 1 + // Embassy still standing... for battle, the Embassy is still standing + // and will only be deleted upon finalizing battle calculations, so we need + // to check for at least 2 Embassies + $minimumExistingEmbassyRecords = ($demolition ? 1 : 2); + + // if they are not an alliance owner, simply check whether we have any Embassies + // at lvl 1+ standing somewhere + if (!$isOwner) { + // TODO: replace magic numbers by constants (18 = Embassy) + if ($this->getSingleFieldTypeCount($userData['id'], 18, '>=', 1) < $minimumExistingEmbassyRecords) { + + // the player has no more Embassies, evict them from the alliance + mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id = '.$userData['id']); + + // unset the alliance in session, if we're evicting + // currently logged-in player + if ($session->uid == $userData['id']) { + $_SESSION['alliance_user'] = 0; + } + + // notify them via in-game messaging, if we come from a demolition, + // otherwise return a result which can be used in battle reports + if ($demolition) { + $this->sendMessage( + $userData['id'], + 2, + 'You left the alliance', + "Hi, ".$userData['username']."!\n\nThis is to inform you that due to a finished demolition of your last Embassy, you have now successfully left your alliance.\n\nYours sincerely,\nServer Robot :)", + 0, + 0, + 0, + 0, + 0, + true); } else { - $fType = $this->getFieldType($wid, $key['buildnumber']); - - // we're actually demolishing an Embassy - if ($fType == 18) { - $isOwner = ($key['alliance'] && $this->isAllianceOwner($key['id']) == $key['alliance']); - - // in case the player is an alliance founder, - // we demolished a lvl 3 Embasy - // and there are no more lvl 3+ Embassies left for them - // disconnect them from the alliance and delete it - // because alliance can only be founded with a lvl 3+ Embassy - if ($isOwner && $key['lvl'] == 2 && $this->getSingleFieldTypeCount($key['id'], 18, 3, '>=') == 0) { - mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id = '.$key['id']); - $this->deleteAlliance($key['alliance']); - $_SESSION['alliance_user'] = 0; - - // notify them via in-game messaging + // player has been removed from the alliance + return false; + } + + } + } else { + // the player IS an alliance owner, check if we need to take any action + $membersCount = $this->countAllianceMembers($userData['alliance']); + $minAllianceEmbassyLevel = $this->getMinEmbassyLevel($membersCount); + + // in this case, the minimum Embassy level cannot go below 3, + // since this player is a leader and as such, he needs at least + // a level 3 Embassy + if ($minAllianceEmbassyLevel < 3) { + $minAllianceEmbassyLevel = 3; + } + + $takeAction = ( + // was the Embassy taken below a threshold level? + ($userData['lvl'] <= $minAllianceEmbassyLevel) + && + // check for standing Embassies with sufficient level + // TODO: replace magic numbers by constants (18 = Embassy) + ($this->getSingleFieldTypeCount($userData['id'], 18, '>=', $minAllianceEmbassyLevel) < $minimumExistingEmbassyRecords) + ); + + // the Embassy got damaged below a sufficient level and there are no more Embassies + // at that level standing on this player's account, additional actions are needed + if ($takeAction) { + + // load all alliance members + $members = $this->getAllMember($userData['alliance']); + + // if we come from demolition, we need to evict all new members + // that accepted an invitation while level 3 of the last + // Embassy was already under demolition. The demolition dialog itself + // already checks if there are no more people other than the owner + // present before the demolition is allowed. + if ($demolition) { + foreach ($members as $member) { + // evict the player from the alliance + mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id = '.$member['id']); + + // notify them via in-game messaging + $this->sendMessage( + $member['id'], + 2, + 'Your alliance was disbanded', + ( + ($member['id'] == $userData['id']) + ? + "Hi, ".$userData['username']."!\n\nThis is to inform you that due to a finished demolition of your last Embassy at level 3, and the fact that you were the leader of your alliance, this alliance has been disbanded.\n\nIn order to found a new alliance, please build a level 3 Embassy again in one of your villages.\n\nYours sincerely,\nServer Robot :)" + : + "Hi, ".$userData['username']."!\n\nThis is to inform you that due to a demolition of your alliance founder's last Embassy below level 3, this alliance has been disbanded.\n\n\You can now accept invitations from other alliances or found a new alliance yourself.\n\nYours sincerely,\nServer Robot :)" + ), + 0, + 0, + 0, + 0, + 0, + true); + } + } else { + // we come from a battle result, therefore we need to check + // for the first player in the alliance who has a sufficient + // level Embassy and to which we can auto-reassign the leadership + $newLeaderFound = false; + // in case we'll need these later to disband the alliance, + // we'll collect them inside this foeach loop + $memberIDs = []; + + foreach ($member as $member) { + if (!$newLeaderFound && $this->getSingleFieldTypeCount($member['id'], 18, '>=', $minAllianceEmbassyLevel) >= $minimumExistingEmbassyRecords) { + // found a new leader for the alliance + $newLeaderFound = true; + $newleader = $member['id']; + $q = "UPDATE " . TB_PREFIX . "alidata set leader = ".(int) $newleader." where id = ".(int) $userData['alliance']; + $this->query($q); + $this->updateAlliPermissions($newleader, $userData['alliance'], "Leader", 1, 1, 1, 1, 1, 1, 1); + $automation->updateMax($newleader); + + // update permissions for the old leader + $this->updateAlliPermissions($userData['id'], $userData['alliance'], "Former Leader", 0, 0, 0, 0, 0, 0, 0); + + // notify new leader via in-game messaging + $this->sendMessage( + $newleader, + 2, + 'You are now an alliance leader', + "Hi, ".$userData['username']."!\n\nThis is to inform you that there was a successful attack on player ".$userData['username']." which has damaged their Embassy badly enough that they are no longer able to sustain the leadership of your alliance.\n\nSince your Embassy level is of a sufficient level, you have been auto-elected to the position of a new leader of your alliance with all duties and responsibilities thereof.\n\nYours sincerely,\nServer Robot :)", + 0, + 0, + 0, + 0, + 0, + true); + } + + $memberIDs[] = $member['id']; + } + + // if there wasn't anyone with a sufficient level of Embassy + // among the existing members, disperse this alliance + if (!$newLeaderFound) { + + // evict all members from the alliance + mysqli_query($this->dblink, 'UPDATE '.TB_PREFIX.'users SET alliance = 0 WHERE id IN('.implode(',', $memberIDs).")"); + + // notify all of them via in-game messaging + foreach ($memberIDs as $id) { $this->sendMessage( - $key['id'], + $id, 2, - 'Your alliance was disbanded', - "Hi!\n\nThis is to inform you that due to a finished demolition of your last Embassy at level 3, and the fact that you were the leader of your alliance, this alliance has been disbanded.\n\n\In order to found a new alliance, please build a level 3 Embassy again in one of your villages.\n\nYours sincerely,\nServer Robot :)", + 'Your alliance was dispersed', + ( + ($id == $userData['id']) + ? + "Hi, ".$userData['username']."!\n\nThis is to inform you that due to a successful attack that has degraded your last Embassy to a level which is unable to hold all ".$membersCount." alliance members, and because there was no other alliance member with an Embassy on a high enough level to overtake the leadership, your alliance has been dispersed.\n\nYours sincerely,\nServer Robot :)" + : + "Hi, ".$userData['username']."!\n\nThis is to inform you that due to a successful attack on your alliance leader's Embassy by another player that degraded it below threshold allowed to hold all ".$membersCount." alliance members, and because there was no other alliance member with an Embassy on a high enough level to overtake the leadership, your alliance has been dispersed.\n\nYours sincerely,\nServer Robot :)" + ), 0, 0, 0, 0, 0, true); - } - } + } + } } + + // execute a method that will delete an alliance + // if no members are left in it + $this->deleteAlliance($userData['alliance']); + + // unset the alliance in session, if we're evicting + // currently logged-in player + if ($userData['alliance'] == $userData['id']) { + $_SESSION['alliance_user'] = 0; + } + + return false; } } - $q = "DELETE FROM " . TB_PREFIX . "demolition WHERE vref=" . $wid; - return mysqli_query($this->dblink,$q); + // no changes in player-to-alliance relationship + return true; } function getJobs($wid) { diff --git a/Templates/Alliance/quitalli.tpl b/Templates/Alliance/quitalli.tpl index 6a698295..3b6a556d 100644 --- a/Templates/Alliance/quitalli.tpl +++ b/Templates/Alliance/quitalli.tpl @@ -27,7 +27,7 @@ include("alli_menu.tpl"); 0) { + if ($isOwner && $membersCount > 1) { ?> @@ -45,8 +45,13 @@ include("alli_menu.tpl");