diff --git a/src/controllers/AdminController.php b/src/controllers/AdminController.php index d8c002be..3adae915 100644 --- a/src/controllers/AdminController.php +++ b/src/controllers/AdminController.php @@ -2540,13 +2540,13 @@ class=
- +
- +
@@ -2894,7 +2894,7 @@ class=
- +
- + {tr('PTS')} + {tr('Capture')} +
+
+ + {tr('PTS')} + {tr('Hold')}
@@ -241,6 +247,12 @@ class=
{tr('PTS')} + {tr('Capture')} +
+
+ + {tr('PTS')} + {tr('Hold')}
diff --git a/src/inc/gameboard/modules/activity.php b/src/inc/gameboard/modules/activity.php index 1564cfa9..9d92ab69 100644 --- a/src/inc/gameboard/modules/activity.php +++ b/src/inc/gameboard/modules/activity.php @@ -13,7 +13,7 @@ class ActivityModuleController extends ModuleController { $activity_ul =
    ; list($all_activity, $config) = await \HH\Asio\va( - ActivityLog::genAllActivity(), + ActivityLog::genAllActivity(array('held')), Configuration::gen('language'), ); $language = $config->getValue(); diff --git a/src/language/lang_en.php b/src/language/lang_en.php index e5644cef..7f2340f1 100644 --- a/src/language/lang_en.php +++ b/src/language/lang_en.php @@ -516,6 +516,8 @@ //Translations for inc/* and inc/gameboard/* 'captured' => 'captured', + 'held' => + 'held', 'Status' => 'Status', 'Completed' => @@ -600,6 +602,10 @@ 'INACTIVE', 'PTS' => 'PTS', + 'Capture' => + 'Capture', + 'Hold' => + 'Hold', 'category' => 'category', 'capture_' => diff --git a/src/models/ActivityLog.php b/src/models/ActivityLog.php index 2cbcc7a3..8605ebf5 100644 --- a/src/models/ActivityLog.php +++ b/src/models/ActivityLog.php @@ -195,6 +195,20 @@ private static function activitylogFromRow( ); } + public static async function genHoldLog( + int $team_id, + int $level_id, + ): Awaitable { + $country_id = await Level::genCountryIdForLevel($level_id); + await self::genCreateActionLog( + "Team", + $team_id, + "held", + "Country", + $country_id, + ); + } + public static async function genCreateActionLog( string $subject_class, int $subject_id, @@ -278,14 +292,21 @@ private static function activitylogFromRow( } public static async function genAllActivity( + array $exclude_actions = array(''), bool $refresh = false, ): Awaitable> { $mc_result = self::getMCRecords('ALL_ACTIVITY'); if (!$mc_result || count($mc_result) === 0 || $refresh) { + $excludes = new Vector(); + foreach ($exclude_actions as $exclude) { + $excludes->add($exclude); + } + $db = await self::genDb(); $activity_log_lines = array(); - $result = await $db->query( - 'SELECT * FROM activity_log ORDER BY ts DESC LIMIT 100', + $result = await $db->queryf( + 'SELECT * FROM activity_log WHERE action NOT IN (%Ls) ORDER BY ts DESC LIMIT 100', + $excludes ); foreach ($result->mapRows() as $row) { $activity_log = self::activitylogFromRow($row); diff --git a/src/models/Level.php b/src/models/Level.php index e559621b..c5c3bf44 100644 --- a/src/models/Level.php +++ b/src/models/Level.php @@ -148,9 +148,11 @@ private static function levelFromRow(Map $row): Level { public static async function importAll( array> $elements, ): Awaitable { + Utils::logMessage('Beginning import of '.count($elements).' levels'); foreach ($elements as $level) { $title = must_have_string($level, 'title'); $type = must_have_string($level, 'type'); + Utils::logMessage("> Importing $type level: $title"); $entity_iso_code = must_have_string($level, 'entity_iso_code'); $c = must_have_string($level, 'category'); $exist = await self::genAlreadyExist($type, $title, $entity_iso_code); @@ -158,11 +160,21 @@ private static function levelFromRow(Map $row): Level { Country::genCheckExists($entity_iso_code), Category::genCheckExists($c), ); // TODO: Combine Awaits - if (!$exist && $entity_exist && $category_exist) { + if ($exist) { + Utils::logMessage('>> Level already exists'); + } else if (!$entity_exist) { + Utils::logMessage(">> Country ($entity_iso_code) does not exist"); + } else if (!$category_exist) { + Utils::logMessage(">> Category ($c) does not exist"); + } else { list($entity, $category) = await \HH\Asio\va( Country::genCountry($entity_iso_code), Category::genSingleCategoryByName($c), ); // TODO: Combine Awaits + + // Handle previous versions + $level = self::correctForVersion($level); + $level_id = await self::genCreate( $type, $title, @@ -178,6 +190,7 @@ private static function levelFromRow(Map $row): Level { must_have_int($level, 'penalty'), ); if (array_key_exists('links', $level)) { + Utils::logMessage('>> Importing links'); $links = must_have_idx($level, 'links'); invariant(is_array($links), 'links must be of type array'); foreach ($links as $link) { @@ -185,6 +198,7 @@ private static function levelFromRow(Map $row): Level { } } if (array_key_exists('attachments', $level)) { + Utils::logMessage('>> Importing attachments'); $attachments = must_have_idx($level, 'attachments'); invariant( is_array($attachments), @@ -198,11 +212,29 @@ private static function levelFromRow(Map $row): Level { ); // TODO: Combine Awaits } } + Utils::logMessage('>> Success'); } } return true; } + private static function correctForVersion( + array $level + ): array { + $version = (isset($level['version']) && !is_null($level['version'])) ? $level['version'] : 1; + // base versions < 2.0 had bonus & points reversed + if ($level['type'] === 'base' && $version < 2) { + Utils::logMessage('>> Base version is < 2. Correcting data.'); + $capture_points = $level['bonus']; + $hold_points = $level['points']; + $level['points'] = $capture_points; + $level['bonus'] = $hold_points; + $level['bonus_fix'] = $hold_points; + } + + return $level; + } + // Export levels. public static async function exportAll( ): Awaitable>> { @@ -244,6 +276,7 @@ private static function levelFromRow(Map $row): Level { 'penalty' => $level->getPenalty(), 'links' => $link_array, 'attachments' => $attachment_array, + 'version' => 2.0 ); array_push($all_levels_data, $one_level); } @@ -1140,8 +1173,10 @@ private static function levelFromRow(Map $row): Level { $score = await ScoreLog::genAllPreviousScore($level_id, $team_id, false); if ($score) { - $points = $level->getPoints(); + // If the team has already captured this base, only give the bonus + $points = $level->getBonus(); } else { + // Otherwise give capture points $points = $level->getPoints() + $level->getBonus(); } @@ -1153,7 +1188,7 @@ private static function levelFromRow(Map $row): Level { ); // Log the score... - await ScoreLog::genLogValidScore( + await ScoreLog::genLogBaseScore( $level_id, $team_id, $points, @@ -1232,7 +1267,8 @@ private static function levelFromRow(Map $row): Level { public static async function genBaseIP(int $base_id): Awaitable { $links = await Link::genAllLinks($base_id); $link = $links[0]; - $ip = explode(':', $link->getLink())[0]; + $ip = preg_replace('$[a-zA-Z]+://$', '', $link->getLink()); + $ip = explode(':', $ip)[0]; return $ip; } diff --git a/src/models/ScoreLog.php b/src/models/ScoreLog.php index d4813900..7baf0b6e 100644 --- a/src/models/ScoreLog.php +++ b/src/models/ScoreLog.php @@ -393,6 +393,45 @@ private static function scorelogFromRow(Map $row): ScoreLog { return $captured; } + // Log successful base capture or hold score. + public static async function genLogBaseScore( + int $level_id, + int $team_id, + int $points, + ): Awaitable { + $completed_level = await MultiTeam::genCompletedLevel($level_id); + Utils::logMessage( "Teams who have completed level ($level_id): " . var_export($completed_level, true)); + $captured = empty($completed_level) || end($completed_level)->getId() != $team_id; + + $db = await self::genDb(); + $result = + await $db->queryf( + 'INSERT INTO scores_log (ts, level_id, team_id, points, type) SELECT NOW(), %d, %d, %d, %s FROM DUAL', + $level_id, + $team_id, + $points, + 'base', + ); + + if ($captured === true) { + await ActivityLog::genCaptureLog($team_id, $level_id); + } else { + await ActivityLog::genHoldLog($team_id, $level_id); + } + self::invalidateMCRecords(); // Invalidate Memcached ScoreLog data. + ActivityLog::invalidateMCRecords('ALL_ACTIVITY'); // Invalidate Memcached ActivityLog data. + MultiTeam::invalidateMCRecords('ALL_TEAMS'); // Invalidate Memcached MultiTeam data. + MultiTeam::invalidateMCRecords('POINTS_BY_TYPE'); // Invalidate Memcached MultiTeam data. + MultiTeam::invalidateMCRecords('LEADERBOARD'); // Invalidate Memcached MultiTeam data. + $completed_level = await MultiTeam::genCompletedLevel($level_id); + if (count($completed_level) === 0) { + MultiTeam::invalidateMCRecords('TEAMS_FIRST_CAP'); // Invalidate Memcached MultiTeam data. + } + MultiTeam::invalidateMCRecords('TEAMS_BY_LEVEL'); // Invalidate Memcached MultiTeam data. + + return $captured; + } + public static async function genScoreLogUpdate( int $level_id, int $team_id, diff --git a/src/static/css/scss/_modals.scss b/src/static/css/scss/_modals.scss index a5e69092..71ea2ca2 100644 --- a/src/static/css/scss/_modals.scss +++ b/src/static/css/scss/_modals.scss @@ -164,6 +164,22 @@ } } +/** + * --country modal + */ +.modal--country-capture { + // link display + .capture-links { + @include flexbox; + flex-wrap: wrap; + + & > a, + & > input { + @include flex(1 0 100%); + } + } +} + /** * --tutorial */ diff --git a/src/static/css/scss/_typography.scss b/src/static/css/scss/_typography.scss index 89448df5..d2c6a899 100644 --- a/src/static/css/scss/_typography.scss +++ b/src/static/css/scss/_typography.scss @@ -382,7 +382,8 @@ table { /** * --point circles */ -.points-display { +.points-display, +.bonus-display { @include flexbox; @include align-center; @@ -397,18 +398,25 @@ table { width: 80px; height: 80px; - .points-number { + .points-number, + .bonus-number { font-size: 3em; display: block; line-height: 1; } - .points-label { + .points-label, + .bonus-label { font-size: .8em; text-transform: uppercase; } } +.bonus-display { + margin-left: 10px; + margin-right: -10px; +} + /* -------------------------------------------- * inactive country * -------------------------------------------- */ diff --git a/src/static/js/fb-ctf.js b/src/static/js/fb-ctf.js index a59058cc..959ed7c7 100644 --- a/src/static/js/fb-ctf.js +++ b/src/static/js/fb-ctf.js @@ -909,6 +909,7 @@ function setupInputListeners() { hint = data ? data.hint : '', hint_cost = data ? data.hint_cost : -1, points = data ? data.points : '', + bonus = data ? data.bonus : '', category = data ? data.category : '', type = data ? data.type : '', completed = data ? data.completed : '', @@ -939,14 +940,15 @@ function setupInputListeners() { } else { var ip = this.split(':')[0]; var port = this.split(':')[1]; - link = $('').attr('type', 'text').attr('disabled', true).attr('value', 'nc ' + ip + ' ' + port); + var value = port ? 'nc ' + ip + ' ' + port : ip; + link = $('').attr('type', 'text').attr('readonly', true).attr('value', value); } $('.capture-links', $container).append(link); - $('.capture-links', $container).append($('
    ')); link_c++; }); } $('.points-number', $container).text(points); + $('.bonus-number', $container).text(bonus); $('.country-type', $container).text(type); $('.country-category', $container).text(category); $('.country-owner', $container).text(owner); @@ -958,9 +960,11 @@ function setupInputListeners() { }); } - // Hide flag submission for bases + // Hide flag submission for bases & display base-style points + console.log('Country type:', type); if (type === 'base') { - $('.answer_no_bases').addClass('completely-hidden'); + $('.answer_no_bases, .points-label, .bonus-label').addClass('completely-hidden'); + $('.bonus-display, .base-bonus, .base-points').removeClass('completely-hidden'); } // Hide flag submission for captured levels @@ -1105,6 +1109,7 @@ function setupInputListeners() { top: mouse_y + 'px' }), points = data ? data.points : '', + bonus = data ? data.bonus : '', category = data ? data.category : '', title = data ? data.title : '', type = data ? data.type : ''; @@ -1112,6 +1117,7 @@ function setupInputListeners() { $('.country-name', $container).text(country); $('.country-title', $container).text(title); $('.points-number', $container).text(points); + $('.bonus-number', $container).text(bonus); $('.country-type', $container).text(type); $('.country-category', $container).text(category); });