Operator-set callsigns: the console's name-bitmap generator recreated
The 1995 console let the operator type player callsigns; the pods render them from 1bpp BITMAPS streamed in the egg ([largebitmap]/[smallbitmap] lists + hex raster pages, bitmapindex per pilot; large 128x32 on the score/ranking display via DPLRenderer name textures, small 64x16 on the kill-feed and radar/target labels). The textual name= pilot key is dead in the shipped binary -- its pilot parse reads bitmapindex only (decomp part_014). Historical bug surfaced by this work: every egg in the repo listed only ONE name bitmap (Aeolus) while pilot 2 pointed at index 2, so player 2+'s callsign has never rendered (null-checked, silently blank). eggmodel.py: EggDoc.set_callsigns rewrites the lists + one raster page per callsign per size + per-pilot name=/bitmapindex= (duplicate names get disambiguated page tags); get_callsign_rows decodes pages back (exact inverse of the engine's nibble-stream parse, GRAPH2D.cpp:340, MSB-first bit order :558); rasterize_callsign/make_callsigns render text via Qt (auto-shrink to fit, NoAntialias, 1bpp threshold) -- the only PySide6-dependent corner of the module. validate() now catches dangling bitmapindexes and missing raster pages. btoperator.py: Callsign column in the roster (free text, PLAYERn default); every save rasterizes the column into the egg exactly like the original console did. MP_BHMC.EGG template repaired: authentic Aeolus raster preserved bit-exact, Boreas page generated and listed. Verified: eggmodel self-test 20/20 (bit-exact raster round-trip, dup disambiguation, dangling-index catch); ASCII-art decode of the app-generated VIPER/MONGOOSE rasters reads correctly; live 2-node relay session on the generated egg -- full ladder, launch, zero name-bitmap load warnings; operator GUI e2e PASS after the column shift. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f04e8019c2
commit
5e993de99d
+30
-15
@@ -141,8 +141,8 @@ class SessionMonitor:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class Operator(QMainWindow):
|
||||
COLS = ["Identity / Address", "Vehicle", "Color", "Badge", "Patch",
|
||||
"Experience", "Local"]
|
||||
COLS = ["Identity / Address", "Callsign", "Vehicle", "Color", "Badge",
|
||||
"Patch", "Experience", "Local"]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -345,15 +345,19 @@ class Operator(QMainWindow):
|
||||
if self.mode.currentIndex() == 0: # relay tags are auto-managed
|
||||
addr.setFlags(addr.flags() & ~Qt.ItemIsEditable)
|
||||
self.table.setItem(r, 0, addr)
|
||||
self.table.setCellWidget(r, 1, self._combo(
|
||||
self.values.vehicles, pilot.get("vehicle") or "bhk1"))
|
||||
# callsign: free text, becomes the name BITMAPS in the egg on save
|
||||
# (the 1995 console's job -- see eggmodel.set_callsigns)
|
||||
self.table.setItem(r, 1, QTableWidgetItem(
|
||||
pilot.get("name") or "PLAYER%d" % (r + 1)))
|
||||
self.table.setCellWidget(r, 2, self._combo(
|
||||
self.values.colors, pilot.get("color") or "White"))
|
||||
self.values.vehicles, pilot.get("vehicle") or "bhk1"))
|
||||
self.table.setCellWidget(r, 3, self._combo(
|
||||
self.values.badges, pilot.get("badge") or "VGL"))
|
||||
self.values.colors, pilot.get("color") or "White"))
|
||||
self.table.setCellWidget(r, 4, self._combo(
|
||||
self.values.patches, pilot.get("patch") or "Yellow"))
|
||||
self.values.badges, pilot.get("badge") or "VGL"))
|
||||
self.table.setCellWidget(r, 5, self._combo(
|
||||
self.values.patches, pilot.get("patch") or "Yellow"))
|
||||
self.table.setCellWidget(r, 6, self._combo(
|
||||
self.values.experiences, pilot.get("experience") or "expert"))
|
||||
chk = QCheckBox()
|
||||
chk.setChecked(local)
|
||||
@@ -362,22 +366,28 @@ class Operator(QMainWindow):
|
||||
wl.setContentsMargins(0, 0, 0, 0)
|
||||
wl.setAlignment(Qt.AlignCenter)
|
||||
wl.addWidget(chk)
|
||||
self.table.setCellWidget(r, 6, wrap)
|
||||
self.table.setCellWidget(r, 7, wrap)
|
||||
|
||||
def _row_pilot(self, r, template):
|
||||
"""Read one roster row back into a pilot dict."""
|
||||
p = dict(template)
|
||||
p["address"] = self.table.item(r, 0).text().strip()
|
||||
p["vehicle"] = self.table.cellWidget(r, 1).currentText()
|
||||
p["color"] = self.table.cellWidget(r, 2).currentText()
|
||||
p["badge"] = self.table.cellWidget(r, 3).currentText()
|
||||
p["patch"] = self.table.cellWidget(r, 4).currentText()
|
||||
p["experience"] = self.table.cellWidget(r, 5).currentText()
|
||||
p["bitmapindex"] = str(min(r + 1, 8))
|
||||
p["name"] = self._row_callsign(r)
|
||||
p["vehicle"] = self.table.cellWidget(r, 2).currentText()
|
||||
p["color"] = self.table.cellWidget(r, 3).currentText()
|
||||
p["badge"] = self.table.cellWidget(r, 4).currentText()
|
||||
p["patch"] = self.table.cellWidget(r, 5).currentText()
|
||||
p["experience"] = self.table.cellWidget(r, 6).currentText()
|
||||
p["bitmapindex"] = str(r + 1)
|
||||
return p
|
||||
|
||||
def _row_callsign(self, r):
|
||||
item = self.table.item(r, 1)
|
||||
text = item.text().strip() if item else ""
|
||||
return text or "PLAYER%d" % (r + 1)
|
||||
|
||||
def _row_local(self, r):
|
||||
wrap = self.table.cellWidget(r, 6)
|
||||
wrap = self.table.cellWidget(r, 7)
|
||||
return wrap.findChild(QCheckBox).isChecked() if wrap else False
|
||||
|
||||
def _pilot_template(self):
|
||||
@@ -401,6 +411,7 @@ class Operator(QMainWindow):
|
||||
address = "192.168.1.%d:1502" % (99 + n)
|
||||
p = self._pilot_template()
|
||||
p["address"] = address
|
||||
p.pop("name", None) # new row gets the PLAYERn default callsign
|
||||
self._add_pilot_row(p, local=(self.mode.currentIndex() == 0))
|
||||
self._renumber_relay_tags()
|
||||
|
||||
@@ -458,6 +469,10 @@ class Operator(QMainWindow):
|
||||
pilots = [self._row_pilot(r, template)
|
||||
for r in range(self.table.rowCount())]
|
||||
self.egg.set_pilots(pilots)
|
||||
# rasterize the callsigns into the egg's name-bitmap pages (what the
|
||||
# 1995 console did when the operator typed a callsign)
|
||||
self.egg.set_callsigns(eggmodel.make_callsigns(
|
||||
[self._row_callsign(r) for r in range(self.table.rowCount())]))
|
||||
return self.egg.validate(self.values)
|
||||
|
||||
def _egg_new(self):
|
||||
|
||||
Reference in New Issue
Block a user