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);
});