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:
parent
8fc34a9534
commit
ad3f1c95b0
12 changed files with 437 additions and 114 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.."'")
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue