Steam achievements! (:

This commit is contained in:
Harmony Honey 2022-05-14 19:46:06 -04:00
parent 70bb0c0efb
commit 432d5e3e5a
33 changed files with 991 additions and 3 deletions

22
addons/steam_api/LICENSE Normal file
View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2021 Sam Murray
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,27 @@
extends Node2D
func _ready():
print(Steam.is_init())
print(Steam.user.get_steam_id().get_account_id())
Steam.clear_achievement("gator_god")
Steam.set_achievement("gator_god")
Steam.set_leaderboard_score("High Score", 982, Steam.LeaderboardUploadScoreMethod.KeepBest, PoolIntArray([5, 5, 3]))
for i in Steam.user_stats.get_num_achievements():
print(Steam.user_stats.get_achievement_name(i))
var scores = yield(Steam.get_leaderboard_scores("High Score", 0, 10, Steam.LeaderboardDataRequest.Global, 3), "done")
print(scores)
Steam.friends.connect("game_overlay_activated", self, "_on_game_overlay_activated")
Steam.friends.activate_game_overlay_to_web_page("https://steamcommunity.com/")
Steam.friends.activate_game_overlay_to_store(1435470, Steam.OverlayToStoreFlag.AddToCart)
Steam.friends.set_rich_presence("steam_display", "cactus")
func _on_game_overlay_activated(active:bool) -> void:
$game_overlay_activated.text = str(active)

View file

@ -0,0 +1,16 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/examples/example.gd" type="Script" id=1]
[node name="Node2D" type="Node2D"]
script = ExtResource( 1 )
[node name="game_overlay_activated" type="Label" parent="."]
margin_left = 362.0
margin_top = 231.0
margin_right = 402.0
margin_bottom = 245.0
text = "0"
__meta__ = {
"_edit_use_anchors_": false
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,7 @@
[plugin]
name="SteamAPI"
description=""
author="samsface"
version=""
script="plugin.gd"

View file

@ -0,0 +1,26 @@
tool
extends EditorPlugin
var setup:Control
func _enter_tree():
setup = load("res://addons/steam_api/setup.tscn").instance()
add_control_to_container(CONTAINER_PROJECT_SETTING_TAB_RIGHT, setup)
setup.connect("disable", self, "_on_disabled")
_on_disabled(setup.settings.disable)
func _on_disabled(value:bool) -> void:
remove_autoload_singleton("Steam")
if value:
print("disable steam integration")
add_autoload_singleton("Steam", "res://addons/steam_api/steam_i.gd")
else:
print("enable steam integration")
add_autoload_singleton("Steam", "res://addons/steam_api/steam.gd")
func disable_plugin():
remove_autoload_singleton("Steam")
remove_control_from_container(CONTAINER_PROJECT_SETTING_TAB_RIGHT, setup)
setup.queue_free()

View file

@ -0,0 +1,65 @@
# 🚂 Godot-Steam-API
> **Godot Steam integration without rebuilding Godot.** Supports 💰Windows, 🐧Linux & 🍏MacOS (x86_x64/arm64).
## 🏁 Getting Started
1. Download this plugin through Godot's asset library https://godotengine.org/asset-library/asset/1020
2. There should now be a SteamAPI tab in your Project Settings, follow the instructions to configure the SteamAPI for your game *(Takes a few seconds)*.
Now you can use the following functions:
```gdscript
# check steam integration is working and enabled, useful if you publish to multiple stores
Steam.is_init()
# achievements
# check if player already unlocked this achievement
var has_alredy_unlocked_achievement:bool = Steam.get_achievement("gator_god")
# give the player this achievement, benign if they already have it
Steam.set_achievement("gator_god")
# clear this achievement (only for debugging)
Steam.clear_achievement("gator_god")
# leaderboards
Steam.set_leaderboard_score("High Scores", 1000)
# Get the first 10 global high scores
var top_10_global_scores = yield(Steam.get_leaderboard_scores("High Scores", 0, 10), "done")
# Get just the current user's high score
var players_score = yield(Steam.get_leaderboard_scores("High Scores", 0, 0, Steam.LeaderboardDataRequest.GlobalAroundUser), "done")
# Get the current user's high score and the two scores infront and behind
var player_rivals_score = yield(Steam.get_leaderboard_scores("High Scores", -1, 1, Steam.LeaderboardDataRequest.GlobalAroundUser), "done")
# rich presence
# Read the docs: https://partner.steamgames.com/doc/features/enhancedrichpresence as this call has lots of hidden magic
# and use this tool to test: https://steamcommunity.com/dev/testrichpresence
Steam.friends.set_rich_presence("status", "cactus")
Steam.friends.clear_rich_presence()
# overlay
Steam.friends.connect("game_overlay_activated", self, "_on_game_overlay_activated")
Steam.friends.activate_game_overlay_to_web_page("https://steamcommunity.com/")
Steam.friends.activate_game_overlay_to_store(1435470, Steam.OverlayToStoreFlag.AddToCart)
```
## 📄 Documentation
This plugin follows the structure and naming of the offical C++ SteamSDK almost verbatim.
The only major difference is I use snake_case to better integrate with Godot.
That is to say, you can just rely on the offical docs: https://partner.steamgames.com/doc/api.
## 🧪 Testing an exported build
Steam integration doesn't work with exported builds straight away. You'll have to either:
- Upload your build to Steam or
- Create a file in the same directory as your exported build named `steam_appid.txt` with just your steam app id in it. Though do not upload this file as part of your build.
## 😶‍🌫️ Can I publish to Itch.io still?
Yes. There's a switch in `Project Settings > Steam API` that disables the integration, i.e. all calls on the API just do nothing and return/yield null. However it is up to the caller to then safely deal with the null values returned by the API in disabled mode.
## 🤔 Troubleshooting
- Did you follow the instruction in the project settings Steam tab?
- Is Steam running?
- Have you published your acheivments in the SteamWorks console? *Publishing isn't releasing the game, I mean the button that publishes your Store page.*
- Tried uninstalling and reinstalling the plugin 😅?

View file

@ -0,0 +1,5 @@
extends Resource
export(bool) var disable:bool
export(String) var app_id:String
export(String) var sdk_dir:String

View file

@ -0,0 +1,9 @@
[gd_resource type="Resource" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/settings.gd" type="Script" id=1]
[resource]
script = ExtResource( 1 )
disable = false
app_id = "1993830"
sdk_dir = "C:\\Apps\\steamworks_sdk_153a\\sdk"

95
addons/steam_api/setup.gd Normal file
View file

@ -0,0 +1,95 @@
tool
extends Control
signal disable
var settings
var settings_path = "res://addons/steam_api/settings.tres"
func _ready():
settings = load(settings_path)
$panel/container/app_id/input.text = settings.app_id
$panel/container/sdk_dir/input.text = settings.sdk_dir
func copy_file_from_steam_sdk_(base_path:String, file_name:String) -> void:
print(settings.sdk_dir + base_path + file_name)
if not File.new().file_exists("res://addons/steam_api/%s" % file_name):
var res := Directory.new().copy(settings.sdk_dir + base_path + file_name, "res://addons/steam_api/%s" % file_name)
if res != OK:
popup_error("Failed to copy %s. Steam integration will not work. Did you set the 'SDK Directory' correctly?" % file_name)
static func exe_path_() -> String:
return OS.get_executable_path().get_base_dir()
func write_steam_app_id_file_() -> void:
var f := File.new()
var res := f.open(exe_path_() + "/steam_appid.txt", File.WRITE)
if res == OK:
f.store_string(str(settings.app_id))
f.close()
else:
popup_error("Failed to write steamapp_id.txt to \"{exe_path}\". Steam integration will not work. For debug builds, steam needs a text file with just your steam_app_id named 'steam_appid.txt' next to your exe in '{exe_path}'. Try write the file manually.".format({"exe_path": exe_path_()}))
func steam_appid_file_has_correct_id_() -> bool:
var file := File.new()
var res := file.open(exe_path_() + "/steam_appid.txt", File.READ)
if res == OK:
return str(settings.app_id) == file.get_line()
else:
return false
func debug_setup_() -> void:
copy_file_from_steam_sdk_("/redistributable_bin/linux64/", "libsteam_api.so")
copy_file_from_steam_sdk_("/redistributable_bin/win64/", "steam_api64.dll")
copy_file_from_steam_sdk_("/redistributable_bin/osx/", "libsteam_api.dylib")
if not steam_appid_file_has_correct_id_():
write_steam_app_id_file_()
func _on_SDK_instructions_meta_clicked(meta):
if meta == "open_sdk_page":
OS.shell_open("https://partner.steamgames.com/downloads/steamworks_sdk.zip")
func popup_error(message):
$panel/warning.popup_centered_clamped(Vector2(400, 100))
$panel/warning/container/error_text.text = message
push_error(message)
func _on_FindSDK_pressed():
_ready()
debug_setup_()
check_status()
func _on_SteamAPI_visibility_changed():
check_status()
func check_status():
var file = File.new()
var status = "[color=green]Done![/color]"
var missing = []
if not file.file_exists("res://addons/steam_api/libsteam_api.so"): missing.push_back("linux")
if not file.file_exists("res://addons/steam_api/steam_api64.dll"): missing.push_back("windows")
if not file.file_exists("res://addons/steam_api/libsteam_api.dylib"): missing.push_back("osx")
if missing.size() > 0:
if missing.size() == 3:
status = "[color=red]No files imported[/color]"
else:
status = "[color=#ffa500]Partially complete, missing: %s[/color]" % String(missing)
status = "[center]%s[/center]" % status
$panel/container/sdk_status/status.bbcode_text = status
func _on_app_id_focus_exited():
save_settings()
func _on_sdk_dir_focus_exited():
save_settings()
func save_settings():
settings.app_id = $panel/container/app_id/input.text
settings.sdk_dir = $panel/container/sdk_dir/input.text
ResourceSaver.save(settings_path, settings)
func _on_disable_pressed():
settings.disable = $panel/container/disable/input.pressed
save_settings()
emit_signal("disable", settings.disable)

213
addons/steam_api/setup.tscn Normal file
View file

@ -0,0 +1,213 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/setup.gd" type="Script" id=1]
[node name="SteamAPI" type="ScrollContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="panel" type="MarginContainer" parent="."]
margin_right = 1024.0
margin_bottom = 600.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Label" type="Label" parent="panel"]
margin_top = 293.0
margin_right = 1024.0
margin_bottom = 307.0
[node name="container" type="VBoxContainer" parent="panel"]
margin_right = 1024.0
margin_bottom = 600.0
[node name="SDK instructions" type="RichTextLabel" parent="panel/container"]
margin_right = 1024.0
margin_bottom = 135.0
rect_clip_content = false
bbcode_enabled = true
bbcode_text = "[color=red]This plugin requires the Steam SDK, you must already be a Steam partner to obtain the SDK (Valve's rules, not mine sorry).[/color]
[b]Setup:[/b]
1. Enter your Steam App ID [color=gray](the number next to your Steam title on the Steamworks page or the number in the store page url)[/color].
2. Download the Steam SDK at [url=open_sdk_page]https://partner.steamgames.com/downloads/steamworks_sdk.zip[/url].
3. Unzip the Steam SDK into any directory on your computer [color=gray](you will not need the SDK once setup is complete)[/color].
4. Enter the directory path where you unzipped the Steam SDK [color=gray](e.g. \"C:\\Users\\gaben\\Downloads\\sdk\")[/color].
5. Click \"Import from SDK Directory\".
"
text = "This plugin requires the Steam SDK, you must already be a Steam partner to obtain the SDK (Valve's rules, not mine sorry).
Setup:
1. Enter your Steam App ID (the number next to your Steam title on the Steamworks page or the number in the store page url).
2. Download the Steam SDK at https://partner.steamgames.com/downloads/steamworks_sdk.zip.
3. Unzip the Steam SDK into any directory on your computer (you will not need the SDK once setup is complete).
4. Enter the directory path where you unzipped the Steam SDK (e.g. \"C:\\Users\\gaben\\Downloads\\sdk\").
5. Click \"Import from SDK Directory\".
"
fit_content_height = true
scroll_active = false
[node name="disable" type="HBoxContainer" parent="panel/container"]
margin_top = 139.0
margin_right = 1024.0
margin_bottom = 163.0
size_flags_horizontal = 3
[node name="title" type="Label" parent="panel/container/disable"]
margin_right = 510.0
margin_bottom = 24.0
rect_min_size = Vector2( 0, 24 )
size_flags_horizontal = 3
text = "Disable Steam Integration (all Steam calls will return or yield null)"
valign = 1
[node name="input" type="CheckBox" parent="panel/container/disable"]
margin_left = 514.0
margin_right = 1024.0
margin_bottom = 24.0
size_flags_horizontal = 3
[node name="app_id" type="HBoxContainer" parent="panel/container"]
margin_top = 167.0
margin_right = 1024.0
margin_bottom = 191.0
size_flags_horizontal = 3
[node name="title" type="Label" parent="panel/container/app_id"]
margin_right = 510.0
margin_bottom = 24.0
rect_min_size = Vector2( 0, 24 )
size_flags_horizontal = 3
text = "Steam App ID (must be a valid id in your steam library)"
valign = 1
[node name="input" type="LineEdit" parent="panel/container/app_id"]
margin_left = 514.0
margin_right = 1024.0
margin_bottom = 24.0
size_flags_horizontal = 3
text = "1435470"
placeholder_text = "1435470"
caret_blink = true
caret_blink_speed = 0.5
[node name="sdk_dir" type="HBoxContainer" parent="panel/container"]
margin_top = 195.0
margin_right = 1024.0
margin_bottom = 219.0
size_flags_horizontal = 3
[node name="title" type="Label" parent="panel/container/sdk_dir"]
margin_right = 510.0
margin_bottom = 24.0
rect_min_size = Vector2( 0, 24 )
size_flags_horizontal = 3
text = "SDK Directory"
valign = 1
[node name="input" type="LineEdit" parent="panel/container/sdk_dir"]
margin_left = 514.0
margin_right = 1024.0
margin_bottom = 24.0
size_flags_horizontal = 3
text = "/home/sam/work/godot-steam-api/lib/steam-sdk"
placeholder_text = "C:\\Users\\gaben\\Downloads\\sdk"
caret_blink = true
caret_blink_speed = 0.5
[node name="HSeparator" type="HSeparator" parent="panel/container"]
margin_top = 223.0
margin_right = 1024.0
margin_bottom = 227.0
[node name="sdk_status" type="HBoxContainer" parent="panel/container"]
margin_top = 231.0
margin_right = 1024.0
margin_bottom = 255.0
[node name="FindSDK" type="Button" parent="panel/container/sdk_status"]
margin_right = 482.0
margin_bottom = 24.0
size_flags_horizontal = 3
text = "Import from SDK Directory"
[node name="VSeparator" type="VSeparator" parent="panel/container/sdk_status"]
margin_left = 486.0
margin_right = 490.0
margin_bottom = 24.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="title" type="Label" parent="panel/container/sdk_status"]
margin_left = 494.0
margin_right = 537.0
margin_bottom = 24.0
rect_min_size = Vector2( 0, 24 )
text = "Status:"
valign = 1
[node name="status" type="RichTextLabel" parent="panel/container/sdk_status"]
margin_left = 541.0
margin_right = 1024.0
margin_bottom = 24.0
rect_min_size = Vector2( 0, 24 )
size_flags_horizontal = 3
bbcode_enabled = true
bbcode_text = "[center][color=red]No files imported[/color][/center]"
text = "No files imported"
fit_content_height = true
scroll_active = false
[node name="warning" type="PopupDialog" parent="panel"]
margin_right = 1024.0
margin_bottom = 600.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="container" type="VBoxContainer" parent="panel/warning"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="RichTextLabel" parent="panel/warning/container"]
margin_right = 1024.0
margin_bottom = 15.0
size_flags_horizontal = 3
size_flags_vertical = 4
bbcode_enabled = true
bbcode_text = "[center][b]Error[/b][/center]"
text = "Error"
fit_content_height = true
scroll_active = false
__meta__ = {
"_edit_use_anchors_": false
}
[node name="error_text" type="Label" parent="panel/warning/container"]
margin_top = 19.0
margin_right = 1024.0
margin_bottom = 600.0
size_flags_horizontal = 3
size_flags_vertical = 3
align = 1
valign = 1
autowrap = true
__meta__ = {
"_edit_use_anchors_": false
}
[connection signal="visibility_changed" from="." to="." method="_on_SteamAPI_visibility_changed"]
[connection signal="meta_clicked" from="panel/container/SDK instructions" to="." method="_on_SDK_instructions_meta_clicked"]
[connection signal="pressed" from="panel/container/disable/input" to="." method="_on_disable_pressed"]
[connection signal="focus_exited" from="panel/container/app_id/input" to="." method="_on_app_id_focus_exited"]
[connection signal="focus_exited" from="panel/container/sdk_dir/input" to="." method="_on_sdk_dir_focus_exited"]
[connection signal="pressed" from="panel/container/sdk_status/FindSDK" to="." method="_on_FindSDK_pressed"]

22
addons/steam_api/steam.gd Normal file
View file

@ -0,0 +1,22 @@
extends SteamI
func _ready() -> void:
var settings = load("res://addons/steam_api/settings.tres")
api_ = SteamAPI.new()
if not api_ or not api_.init():
api_ = null
push_error("Failed to init steam api. Is Steam running? Is the plugin activated? Did you do the setup in ProjectSettings > SteamAPI")
if OS.has_feature("standalone"):
push_error("You're running an exported build. Steam integration will only work if the game is launched by Steam. Either upload this build to Steam or create a file in the same directory as your exported build named 'steamapp_id.txt' with just your steam app id in it.")
return
user = User.new(SteamUser.new())
user_stats = UserStats.new(SteamUserStats.new())
user_stats.request_current_stats()
friends = Friends.new(SteamFriends.new())
#warning-ignore:unused_argument
func _process(delta:float) -> void:
if api_:
api_.run_callbacks()

View file

@ -0,0 +1,20 @@
[general]
singleton=false
load_once=true
symbol_prefix="godot_"
reloadable=true
[entry]
OSX.64="res://addons/steam_api/libsteamsdk-godot.dylib"
Windows.64="res://addons/steam_api/steamsdk-godot.dll"
X11.64="res://addons/steam_api/libsteamsdk-godot.so"
Server="res://addons/steam_api/libsteamsdk-godot.so"
[dependencies]
OSX.64=[ "res://addons/steam_api/libsteam_api.dylib" ]
Windows.64=[ "res://addons/steam_api/steam_api64.dll" ]
X11.64=[ "res://addons/steam_api/libsteam_api.so" ]
Server=[ "res://addons/steam_api/libsteam_api.so" ]

View file

@ -0,0 +1,9 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_api"
class_name = "SteamAPI"
library = ExtResource( 1 )
script_class_name = "SteamAPI"

Binary file not shown.

View file

@ -0,0 +1,9 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_callback"
class_name = "SteamCallback"
library = ExtResource( 1 )
script_class_name = "SteamCallback"

View file

@ -0,0 +1,9 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_find_leaderboard_result"
class_name = "SteamLeaderboardFindResult"
library = ExtResource( 1 )
script_class_name = "SteamLeaderboardFindResult"

View file

@ -0,0 +1,10 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_friends"
class_name = "SteamFriends"
library = ExtResource( 1 )
script_class_name = "SteamFriends"

230
addons/steam_api/steam_i.gd Normal file
View file

@ -0,0 +1,230 @@
extends Node
class_name SteamI
signal leaderboard_scores
enum LeaderboardUploadScoreMethod {
None = 0,
KeepBest = 1,
ForceUpdate = 2,
}
enum LeaderboardDataRequest {
Global = 0,
GlobalAroundUser = 1,
Friends = 2,
Users = 3
}
enum OverlayToStoreFlag {
None = 0,
AddToCart = 1,
AddToCartAndShow = 2
}
enum LeaderboardSortMethod {
None = 0,
Ascending = 1,
Descending = 2
}
enum LeaderboardDisplayType {
None = 0,
Numeric = 1,
TimeSeconds = 2,
TimeMilliSeconds = 3
}
class Callback:
signal done
class Proxy_:
signal keep_alive_hack_
var object_to_proxy_
func _init(object_to_proxy) -> void:
object_to_proxy_ = object_to_proxy
func call_(func_name, args := []):
if not object_to_proxy_:
return
return object_to_proxy_.callv(func_name, args)
func callback_(func_name, args := []):
if not object_to_proxy_:
var result = Callback.new()
result.call_deferred("emit_signal", "done", null)
call_deferred("emit_signal", "keep_alive_hack_", result)
return result
return object_to_proxy_.callv(func_name, args)
class User extends Proxy_:
func _init(o).(o) -> void:
pass
func get_steam_id() -> SteamId:
return call_("get_steam_id")
class UserStats extends Proxy_:
func _init(o).(o) -> void:
pass
func set_achievement(achievement_api_name:String) -> bool:
return call_("set_achievement", [achievement_api_name])
func clear_achievement(achievement_api_name:String) -> bool:
return call_("clear_achievement", [achievement_api_name])
func get_achievement(achievement_api_name:String) -> bool:
return call_("get_achievement", [achievement_api_name])
func get_num_achievements() -> int:
return call_("get_num_achievements")
func get_achievement_name(idx:int) -> String:
return call_("get_achievement_name", [idx])
func request_current_stats() -> bool:
return call_("request_current_stats")
func store_stats() -> bool:
return call_("store_stats")
func find_leaderboard(leaderboard_name:String):
return callback_("find_leaderboard", [leaderboard_name])
func find_or_create_leaderboard(leaderboard_name:String, sort_method:int, display_type:int):
return callback_("find_or_create_leaderboard", [leaderboard_name, sort_method, display_type])
func upload_leaderboard_score(leaderboard, method:int, score:int, details:PoolIntArray):
return callback_("upload_leaderboard_score", [leaderboard, method, score, details])
func download_leaderboard_entries(leaderboard, data_request:int, begin:int, end:int):
return callback_("download_leaderboard_entries", [leaderboard, data_request, begin, end])
func get_downloaded_leaderboard_entry(leaderboard, index:int, max_details:int):
return callback_("get_downloaded_leaderboard_entry", [leaderboard, index, max_details])
class Friends extends Proxy_:
signal game_overlay_activated
func _init(o).(o) -> void:
if o:
o.connect("game_overlay_activated", self, "_on_game_overlay_activated")
func _on_game_overlay_activated(active:bool) -> void:
emit_signal("game_overlay_activated", active)
func request_user_information(steam_user_id, require_name_only:bool) -> bool:
return call_("request_user_information", [steam_user_id, require_name_only])
func get_friend_persona_name(steam_user_id) -> String:
return call_("get_friend_persona_name", [steam_user_id])
func activate_game_overlay_to_web_page(url:String) -> void:
call_("activate_game_overlay_to_web_page", [url])
func activate_game_overlay_to_store(app_id:int, where:int) -> void:
call_("activate_game_overlay_to_store", [app_id, where])
func set_rich_presence(key:String, value:String) -> bool:
return call_("set_rich_presence", [key, value])
func clear_rich_presence() -> void:
call_("clear_rich_presence")
var api_
var user:User = User.new(null)
var user_stats:UserStats = UserStats.new(null)
var friends:Friends = Friends.new(null)
func is_init() -> bool:
if api_:
return api_.is_init()
return false
func set_achievement(name:String) -> void:
user_stats.set_achievement(name)
user_stats.store_stats()
func get_achievement(name:String) -> bool:
return user_stats.get_achievement(name)
func clear_achievement(name:String) -> void:
user_stats.clear_achievement(name)
user_stats.store_stats()
func set_leaderboard_score(leaderboard_name:String, score:int, method:int = LeaderboardUploadScoreMethod.KeepBest, details:PoolIntArray = PoolIntArray()) -> void:
var find_leaderboard_result = yield(user_stats.find_leaderboard(leaderboard_name), "done")
if not find_leaderboard_result:
return
if not find_leaderboard_result.get_leaderboard_found():
return
var leaderboard = find_leaderboard_result.get_leaderboard()
if not leaderboard:
return
yield(user_stats.upload_leaderboard_score(leaderboard, method, score, details), "done")
func get_leaderboard_scores(leaderboard_name:String, begin:int, end:int, method:int = LeaderboardDataRequest.Global, max_details:int = 0):
var callback := Callback.new()
get_leaderboard_scores_(leaderboard_name, begin, end, method, max_details, callback)
return callback
func get_leaderboard_scores_(leaderboard_name:String, begin:int, end:int, method:int, max_details, callback:Callback) -> Callback:
var res := []
var user_stats_ = user_stats.object_to_proxy_
var friends_ = friends.object_to_proxy_
if not user_stats_:
return callback.emit_signal("done", res)
if not friends_:
return callback.emit_signal("done", res)
var find_leaderboard_result = yield(user_stats_.find_leaderboard(leaderboard_name), "done")
if not find_leaderboard_result.get_leaderboard_found():
return callback.emit_signal("done", res)
var leaderboard = find_leaderboard_result.get_leaderboard()
if not leaderboard:
return callback.emit_signal("done", res)
var download = yield(user_stats_.download_leaderboard_entries(leaderboard, method, begin, end), "done")
if not download:
return callback.emit_signal("done", res)
var entries = download.get_entries()
if not entries:
return callback.emit_signal("done", res)
for i in download.get_entry_count():
var entry = user_stats_.get_downloaded_leaderboard_entry(entries, i, max_details)
if not entry:
continue
var score := {}
score["global_rank"] = entry.get_global_rank()
score["score"] = entry.get_score()
score["details"] = entry.get_details()
if not friends_.request_user_information(entry.get_steam_id_user(), true):
score["persona_name"] = friends_.get_friend_persona_name(entry.get_steam_id_user())
else:
score["persona_name"] = "Frank"
res.push_back(score)
return callback.emit_signal("done", res)
func activate_game_overlay_to_web_page(url:String) -> void:
friends.activate_game_overlay_to_web_page(url)
func activate_game_overlay_to_store(app_id:int, where:int = 0) -> void:
friends.activate_game_overlay_to_store(app_id, where)

View file

@ -0,0 +1,10 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_id"
class_name = "SteamId"
library = ExtResource( 1 )
script_class_name = "SteamId"

View file

@ -0,0 +1,10 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_leaderboard"
class_name = "SteamLeaderboard"
library = ExtResource( 1 )
script_class_name = "SteamLeaderboard"

View file

@ -0,0 +1,10 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_leaderboard_entries"
class_name = "SteamLeaderboardEntries"
library = ExtResource( 1 )
script_class_name = "SteamLeaderboardEntries"

View file

@ -0,0 +1,10 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_leaderboard_entry"
class_name = "SteamLeaderboardEntry"
library = ExtResource( 1 )
script_class_name = "SteamLeaderboardEntry"

View file

@ -0,0 +1,9 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_leaderboard_scores_downloaded"
class_name = "SteamLeaderboardScoresDownloaded"
library = ExtResource( 1 )
script_class_name = "SteamLeaderboardScoresDownloaded"

View file

@ -0,0 +1,9 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_leaderboard_score_uploaded"
class_name = "SteamLeaderboardScoreUploaded"
library = ExtResource( 1 )
script_class_name = "SteamLeaderboardScoreUploaded"

View file

@ -0,0 +1,10 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_user"
class_name = "SteamUser"
library = ExtResource( 1 )
script_class_name = "SteamUser"

View file

@ -0,0 +1,9 @@
[gd_resource type="NativeScript" load_steps=2 format=2]
[ext_resource path="res://addons/steam_api/steam_api.gdnlib" type="GDNativeLibrary" id=1]
[resource]
resource_name = "steam_user_stats"
class_name = "SteamUserStats"
library = ExtResource( 1 )
script_class_name = "SteamUserStats"

Binary file not shown.

View file

@ -48,6 +48,71 @@ _global_script_classes=[ {
"class": "SaveDict",
"language": "GDScript",
"path": "res://src/class/SaveDict.gd"
}, {
"base": "",
"class": "SteamAPI",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_api.gdns"
}, {
"base": "",
"class": "SteamCallback",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_callback.gdns"
}, {
"base": "",
"class": "SteamFriends",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_friends.gdns"
}, {
"base": "Node",
"class": "SteamI",
"language": "GDScript",
"path": "res://addons/steam_api/steam_i.gd"
}, {
"base": "",
"class": "SteamId",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_id.gdns"
}, {
"base": "",
"class": "SteamLeaderboard",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_leaderboard.gdns"
}, {
"base": "",
"class": "SteamLeaderboardEntries",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_leaderboard_entries.gdns"
}, {
"base": "",
"class": "SteamLeaderboardEntry",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_leaderboard_entry.gdns"
}, {
"base": "",
"class": "SteamLeaderboardFindResult",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_find_leaderboard_result.gdns"
}, {
"base": "",
"class": "SteamLeaderboardScoreUploaded",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_leaderboard_uploaded.gdns"
}, {
"base": "",
"class": "SteamLeaderboardScoresDownloaded",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_leaderboard_scores_downloaded.gdns"
}, {
"base": "",
"class": "SteamUser",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_user.gdns"
}, {
"base": "",
"class": "SteamUserStats",
"language": "NativeScript",
"path": "res://addons/steam_api/steam_user_stats.gdns"
} ]
_global_script_class_icons={
"Box": "",
@ -57,7 +122,20 @@ _global_script_class_icons={
"OnOff": "",
"PaletteSwap": "",
"Player": "",
"SaveDict": ""
"SaveDict": "",
"SteamAPI": "",
"SteamCallback": "",
"SteamFriends": "",
"SteamI": "",
"SteamId": "",
"SteamLeaderboard": "",
"SteamLeaderboardEntries": "",
"SteamLeaderboardEntry": "",
"SteamLeaderboardFindResult": "",
"SteamLeaderboardScoreUploaded": "",
"SteamLeaderboardScoresDownloaded": "",
"SteamUser": "",
"SteamUserStats": ""
}
[application]
@ -89,6 +167,7 @@ MenuOptions="*res://src/menu/options/MenuOptions.tscn"
MenuRemap="*res://src/menu/options/MenuRemap.tscn"
Music="*res://src/autoload/Music.tscn"
Audio="*res://src/autoload/Audio.tscn"
Steam="*res://addons/steam_api/steam.gd"
[display]
@ -98,6 +177,10 @@ window/size/fullscreen=true
window/stretch/mode="2d"
window/stretch/aspect="keep"
[editor_plugins]
enabled=PoolStringArray( "res://addons/steam_api/plugin.cfg" )
[input]
ui_accept={

View file

@ -130,10 +130,9 @@ func change_scene():
Cam.reset_zoom()
BG.set_colors(0)
emit_signal("scene_changed")
save_data()
try_achievement()
func toggle_fullscreen():
OS.window_fullscreen = !OS.window_fullscreen
@ -396,3 +395,38 @@ func load_keys(path := "user://keys.tres"):
for e in r.dict[a]:
InputMap.action_add_event(a, e)
### Steam ###
func try_achievement():
if !Steam.is_init(): return
var map := ""
match csfn:
"res://src/map/worlds/1/0_hub.tscn":
map = "grass1"
"res://src/map/worlds/2/0_hub.tscn":
map = "stone1"
"res://src/map/worlds/3/0_hub.tscn":
map = "cacti1"
"res://src/map/worlds/2A/0_hub.tscn":
map = "snow1"
"res://src/map/worlds/3A/0_hub.tscn":
map = "grass2"
"res://src/map/worlds/2B/0_hub.tscn":
map = "stone2"
"res://src/map/worlds/3B/0_hub.tscn":
map = "cacti2"
"res://src/map/worlds/2C/0_hub.tscn":
map = "snow2"
if map != "":
Steam.set_achievement(map)
if gem_count > 49:
Steam.set_achievement("gem50")
if csfn == "res://src/menu/Ending.tscn" and save_time < 3600:
Steam.set_achievement("speedrun")