blueprint function
This commit is contained in:
114
blueprint_engine.py
Normal file
114
blueprint_engine.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
STANDARD_BLUEPRINT = [
|
||||||
|
{"barracks": 1, "farm": 3, "lumber": 2, "stoner": 2, "ironer": 2, "storage": 2, "main": 2, "temple": 1},
|
||||||
|
{"barracks": 1, "farm": 3, "lumber": 3, "stoner": 3, "ironer": 3, "storage": 6, "main": 8},
|
||||||
|
{"farm": 8, "lumber": 8, "ironer": 8, "stoner": 8, "market": 5, "temple": 5, "barracks": 5},
|
||||||
|
{"academy": 13},
|
||||||
|
{"storage": 12, "farm": 12},
|
||||||
|
{"main": 25},
|
||||||
|
{"storage": 21, "farm": 15},
|
||||||
|
{"lumber": 15, "stoner": 10, "ironer": 12},
|
||||||
|
{"docks": 10},
|
||||||
|
{"academy": 30},
|
||||||
|
{"farm": 20, "storage": 25},
|
||||||
|
{"market": 15, "trade_office": 1, "hide": 10},
|
||||||
|
{"market": 30, "farm": 35, "thermal": 1, "academy": 36},
|
||||||
|
{"farm": 45, "storage": 35, "lumber": 40, "ironer": 40, "stoner": 40},
|
||||||
|
{"temple": 30}
|
||||||
|
]
|
||||||
|
|
||||||
|
RESEARCH_LIST = [
|
||||||
|
"booty", "pottery", "architecture", "building_crane",
|
||||||
|
"shipwright", "plow", "mathematics", "combat_experience",
|
||||||
|
"strong_wine", "take_over", "colonize_ship"
|
||||||
|
]
|
||||||
|
|
||||||
|
RESEARCH_LEVELS = {
|
||||||
|
"booty": 7,
|
||||||
|
"pottery": 7,
|
||||||
|
"architecture": 10,
|
||||||
|
"building_crane": 13,
|
||||||
|
"shipwright": 13,
|
||||||
|
"plow": 22,
|
||||||
|
"mathematics": 25,
|
||||||
|
"combat_experience": 34,
|
||||||
|
"strong_wine": 34,
|
||||||
|
"take_over": 28,
|
||||||
|
"colonize_ship": 13
|
||||||
|
}
|
||||||
|
|
||||||
|
def evaluate_blueprints(conn):
|
||||||
|
blueprints = conn.execute('SELECT town_id, blueprint_name FROM town_blueprints WHERE is_active = 1').fetchall()
|
||||||
|
if not blueprints:
|
||||||
|
return
|
||||||
|
|
||||||
|
for row in blueprints:
|
||||||
|
town_id = str(row['town_id'])
|
||||||
|
town_row = conn.execute('SELECT data FROM town_state WHERE town_id = ?', (town_id,)).fetchone()
|
||||||
|
if not town_row:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
town = json.loads(town_row['data'])
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
build_queue = town.get('build_queue', [])
|
||||||
|
buildings = town.get('buildings', {})
|
||||||
|
build_data = town.get('build_data', {})
|
||||||
|
|
||||||
|
# Don't queue anything if there's already a pending/executing command in DB
|
||||||
|
db_pending = conn.execute("SELECT id FROM commands WHERE town_id = ? AND type IN ('build', 'research') AND status IN ('pending', 'executing')", (town_id,)).fetchall()
|
||||||
|
if db_pending:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Calculate Future Levels based on current + game queue
|
||||||
|
future_levels = {k: v for k, v in buildings.items()}
|
||||||
|
for q_item in build_queue:
|
||||||
|
b_type = q_item.get('building_type') or q_item.get('name')
|
||||||
|
if b_type:
|
||||||
|
future_levels[b_type] = future_levels.get(b_type, 0) + 1
|
||||||
|
|
||||||
|
# Find next required building
|
||||||
|
target_building = None
|
||||||
|
for phase in STANDARD_BLUEPRINT:
|
||||||
|
incomplete = False
|
||||||
|
for b_name, req_level in phase.items():
|
||||||
|
if future_levels.get(b_name, 0) < req_level:
|
||||||
|
incomplete = True
|
||||||
|
b_info = build_data.get(b_name)
|
||||||
|
if b_info and not b_info.get('has_max_level'):
|
||||||
|
if b_info.get('enough_resources') != False:
|
||||||
|
target_building = b_name
|
||||||
|
break
|
||||||
|
if incomplete:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Handle Academy Tech Research if no building was prioritized
|
||||||
|
target_research = None
|
||||||
|
if not target_building:
|
||||||
|
academy_level = future_levels.get('academy', 0)
|
||||||
|
researched = town.get('researches', {})
|
||||||
|
research_queue = town.get('research_queue', [])
|
||||||
|
|
||||||
|
for r_name in RESEARCH_LIST:
|
||||||
|
if not researched.get(r_name) and r_name not in [q.get('type') for q in research_queue]:
|
||||||
|
# Check if academy level is high enough
|
||||||
|
req_level = RESEARCH_LEVELS.get(r_name, 99)
|
||||||
|
if academy_level >= req_level:
|
||||||
|
target_research = r_name
|
||||||
|
break
|
||||||
|
|
||||||
|
if target_building:
|
||||||
|
payload_str = json.dumps({"building_id": target_building})
|
||||||
|
conn.execute('''
|
||||||
|
INSERT INTO commands (town_id, town_name, type, payload, status, player_id)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
''', (town_id, town.get('town_name'), 'build', payload_str, 'pending', town.get('player_id')))
|
||||||
|
elif target_research:
|
||||||
|
payload_str = json.dumps({"research_id": target_research})
|
||||||
|
conn.execute('''
|
||||||
|
INSERT INTO commands (town_id, town_name, type, payload, status, player_id)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
''', (town_id, town.get('town_name'), 'research', payload_str, 'pending', town.get('player_id')))
|
||||||
10
db.py
10
db.py
@@ -92,6 +92,16 @@ def init_db():
|
|||||||
''')
|
''')
|
||||||
c.execute('CREATE INDEX IF NOT EXISTS idx_bot_logs_player_feature ON bot_logs(player_id, feature)')
|
c.execute('CREATE INDEX IF NOT EXISTS idx_bot_logs_player_feature ON bot_logs(player_id, feature)')
|
||||||
|
|
||||||
|
# Blueprints - assigns a blueprint to a specific town
|
||||||
|
c.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS town_blueprints (
|
||||||
|
town_id TEXT PRIMARY KEY,
|
||||||
|
blueprint_name TEXT NOT NULL,
|
||||||
|
is_active INTEGER NOT NULL DEFAULT 1,
|
||||||
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
# Migration: add new columns if upgrading an existing database
|
# Migration: add new columns if upgrading an existing database
|
||||||
for _col in [
|
for _col in [
|
||||||
'ALTER TABLE town_state ADD COLUMN player_id TEXT',
|
'ALTER TABLE town_state ADD COLUMN player_id TEXT',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import json
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import os
|
import os
|
||||||
from flask import make_response
|
from flask import make_response
|
||||||
|
from blueprint_engine import evaluate_blueprints
|
||||||
|
|
||||||
api = Blueprint('api', __name__)
|
api = Blueprint('api', __name__)
|
||||||
|
|
||||||
@@ -94,6 +95,13 @@ def receive_state():
|
|||||||
datetime.utcnow().isoformat()
|
datetime.utcnow().isoformat()
|
||||||
))
|
))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
|
try:
|
||||||
|
evaluate_blueprints(conn)
|
||||||
|
conn.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print("Error evaluating blueprints:", e)
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'ok': True, 'towns_updated': len(towns)})
|
return jsonify({'ok': True, 'towns_updated': len(towns)})
|
||||||
|
|
||||||
|
|||||||
@@ -178,11 +178,13 @@ def get_towns():
|
|||||||
player_id = request.args.get('player_id')
|
player_id = request.args.get('player_id')
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
rows = conn.execute('''
|
rows = conn.execute('''
|
||||||
SELECT town_id, town_name, player, player_id, alliance_id,
|
SELECT ts.town_id, ts.town_name, ts.player, ts.player_id, ts.alliance_id,
|
||||||
world_id, x, y, sea, data, updated_at
|
ts.world_id, ts.x, ts.y, ts.sea, ts.data, ts.updated_at,
|
||||||
FROM town_state
|
tb.blueprint_name, tb.is_active as blueprint_active
|
||||||
WHERE player_id = ?
|
FROM town_state ts
|
||||||
ORDER BY town_name ASC
|
LEFT JOIN town_blueprints tb ON ts.town_id = tb.town_id AND tb.is_active = 1
|
||||||
|
WHERE ts.player_id = ?
|
||||||
|
ORDER BY ts.town_name ASC
|
||||||
''', (player_id, )).fetchall()
|
''', (player_id, )).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@@ -220,11 +222,51 @@ def get_towns():
|
|||||||
'bonuses': d.get('bonuses', {}),
|
'bonuses': d.get('bonuses', {}),
|
||||||
'wonder_points': d.get('wonder_points', 0),
|
'wonder_points': d.get('wonder_points', 0),
|
||||||
'total_points': d.get('total_points', 0),
|
'total_points': d.get('total_points', 0),
|
||||||
'alliance_name': d.get('alliance_name', None)
|
'alliance_name': d.get('alliance_name', None),
|
||||||
|
'blueprint_name': row['blueprint_name'],
|
||||||
|
'blueprint_active': bool(row['blueprint_active'])
|
||||||
})
|
})
|
||||||
return jsonify(towns)
|
return jsonify(towns)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# POST /dashboard/blueprints
|
||||||
|
# Toggle a blueprint for a specific town
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@dashboard.route('/dashboard/blueprints', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def toggle_blueprint():
|
||||||
|
data = request.get_json(silent=True) or {}
|
||||||
|
town_id = data.get('town_id')
|
||||||
|
blueprint_name = data.get('blueprint_name', 'Standard Growth')
|
||||||
|
|
||||||
|
if not town_id:
|
||||||
|
return jsonify({'error': 'missing town_id'}), 400
|
||||||
|
|
||||||
|
conn = get_db()
|
||||||
|
|
||||||
|
# Check if currently active
|
||||||
|
row = conn.execute('SELECT is_active FROM town_blueprints WHERE town_id = ?', (town_id,)).fetchone()
|
||||||
|
|
||||||
|
new_state = 1
|
||||||
|
if row and row['is_active'] == 1:
|
||||||
|
new_state = 0 # Toggle off
|
||||||
|
|
||||||
|
conn.execute('''
|
||||||
|
INSERT INTO town_blueprints (town_id, blueprint_name, is_active, updated_at)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
ON CONFLICT(town_id) DO UPDATE SET
|
||||||
|
blueprint_name = excluded.blueprint_name,
|
||||||
|
is_active = excluded.is_active,
|
||||||
|
updated_at = excluded.updated_at
|
||||||
|
''', (town_id, blueprint_name, new_state, datetime.utcnow().isoformat()))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return jsonify({'ok': True, 'is_active': bool(new_state), 'blueprint_name': blueprint_name})
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# GET /dashboard/client-status
|
# GET /dashboard/client-status
|
||||||
# Returns whether the Tampermonkey client is considered online.
|
# Returns whether the Tampermonkey client is considered online.
|
||||||
|
|||||||
@@ -318,9 +318,9 @@ tr:hover td { background: #1e1e40; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
Building, Academy, Unit & Market Modals
|
Building, Academy, Unit, Market & Blueprint Modals
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
#building-modal-overlay, #academy-modal-overlay, #unit-modal-overlay, #market-modal-overlay {
|
#building-modal-overlay, #academy-modal-overlay, #unit-modal-overlay, #market-modal-overlay, #blueprints-modal-overlay {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@@ -329,9 +329,9 @@ tr:hover td { background: #1e1e40; }
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
#building-modal-overlay.open, #academy-modal-overlay.open, #unit-modal-overlay.open, #market-modal-overlay.open { display: flex; }
|
#building-modal-overlay.open, #academy-modal-overlay.open, #unit-modal-overlay.open, #market-modal-overlay.open, #blueprints-modal-overlay.open { display: flex; }
|
||||||
|
|
||||||
#building-modal, #academy-modal, #unit-modal, #market-modal {
|
#building-modal, #academy-modal, #unit-modal, #market-modal, #blueprints-modal {
|
||||||
background: #16213e;
|
background: #16213e;
|
||||||
border: 2px solid #c8a44a;
|
border: 2px solid #c8a44a;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|||||||
@@ -182,6 +182,30 @@ window.sendCommand = async function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload = { research_id };
|
payload = { research_id };
|
||||||
|
} else if (type === 'blueprints') {
|
||||||
|
const blueprint_name = window.selectedBlueprintName;
|
||||||
|
if (!blueprint_name) return alert('Παρακαλώ επιλέξτε Blueprint.');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/dashboard/blueprints', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
town_id: town.town_id,
|
||||||
|
blueprint_name: blueprint_name
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.ok) {
|
||||||
|
alert(data.is_active ? 'Blueprint ενεργοποιήθηκε!' : 'Blueprint απενεργοποιήθηκε!');
|
||||||
|
window.fetchTowns(); // refresh towns to update UI state
|
||||||
|
} else {
|
||||||
|
alert('Σφάλμα: ' + data.error);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
alert('Failed to toggle blueprint: ' + e);
|
||||||
|
}
|
||||||
|
return; // Exit here as we don't send this to /dashboard/commands
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ window.updateSelectionDisplay = function() {
|
|||||||
}
|
}
|
||||||
} else if (type === 'market_offer') {
|
} else if (type === 'market_offer') {
|
||||||
labelEl.innerHTML = `🛒 Ρυθμίσεις Παζαριού`;
|
labelEl.innerHTML = `🛒 Ρυθμίσεις Παζαριού`;
|
||||||
|
} else if (type === 'blueprints') {
|
||||||
|
if (window.selectedBlueprintName) {
|
||||||
|
labelEl.innerHTML = `📜 ${window.selectedBlueprintName}`;
|
||||||
|
} else {
|
||||||
|
labelEl.innerHTML = `-- Επιλέξτε Blueprint --`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,6 +64,22 @@ window.reopenActiveModal = function() {
|
|||||||
else if (type === 'recruit') window.openUnitModal();
|
else if (type === 'recruit') window.openUnitModal();
|
||||||
else if (type === 'research') window.openAcademyModal();
|
else if (type === 'research') window.openAcademyModal();
|
||||||
else if (type === 'market_offer') window.openMarketModal();
|
else if (type === 'market_offer') window.openMarketModal();
|
||||||
|
else if (type === 'blueprints') window.openBlueprintsModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.selectedBlueprintName = null;
|
||||||
|
window.selectBlueprint = function(name) {
|
||||||
|
window.selectedBlueprintName = name;
|
||||||
|
window.updateSelectionDisplay();
|
||||||
|
window.closeBlueprintsModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.openBlueprintsModal = function() {
|
||||||
|
document.getElementById('blueprints-modal-overlay').classList.add('open');
|
||||||
|
};
|
||||||
|
window.closeBlueprintsModal = function(e) {
|
||||||
|
if (e && e.target.id !== 'blueprints-modal-overlay' && !e.target.classList.contains('modal-close')) return;
|
||||||
|
document.getElementById('blueprints-modal-overlay').classList.remove('open');
|
||||||
};
|
};
|
||||||
|
|
||||||
window.openMarketModal = function() {
|
window.openMarketModal = function() {
|
||||||
|
|||||||
@@ -212,4 +212,43 @@ window.renderTownDetails = function() {
|
|||||||
if(unitsHtml === '') unitsHtml = '<div style="color:#666">Κανένα στράτευμα</div>';
|
if(unitsHtml === '') unitsHtml = '<div style="color:#666">Κανένα στράτευμα</div>';
|
||||||
|
|
||||||
document.getElementById('td-units').innerHTML = unitsHtml;
|
document.getElementById('td-units').innerHTML = unitsHtml;
|
||||||
|
|
||||||
|
// ---- Blueprint Lock & Indicator ----
|
||||||
|
const isBlueprintActive = !!t.blueprint_active;
|
||||||
|
const bpName = t.blueprint_name || 'Standard Growth';
|
||||||
|
|
||||||
|
if (isBlueprintActive) {
|
||||||
|
document.getElementById('td-general').innerHTML += `
|
||||||
|
<div style="margin-top:10px; padding: 4px 8px; background: rgba(200,164,74,0.15); border-left: 3px solid #c8a44a; border-radius:4px; font-size:0.8rem;">
|
||||||
|
🤖 Blueprint: <strong style="color:#c8a44a">${bpName}</strong> <span style="color:#2ecc71">(ΕΝΕΡΓΟ)</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const btnBuild = document.getElementById('seg-build');
|
||||||
|
const btnResearch = document.getElementById('seg-research');
|
||||||
|
|
||||||
|
if (btnBuild && btnResearch) {
|
||||||
|
if (isBlueprintActive) {
|
||||||
|
btnBuild.style.opacity = '0.3';
|
||||||
|
btnBuild.style.pointerEvents = 'none';
|
||||||
|
btnBuild.title = 'Απενεργοποιημένο λόγω Blueprint';
|
||||||
|
|
||||||
|
btnResearch.style.opacity = '0.3';
|
||||||
|
btnResearch.style.pointerEvents = 'none';
|
||||||
|
btnResearch.title = 'Απενεργοποιημένο λόγω Blueprint';
|
||||||
|
|
||||||
|
if (window.currentCmdType === 'build' || window.currentCmdType === 'research') {
|
||||||
|
window.setCmdType('recruit');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
btnBuild.style.opacity = '1';
|
||||||
|
btnBuild.style.pointerEvents = 'auto';
|
||||||
|
btnBuild.title = '';
|
||||||
|
|
||||||
|
btnResearch.style.opacity = '1';
|
||||||
|
btnResearch.style.pointerEvents = 'auto';
|
||||||
|
btnResearch.title = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,6 +91,7 @@
|
|||||||
<button class="seg-btn" id="seg-recruit" onclick="window.setCmdType('recruit', true)" style="flex: 1; padding: 10px; border: none; background: transparent; color: #888; border-radius: 6px; cursor: pointer; transition: 0.2s;">⚔️ Στρατός</button>
|
<button class="seg-btn" id="seg-recruit" onclick="window.setCmdType('recruit', true)" style="flex: 1; padding: 10px; border: none; background: transparent; color: #888; border-radius: 6px; cursor: pointer; transition: 0.2s;">⚔️ Στρατός</button>
|
||||||
<button class="seg-btn" id="seg-market" onclick="window.setCmdType('market_offer', true)" style="flex: 1; padding: 10px; border: none; background: transparent; color: #888; border-radius: 6px; cursor: pointer; transition: 0.2s;">🛒 Παζάρι</button>
|
<button class="seg-btn" id="seg-market" onclick="window.setCmdType('market_offer', true)" style="flex: 1; padding: 10px; border: none; background: transparent; color: #888; border-radius: 6px; cursor: pointer; transition: 0.2s;">🛒 Παζάρι</button>
|
||||||
<button class="seg-btn" id="seg-research" onclick="window.setCmdType('research', true)" style="flex: 1; padding: 10px; border: none; background: transparent; color: #888; border-radius: 6px; cursor: pointer; transition: 0.2s;">🦉 Έρευνα</button>
|
<button class="seg-btn" id="seg-research" onclick="window.setCmdType('research', true)" style="flex: 1; padding: 10px; border: none; background: transparent; color: #888; border-radius: 6px; cursor: pointer; transition: 0.2s;">🦉 Έρευνα</button>
|
||||||
|
<button class="seg-btn" id="seg-blueprints" onclick="window.setCmdType('blueprints', true)" style="flex: 1; padding: 10px; border: none; background: transparent; color: #888; border-radius: 6px; cursor: pointer; transition: 0.2s;">📜 Blueprints</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dynamic Selection Area -->
|
<!-- Dynamic Selection Area -->
|
||||||
@@ -127,6 +128,31 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ====== Blueprints Modal ====== -->
|
||||||
|
<div class="modal-overlay" id="blueprints-modal-overlay" onclick="window.closeBlueprintsModal(event)">
|
||||||
|
<div class="custom-modal" id="blueprints-modal" style="max-width: 400px;">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>📜 Επιλογή Blueprint</h3>
|
||||||
|
<button class="modal-close" onclick="window.closeBlueprintsModal()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 15px;">
|
||||||
|
<p style="color:#ccc; font-size:0.85rem; margin-bottom:15px;">Επιλέξτε ένα Blueprint για να αναλάβει το Python την αυτόματη κατασκευή της πόλης.</p>
|
||||||
|
|
||||||
|
<div id="blueprint-list" style="display:flex; flex-direction:column; gap:10px; margin-bottom:20px;">
|
||||||
|
<!-- Currently just one blueprint -->
|
||||||
|
<div class="bld-card" id="bp-card-standard" onclick="window.selectBlueprint('Standard Growth')" style="width:100%; justify-content:flex-start; cursor:pointer;">
|
||||||
|
<span class="bld-icon" style="font-size:2rem;">🏙️</span>
|
||||||
|
<div style="display:flex; flex-direction:column; align-items:flex-start;">
|
||||||
|
<span class="bld-name" style="margin-top:0; font-size:1rem; font-weight:bold;">Standard Growth</span>
|
||||||
|
<span style="font-size:0.75rem; color:#888;">Αυτόματη ανάπτυξη κτιρίων & ακαδημίας</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ====== Market Modal ====== -->
|
<!-- ====== Market Modal ====== -->
|
||||||
<div class="modal-overlay" id="market-modal-overlay" onclick="window.closeMarketModal(event)">
|
<div class="modal-overlay" id="market-modal-overlay" onclick="window.closeMarketModal(event)">
|
||||||
<div class="custom-modal" id="market-modal" style="max-width: 500px;">
|
<div class="custom-modal" id="market-modal" style="max-width: 500px;">
|
||||||
|
|||||||
Reference in New Issue
Block a user