Alpha V2.5.10

This commit is contained in:
Flatlogic Bot 2026-03-07 18:42:15 +00:00
parent b345880797
commit b14847202a
7 changed files with 225 additions and 76 deletions

212
admin.php
View File

@ -642,9 +642,11 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["action"]) && $_POST["
$faction_id = !empty($_POST["faction_id"]) ? (int)$_POST["faction_id"] : null;
$can_be_destroyed = isset($_POST["can_be_destroyed"]) ? 1 : 0;
$can_be_captured = isset($_POST["can_be_captured"]) ? 1 : 0;
$points_per_hit = (int)$_POST["points_per_hit"];
$bonus_destruction = (int)$_POST["bonus_destruction"];
$bonus_capture = (int)$_POST["bonus_capture"];
// Fallback for legacy fields if not in form
$points_per_hit = isset($_POST["points_per_hit"]) ? (int)$_POST["points_per_hit"] : 0;
$bonus_destruction = isset($_POST["bonus_destruction"]) ? (int)$_POST["bonus_destruction"] : 0;
$bonus_capture = isset($_POST["bonus_capture"]) ? (int)$_POST["bonus_capture"] : 0;
$grid_data = $_POST["grid_data"];
$cost_resource_id = !empty($_POST["cost_resource_id"]) ? (int)$_POST["cost_resource_id"] : null;
@ -683,16 +685,14 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["action"]) && $_POST["
// Handle Rewards
$db->prepare("DELETE FROM unit_rewards WHERE unit_id = ?")->execute([$id]);
if (isset($_POST["reward_action"]) && is_array($_POST["reward_action"])) {
$ins_rw = $db->prepare("INSERT INTO unit_rewards (unit_id, action_type, resource_id, amount) VALUES (?, ?, ?, ?)");
$ins_rw = $db->prepare("INSERT INTO unit_rewards (unit_id, action_type, lootbox_id) VALUES (?, ?, ?)");
foreach ($_POST["reward_action"] as $idx => $action) {
$res_id = (int)$_POST["reward_resource_id"][$idx];
$amount = (int)$_POST["reward_amount"][$idx];
if ($amount > 0) {
$ins_rw->execute([$id, $action, $res_id, $amount]);
$lb_id = (int)$_POST["reward_lootbox_id"][$idx];
if ($lb_id > 0) {
$ins_rw->execute([$id, $action, $lb_id]);
}
}
}
header("Location: admin.php?tab=units&success=1");
exit;
}
@ -757,6 +757,7 @@ if ($tab === 'users') {
} elseif ($tab === 'levels') {
$levels_list = $db->query("SELECT l.*, r.name as resource_name FROM levels l JOIN game_resources r ON l.resource_id = r.id ORDER BY l.required_quantity ASC")->fetchAll();
$resources_list = $db->query("SELECT id, name FROM game_resources ORDER BY name ASC")->fetchAll();
$lootboxes_list = $db->query("SELECT id, name FROM lootboxes ORDER BY name ASC")->fetchAll();
} elseif ($tab === 'resources') {
$resources_list = $db->query("SELECT * FROM game_resources ORDER BY name ASC")->fetchAll();
} elseif ($tab === 'project_logs') {
@ -787,13 +788,14 @@ if ($tab === 'users') {
elseif ($tab === "units") {
$units_list = $db->query("SELECT u.*, f.name as faction_name FROM units u LEFT JOIN factions f ON u.faction_id = f.id ORDER BY u.name ASC")->fetchAll();
foreach ($units_list as &$unit) {
$stmt_rw = $db->prepare("SELECT action_type, resource_id, amount FROM unit_rewards WHERE unit_id = ?");
$stmt_rw = $db->prepare("SELECT action_type, lootbox_id, resource_id, amount FROM unit_rewards WHERE unit_id = ?");
$stmt_rw->execute([$unit["id"]]);
$unit["rewards"] = $stmt_rw->fetchAll();
}
unset($unit);
$factions_list = $db->query("SELECT id, name FROM factions ORDER BY name ASC")->fetchAll();
$resources_list = $db->query("SELECT id, name FROM game_resources ORDER BY name ASC")->fetchAll();
$lootboxes_list = $db->query("SELECT id, name FROM lootboxes ORDER BY name ASC")->fetchAll();
}
?>
<!DOCTYPE html>
@ -919,9 +921,9 @@ elseif ($tab === "units") {
.ms-item { padding: 5px 8px; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 11px; }
.ms-item:hover { background: #2d3545; }
.ms-item input { width: auto !important; }
.unit-grid-container { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.unit-grid { display: grid; grid-template-columns: repeat(6, 64px); grid-template-rows: repeat(6, 64px); gap: 2px; background: #2d3545; padding: 2px; width: fit-content; }
.unit-cell { width: 64px; height: 64px; background: #0f172a; cursor: pointer; border: 1px solid #334155; }
.unit-grid-container { display: flex; flex-direction: column; gap: 10px; margin: 20px 0; padding: 15px; background: #1a202c; border-radius: 8px; border: 1px solid #2d3545; width: fit-content; }
.unit-grid { display: grid; grid-template-columns: repeat(6, 40px); grid-template-rows: repeat(6, 40px); gap: 2px; background: #2d3545; padding: 2px; width: fit-content; }
.unit-cell { width: 40px; height: 40px; background: #0f172a; cursor: pointer; border: 1px solid #334155; }
.unit-cell.active { background: #88c0d0; }
.unit-cell:hover { border-color: #88c0d0; }
</style>
@ -1049,17 +1051,17 @@ elseif ($tab === "units") {
</div>
</div>
<div style="display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-end;">
<div style="display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-end; padding-top: 15px; border-top: 1px solid #1a202c;">
<div class="form-group" style="flex: 0 0 150px;">
<label>Destructible</label>
<label style="color: #bf616a;"> Destructible</label>
<div style="padding: 10px 0;">
<label class="switch">
<input type="checkbox" name="can_be_destroyed" id="unit_can_be_destroyed" value="1" checked onchange="toggleUnitField('destroyed')">
<input type="checkbox" name="can_be_destroyed" id="unit_can_be_destroyed" value="1" checked onclick="toggleUnitField('destroyed')">
<span class="slider round"></span>
</label>
</div>
</div>
<div class="form-group" style="flex: 2; min-width: 250px;" id="group_destruction_res">
<div class="form-group" style="flex: 2; min-width: 250px;" id="group_destroyed_res">
<label>Ressource attribuée (Destruction)</label>
<select name="destruction_resource_id" id="unit_destruction_resource_id">
<option value="">-- Sélectionner une ressource --</option>
@ -1068,23 +1070,23 @@ elseif ($tab === "units") {
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="flex: 1; min-width: 200px;" id="group_destruction_amt">
<div class="form-group" style="flex: 1; min-width: 200px;" id="group_destroyed_amt">
<label>Quantité (Destruction)</label>
<input type="number" name="destruction_amount" id="unit_destruction_amount" value="0" min="0">
</div>
</div>
<div style="display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-end;">
<div style="display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-end; padding-top: 15px; border-top: 1px solid #1a202c;">
<div class="form-group" style="flex: 0 0 150px;">
<label>Capturable</label>
<label style="color: #a3be8c;"> Capturable</label>
<div style="padding: 10px 0;">
<label class="switch">
<input type="checkbox" name="can_be_captured" id="unit_can_be_captured" value="1" onchange="toggleUnitField('captured')">
<input type="checkbox" name="can_be_captured" id="unit_can_be_captured" value="1" onclick="toggleUnitField('captured')">
<span class="slider round"></span>
</label>
</div>
</div>
<div class="form-group" style="flex: 2; min-width: 250px; display: none;" id="group_capture_res">
<div class="form-group" style="flex: 2; min-width: 250px; display: none;" id="group_captured_res">
<label>Ressource attribuée (Capture)</label>
<select name="capture_resource_id" id="unit_capture_resource_id">
<option value="">-- Sélectionner une ressource --</option>
@ -1093,7 +1095,7 @@ elseif ($tab === "units") {
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="flex: 1; min-width: 200px; display: none;" id="group_capture_amt">
<div class="form-group" style="flex: 1; min-width: 200px; display: none;" id="group_captured_amt">
<label>Quantité (Capture)</label>
<input type="number" name="capture_amount" id="unit_capture_amount" value="0" min="0">
</div>
@ -1109,18 +1111,22 @@ elseif ($tab === "units") {
</div>
<div class="form-group">
<label>Récompenses additionnelles (Action / Ressource / Quantité)</label>
<label>Récompenses additionnelles (Action / Lootbox)</label>
<div id="unitRewardsContainer">
<div class="sub-form-header">
<div style="flex: 1;">Action</div>
<div style="flex: 1;">Ressource</div>
<div style="flex: 1;">Quantité</div>
<div style="flex: 1;">Lootbox</div>
<div class="btn-del-row-placeholder"></div>
</div>
</div>
<button type="button" class="btn" style="background: #4c566a; color: #fff; margin-top: 5px;" onclick="addUnitRewardRow()">+ AJOUTER UNE RÉCOMPENSE</button>
</div>
<div style="margin-top: 20px; padding: 10px; background: rgba(0,0,0,0.3); border: 1px solid #334155; display: none;" id="unitDebugBox">
<label style="color: #ebcb8b;">[DEBUG] RAW UNIT DATA:</label>
<pre id="unitDebugData" style="font-size: 10px; color: #a3be8c; white-space: pre-wrap;"></pre>
</div>
<div style="margin-top: 20px;">
<button type="submit" class="btn btn-add" id="unit_submit_btn">ENREGISTRER L'UNITÉ</button>
<button type="button" class="btn" style="background: #4c566a; color: #fff;" onclick="resetUnitForm()">ANNULER</button>
@ -1190,11 +1196,11 @@ elseif ($tab === "units") {
<?php foreach ($unit['rewards'] as $rw): ?>
<?php
$rName = '?';
foreach($resources_list as $res) { if($res['id'] == $rw['resource_id']) { $rName = $res['name']; break; } }
$lbName = '?';
foreach($lootboxes_list as $lb) { if($lb['id'] == $rw['lootbox_id']) { $lbName = $lb['name']; break; } }
?>
<span style="color: #8c92a3;"><?php echo $rw['action_type'] === 'destroy' ? 'Destr' : 'Capt'; ?>:</span>
+<?php echo $rw['amount']; ?> <?php echo htmlspecialchars($rName); ?><br>
Lootbox: <?php echo htmlspecialchars($lbName); ?><br>
<?php endforeach; ?>
</td>
<td>
@ -1942,7 +1948,7 @@ elseif ($tab === "units") {
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px; padding-top: 15px; margin-top: 15px; border-top: 1px solid #334155;">
<div style="display: align-items: center; gap: 10px; padding-top: 15px; margin-top: 15px; border-top: 1px solid #334155;">
<input type="checkbox" name="is_empty_case" id="rule_empty" style="width: auto;">
<label for="rule_empty" style="margin-bottom: 0; color: #ebcb8b;">Cas "CASE VIDE" (Aucune faction nulle part)</label>
</div>
@ -2338,40 +2344,80 @@ elseif ($tab === "units") {
function checkUnitFormValidity(){const n=document.getElementById('unit_name')?.value,s=document.getElementById('unit_slug')?.value,g=document.getElementById('unit_grid_data')?.value,b=document.getElementById('unit_submit_btn');if(b){const v=n&&s&&g&&g!=='[]'&&g!=='';b.disabled=!v;b.style.opacity=v?'1':'0.5';}}
function toggleUnitField(type) {
const isChecked = document.getElementById('unit_can_be_' + type).checked;
const resGroup = document.getElementById('group_' + type + '_res");
const amtGroup = document.getElementById('group_' + type + '_amt");
if (resGroup && amtGroup) {
resGroup.style.display = isChecked ? 'block' : 'none';
amtGroup.style.display = isChecked ? 'block' : 'none';
console.log('[DEBUG] toggleUnitField called for type:', type);
try {
const cb = document.getElementById('unit_can_be_' + type);
if (!cb) {
console.error('[DEBUG] Checkbox NOT found for type:', type);
return;
}
const isChecked = cb.checked;
console.log('[DEBUG] isChecked:', isChecked);
const resGrp = document.getElementById('group_' + type + '_res');
const amtGrp = document.getElementById('group_' + type + '_amt');
if (resGrp) {
resGrp.style.display = isChecked ? 'block' : 'none';
}
if (amtGrp) {
amtGrp.style.display = isChecked ? 'block' : 'none';
}
} catch (err) {
console.error('[DEBUG] Error in toggleUnitField:', err);
}
}
function editUnit(data) {
console.log('[DEBUG] editUnit triggered with:', data);
resetUnitForm();
document.getElementById('unit_id').value = data.id;
document.getElementById('unit_name').value = data.name;
document.getElementById('unit_slug').value = data.slug;
document.getElementById('unit_faction_id').value = data.faction_id;
document.getElementById('unit_cost_resource_id').value = data.cost_resource_id || "";
document.getElementById('unit_cost_amount').value = data.cost_amount || 1;
document.getElementById('unit_can_be_destroyed').checked = data.can_be_destroyed == 1;
document.getElementById('unit_can_be_captured').checked = data.can_be_captured == 1;
document.getElementById('unit_destruction_resource_id').value = data.destruction_resource_id || "";
document.getElementById('unit_destruction_amount').value = data.destruction_amount || 0;
document.getElementById('unit_capture_resource_id').value = data.capture_resource_id || "";
document.getElementById('unit_capture_amount').value = data.capture_amount || 0;
document.getElementById('unit_grid_data').value = data.grid_data;
setUnitGridFromData(data.grid_data);
toggleUnitField('destroyed');
toggleUnitField('captured');
if (data.rewards) {
data.rewards.forEach(r => {
addUnitRewardRow(r.action_type, r.resource_id, r.amount);
});
try {
document.getElementById('unit_id').value = data.id;
document.getElementById('unit_name').value = data.name;
document.getElementById('unit_slug').value = data.slug;
document.getElementById('unit_faction_id').value = data.faction_id || "";
document.getElementById('unit_cost_resource_id').value = data.cost_resource_id || "";
document.getElementById('unit_cost_amount').value = data.cost_amount || 1;
// Set Checkboxes
const cbDestroyed = document.getElementById('unit_can_be_destroyed');
const cbCaptured = document.getElementById('unit_can_be_captured');
if (cbDestroyed) cbDestroyed.checked = (parseInt(data.can_be_destroyed) === 1);
if (cbCaptured) cbCaptured.checked = (parseInt(data.can_be_captured) === 1);
// Set Resources & Amounts
document.getElementById('unit_destruction_resource_id').value = data.destruction_resource_id || "";
document.getElementById('unit_destruction_amount').value = data.destruction_amount || 0;
document.getElementById('unit_capture_resource_id').value = data.capture_resource_id || "";
document.getElementById('unit_capture_amount').value = data.capture_amount || 0;
// Set Grid
document.getElementById('unit_grid_data').value = data.grid_data || "[]";
setUnitGridFromData(data.grid_data || "[]");
// Show/Hide Groups
toggleUnitField('destroyed');
toggleUnitField('captured');
// Handle Rewards
if (data.rewards && Array.isArray(data.rewards)) {
data.rewards.forEach(r => {
addUnitRewardRow(r.action_type, r.lootbox_id);
});
}
// Show Debug info
const debugBox = document.getElementById('unitDebugBox');
const debugData = document.getElementById('unitDebugData');
if (debugBox && debugData) {
debugBox.style.display = 'block';
debugData.innerText = JSON.stringify(data, null, 2);
}
} catch (err) {
console.error('[DEBUG] Error in editUnit:', err);
}
if(typeof checkUnitFormValidity==='function')checkUnitFormValidity();
window.scrollTo(0,0);
}
@ -2379,14 +2425,24 @@ elseif ($tab === "units") {
function resetUnitForm() {
document.getElementById('unitForm').reset();
document.getElementById('unit_id').value = 0;
document.getElementById('unit_grid_data').value = "";
document.getElementById('unit_grid_data').value = "[]";
const debugBox = document.getElementById('unitDebugBox');
if (debugBox) debugBox.style.display = 'none';
// Defaults
const cbDestroyed = document.getElementById('unit_can_be_destroyed');
const cbCaptured = document.getElementById('unit_can_be_captured');
if (cbDestroyed) cbDestroyed.checked = true;
if (cbCaptured) cbCaptured.checked = false;
toggleUnitField('destroyed');
toggleUnitField('captured');
document.getElementById('unitRewardsContainer').innerHTML = `
<div class="sub-form-header">
<div style="flex: 1;">Action</div>
<div style="flex: 1;">Ressource</div>
<div style="flex: 1;">Quantité</div>
<div style="flex: 1;">Lootbox</div>
<div class="btn-del-row-placeholder"></div>
</div>`;
const cells = document.querySelectorAll('.unit-cell');
@ -2394,34 +2450,33 @@ elseif ($tab === "units") {
if(typeof checkUnitFormValidity==='function')checkUnitFormValidity();
}
function addUnitRewardRow(action = 'destroy', resId = '', amount = 0) {
const container = document.getElementById('unitRewardsContainer');
const row = document.createElement('div');
row.className = 'sub-form-row';
function addUnitRewardRow(action = "destroy", lootboxId = "") {
const container = document.getElementById("unitRewardsContainer");
const row = document.createElement("div");
row.className = "sub-form-row";
let resOptions = '';
<?php foreach ($resources_list as $res): ?>
resOptions += '<option value="<?php echo $res['id']; ?>" ' + (resId == <?php echo $res['id']; ?> ? 'selected' : '') + '><?php echo addslashes($res['name']); ?></option>';
let lbOptions = "";
<?php foreach ($lootboxes_list as $lb): ?>
lbOptions += `<option value="<?php echo $lb["id"]; ?>" ${lootboxId == <?php echo $lb["id"]; ?> ? "selected" : "" }><?php echo addslashes($lb["name"]); ?></option>`;
<?php endforeach; ?>
row.innerHTML = `
<select name="reward_action[]" style="flex: 1;">
<option value="destroy" ${action === 'destroy' ? 'selected' : ''}>Destruction</option>
<option value="capture" ${action === 'capture' ? 'selected' : ''}>Capture</option>
<option value="destroy" ${action === "destroy" ? "selected" : ""}>Destruction</option>
<option value="capture" ${action === "capture" ? "selected" : ""}>Capture</option>
</select>
<select name="reward_resource_id[]" style="flex: 1;">
<option value="">-- Ressource --</option>
${resOptions}
<select name="reward_lootbox_id[]" style="flex: 1;">
<option value="">-- Lootbox --</option>
${lbOptions}
</select>
<input type="number" name="reward_amount[]" value="${amount}" style="flex: 1;" placeholder="Qté">
<div class="btn-del-row" onclick="this.parentElement.remove()">×</div>
`;
container.appendChild(row);
}
function createUnitGrid() {
const grid = document.getElementById('unitGrid');
if (!grid) return;
grid.innerHTML = '';
for (let i = 0; i < 36; i++) {
const cell = document.createElement('div');
cell.className = 'unit-cell';
@ -2447,12 +2502,15 @@ elseif ($tab === "units") {
const cells = document.querySelectorAll('.unit-cell');
cells.forEach(c => c.classList.remove('active'));
try {
if (!json || json === "") json = "[]";
const activeIndices = JSON.parse(json);
activeIndices.forEach(idx => {
const cell = document.querySelector(`.unit-cell[data-index="${idx}"]`);
if (cell) cell.classList.add('active');
});
} catch(e) {}
} catch(e) {
console.error('[DEBUG] Error parsing grid data:', e);
}
}
function editObject(data) {
@ -2809,8 +2867,10 @@ elseif ($tab === "units") {
updateRankFields();
if (document.getElementById('tab') === 'units' || <?php echo $tab === 'units' ? 'true' : 'false'; ?>) {
createUnitGrid();
toggleUnitField('destroyed');
toggleUnitField('captured');
}
});
</script>
</body>
</html>
</html>

24
admin_php_toggle_fix.txt Normal file
View File

@ -0,0 +1,24 @@
function toggleUnitField(type) {
console.log('[DEBUG] toggleUnitField called for type:', type);
try {
const cb = document.getElementById('unit_can_be_' + type);
if (!cb) {
console.error('[DEBUG] Checkbox NOT found for type:', type);
return;
}
const isChecked = cb.checked;
console.log('[DEBUG] isChecked:', isChecked);
const resGrp = document.getElementById('group_' + type + '_res');
const amtGrp = document.getElementById('group_' + type + '_amt');
if (resGrp) {
resGrp.style.display = isChecked ? 'block' : 'none';
}
if (amtGrp) {
amtGrp.style.display = isChecked ? 'block' : 'none';
}
} catch (err) {
console.error('[DEBUG] Error in toggleUnitField:', err);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

View File

@ -0,0 +1,16 @@
<?php
require_once __DIR__ . '/config.php';
$db = db();
try {
// Add lootbox_id column and make resource_id and amount nullable
$db->exec("ALTER TABLE unit_rewards ADD COLUMN lootbox_id INT NULL AFTER action_type");
$db->exec("ALTER TABLE unit_rewards MODIFY COLUMN resource_id INT NULL");
$db->exec("ALTER TABLE unit_rewards MODIFY COLUMN amount INT NULL");
$db->exec("ALTER TABLE unit_rewards ADD CONSTRAINT fk_unit_rewards_lootbox FOREIGN KEY (lootbox_id) REFERENCES lootboxes(id) ON DELETE SET NULL");
echo "Migration completed: unit_rewards table updated.\n";
} catch (PDOException $e) {
echo "Migration info/error: " . $e->getMessage() . "\n";
}

49
patch_js_fix.php Normal file
View File

@ -0,0 +1,49 @@
<?php
$file = 'admin.php';
$content = file_get_contents($file);
// Fix JS syntax error in toggleUnitField
$old_js = " function toggleUnitField(type) {
const isChecked = document.getElementById('unit_can_be_' + type).checked;
const resGroup = document.getElementById('group_' + type + '_res\');
const amtGroup = document.getElementById('group_' + type + '_amt\');
if (resGroup && amtGroup) {
resGroup.style.display = isChecked ? 'block' : 'none';
amtGroup.style.display = isChecked ? 'block' : 'none';
}
}";
$new_js = " function toggleUnitField(type) {
const isChecked = document.getElementById('unit_can_be_' + type).checked;
const resGroup = document.getElementById('group_' + type + '_res');
const amtGroup = document.getElementById('group_' + type + '_amt');
if (resGroup && amtGroup) {
resGroup.style.display = isChecked ? 'flex' : 'none';
amtGroup.style.display = isChecked ? 'flex' : 'none';
}
}";
// I'll also change 'block' to 'flex' because they are inside a flex container and might need to behave like one.
// Actually, they are form-groups, so block is usually fine, but 'flex' might be better for the alignment if I use display: flex on the container.
// Wait, the container has display: flex; align-items: flex-end;
// If the children are display: none/block, it should be fine.
// Let's re-examine the Capturable row HTML.
// <div class=\"form-group\" style=\"flex: 2; min-width: 250px; display: none;\" id=\"group_capture_res\">
// If I change it to 'block', it might not respect the flex properties of the parent as intended.
// Actually, it should.
// BUT, I'll also fix the 'Destructible' and 'Capturable' layout.
// Maybe using a table or a more robust grid for these rows would be better.
// The user said "tu as de nouveau cassé la grille".
// Maybe the unit-grid (6x6) is not showing up because of the JS error!
// If there's a JS error in toggleUnitField, and it's called during page load or initialization, it might stop subsequent scripts from running.
$content = str_replace($old_js, $new_js, $content);
file_put_contents($file, $content);
echo "Fixed JS syntax error in toggleUnitField.";
?>