feat(preferences): apply auto-completion (village name suggestions) [#198]

Honour the per-user auto-completion checkboxes on the rally point and the
marketplace, where the target village is typed by name (dname field):
  - v1: own villages
  - v2: villages in the surroundings of the active village
  - v3: villages of the player's alliance members

A new Database::getAutoCompleteVillages() builds the bounded, de-duplicated
name list from the enabled categories (system accounts excluded). A shared
Templates/villageAutocomplete.tpl renders a native <datalist> consumed by
the dname input via list="dnameSuggest"; nothing is emitted when every box
is unchecked, keeping the previous behaviour. Removes the "not coded yet"
tag from the auto-completion section in the preferences form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ferywir
2026-06-12 08:42:44 +02:00
parent 8d46fc78ae
commit 6d57cd7fe1
5 changed files with 97 additions and 6 deletions
+61
View File
@@ -5377,6 +5377,67 @@ $q = "INSERT INTO ".TB_PREFIX."demolition VALUES (
return $this->getVillage($name, 1, $use_cache)['wref'];
}
/**
* Build the village-name autocomplete suggestions for the rally point and
* the marketplace, honouring the player's auto-completion preferences
* (issue #198):
* v1 -> own villages
* v2 -> villages in the surroundings of the active village
* v3 -> villages of the player's alliance members
* System accounts (Nature, Natars, Multihunter, ...) are excluded.
*
* @param int $uid Player id (own villages / self).
* @param int $alliance Player's alliance id (0 = none).
* @param int $x Active village X coordinate (vicinity center).
* @param int $y Active village Y coordinate (vicinity center).
* @param bool $v1 Include own villages.
* @param bool $v2 Include surrounding villages.
* @param bool $v3 Include alliance villages.
* @param int $radius Vicinity radius in fields (v2).
* @param int $limit Max number of names returned.
* @return string[] Distinct village names.
*/
function getAutoCompleteVillages($uid, $alliance, $x, $y, $v1, $v2, $v3, $radius = 25, $limit = 100) {
$uid = (int) $uid;
$alliance = (int) $alliance;
$x = (int) $x;
$y = (int) $y;
$radius = (int) $radius;
$limit = (int) $limit;
$joins = "JOIN " . TB_PREFIX . "users u ON u.id = v.owner ";
$conds = [];
if ($v1) {
$conds[] = "v.owner = $uid";
}
if ($v3 && $alliance > 0) {
$conds[] = "u.alliance = $alliance";
}
if ($v2) {
$joins .= "JOIN " . TB_PREFIX . "wdata w ON w.id = v.wref ";
$conds[] = "(ABS(w.x - $x) <= $radius AND ABS(w.y - $y) <= $radius)";
}
if (!count($conds)) {
return [];
}
$q = "SELECT DISTINCT v.name FROM " . TB_PREFIX . "vdata v " .
$joins .
"WHERE u.id > 5 AND (" . implode(' OR ', $conds) . ") " .
"ORDER BY v.name LIMIT $limit";
$result = mysqli_query($this->dblink, $q);
$names = [];
if ($result) {
while ($row = mysqli_fetch_assoc($result)) {
$names[] = $row['name'];
}
}
return $names;
}
function getVillageByOwner($uid, $use_cache = true) {
$uid = (int) $uid;
+1 -1
View File
@@ -134,7 +134,7 @@ if (isset($_GET['z'])) {
<table id="target_select" class="res_target" cellpadding="1" cellspacing="1">
<tr><td class="mer"><?php echo MERCHANT;?> <?php echo $merchantAvail;?>/<?php echo $totalMerchants;?></td></tr>
<tr><td class="vil"><span><?php echo VILLAGES;?>:</span> <input class="text" type="text" name="dname" value="" maxlength="30" tabindex="5"></td></tr>
<tr><td class="vil"><span><?php echo VILLAGES;?>:</span> <input class="text" type="text" name="dname" value="" maxlength="30" tabindex="5" list="dnameSuggest" autocomplete="off"><?php include("Templates/villageAutocomplete.tpl"); ?></td></tr>
<tr><td class="or"><?php echo OR_;?></td></tr>
<tr>
<td class="coo">
+1 -4
View File
@@ -350,10 +350,7 @@ if(isset($_POST['lang']))
<thead>
<tr>
<th colspan="2">
Auto completion
<span style="color:#999; font-weight:400; font-size:0.9em; font-style:italic; opacity:0.7;">
<?php echo TZ_NOT_CODED_YET; ?>
</span>
<?php echo AUTO_COMPL; ?>
</th>
</tr>
<tr><td colspan="2"><?php echo TZ_USED_FOR_RALLY_POINT_AND_MARKETPLA; ?></td></tr>
+2 -1
View File
@@ -44,7 +44,8 @@ if (isset($_GET['z'])) {
</td>
<td class="vil">
<span><?php echo TZ_VILLAGE; ?></span>
<input class="text" name="dname" value="<?php echo htmlspecialchars($form->getValue('dname'), ENT_QUOTES); ?>" maxlength="20" type="text">
<input class="text" name="dname" value="<?php echo htmlspecialchars($form->getValue('dname'), ENT_QUOTES); ?>" maxlength="20" type="text" list="dnameSuggest" autocomplete="off">
<?php include("Templates/villageAutocomplete.tpl"); ?>
</td>
</tr>
<tr>
+32
View File
@@ -0,0 +1,32 @@
<?php
/**
* Village-name autocomplete datalist for the rally point and the marketplace.
* Honours the player's auto-completion preferences (issue #198): v1 own
* villages, v2 surrounding villages, v3 alliance villages. Renders a
* <datalist id="dnameSuggest"> consumed by the dname input via
* list="dnameSuggest". Nothing is emitted when every box is unchecked.
*/
global $database, $session, $village;
$acV1 = !empty($session->userinfo['v1']);
$acV2 = !empty($session->userinfo['v2']);
$acV3 = !empty($session->userinfo['v3']);
if ($acV1 || $acV2 || $acV3) {
$acNames = $database->getAutoCompleteVillages(
$session->uid,
$session->alliance,
$village->coor['x'] ?? 0,
$village->coor['y'] ?? 0,
$acV1,
$acV2,
$acV3
);
echo '<datalist id="dnameSuggest">';
foreach ($acNames as $acName) {
echo '<option value="' . htmlspecialchars($acName, ENT_QUOTES) . '">';
}
echo '</datalist>';
}
?>