63 lines
1.9 KiB
Python
63 lines
1.9 KiB
Python
import json
|
|
import math
|
|
|
|
DEFAULT_STATE = '{"version": 1, "high_score": 0, "last_score": 0, "best_wave": 1}'
|
|
|
|
|
|
def parse_game_state(raw):
|
|
"""Read the JSON written by the browser game; bad or missing data falls back to zeros."""
|
|
try:
|
|
data = json.loads(raw or "{}")
|
|
except (TypeError, ValueError):
|
|
data = {}
|
|
if not isinstance(data, dict):
|
|
data = {}
|
|
|
|
def as_int(key, default):
|
|
value = data.get(key, default)
|
|
# bool is a subclass of int: reject it explicitly
|
|
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
return default
|
|
if isinstance(value, float) and not math.isfinite(value): # json.loads accepts NaN/Infinity
|
|
return default
|
|
return max(0, int(value))
|
|
|
|
return {
|
|
"high_score": as_int("high_score", 0),
|
|
"last_score": as_int("last_score", 0),
|
|
"best_wave": max(1, as_int("best_wave", 1)),
|
|
}
|
|
|
|
|
|
class IsulionSpaceInvaders:
|
|
"""
|
|
A small Space Invaders game played inside the node (see web/space_invaders.js).
|
|
The browser stores scores in the hidden ``game_state`` widget; executing the
|
|
node outputs them, so a session's best score can feed a prompt or a seed.
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"game_state": ("STRING", {"default": DEFAULT_STATE}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("INT", "INT", "STRING")
|
|
RETURN_NAMES = ("high_score", "last_score", "summary")
|
|
FUNCTION = "report"
|
|
CATEGORY = "Isulion/Games"
|
|
OUTPUT_NODE = True
|
|
|
|
def report(self, game_state):
|
|
state = parse_game_state(game_state)
|
|
summary = (
|
|
f"High score: {state['high_score']} | Last score: {state['last_score']} "
|
|
f"| Best wave: {state['best_wave']}"
|
|
)
|
|
return {
|
|
"ui": {"summary": [summary]},
|
|
"result": (state["high_score"], state["last_score"], summary),
|
|
}
|