extends Node ## indiedev.games game events and leaderboards for Godot 4. ## ## Add this script in Project Settings > Globals (Autoload) with the name IndieDev, then call it anywhere: ## ## IndieDev.event("level_complete") ## IndieDev.event("level_time", 83.5) # an optional number, such as a score or a time in seconds ## ## Leaderboards (beta). Set the board up first on your Game Analytics page, then: ## ## IndieDev.submit_score("high_scores", "AVA", 1200) # the answer arrives in leaderboard_loaded ## IndieDev.load_leaderboard("high_scores", 10) ## ## func _ready(): ## IndieDev.leaderboard_loaded.connect(_on_leaderboard) ## ## func _on_leaderboard(board: String, result: Dictionary): ## # result.ok, result.rank (after a submit), result.entries: [{"rank": 1, "name": "AVA", "score": 1200}] ## pass ## ## It talks to the indiedev.games page the game runs in, and does nothing outside a web export. ## Only the name and the number are sent. See https://indiedev.games/help/game-events-godot-defold ## and https://indiedev.games/help/game-leaderboards ## Emitted when the answer to submit_score or load_leaderboard arrives, or when it failed (result.ok is false). signal leaderboard_loaded(board: String, result: Dictionary) var _next_id := 1 var _reply_callback = null func _ready() -> void: if not OS.has_feature("web"): return # Keep a reference to the callback, or Godot frees it and the page cannot call it. _reply_callback = JavaScriptBridge.create_callback(_on_reply) JavaScriptBridge.get_interface("window").indiedevReply = _reply_callback JavaScriptBridge.eval("window.addEventListener('message', function (e) { var d = e.data; if (d && d.indiedev === 1 && d.type === 'scores') { window.indiedevReply(JSON.stringify(d)); } });") func event(event_name: String, value = null) -> void: if not OS.has_feature("web"): return var message := {"indiedev": 1, "type": "event", "name": event_name} var is_number: bool = typeof(value) == TYPE_INT or (typeof(value) == TYPE_FLOAT and is_finite(value)) if is_number: message["value"] = value JavaScriptBridge.eval("window.parent.postMessage(" + JSON.stringify(message) + ", '*')") ## The player's name is shown to everyone: 1 to 12 letters, digits, spaces, dashes or underscores. Never use a real name. func submit_score(board: String, player_name: String, score: float) -> void: _ask({"type": "score", "board": board, "name": player_name, "score": score}) func load_leaderboard(board: String, limit: int = 10) -> void: _ask({"type": "leaderboard", "board": board, "limit": limit}) func _ask(message: Dictionary) -> void: if not OS.has_feature("web"): leaderboard_loaded.emit(str(message.get("board", "")), {"ok": false, "error": "not_web", "entries": []}) return message["indiedev"] = 1 message["id"] = _next_id _next_id += 1 JavaScriptBridge.eval("window.parent.postMessage(" + JSON.stringify(message) + ", '*')") func _on_reply(args: Array) -> void: var result = JSON.parse_string(str(args[0])) if typeof(result) != TYPE_DICTIONARY: return leaderboard_loaded.emit(str(result.get("board", "")), result)