Add support for distinct sub-achievements.

This adds support for having up to 28 distinct sub-achievements within a single achievement. This limit exists since wesnoth's layout isn't smart enough to tell a horizontal listbox to actually use its scrollbar instead of forcing a horizontal scrollbar on the whole window.

Additionally this adds the [set_sub_achievement] and [has_sub_achievement] WML tags and their respective lua functions. [has_sub_achievement] is unsafe for use in MP, for the same reasons that [has_achievement] is.
This commit is contained in:
Pentarctagon 2023-05-05 13:25:44 -05:00 committed by Pentarctagon
parent 8fc34a9534
commit ad3f1c95b0
12 changed files with 437 additions and 114 deletions

View file

@ -3,6 +3,36 @@
### Definition of the window for showing achievements and their statuses
###
#define SUB_ACHIEVEMENT ID
[column]
grow_factor = 1
border = "all"
border_size = 2
[drawing]
id = "sub_icon{ID}"
definition = "default"
width = 36
height = 36
[draw]
[image]
name = "(text)"
w = "(min(image_original_width, 36))"
h = "(min(image_original_height, 36))"
{GUI_CENTERED_IMAGE}
[/image]
[/draw]
[/drawing]
[/column]
#enddef
[window]
id = "achievements_dialog"
description = "Dialog for displaying achievements and their statuses"
@ -177,6 +207,58 @@
[/progress_bar]
[/column]
[/row]
[row]
[column]
vertical_alignment = "center"
horizontal_alignment = "left"
[grid]
[row]
{SUB_ACHIEVEMENT 0}
{SUB_ACHIEVEMENT 1}
{SUB_ACHIEVEMENT 2}
{SUB_ACHIEVEMENT 3}
{SUB_ACHIEVEMENT 4}
{SUB_ACHIEVEMENT 5}
{SUB_ACHIEVEMENT 6}
{SUB_ACHIEVEMENT 7}
{SUB_ACHIEVEMENT 8}
{SUB_ACHIEVEMENT 9}
{SUB_ACHIEVEMENT 10}
{SUB_ACHIEVEMENT 11}
{SUB_ACHIEVEMENT 12}
{SUB_ACHIEVEMENT 13}
[/row]
[/grid]
[/column]
[/row]
[row]
[column]
vertical_alignment = "center"
horizontal_alignment = "left"
[grid]
[row]
{SUB_ACHIEVEMENT 14}
{SUB_ACHIEVEMENT 15}
{SUB_ACHIEVEMENT 16}
{SUB_ACHIEVEMENT 17}
{SUB_ACHIEVEMENT 18}
{SUB_ACHIEVEMENT 19}
{SUB_ACHIEVEMENT 20}
{SUB_ACHIEVEMENT 21}
{SUB_ACHIEVEMENT 22}
{SUB_ACHIEVEMENT 23}
{SUB_ACHIEVEMENT 24}
{SUB_ACHIEVEMENT 25}
{SUB_ACHIEVEMENT 26}
{SUB_ACHIEVEMENT 27}
[/row]
[/grid]
[/column]
[/row]
[/grid]
[/column]
[/row]
@ -235,3 +317,5 @@
[/resolution]
[/window]
#undef SUB_ACHIEVEMENT

View file

@ -42,3 +42,7 @@ end
function wesnoth.wml_conditionals.has_achievement(cfg)
return wesnoth.achievements.has(cfg.content_for, cfg.id);
end
function wesnoth.wml_conditionals.has_sub_achievement(cfg)
return wesnoth.achievements.has_sub_achievement(cfg.content_for, cfg.id, cfg.sub_id);
end

View file

@ -1016,6 +1016,10 @@ function wml_actions.set_achievement(cfg)
wesnoth.achievements.set(cfg.content_for, cfg.id)
end
function wml_actions.set_sub_achievement(cfg)
wesnoth.achievements.set_sub_achievement(cfg.content_for, cfg.id, cfg.sub_id)
end
function wml_actions.progress_achievement(cfg)
if not tonumber(cfg.amount) then
wml.error("[progress_achievement] amount attribute not a number for content '"..cfg.content_for.."' and achievement '"..cfg.id.."'")

View file

@ -21,6 +21,13 @@
{SIMPLE_KEY hidden bool}
{SIMPLE_KEY max_progress int}
{SIMPLE_KEY sound string}
[tag]
name="sub_achievement"
max="28"
{REQUIRED_KEY id string}
{REQUIRED_KEY description t_string}
{REQUIRED_KEY icon string}
[/tag]
[/tag]
[/tag]
[/tag]

View file

@ -25,6 +25,72 @@
static lg::log_domain log_config("config");
#define ERR_CONFIG LOG_STREAM(err, log_config)
// TODO: testing
sub_achievement::sub_achievement(const config& cfg, bool achieved)
: id_(cfg["id"].str())
, description_(cfg["description"].t_str())
, icon_(achieved ? cfg["icon"].str() : cfg["icon"].str()+"~GS()")
, achieved_(achieved)
{}
achievement::achievement(const config& cfg, const std::string& content_for, bool achieved, int progress)
: id_(cfg["id"].str())
, name_(cfg["name"].t_str())
, name_completed_(cfg["name_completed"].t_str())
, description_(cfg["description"].t_str())
, description_completed_(cfg["description_completed"].t_str())
, icon_(cfg["icon"].str()+"~GS()")
, icon_completed_(cfg["icon_completed"].str())
, hidden_(cfg["hidden"].to_bool())
, achieved_(achieved)
, max_progress_(cfg["max_progress"].to_int(0))
, current_progress_(progress)
, sound_path_(cfg["sound"].str())
, sub_achievements_()
{
if(name_completed_.empty()) {
name_completed_ = name_;
}
if(description_completed_.empty()) {
description_completed_ = description_;
}
if(icon_completed_.empty()) {
// avoid the ~GS() appended to icon_
icon_completed_ = cfg["icon"].str();
}
for(const config& sub_ach : cfg.child_range("sub_achievement"))
{
std::string sub_id = sub_ach["id"].str();
if(sub_id.empty()) {
ERR_CONFIG << "Achievement " << id_ << " has a sub-achievement missing the id attribute:\n" << sub_ach.debug();
} else {
sub_achievements_.emplace_back(sub_ach, achieved_ || preferences::sub_achievement(content_for, id_, sub_id));
max_progress_++;
}
}
}
achievement_group::achievement_group(const config& cfg)
: display_name_(cfg["display_name"].t_str())
, content_for_(cfg["content_for"].str())
, achievements_()
{
for(const config& ach : cfg.child_range("achievement")) {
std::string id = ach["id"].str();
if(id.empty()) {
ERR_CONFIG << content_for_ + " achievement missing id attribute:\n" << ach.debug();
} else if(id.find(',') != std::string::npos) {
ERR_CONFIG << content_for_ + " achievement id " << id << " contains a comma, skipping.";
continue;
} else {
achievements_.emplace_back(ach, content_for_, preferences::achievement(content_for_, id), preferences::progress_achievement(content_for_, id));
}
}
}
achievements::achievements()
: achievement_list_()
{
@ -96,22 +162,3 @@ void achievements::process_achievements_file(const config& cfg, const std::strin
achievement_list_.emplace_back(achgrp);
}
}
achievement_group::achievement_group(const config& cfg)
: display_name_(cfg["display_name"].t_str())
, content_for_(cfg["content_for"].str())
, achievements_()
{
for(const config& ach : cfg.child_range("achievement")) {
std::string id = ach["id"].str();
if(id.empty()) {
ERR_CONFIG << content_for_ + " achievement missing id attribute:\n" << ach.debug();
} else if(id.find(',') != std::string::npos) {
ERR_CONFIG << content_for_ + " achievement missing id " << id << " contains a comma, skipping.";
continue;
} else {
achievements_.emplace_back(ach, preferences::achievement(content_for_, id), preferences::progress_achievement(content_for_, id));
}
}
}

View file

@ -22,6 +22,24 @@
#include "config.hpp"
#include "tstring.hpp"
/**
* Represents a distinct sub-achievement within another achievement.
* This is intentionally a much simpler object than the regular achievements.
*/
struct sub_achievement
{
/** The ID of the sub-achievement. Must be unique per achievement */
std::string id_;
/** The description of the sub-achievement to be shown in its tooltip */
t_string description_;
/** The icon of the sub-achievement to show on the UI. */
std::string icon_;
/** Whether the sub-achievement has been completed. */
bool achieved_;
sub_achievement(const config& cfg, bool achieved);
};
/**
* Represents a single achievement and its data.
*/
@ -51,32 +69,10 @@ struct achievement
int current_progress_;
/** The path to a sound to play when an achievement is completed */
std::string sound_path_;
/** The list of distinct sub-achievements for this achievement */
std::vector<sub_achievement> sub_achievements_;
achievement(const config& cfg, bool achieved, int progress)
: id_(cfg["id"].str())
, name_(cfg["name"].t_str())
, name_completed_(cfg["name_completed"].t_str())
, description_(cfg["description"].t_str())
, description_completed_(cfg["description_completed"].t_str())
, icon_(cfg["icon"].str()+"~GS()")
, icon_completed_(cfg["icon_completed"].str())
, hidden_(cfg["hidden"].to_bool())
, achieved_(achieved)
, max_progress_(cfg["max_progress"].to_int(0))
, current_progress_(progress)
, sound_path_(cfg["sound"].str())
{
if(name_completed_.empty()) {
name_completed_ = name_;
}
if(description_completed_.empty()) {
description_completed_ = description_;
}
if(icon_completed_.empty()) {
// avoid the ~GS() appended to icon_
icon_completed_ = cfg["icon"].str();
}
}
achievement(const config& cfg, const std::string& content_for, bool achieved, int progress);
};
/**

View file

@ -21,12 +21,18 @@
#include "gettext.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/widgets/grid.hpp"
#include "gui/widgets/image.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/progress_bar.hpp"
#include "gui/widgets/window.hpp"
#include "log.hpp"
#include "preferences/general.hpp"
static lg::log_domain log_config("config");
#define ERR_CONFIG LOG_STREAM(err, log_config)
constexpr int sub_achievements_limit = 28;
namespace gui2::dialogs
{
@ -45,7 +51,7 @@ void achievements_dialog::pre_show(window& win)
{
std::vector<config> content_list;
content_names_ = &find_widget<menu_button>(&win, "selected_achievements_list", false);
connect_signal_notify_modified(*content_names_, std::bind(&achievements_dialog::set_achievements_content, this));
connect_signal_notify_modified(*content_names_, std::bind(&achievements_dialog::set_achievements_row, this));
achievements_box_ = find_widget<listbox>(&win, "achievements_list", false, true);
@ -56,64 +62,16 @@ void achievements_dialog::pre_show(window& win)
// only display the achievements for the first dropdown option on first showing the dialog
if(list.content_for_ == last_selected_ || last_selected_ == "") {
selected = content_list.size();
last_selected_ = list.content_for_;
int achieved_count = 0;
for(const auto& ach : list.achievements_) {
if(ach.achieved_) {
achieved_count++;
} else if(ach.hidden_ && !ach.achieved_) {
continue;
}
widget_data row;
widget_item item;
item["label"] = !ach.achieved_ ? ach.icon_ : ach.icon_completed_;
row.emplace("icon", item);
if(!ach.achieved_) {
t_string name = ach.name_;
if(ach.max_progress_ != 0 && ach.current_progress_ != -1) {
name += " ("+std::to_string(ach.current_progress_)+"/"+std::to_string(ach.max_progress_)+")";
}
item["label"] = name;
} else {
item["label"] = ach.name_completed_;
item["definition"] = "gold_large";
}
row.emplace("name", item);
if(!ach.achieved_) {
item["label"] = ach.description_;
} else {
item["label"] = "<span color='green'>"+ach.description_completed_+"</span>";
}
row.emplace("description", item);
grid& newrow = achievements_box_->add_row(row);
progress_bar* achievement_progress = static_cast<progress_bar*>(newrow.find("achievement_progress", false));
if(ach.max_progress_ != 0 && ach.current_progress_ != -1) {
achievement_progress->set_percentage((ach.current_progress_/double(ach.max_progress_))*100);
} else {
achievement_progress->set_visible(gui2::widget::visibility::invisible);
}
auto name = static_cast<label*>(newrow.find("name", false));
auto& canvas = name->get_canvas(0);
canvas.set_variable("achieved", wfl::variant(ach.achieved_));
}
label* achieved_label = find_widget<label>(&win, "achievement_count", false, true);
achieved_label->set_label(_("Completed")+" "+std::to_string(achieved_count)+"/"+std::to_string(list.achievements_.size()));
}
// populate all possibilities into the dropdown
content_list.emplace_back("label", list.display_name_);
}
if(content_list.size() > 0) {
content_names_->set_values(content_list);
content_names_->set_selected(selected, false);
set_achievements_row();
}
}
@ -122,13 +80,12 @@ void achievements_dialog::post_show(window&)
preferences::set_selected_achievement_group(last_selected_);
}
void achievements_dialog::set_achievements_content()
void achievements_dialog::set_achievements_row()
{
const achievement_group& list = game_config_manager::get()->get_achievements().at(content_names_->get_value());
achievements_box_->clear();
int achieved_count = 0;
achievement_group list = game_config_manager::get()->get_achievements().at(content_names_->get_value());
last_selected_ = list.content_for_;
int achieved_count = 0;
for(const auto& ach : list.achievements_) {
if(ach.achieved_) {
@ -144,7 +101,11 @@ void achievements_dialog::set_achievements_content()
row.emplace("icon", item);
if(!ach.achieved_) {
item["label"] = ach.name_;
t_string name = ach.name_;
if(ach.max_progress_ != 0 && ach.current_progress_ != -1) {
name += " ("+std::to_string(ach.current_progress_)+"/"+std::to_string(ach.max_progress_)+")";
}
item["label"] = name;
} else {
item["label"] = ach.name_completed_;
item["definition"] = "gold_large";
@ -166,13 +127,43 @@ void achievements_dialog::set_achievements_content()
achievement_progress->set_visible(gui2::widget::visibility::invisible);
}
auto name = static_cast<label*>(newrow.find("name", false));
auto& canvas = name->get_canvas(0);
label* name = static_cast<label*>(newrow.find("name", false));
canvas& canvas = name->get_canvas(0);
canvas.set_variable("achieved", wfl::variant(ach.achieved_));
set_sub_achievements(newrow, ach);
}
label* achieved_label = find_widget<label>(get_window(), "achievement_count", false, true);
achieved_label->set_label(_("Completed")+" "+std::to_string(achieved_count)+"/"+std::to_string(list.achievements_.size()));
}
void achievements_dialog::set_sub_achievements(grid& newrow, const achievement& ach)
{
int i = 0;
// set any sub achievements
for(const sub_achievement& sub_ach : ach.sub_achievements_)
{
if(i == sub_achievements_limit)
{
ERR_CONFIG << "Too many sub achievements";
break;
}
else
{
image* img = static_cast<image*>(newrow.find("sub_icon"+std::to_string(i), false));
img->set_label(sub_ach.icon_);
img->set_tooltip(sub_ach.description_);
}
i++;
}
// if an achievement hasn't defined the maximum possible sub-achievements, hide the [image]s for the rest
for(; i < sub_achievements_limit; i++)
{
static_cast<image*>(newrow.find("sub_icon"+std::to_string(i), false))->set_visible(visibility::invisible);
}
}
} // namespace gui2::dialogs

View file

@ -48,7 +48,9 @@ private:
listbox* achievements_box_;
menu_button* content_names_;
void set_achievements_content();
void set_sub_achievements(grid& newrow, const achievement& ach);
void set_achievements_row();
virtual const std::string& window_id() const override;

View file

@ -1129,4 +1129,82 @@ int progress_achievement(const std::string& content_for, const std::string& id,
return 0;
}
bool sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
{
// this achievement is already completed
if(achievement(content_for, id))
{
return true;
}
for(config& ach : prefs.child_range("achievements"))
{
if(ach["content_for"].str() == content_for)
{
// check if the specific sub-achievement has been completed but the overall achievement is not completed
auto in_progress = ach.optional_child("in_progress");
if(in_progress.has_value() && in_progress["id"] == id)
{
std::vector<std::string> sub_ids = utils::split(ach["sub_ids"]);
return std::find(sub_ids.begin(), sub_ids.end(), sub_id) != sub_ids.end();
}
}
}
return false;
}
void set_sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
{
// this achievement is already completed
if(achievement(content_for, id))
{
return;
}
for(config& ach : prefs.child_range("achievements"))
{
// if achievements already exist for this content and the achievement has not already been set, add it
if(ach["content_for"].str() == content_for)
{
// check if this achievement has had sub-achievements set before
for(config& in_progress : ach.child_range("in_progress"))
{
if(in_progress["id"].str() == id)
{
std::vector<std::string> sub_ids = utils::split(ach["ids"]);
if(std::find(sub_ids.begin(), sub_ids.end(), sub_id) == sub_ids.end())
{
ach["sub_ids"] = ach["sub_ids"].str() + "," + sub_id;
}
ach["progress_at"] = sub_ids.size()+1;
return;
}
}
// else if this is the first sub-achievement being set
config set_progress;
set_progress["id"] = id;
set_progress["sub_ids"] = sub_id;
set_progress["progress_at"] = 1;
ach.add_child("in_progress", set_progress);
}
}
// else not only has this achievement not had a sub-achievement completed before, this is the first achievement for this achievement group to be added
config ach;
config set_progress;
set_progress["id"] = id;
set_progress["sub_ids"] = sub_id;
set_progress["progress_at"] = 1;
ach["content_for"] = content_for;
ach["ids"] = "";
ach.add_child("in_progress", set_progress);
prefs.add_child("achievements", ach);
}
} // end namespace preferences

View file

@ -296,6 +296,8 @@ namespace preferences {
* If you only want to check the achievement's current progress, then omit the last three arguments.
* @a amount defaults to 0, which will result in the current progress value being returned without being changed (x + 0 == x).
*
* Note that this uses the same [in_progress] as is used for set_sub_achievement().
*
* @param content_for The id of the achievement group this achievement is in.
* @param id The id for the specific achievement in the achievement group.
* @param limit The maximum value that a specific call to this function can increase the achievement progress value.
@ -305,4 +307,23 @@ namespace preferences {
*/
int progress_achievement(const std::string& content_for, const std::string& id, int limit = 999999, int max_progress = 999999, int amount = 0);
/**
* @param content_for The achievement group the achievement is part of.
* @param id The ID of the achievement within the achievement group.
* @param sub_id The ID of the sub-achievement within the achievement.
* @return True if the sub-achievement exists and is completed, false otherwise.
*/
bool sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id);
/**
* Marks the specified sub-achievement as completed.
*
* Note that this uses the same [in_progress] as is used for progress_achievement().
*
* @param content_for The achievement group the achievement is part of.
* @param id The ID of the achievement within the achievement group.
* @param sub_id The ID of the sub-achievement within the achievement.
*/
void set_sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id);
} // end namespace preferences

View file

@ -3077,8 +3077,8 @@ int game_lua_kernel::intf_play_sound(lua_State *L)
*/
int game_lua_kernel::intf_set_achievement(lua_State *L)
{
const char *content_for = luaL_checkstring(L, 1);
const char *id = luaL_checkstring(L, 2);
const char* content_for = luaL_checkstring(L, 1);
const char* id = luaL_checkstring(L, 2);
for(achievement_group& group : game_config_manager::get()->get_achievements()) {
if(group.content_for_ == content_for) {
@ -3126,8 +3126,8 @@ int game_lua_kernel::intf_set_achievement(lua_State *L)
*/
int game_lua_kernel::intf_has_achievement(lua_State *L)
{
const char *content_for = luaL_checkstring(L, 1);
const char *id = luaL_checkstring(L, 2);
const char* content_for = luaL_checkstring(L, 1);
const char* id = luaL_checkstring(L, 2);
if(resources::controller->is_networked_mp() && synced_context::is_synced()) {
ERR_LUA << "Returning false for whether a player has completed an achievement due to being networked multiplayer.";
@ -3147,8 +3147,8 @@ int game_lua_kernel::intf_has_achievement(lua_State *L)
*/
int game_lua_kernel::intf_get_achievement(lua_State *L)
{
const char *content_for = luaL_checkstring(L, 1);
const char *id = luaL_checkstring(L, 2);
const char* content_for = luaL_checkstring(L, 1);
const char* id = luaL_checkstring(L, 2);
config cfg;
for(const auto& group : game_config_manager::get()->get_achievements()) {
@ -3167,6 +3167,15 @@ int game_lua_kernel::intf_get_achievement(lua_State *L)
cfg["achieved"] = achieve.achieved_;
cfg["max_progress"] = achieve.max_progress_;
cfg["current_progress"] = achieve.current_progress_;
for(const auto& sub_ach : achieve.sub_achievements_) {
config& sub = cfg.add_child("sub_achievement");
sub["id"] = sub_ach.id_;
sub["description"] = sub_ach.description_;
sub["icon"] = sub_ach.icon_;
sub["achieved"] = sub_ach.achieved_;
}
luaW_pushconfig(L, cfg);
return 1;
}
@ -3186,7 +3195,7 @@ int game_lua_kernel::intf_get_achievement(lua_State *L)
/**
* Progresses the provided achievement.
* - Arg 1: string - content_for.
* - Arg 2: string - id.
* - Arg 2: string - achievement id.
* - Arg 3: int - the amount to progress the achievement.
* - Arg 4: int - the limit the achievement can progress by
* - Ret 1: int - the achievement's current progress after adding amount or -1 if not a progressable achievement (including if it's already achieved)
@ -3194,8 +3203,8 @@ int game_lua_kernel::intf_get_achievement(lua_State *L)
*/
int game_lua_kernel::intf_progress_achievement(lua_State *L)
{
const char *content_for = luaL_checkstring(L, 1);
const char *id = luaL_checkstring(L, 2);
const char* content_for = luaL_checkstring(L, 1);
const char* id = luaL_checkstring(L, 2);
int amount = luaL_checkinteger(L, 3);
int limit = luaL_optinteger(L, 4, 999999999);
@ -3204,8 +3213,8 @@ int game_lua_kernel::intf_progress_achievement(lua_State *L)
for(achievement& achieve : group.achievements_) {
if(achieve.id_ == id) {
// check that this is a progressable achievement
if(achieve.max_progress_ == 0) {
ERR_LUA << "Attempted to progress achievement " << id << " for achievement group " << content_for << ", which does not have max_progress set.";
if(achieve.max_progress_ == 0 || achieve.sub_achievements_.size() > 0) {
ERR_LUA << "Attempted to progress achievement " << id << " for achievement group " << content_for << ", is not a progressible achievement.";
lua_pushinteger(L, -1);
lua_pushinteger(L, -1);
return 2;
@ -3239,6 +3248,82 @@ int game_lua_kernel::intf_progress_achievement(lua_State *L)
return lua_error(L);
}
/**
* Returns whether an achievement has been completed.
* - Arg 1: string - content_for.
* - Arg 2: string - achievement id.
* - Arg 3: string - sub-achievement id
* - Ret 1: boolean.
*/
int game_lua_kernel::intf_has_sub_achievement(lua_State *L)
{
const char* content_for = luaL_checkstring(L, 1);
const char* id = luaL_checkstring(L, 2);
const char* sub_id = luaL_checkstring(L, 3);
if(resources::controller->is_networked_mp() && synced_context::is_synced()) {
ERR_LUA << "Returning false for whether a player has completed an achievement due to being networked multiplayer.";
lua_pushboolean(L, false);
} else {
lua_pushboolean(L, preferences::sub_achievement(content_for, id, sub_id));
}
return 1;
}
/**
* Marks a single sub-achievement as completed.
* - Arg 1: string - content_for.
* - Arg 2: string - achievement id.
* - Arg 3: string - sub-achievement id
*/
int game_lua_kernel::intf_set_sub_achievement(lua_State *L)
{
const char* content_for = luaL_checkstring(L, 1);
const char* id = luaL_checkstring(L, 2);
const char* sub_id = luaL_checkstring(L, 3);
for(achievement_group& group : game_config_manager::get()->get_achievements()) {
if(group.content_for_ == content_for) {
for(achievement& achieve : group.achievements_) {
if(achieve.id_ == id) {
// the whole achievement is already completed
if(achieve.achieved_) {
return 0;
}
for(sub_achievement& sub_ach : achieve.sub_achievements_) {
if(sub_ach.id_ == sub_id) {
// this particular sub-achievement is already achieved
if(sub_ach.achieved_) {
return 0;
} else {
sub_ach.achieved_ = true;
preferences::set_sub_achievement(content_for, id, sub_id);
achieve.current_progress_++;
achieve.achieved_ = achieve.current_progress_ == achieve.max_progress_;
if(achieve.achieved_) {
preferences::set_achievement(content_for, id);
}
}
}
}
// sub-achievement not found - existing achievement group and achievement but non-existing sub-achievement id
lua_push(L, "Achievement " + std::string(id) + " not found for achievement group " + content_for);
return lua_error(L);
}
}
// achievement not found - existing achievement group but non-existing achievement id
lua_push(L, "Achievement " + std::string(id) + " not found for achievement group " + content_for);
return lua_error(L);
}
}
// achievement group not found
lua_push(L, "Achievement group " + std::string(content_for) + " not found");
return lua_error(L);
}
/**
* Scrolls to given tile.
* - Arg 1: location.
@ -5067,6 +5152,8 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
{ "has", &dispatch<&game_lua_kernel::intf_has_achievement> },
{ "get", &dispatch<&game_lua_kernel::intf_get_achievement> },
{ "progress", &dispatch<&game_lua_kernel::intf_progress_achievement> },
{ "has_sub_achievement", &dispatch<&game_lua_kernel::intf_has_sub_achievement> },
{ "set_sub_achievement", &dispatch<&game_lua_kernel::intf_set_sub_achievement> },
{ nullptr, nullptr }
};
lua_getglobal(L, "wesnoth");

View file

@ -121,8 +121,10 @@ class game_lua_kernel : public lua_kernel_base
int intf_play_sound(lua_State *L);
int intf_set_achievement(lua_State *L);
int intf_has_achievement(lua_State *L);
int intf_has_sub_achievement(lua_State *L);
int intf_get_achievement(lua_State *L);
int intf_progress_achievement(lua_State *L);
int intf_set_sub_achievement(lua_State *L);
int intf_set_floating_label(lua_State* L, bool spawn);
int intf_remove_floating_label(lua_State* L);
int intf_move_floating_label(lua_State* L);