new market
This commit is contained in:
@@ -786,6 +786,80 @@
|
|||||||
await sleep(500);
|
await sleep(500);
|
||||||
return { ok: true, msg: `Market offer posted: ${offer} ${offer_type} => ${demand} ${demand_type}` };
|
return { ok: true, msg: `Market offer posted: ${offer} ${offer_type} => ${demand} ${demand_type}` };
|
||||||
}
|
}
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Execute: Scan Market (On-Demand)
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
async function executeScanMarket(cmd) {
|
||||||
|
const { town_id } = cmd;
|
||||||
|
|
||||||
|
const reactionMs = randInt(1500, 3000);
|
||||||
|
log(`Waiting ${reactionMs}ms before scanning market...`);
|
||||||
|
await sleep(reactionMs);
|
||||||
|
|
||||||
|
if (paused) return { ok: false, msg: 'Aborted due to pause/captcha' };
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', {
|
||||||
|
model_url: 'BuildingMarket',
|
||||||
|
action_name: 'getData',
|
||||||
|
arguments: {
|
||||||
|
limit: 20,
|
||||||
|
offset: 0,
|
||||||
|
demand_type: 'all_but_gold',
|
||||||
|
offer_type: 'all_but_gold',
|
||||||
|
max_ratio: 3,
|
||||||
|
max_delivery_time: 172800,
|
||||||
|
visibility: 2,
|
||||||
|
order_by: 'ratio',
|
||||||
|
order_direction: 'desc'
|
||||||
|
},
|
||||||
|
town_id: town_id,
|
||||||
|
nl_init: true
|
||||||
|
}, false, {
|
||||||
|
success: async function(resp) {
|
||||||
|
try {
|
||||||
|
// Send the data back to our backend
|
||||||
|
await fetch(`${BASE_URL}/api/market_data?player_id=${uw.Game.player_id}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(resp)
|
||||||
|
});
|
||||||
|
resolve({ ok: true, msg: 'Market scanned and data uploaded' });
|
||||||
|
} catch (e) {
|
||||||
|
resolve({ ok: false, msg: 'Failed to upload market data: ' + e });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
resolve({ ok: false, msg: 'Failed to fetch market data from game' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Execute: Accept Market Offer
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
async function executeAcceptMarketOffer(cmd) {
|
||||||
|
const { town_id, payload } = cmd;
|
||||||
|
const { offer_id, amount } = payload;
|
||||||
|
|
||||||
|
const reactionMs = randInt(800, 2000);
|
||||||
|
log(`Waiting ${reactionMs}ms before accepting offer...`);
|
||||||
|
await sleep(reactionMs);
|
||||||
|
|
||||||
|
if (paused) return { ok: false, msg: 'Aborted due to pause/captcha' };
|
||||||
|
|
||||||
|
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', {
|
||||||
|
model_url: 'BuildingMarket',
|
||||||
|
action_name: 'acceptOffer',
|
||||||
|
arguments: { offer_id, amount },
|
||||||
|
town_id: town_id,
|
||||||
|
nl_init: true
|
||||||
|
});
|
||||||
|
|
||||||
|
await sleep(500);
|
||||||
|
return { ok: true, msg: `Accepted market offer ${offer_id} for amount ${amount}` };
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Execute: Research (Academy)
|
// Execute: Research (Academy)
|
||||||
@@ -836,6 +910,8 @@
|
|||||||
// Build queue, Recruit queue and Market queue are independent
|
// Build queue, Recruit queue and Market queue are independent
|
||||||
const buildCmd = cmdData.build;
|
const buildCmd = cmdData.build;
|
||||||
const recruitCmd = cmdData.recruit;
|
const recruitCmd = cmdData.recruit;
|
||||||
|
const scanMarketCmd = cmdData.scan_market;
|
||||||
|
const acceptMarketCmd = cmdData.accept_market_offer;
|
||||||
const marketCmd = cmdData.market;
|
const marketCmd = cmdData.market;
|
||||||
const researchCmd = cmdData.research;
|
const researchCmd = cmdData.research;
|
||||||
const farmCmd = cmdData.farm;
|
const farmCmd = cmdData.farm;
|
||||||
@@ -851,17 +927,24 @@
|
|||||||
const execute = async (cmd) => {
|
const execute = async (cmd) => {
|
||||||
if (!cmd) return;
|
if (!cmd) return;
|
||||||
log(`Executing command #${cmd.id} — type:${cmd.type} town:${cmd.town_id}`);
|
log(`Executing command #${cmd.id} — type:${cmd.type} town:${cmd.town_id}`);
|
||||||
|
if (paused) {
|
||||||
|
log(`[Paused] Ignoring command #${cmd.id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
if (cmd.type === 'build') result = await executeBuild(cmd);
|
if (cmd.type === 'build') result = await executeBuild(cmd);
|
||||||
else if (cmd.type === 'recruit') result = await executeRecruit(cmd);
|
else if (cmd.type === 'recruit') result = await executeRecruit(cmd);
|
||||||
else if (cmd.type === 'market_offer') result = await executeMarketOffer(cmd);
|
else if (cmd.type === 'market_offer') result = await executeMarketOffer(cmd);
|
||||||
|
else if (cmd.type === 'scan_market') result = await executeScanMarket(cmd);
|
||||||
|
else if (cmd.type === 'accept_market_offer') result = await executeAcceptMarketOffer(cmd);
|
||||||
else if (cmd.type === 'research') result = await executeResearch(cmd);
|
else if (cmd.type === 'research') result = await executeResearch(cmd);
|
||||||
else if (cmd.type === 'farm_loot') result = await executeFarmLoot(cmd);
|
else if (cmd.type === 'farm_loot') result = await executeFarmLoot(cmd);
|
||||||
else if (cmd.type === 'farm_upgrade') result = await executeFarmUpgrade(cmd);
|
else if (cmd.type === 'farm_upgrade') result = await executeFarmUpgrade(cmd);
|
||||||
else result = { ok: false, msg: `Unknown type: ${cmd.type}` };
|
else result = { ok: false, msg: `Unknown type: ${cmd.type}` };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
result = { ok: false, msg: `Exception: ${e.message}` };
|
result = { ok: false, msg: `Exception: ${e}` };
|
||||||
}
|
}
|
||||||
const finalStatus = result.requeue ? 'pending' : (result.ok ? 'done' : 'failed');
|
const finalStatus = result.requeue ? 'pending' : (result.ok ? 'done' : 'failed');
|
||||||
log(`Command #${cmd.id}: ${finalStatus === 'done' ? '✅' : finalStatus === 'pending' ? '⏳' : '❌'} ${result.msg}`);
|
log(`Command #${cmd.id}: ${finalStatus === 'done' ? '✅' : finalStatus === 'pending' ? '⏳' : '❌'} ${result.msg}`);
|
||||||
@@ -872,6 +955,8 @@
|
|||||||
await execute(buildCmd);
|
await execute(buildCmd);
|
||||||
await execute(recruitCmd);
|
await execute(recruitCmd);
|
||||||
await execute(marketCmd);
|
await execute(marketCmd);
|
||||||
|
await execute(scanMarketCmd);
|
||||||
|
await execute(acceptMarketCmd);
|
||||||
await execute(researchCmd);
|
await execute(researchCmd);
|
||||||
await execute(farmCmd);
|
await execute(farmCmd);
|
||||||
await execute(farmUpgradeCmd);
|
await execute(farmUpgradeCmd);
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ def get_pending_command():
|
|||||||
farm_cmd = _fetch_pending_of_type(c, 'farm_loot', player_id)
|
farm_cmd = _fetch_pending_of_type(c, 'farm_loot', player_id)
|
||||||
farm_upgrade_cmd = _fetch_pending_of_type(c, 'farm_upgrade', player_id)
|
farm_upgrade_cmd = _fetch_pending_of_type(c, 'farm_upgrade', player_id)
|
||||||
research_cmd = _fetch_pending_of_type(c, 'research', player_id)
|
research_cmd = _fetch_pending_of_type(c, 'research', player_id)
|
||||||
|
scan_market_cmd = _fetch_pending_of_type(c, 'scan_market', player_id)
|
||||||
|
accept_market_offer_cmd = _fetch_pending_of_type(c, 'accept_market_offer', player_id)
|
||||||
sync_req = _check_and_reset_sync(c, player_id)
|
sync_req = _check_and_reset_sync(c, player_id)
|
||||||
|
|
||||||
# Also return current farm settings so TM knows loot_option
|
# Also return current farm settings so TM knows loot_option
|
||||||
@@ -119,6 +121,8 @@ def get_pending_command():
|
|||||||
'build': build_cmd,
|
'build': build_cmd,
|
||||||
'recruit': recruit_cmd,
|
'recruit': recruit_cmd,
|
||||||
'market': market_cmd,
|
'market': market_cmd,
|
||||||
|
'scan_market': scan_market_cmd,
|
||||||
|
'accept_market_offer': accept_market_offer_cmd,
|
||||||
'research': research_cmd,
|
'research': research_cmd,
|
||||||
'farm': farm_cmd,
|
'farm': farm_cmd,
|
||||||
'farm_upgrade': farm_upgrade_cmd,
|
'farm_upgrade': farm_upgrade_cmd,
|
||||||
@@ -202,3 +206,28 @@ def captcha_alert():
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'ok': True})
|
return jsonify({'ok': True})
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# POST /api/market_data
|
||||||
|
# Tampermonkey uploads the market scan data.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@api.route('/api/market_data', methods=['POST'])
|
||||||
|
def upload_market_data():
|
||||||
|
player_id = request.args.get('player_id')
|
||||||
|
if not player_id:
|
||||||
|
return jsonify({'error': 'no player_id provided'}), 400
|
||||||
|
|
||||||
|
data = request.get_json(silent=True) or {}
|
||||||
|
kv_key = f'market_data_{player_id}'
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
conn.execute('''
|
||||||
|
INSERT INTO kv_store (key, value, updated_at)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON CONFLICT(key) DO UPDATE SET
|
||||||
|
value = excluded.value,
|
||||||
|
updated_at = excluded.updated_at
|
||||||
|
''', (kv_key, json.dumps(data), datetime.utcnow().isoformat()))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'ok': True})
|
||||||
|
|||||||
@@ -129,6 +129,24 @@ def get_farm_data():
|
|||||||
return jsonify(farms_summary)
|
return jsonify(farms_summary)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# GET /dashboard/market-data
|
||||||
|
# Returns the latest market scan data for a player.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@dashboard.route('/dashboard/market-data', methods=['GET'])
|
||||||
|
def get_market_data():
|
||||||
|
player_id = request.args.get('player_id')
|
||||||
|
conn = get_db()
|
||||||
|
row = conn.execute(
|
||||||
|
"SELECT value, updated_at FROM kv_store WHERE key = ?", (f'market_data_{player_id}', )
|
||||||
|
).fetchone()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if row:
|
||||||
|
return jsonify({'data': json.loads(row['value']), 'updated_at': row['updated_at']})
|
||||||
|
return jsonify({'data': None, 'updated_at': None})
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# GET /dashboard/towns
|
# GET /dashboard/towns
|
||||||
# Returns all known towns with their latest state snapshot.
|
# Returns all known towns with their latest state snapshot.
|
||||||
@@ -266,8 +284,8 @@ def create_command():
|
|||||||
return jsonify({'error': f'missing field: {field}'}), 400
|
return jsonify({'error': f'missing field: {field}'}), 400
|
||||||
|
|
||||||
cmd_type = data['type']
|
cmd_type = data['type']
|
||||||
if cmd_type not in ('build', 'recruit', 'market_offer', 'farm_loot', 'farm_upgrade', 'research'):
|
if cmd_type not in ('build', 'recruit', 'market_offer', 'farm_loot', 'farm_upgrade', 'research', 'scan_market', 'accept_market_offer'):
|
||||||
return jsonify({'error': 'type must be build, recruit, market_offer, farm_loot, farm_upgrade, or research'}), 400
|
return jsonify({'error': 'type must be build, recruit, market_offer, farm_loot, farm_upgrade, research, scan_market, or accept_market_offer'}), 400
|
||||||
|
|
||||||
# Reject if the Tampermonkey client is offline (no state push in last 150 s)
|
# Reject if the Tampermonkey client is offline (no state push in last 150 s)
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
|
|||||||
133
static/js/api.js
133
static/js/api.js
@@ -247,9 +247,6 @@ window.requestLiveSync = async function() {
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
btn.textContent = '✅ Requested!';
|
btn.textContent = '✅ Requested!';
|
||||||
setTimeout(() => {
|
|
||||||
btn.textContent = originalText;
|
|
||||||
btn.disabled = false;
|
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -260,3 +257,133 @@ window.requestLiveSync = async function() {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.scanMarket = async function() {
|
||||||
|
if (!window.clientOnline) return alert('Το script είναι offline.');
|
||||||
|
const town = window.getSelectedTown();
|
||||||
|
if (!town) return alert('Select a town first.');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/dashboard/commands', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
town_id: town.town_id,
|
||||||
|
town_name: town.town_name,
|
||||||
|
type: 'scan_market',
|
||||||
|
payload: {},
|
||||||
|
player_id: window.PLAYER_ID
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.ok) {
|
||||||
|
document.getElementById('market-offers-content').innerHTML = '<span style="color:#c8a44a;">⏳ Αναζήτηση σε εξέλιξη... Περιμένετε!</span>';
|
||||||
|
window.fetchLog();
|
||||||
|
|
||||||
|
// Auto fetch market data every 2 seconds for the next 15 seconds
|
||||||
|
let attempts = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
window.fetchMarketData();
|
||||||
|
attempts++;
|
||||||
|
if (attempts > 7) clearInterval(interval);
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + JSON.stringify(data));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('Failed to send scan_market command: ' + e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.acceptMarketOffer = async function(offer_id, amount) {
|
||||||
|
if (!window.clientOnline) return alert('Το script είναι offline.');
|
||||||
|
const town = window.getSelectedTown();
|
||||||
|
if (!town) return alert('Select a town first.');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/dashboard/commands', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
town_id: town.town_id,
|
||||||
|
town_name: town.town_name,
|
||||||
|
type: 'accept_market_offer',
|
||||||
|
payload: { offer_id: parseInt(offer_id), amount: parseInt(amount) },
|
||||||
|
player_id: window.PLAYER_ID
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.ok) {
|
||||||
|
window.fetchLog();
|
||||||
|
alert(`Εστάλη εντολή αποδοχής για την προσφορά #${offer_id}!`);
|
||||||
|
// Update local UI immediately so user doesn't double click
|
||||||
|
document.getElementById(`offer-btn-${offer_id}`).disabled = true;
|
||||||
|
document.getElementById(`offer-btn-${offer_id}`).innerText = '⏳';
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + JSON.stringify(data));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('Failed to send accept_market_offer command: ' + e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.lastMarketUpdate = null;
|
||||||
|
window.fetchMarketData = async function() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/dashboard/market-data?player_id=' + window.PLAYER_ID);
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data || !data.data || !data.data.offers) return;
|
||||||
|
|
||||||
|
// Only re-render if the timestamp has changed
|
||||||
|
if (window.lastMarketUpdate === data.updated_at) return;
|
||||||
|
window.lastMarketUpdate = data.updated_at;
|
||||||
|
|
||||||
|
const offers = data.data.offers;
|
||||||
|
if (offers.length === 0) {
|
||||||
|
document.getElementById('market-offers-content').innerHTML = '<span style="color:#aaa;">Καμία διαθέσιμη προσφορά.</span>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = `<table style="width:100%; border-collapse:collapse; font-size:0.75rem;">
|
||||||
|
<tr style="border-bottom:1px solid #2a4a6a; color:#c8a44a;">
|
||||||
|
<th style="padding:4px; text-align:left;">Παίκτης</th>
|
||||||
|
<th style="padding:4px; text-align:right;">Προσφέρει</th>
|
||||||
|
<th style="padding:4px; text-align:right;">Ζητάει</th>
|
||||||
|
<th style="padding:4px; text-align:center;">Λόγος</th>
|
||||||
|
<th style="padding:4px; text-align:center;">Διάρκεια</th>
|
||||||
|
<th style="padding:4px;"></th>
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
for (const off of offers) {
|
||||||
|
const ratio = (off.demand / off.offer).toFixed(2);
|
||||||
|
// Map resources to emoji
|
||||||
|
const offerEmoji = off.offer_type === 'wood' ? '🪵' : off.offer_type === 'stone' ? '🪨' : '🪙';
|
||||||
|
const demandEmoji = off.demand_type === 'wood' ? '🪵' : off.demand_type === 'stone' ? '🪨' : '🪙';
|
||||||
|
|
||||||
|
let seconds = off.delivery_time;
|
||||||
|
let h = Math.floor(seconds / 3600);
|
||||||
|
let m = Math.floor((seconds % 3600) / 60);
|
||||||
|
let timeStr = `${h}h ${m}m`;
|
||||||
|
|
||||||
|
html += `<tr style="border-bottom:1px solid #1a3a5a;">
|
||||||
|
<td style="padding:6px 4px; color:#ddd;">${off.player_name || 'NPC'}</td>
|
||||||
|
<td style="padding:6px 4px; text-align:right; color:#88aa55;">${off.offer} ${offerEmoji}</td>
|
||||||
|
<td style="padding:6px 4px; text-align:right; color:#ee4444;">${off.demand} ${demandEmoji}</td>
|
||||||
|
<td style="padding:6px 4px; text-align:center; color:#aaa;">1 : ${ratio}</td>
|
||||||
|
<td style="padding:6px 4px; text-align:center; color:#aaa;">${timeStr}</td>
|
||||||
|
<td style="padding:6px 4px; text-align:right;">
|
||||||
|
<button id="offer-btn-${off.id}" class="btn btn-gold btn-sm" onclick="window.acceptMarketOffer(${off.id}, ${off.offer})">✓</button>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
}
|
||||||
|
html += '</table>';
|
||||||
|
|
||||||
|
// Add timestamp note
|
||||||
|
let d = new Date(data.updated_at + "Z"); // SQLite UTC
|
||||||
|
html += `<div style="text-align:right; font-size:0.65rem; color:#666; margin-top:5px; padding-right:5px;">Τελευταία ανανέωση: ${d.toLocaleTimeString()}</div>`;
|
||||||
|
|
||||||
|
document.getElementById('market-offers-content').innerHTML = html;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to fetch market data:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -169,6 +169,8 @@ window.renderTownDetails = function() {
|
|||||||
document.getElementById('td-market').innerHTML = mCap > 0
|
document.getElementById('td-market').innerHTML = mCap > 0
|
||||||
? `📦 Εμπορική Χωρητικότητα: <strong>${window.fmt(mCap)}</strong>`
|
? `📦 Εμπορική Χωρητικότητα: <strong>${window.fmt(mCap)}</strong>`
|
||||||
: '';
|
: '';
|
||||||
|
const mCapLabel = document.getElementById('market-capacity-label');
|
||||||
|
if (mCapLabel) mCapLabel.textContent = `Χωρητικότητα: ${window.fmt(mCap)}`;
|
||||||
|
|
||||||
const godName = t.god ? t.god.charAt(0).toUpperCase() + t.god.slice(1) : 'Κανένας';
|
const godName = t.god ? t.god.charAt(0).toUpperCase() + t.god.slice(1) : 'Κανένας';
|
||||||
const seaStr = t.sea != null ? `Θ${t.sea}` : '—';
|
const seaStr = t.sea != null ? `Θ${t.sea}` : '—';
|
||||||
|
|||||||
@@ -151,6 +151,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Market options -->
|
||||||
<!-- Market options -->
|
<!-- Market options -->
|
||||||
<div class="form-group" id="market-options" style="display:none">
|
<div class="form-group" id="market-options" style="display:none">
|
||||||
<div style="display:flex; gap:10px; margin-bottom:10px;">
|
<div style="display:flex; gap:10px; margin-bottom:10px;">
|
||||||
@@ -198,11 +199,23 @@
|
|||||||
<div style="flex:1;">
|
<div style="flex:1;">
|
||||||
<label>Ορατότητα</label>
|
<label>Ορατότητα</label>
|
||||||
<select id="market-visibility">
|
<select id="market-visibility">
|
||||||
<option value="all">Όλοι</option>
|
<option value="1">Συμμαχία Μόνο</option>
|
||||||
<option value="alliance">Συμμαχία</option>
|
<option value="2">Όλοι</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr style="border: 0; border-top: 1px solid #2a2a4a; margin: 15px 0 10px 0; width: 100%;"/>
|
||||||
|
<div style="display:flex; justify-content: space-between; align-items: center; width: 100%;">
|
||||||
|
<label style="color:#c8a44a; font-size: 0.8rem;">Λίστα Παζαριού (Προσφορές Άλλων)</label>
|
||||||
|
<div>
|
||||||
|
<span id="market-capacity-label" style="font-size:0.75rem; color:#aaa; margin-right: 10px;">Χωρητικότητα: 0</span>
|
||||||
|
<button class="btn btn-gold btn-sm" id="btn-scan-market" type="button" onclick="window.scanMarket()">Αναζήτηση (Scan)</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="market-offers-container" style="max-height: 300px; overflow-y: auto; width: 100%; margin-top: 10px; background: #0f2a50; border-radius: 4px; border: 1px solid #2a4a6a;">
|
||||||
|
<div id="market-offers-content" style="color: #888; font-size: 0.8rem; text-align: center; padding: 15px;">Πατήστε "Αναζήτηση" για να φορτώσετε τη λίστα.</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" id="amount-group" style="display:none">
|
<div class="form-group" id="amount-group" style="display:none">
|
||||||
|
|||||||
Reference in New Issue
Block a user