From f8423a5fa2a6975b2458e6f680b925840be531cf Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Sat, 8 Jul 2023 05:44:58 +0300 Subject: [PATCH 23/23] Ruledit: Add Achievements tab See osdn #48134 Signed-off-by: Marko Lindqvist --- meson.build | 2 + tools/ruledit/Makefile.am | 3 + tools/ruledit/ruledit_qt.cpp | 4 + tools/ruledit/ruledit_qt.h | 2 + tools/ruledit/tab_achievement.cpp | 295 ++++++++++++++++++++++++++++++ tools/ruledit/tab_achievement.h | 59 ++++++ tools/ruledit/validity.c | 14 ++ tools/ruledit/validity.h | 2 + translations/ruledit/POTFILES.in | 1 + 9 files changed, 382 insertions(+) create mode 100644 tools/ruledit/tab_achievement.cpp create mode 100644 tools/ruledit/tab_achievement.h diff --git a/meson.build b/meson.build index 6d16cef30d..eb1364ba67 100644 --- a/meson.build +++ b/meson.build @@ -3521,6 +3521,7 @@ mocced_ruledit = qt_mod.preprocess( 'tools/ruledit/req_vec_fix.h', 'tools/ruledit/requirers_dlg.h', 'tools/ruledit/ruledit_qt.h', + 'tools/ruledit/tab_achievement.h', 'tools/ruledit/tab_enablers.h', 'tools/ruledit/tab_extras.h', 'tools/ruledit/tab_misc.h', @@ -3546,6 +3547,7 @@ executable('freeciv-ruledit', 'tools/ruledit/req_vec_fix.cpp', 'tools/ruledit/ruledit.cpp', 'tools/ruledit/ruledit_qt.cpp', + 'tools/ruledit/tab_achievement.cpp', 'tools/ruledit/tab_building.cpp', 'tools/ruledit/tab_enablers.cpp', 'tools/ruledit/tab_extras.cpp', diff --git a/tools/ruledit/Makefile.am b/tools/ruledit/Makefile.am index eccd908088..6c5fa16e7d 100644 --- a/tools/ruledit/Makefile.am +++ b/tools/ruledit/Makefile.am @@ -34,6 +34,7 @@ MOC_FILES = \ meta_req_vec_fix.cpp \ meta_requirers_dlg.cpp \ meta_ruledit_qt.cpp \ + meta_tab_achievement.cpp \ meta_tab_building.cpp \ meta_tab_enablers.cpp \ meta_tab_extras.cpp \ @@ -59,6 +60,8 @@ freeciv_ruledit_SOURCES = \ edit_utype.h \ effect_edit.cpp \ effect_edit.h \ + tab_achievement.cpp \ + tab_achievement.h \ tab_building.cpp \ tab_building.h \ tab_enablers.cpp \ diff --git a/tools/ruledit/ruledit_qt.cpp b/tools/ruledit/ruledit_qt.cpp index 84930d843b..9805fcad5c 100644 --- a/tools/ruledit/ruledit_qt.cpp +++ b/tools/ruledit/ruledit_qt.cpp @@ -45,6 +45,7 @@ #include "req_edit.h" #include "req_vec_fix.h" #include "ruledit.h" +#include "tab_achievement.h" #include "tab_building.h" #include "tab_enablers.h" #include "tab_extras.h" @@ -172,6 +173,8 @@ void ruledit_gui::setup(QWidget *central_in) stack->addTab(bldg, QString::fromUtf8(R__("Buildings"))); unit = new tab_unit(this); stack->addTab(unit, QString::fromUtf8(R__("Units"))); + ach = new tab_achievement(this); + stack->addTab(ach, QString::fromUtf8(R__("Achievements"))); good = new tab_good(this); stack->addTab(good, QString::fromUtf8(R__("Goods"))); gov = new tab_gov(this); @@ -253,6 +256,7 @@ void ruledit_gui::launch_now() data.nationlist = NULL; } + ach->refresh(); bldg->refresh(); misc->ruleset_loaded(); nation->refresh(); diff --git a/tools/ruledit/ruledit_qt.h b/tools/ruledit/ruledit_qt.h index e5ba0b1f16..34613ef4c7 100644 --- a/tools/ruledit/ruledit_qt.h +++ b/tools/ruledit/ruledit_qt.h @@ -29,6 +29,7 @@ class QLineEdit; class QStackedLayout; class requirers_dlg; +class tab_achievement; class tab_building; class tab_good; class tab_gov; @@ -131,6 +132,7 @@ signals: QWidget *central; QStackedLayout *main_layout; + tab_achievement *ach; tab_building *bldg; tab_misc *misc; tab_tech *tech; diff --git a/tools/ruledit/tab_achievement.cpp b/tools/ruledit/tab_achievement.cpp new file mode 100644 index 0000000000..c9f4616e34 --- /dev/null +++ b/tools/ruledit/tab_achievement.cpp @@ -0,0 +1,295 @@ +/*********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +// Qt +#include +#include +#include +#include +#include +#include +#include + +// utility +#include "fcintl.h" +#include "log.h" + +// common +#include "achievements.h" +#include "game.h" + +// ruledit +#include "req_edit.h" +#include "ruledit.h" +#include "ruledit_qt.h" +#include "validity.h" + +#include "tab_achievement.h" + +/**********************************************************************//** + Setup tab_achievement object +**************************************************************************/ +tab_achievement::tab_achievement(ruledit_gui *ui_in) : QWidget() +{ + QVBoxLayout *main_layout = new QVBoxLayout(this); + QGridLayout *ach_layout = new QGridLayout(); + QLabel *label; + QPushButton *effects_button; + QPushButton *add_button; + QPushButton *delete_button; + + ui = ui_in; + selected = nullptr; + + ach_list = new QListWidget(this); + + connect(ach_list, + SIGNAL(itemSelectionChanged()), this, SLOT(select_achievement())); + main_layout->addWidget(ach_list); + + ach_layout->setSizeConstraint(QLayout::SetMaximumSize); + + label = new QLabel(QString::fromUtf8(R__("Rule Name"))); + label->setParent(this); + rname = new QLineEdit(this); + rname->setText(R__("None")); + connect(rname, SIGNAL(returnPressed()), this, SLOT(name_given())); + ach_layout->addWidget(label, 0, 0); + ach_layout->addWidget(rname, 0, 2); + + label = new QLabel(QString::fromUtf8(R__("Name"))); + label->setParent(this); + same_name = new QCheckBox(); + connect(same_name, SIGNAL(toggled(bool)), this, SLOT(same_name_toggle(bool))); + name = new QLineEdit(this); + name->setText(R__("None")); + connect(name, SIGNAL(returnPressed()), this, SLOT(name_given())); + ach_layout->addWidget(label, 1, 0); + ach_layout->addWidget(same_name, 1, 1); + ach_layout->addWidget(name, 1, 2); + + effects_button = new QPushButton(QString::fromUtf8(R__("Effects")), this); + connect(effects_button, SIGNAL(pressed()), this, SLOT(edit_effects())); + ach_layout->addWidget(effects_button, 3, 2); + + add_button = new QPushButton(QString::fromUtf8(R__("Add Achievement")), this); + connect(add_button, SIGNAL(pressed()), this, SLOT(add_now())); + ach_layout->addWidget(add_button, 4, 0); + show_experimental(add_button); + + delete_button = new QPushButton(QString::fromUtf8(R__("Remove this Achievement")), + this); + connect(delete_button, SIGNAL(pressed()), this, SLOT(delete_now())); + ach_layout->addWidget(delete_button, 4, 2); + show_experimental(delete_button); + + refresh(); + update_achievement_info(nullptr); + + main_layout->addLayout(ach_layout); + + setLayout(main_layout); +} + +/**********************************************************************//** + Refresh the information. +**************************************************************************/ +void tab_achievement::refresh() +{ + ach_list->clear(); + + achievements_re_active_iterate(pach) { + QListWidgetItem *item + = new QListWidgetItem(QString::fromUtf8(achievement_rule_name(pach))); + + ach_list->insertItem(achievement_index(pach), item); + } achievements_re_active_iterate_end; +} + +/**********************************************************************//** + Update info of the achievement +**************************************************************************/ +void tab_achievement::update_achievement_info(struct achievement *pach) +{ + selected = pach; + + if (selected != 0) { + QString dispn = QString::fromUtf8(untranslated_name(&(pach->name))); + QString rulen = QString::fromUtf8(achievement_rule_name(pach)); + + name->setText(dispn); + rname->setText(rulen); + if (dispn == rulen) { + name->setEnabled(false); + same_name->setChecked(true); + } else { + same_name->setChecked(false); + name->setEnabled(true); + } + } else { + name->setText(R__("None")); + rname->setText(R__("None")); + same_name->setChecked(true); + name->setEnabled(false); + } +} + +/**********************************************************************//** + User selected achievement from the list. +**************************************************************************/ +void tab_achievement::select_achievement() +{ + QList select_list = ach_list->selectedItems(); + + if (!select_list.isEmpty()) { + QByteArray gn_bytes; + + gn_bytes = select_list.at(0)->text().toUtf8(); + update_achievement_info(achievement_by_rule_name(gn_bytes.data())); + } +} + +/**********************************************************************//** + User entered name for the achievement +**************************************************************************/ +void tab_achievement::name_given() +{ + if (selected != nullptr) { + QByteArray name_bytes; + QByteArray rname_bytes; + + achievements_iterate(pach) { + if (pach != selected && !pach->ruledit_disabled) { + rname_bytes = rname->text().toUtf8(); + if (!strcmp(achievement_rule_name(pach), rname_bytes.data())) { + ui->display_msg(R__("An achievement with that rule name already exists!")); + return; + } + } + } achievements_iterate_end; + + if (same_name->isChecked()) { + name->setText(rname->text()); + } + + name_bytes = name->text().toUtf8(); + rname_bytes = rname->text().toUtf8(); + names_set(&(selected->name), 0, + name_bytes.data(), + rname_bytes.data()); + refresh(); + } +} + +/**********************************************************************//** + User requested achievement deletion +**************************************************************************/ +void tab_achievement::delete_now() +{ + if (selected != 0) { + requirers_dlg *requirers; + + requirers = ui->create_requirers(achievement_rule_name(selected)); + if (is_achievement_needed(selected, &ruledit_qt_display_requirers, + requirers)) { + return; + } + + selected->ruledit_disabled = true; + + refresh(); + update_achievement_info(nullptr); + } +} + +/**********************************************************************//** + Initialize new achievement for use. +**************************************************************************/ +bool tab_achievement::initialize_new_achievement(struct achievement *pach) +{ + if (achievement_by_rule_name("New Achievement") != nullptr) { + return false; + } + + name_set(&(pach->name), 0, "New Achievement"); + + return true; +} + +/**********************************************************************//** + User requested new achievement +**************************************************************************/ +void tab_achievement::add_now() +{ + struct achievement *new_ach; + + // Try to reuse freed achievement slot + achievements_iterate(pach) { + if (pach->ruledit_disabled) { + if (initialize_new_achievement(pach)) { + pach->ruledit_disabled = false; + update_achievement_info(pach); + refresh(); + } + return; + } + } achievements_iterate_end; + + // Try to add completely new achievement + if (game.control.num_achievement_types >= MAX_ACHIEVEMENT_TYPES) { + return; + } + + // num_achievement_types must be big enough to hold new achievement or + // achievement_by_number() fails. + game.control.num_achievement_types++; + new_ach = achievement_by_number(game.control.num_achievement_types - 1); + if (initialize_new_achievement(new_ach)) { + update_achievement_info(new_ach); + + refresh(); + } else { + game.control.num_achievement_types--; // Restore + } +} + +/**********************************************************************//** + Toggled whether rule_name and name should be kept identical +**************************************************************************/ +void tab_achievement::same_name_toggle(bool checked) +{ + name->setEnabled(!checked); + if (checked) { + name->setText(rname->text()); + } +} + +/**********************************************************************//** + User wants to edit effects +**************************************************************************/ +void tab_achievement::edit_effects() +{ + if (selected != nullptr) { + struct universal uni; + + uni.value.achievement = selected; + uni.kind = VUT_ACHIEVEMENT; + + ui->open_effect_edit(QString::fromUtf8(achievement_rule_name(selected)), + &uni, EFMC_NORMAL); + } +} diff --git a/tools/ruledit/tab_achievement.h b/tools/ruledit/tab_achievement.h new file mode 100644 index 0000000000..0b38643826 --- /dev/null +++ b/tools/ruledit/tab_achievement.h @@ -0,0 +1,59 @@ +/*********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ + +#ifndef FC__TAB_ACHIEVEMENT_H +#define FC__TAB_ACHIEVEMENT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// Qt +#include + +class QCheckBox; +class QLineEdit; +class QListWidget; + +class ruledit_gui; + +class tab_achievement : public QWidget +{ + Q_OBJECT + + public: + explicit tab_achievement(ruledit_gui *ui_in); + void refresh(); + + private: + ruledit_gui *ui; + void update_achievement_info(struct achievement *pach); + bool initialize_new_achievement(struct achievement *pach); + + QLineEdit *name; + QLineEdit *rname; + QListWidget *ach_list; + QCheckBox *same_name; + + struct achievement *selected; + + private slots: + void name_given(); + void select_achievement(); + void add_now(); + void delete_now(); + void same_name_toggle(bool checked); + void edit_effects(); +}; + +#endif // FC__TAB_ACHIEVEMENT_H diff --git a/tools/ruledit/validity.c b/tools/ruledit/validity.c index a36145fbce..dc0441e70f 100644 --- a/tools/ruledit/validity.c +++ b/tools/ruledit/validity.c @@ -257,6 +257,20 @@ bool is_utype_needed(struct unit_type *ptype, requirers_cb cb, return needed; } +/**********************************************************************//** + Check if anything in ruleset needs achievement type +**************************************************************************/ +bool is_achievement_needed(struct achievement *pach, requirers_cb cb, + void *data) +{ + struct universal uni = { .value.achievement = pach, .kind = VUT_ACHIEVEMENT }; + bool needed = FALSE; + + needed |= is_universal_needed(&uni, cb, data); + + return needed; +} + /**********************************************************************//** Check if anything in ruleset needs goods type **************************************************************************/ diff --git a/tools/ruledit/validity.h b/tools/ruledit/validity.h index b3498619f8..eeaf7d5e86 100644 --- a/tools/ruledit/validity.h +++ b/tools/ruledit/validity.h @@ -22,6 +22,8 @@ typedef void (*requirers_cb)(const char *msg, void *data); bool is_tech_needed(struct advance *padv, requirers_cb cb, void *data); bool is_building_needed(struct impr_type *pimpr, requirers_cb cb, void *data); bool is_utype_needed(struct unit_type *ptype, requirers_cb cb, void *data); +bool is_achievement_needed(struct achievement *pach, requirers_cb cb, + void *data); bool is_good_needed(struct goods_type *pgood, requirers_cb cb, void *data); bool is_government_needed(struct government *pgov, requirers_cb cb, void *data); bool is_extra_needed(struct extra_type *pextra, requirers_cb cb, void *data); diff --git a/translations/ruledit/POTFILES.in b/translations/ruledit/POTFILES.in index a83169feac..4a44959f46 100644 --- a/translations/ruledit/POTFILES.in +++ b/translations/ruledit/POTFILES.in @@ -10,6 +10,7 @@ tools/ruledit/req_vec_fix.cpp tools/ruledit/requirers_dlg.cpp tools/ruledit/ruledit.cpp tools/ruledit/ruledit_qt.cpp +tools/ruledit/tab_achievement.cpp tools/ruledit/tab_building.cpp tools/ruledit/tab_enablers.cpp tools/ruledit/tab_extras.cpp -- 2.40.1