[WebHost] Add Super Metroid support to Web Tracker (#153)
* [WebHost]: Added Super Metroid tracker, based on TimeSpinner & OOT
This commit is contained in:
parent
0558351a12
commit
f003c7130f
|
@ -0,0 +1,49 @@
|
|||
window.addEventListener('load', () => {
|
||||
// Reload tracker every 15 seconds
|
||||
const url = window.location;
|
||||
setInterval(() => {
|
||||
const ajax = new XMLHttpRequest();
|
||||
ajax.onreadystatechange = () => {
|
||||
if (ajax.readyState !== 4) { return; }
|
||||
|
||||
// Create a fake DOM using the returned HTML
|
||||
const domParser = new DOMParser();
|
||||
const fakeDOM = domParser.parseFromString(ajax.responseText, 'text/html');
|
||||
|
||||
// Update item tracker
|
||||
document.getElementById('inventory-table').innerHTML = fakeDOM.getElementById('inventory-table').innerHTML;
|
||||
// Update only counters in the location-table
|
||||
let counters = document.getElementsByClassName('counter');
|
||||
const fakeCounters = fakeDOM.getElementsByClassName('counter');
|
||||
for (let i = 0; i < counters.length; i++) {
|
||||
counters[i].innerHTML = fakeCounters[i].innerHTML;
|
||||
}
|
||||
};
|
||||
ajax.open('GET', url);
|
||||
ajax.send();
|
||||
}, 15000)
|
||||
|
||||
// Collapsible advancement sections
|
||||
const categories = document.getElementsByClassName("location-category");
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
let hide_id = categories[i].id.split('-')[0];
|
||||
if (hide_id == 'Total') {
|
||||
continue;
|
||||
}
|
||||
categories[i].addEventListener('click', function() {
|
||||
// Toggle the advancement list
|
||||
document.getElementById(hide_id).classList.toggle("hide");
|
||||
// Change text of the header
|
||||
const tab_header = document.getElementById(hide_id+'-header').children[0];
|
||||
const orig_text = tab_header.innerHTML;
|
||||
let new_text;
|
||||
if (orig_text.includes("▼")) {
|
||||
new_text = orig_text.replace("▼", "▲");
|
||||
}
|
||||
else {
|
||||
new_text = orig_text.replace("▲", "▼");
|
||||
}
|
||||
tab_header.innerHTML = new_text;
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,104 @@
|
|||
#player-tracker-wrapper{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#inventory-table{
|
||||
border-top: 2px solid #000000;
|
||||
border-left: 2px solid #000000;
|
||||
border-right: 2px solid #000000;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
padding: 3px 3px 10px;
|
||||
width: 384px;
|
||||
background-color: #546E7A;
|
||||
}
|
||||
|
||||
#inventory-table td{
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#inventory-table img{
|
||||
height: 100%;
|
||||
max-width: 40px;
|
||||
max-height: 40px;
|
||||
min-width: 40px;
|
||||
min-height: 40px;
|
||||
filter: grayscale(100%) contrast(75%) brightness(30%);
|
||||
}
|
||||
|
||||
#inventory-table img.acquired{
|
||||
filter: none;
|
||||
}
|
||||
|
||||
#inventory-table div.counted-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#inventory-table div.item-count {
|
||||
position: absolute;
|
||||
color: white;
|
||||
font-family: "Minecraftia", monospace;
|
||||
font-weight: bold;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
#location-table{
|
||||
width: 384px;
|
||||
border-left: 2px solid #000000;
|
||||
border-right: 2px solid #000000;
|
||||
border-bottom: 2px solid #000000;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
background-color: #546E7A;
|
||||
color: #000000;
|
||||
padding: 0 3px 3px;
|
||||
font-size: 14px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#location-table th{
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#location-table td{
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#location-table td.counter {
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#location-table td.toggle-arrow {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#location-table tr#Total-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#location-table img{
|
||||
height: 100%;
|
||||
max-width: 30px;
|
||||
max-height: 30px;
|
||||
}
|
||||
|
||||
#location-table tbody.locations {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#location-table td.location-name {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ player_name }}'s Tracker</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/supermetroidTracker.css') }}"/>
|
||||
<script type="application/ecmascript" src="{{ url_for('static', filename='assets/supermetroidTracker.js') }}"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="player-tracker-wrapper" data-tracker="{{ room.tracker|suuid }}">
|
||||
<table id="inventory-table">
|
||||
<tr>
|
||||
<td><img src="{{ icons['Charge Beam'] }}" class="{{ 'acquired' if 'Charge Beam' in acquired_items }}" title="Charge Beam" /></td>
|
||||
<td><img src="{{ icons['Ice Beam'] }}" class="{{ 'acquired' if 'Ice Beam' in acquired_items }}" title="Ice Beam" /></td>
|
||||
<td><img src="{{ icons['Wave Beam'] }}" class="{{ 'acquired' if 'Wave Beam' in acquired_items }}" title="Wave Beam" /></td>
|
||||
<td><img src="{{ icons['Spazer'] }}" class="{{ 'acquired' if 'Spazer' in acquired_items }}" title="S p a z e r" /></td>
|
||||
<td><img src="{{ icons['Plasma Beam'] }}" class="{{ 'acquired' if 'Plasma Beam' in acquired_items }}" title="Plasma Beam" /></td>
|
||||
<td><img src="{{ icons['Varia Suit'] }}" class="{{ 'acquired' if 'Varia Suit' in acquired_items }}" title="Varia Suit" /></td>
|
||||
<td><img src="{{ icons['Gravity Suit'] }}" class="{{ 'acquired' if 'Gravity Suit' in acquired_items }}" title="Gravity Suit" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Morph Ball'] }}" class="{{ 'acquired' if 'Morph Ball' in acquired_items }}" title="Morph Ball" /></td>
|
||||
<td><img src="{{ icons['Bomb'] }}" class="{{ 'acquired' if 'Bomb' in acquired_items }}" title="Bomb" /></td>
|
||||
<td><img src="{{ icons['Spring Ball'] }}" class="{{ 'acquired' if 'Spring Ball' in acquired_items }}" title="Spring Ball" /></td>
|
||||
<td><img src="{{ icons['Screw Attack'] }}" class="{{ 'acquired' if 'Screw Attack' in acquired_items }}" title="Screw Attack" /></td>
|
||||
<td><img src="{{ icons['Hi-Jump Boots'] }}" class="{{ 'acquired' if 'Hi-Jump Boots' in acquired_items }}" title="Hi-Jump Boots" /></td>
|
||||
<td><img src="{{ icons['Space Jump'] }}" class="{{ 'acquired' if 'Space Jump' in acquired_items }}" title="Space Jump" /></td>
|
||||
<td><img src="{{ icons['Speed Booster'] }}" class="{{ 'acquired' if 'Speed Booster' in acquired_items }}" title="Speed Booster" /></td>
|
||||
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Energy Tank'] }}" class="{{ 'acquired' if energy_count > 0 }}" title="Energy Tank" />
|
||||
<div class="item-count">{{ energy_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Reserve Tank'] }}" class="{{ 'acquired' if reserve_count > 0 }}" title="Reserve Tank" />
|
||||
<div class="item-count">{{ reserve_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Missile'] }}" class="{{ 'acquired' if missile_count > 0 }}" title="Missile ({{missile_count * 5}})" />
|
||||
<div class="item-count">{{ missile_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Super Missile'] }}" class="{{ 'acquired' if super_count > 0 }}" title="Super Missile ({{super_count * 5}})" />
|
||||
<div class="item-count">{{ super_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Power Bomb'] }}" class="{{ 'acquired' if power_count > 0 }}" title="Power Bomb ({{power_count * 5}})" />
|
||||
<div class="item-count">{{ power_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><img src="{{ icons['Grappling Beam'] }}" class="{{ 'acquired' if 'Grappling Beam' in acquired_items }}" title="Grappling Beam" /></td>
|
||||
<td><img src="{{ icons['X-Ray Scope'] }}" class="{{ 'acquired' if 'X-Ray Scope' in acquired_items }}" title="X-Ray Scope" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table id="location-table">
|
||||
{% for area in checks_done %}
|
||||
<tr class="location-category" id="{{area}}-header">
|
||||
<td>{{ area }} {{'▼' if area != 'Total'}}</td>
|
||||
<td class="counter">{{ checks_done[area] }} / {{ checks_in_area[area] }}</td>
|
||||
</tr>
|
||||
<tbody class="locations hide" id="{{area}}">
|
||||
{% for location in location_info[area] %}
|
||||
<tr>
|
||||
<td class="location-name">{{ location }}</td>
|
||||
<td class="counter">{{ '✔' if location_info[area][location] else '' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -774,6 +774,106 @@ def __renderTimespinnerTracker(multisave: Dict[str, Any], room: Room, locations:
|
|||
checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info,
|
||||
**display_data)
|
||||
|
||||
def __renderSuperMetroidTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int]]],
|
||||
inventory: Counter, team: int, player: int, playerName: str,
|
||||
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int]) -> str:
|
||||
|
||||
icons = {
|
||||
"Energy Tank": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/ETank.png",
|
||||
"Missile": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Missile.png",
|
||||
"Super Missile": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Super.png",
|
||||
"Power Bomb": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/PowerBomb.png",
|
||||
"Bomb": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Bomb.png",
|
||||
"Charge Beam": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Charge.png",
|
||||
"Ice Beam": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Ice.png",
|
||||
"Hi-Jump Boots": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/HiJump.png",
|
||||
"Speed Booster": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/SpeedBooster.png",
|
||||
"Wave Beam": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Wave.png",
|
||||
"Spazer": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Spazer.png",
|
||||
"Spring Ball": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/SpringBall.png",
|
||||
"Varia Suit": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Varia.png",
|
||||
"Plasma Beam": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Plasma.png",
|
||||
"Grappling Beam": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Grapple.png",
|
||||
"Morph Ball": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Morph.png",
|
||||
"Reserve Tank": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Reserve.png",
|
||||
"Gravity Suit": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/Gravity.png",
|
||||
"X-Ray Scope": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/XRayScope.png",
|
||||
"Space Jump": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/SpaceJump.png",
|
||||
"Screw Attack": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/ScrewAttack.png",
|
||||
"Nothing": "",
|
||||
"No Energy": "",
|
||||
"Kraid": "",
|
||||
"Phantoon": "",
|
||||
"Draygon": "",
|
||||
"Ridley": "",
|
||||
"Mother Brain": "",
|
||||
}
|
||||
|
||||
multi_items = {
|
||||
"Energy Tank": 83000,
|
||||
"Missile": 83001,
|
||||
"Super Missile": 83002,
|
||||
"Power Bomb": 83003,
|
||||
"Reserve Tank": 83020,
|
||||
}
|
||||
|
||||
supermetroid_location_ids = {
|
||||
'Crateria/Blue Brinstar': [82005, 82007, 82008, 82026, 82029,
|
||||
82000, 82004, 82006, 82009, 82010,
|
||||
82011, 82012, 82027, 82028, 82034,
|
||||
82036, 82037],
|
||||
'Green/Pink Brinstar': [82017, 82023, 82030, 82033, 82035,
|
||||
82013, 82014, 82015, 82016, 82018,
|
||||
82019, 82021, 82022, 82024, 82025,
|
||||
82031],
|
||||
'Red Brinstar': [82038, 82042, 82039, 82040, 82041],
|
||||
'Kraid': [82043, 82048, 82044],
|
||||
'Norfair': [82050, 82053, 82061, 82066, 82068,
|
||||
82049, 82051, 82054, 82055, 82056,
|
||||
82062, 82063, 82064, 82065, 82067],
|
||||
'Lower Norfair': [82078, 82079, 82080, 82070, 82071,
|
||||
82073, 82074, 82075, 82076, 82077],
|
||||
'Crocomire': [82052, 82060, 82057, 82058, 82059],
|
||||
'Wrecked Ship': [82129, 82132, 82134, 82135, 82001,
|
||||
82002, 82003, 82128, 82130, 82131,
|
||||
82133],
|
||||
'West Maridia': [82138, 82136, 82137, 82139, 82140,
|
||||
82141, 82142],
|
||||
'East Maridia': [82143, 82145, 82150, 82152, 82154,
|
||||
82144, 82146, 82147, 82148, 82149,
|
||||
82151],
|
||||
}
|
||||
|
||||
display_data = {}
|
||||
|
||||
|
||||
for item_name, item_id in multi_items.items():
|
||||
base_name = item_name.split()[0].lower()
|
||||
count = inventory[item_id]
|
||||
display_data[base_name+"_count"] = inventory[item_id]
|
||||
|
||||
# Victory condition
|
||||
game_state = multisave.get("client_game_state", {}).get((team, player), 0)
|
||||
display_data['game_finished'] = game_state == 30
|
||||
|
||||
# Turn location IDs into advancement tab counts
|
||||
checked_locations = multisave.get("location_checks", {}).get((team, player), set())
|
||||
lookup_name = lambda id: lookup_any_location_id_to_name[id]
|
||||
location_info = {tab_name: {lookup_name(id): (id in checked_locations) for id in tab_locations}
|
||||
for tab_name, tab_locations in supermetroid_location_ids.items()}
|
||||
checks_done = {tab_name: len([id for id in tab_locations if id in checked_locations])
|
||||
for tab_name, tab_locations in supermetroid_location_ids.items()}
|
||||
checks_done['Total'] = len(checked_locations)
|
||||
checks_in_area = {tab_name: len(tab_locations) for tab_name, tab_locations in supermetroid_location_ids.items()}
|
||||
checks_in_area['Total'] = sum(checks_in_area.values())
|
||||
|
||||
return render_template("supermetroidTracker.html",
|
||||
inventory=inventory, icons=icons,
|
||||
acquired_items={lookup_any_item_id_to_name[id] for id in inventory if
|
||||
id in lookup_any_item_id_to_name},
|
||||
player=player, team=team, room=room, player_name=playerName,
|
||||
checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info,
|
||||
**display_data)
|
||||
|
||||
def __renderGenericTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int]]],
|
||||
inventory: Counter, team: int, player: int, playerName: str,
|
||||
|
@ -887,5 +987,6 @@ game_specific_trackers: typing.Dict[str, typing.Callable] = {
|
|||
"Minecraft": __renderMinecraftTracker,
|
||||
"Ocarina of Time": __renderOoTTracker,
|
||||
"Timespinner": __renderTimespinnerTracker,
|
||||
"A Link to the Past": __renderAlttpTracker
|
||||
"A Link to the Past": __renderAlttpTracker,
|
||||
"Super Metroid": __renderSuperMetroidTracker
|
||||
}
|
Loading…
Reference in New Issue