From b4d9c3e9c4df52272ad8e3a76d27b9c224bf633f Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Mon, 5 Jan 2026 22:24:40 -0500 Subject: [PATCH] make ship and weapon class display names SCP_string All other display names are `SCP_string`, so make these `SCP_string` as well to be consistent and to allow arbitrary-length display names. Also fix a few infelicities with German translation and the script API. --- code/localization/localize.cpp | 24 +++++++++---------- code/localization/localize.h | 4 ++-- code/menuui/techmenu.cpp | 3 --- code/parse/parselo.cpp | 24 ++++++++++++++++++- code/parse/parselo.h | 1 + code/scripting/api/objs/shipclass.cpp | 16 ++++++------- code/scripting/api/objs/weaponclass.cpp | 12 ++++++---- code/ship/ship.cpp | 21 +++++++++++----- code/ship/ship.h | 2 +- code/weapon/weapon.h | 2 +- code/weapon/weapons.cpp | 15 ++++++------ fred2/shipeditordlg.cpp | 4 ++-- .../ShipEditor/ShipEditorDialogModel.cpp | 4 ++-- 13 files changed, 83 insertions(+), 49 deletions(-) diff --git a/code/localization/localize.cpp b/code/localization/localize.cpp index bc4b217489e..5ad87fe4bfc 100644 --- a/code/localization/localize.cpp +++ b/code/localization/localize.cpp @@ -1361,16 +1361,16 @@ void lcl_get_language_name(char *lang_name) // For displaying weapon names in german version // since we can't actually just change them outright. // -void lcl_translate_wep_name_gr(char *name) +void lcl_translate_wep_name_gr(SCP_string &name) { - if (!strcmp(name, "Morning Star")) { - strcpy(name, "Morgenstern"); - } else if (!strcmp(name, "MorningStar")) { - strcpy(name, "Morgenstern D"); - } else if (!strcmp(name, "UD-8 Kayser")) { - strcpy(name, "Kayserstrahl"); - } else if (!strcmp(name, "UD-D Kayser")) { - strcpy(name, "Kayserstrahl"); + if (name == "Morning Star") { + name = "Morgenstern"; + } else if (name == "MorningStar") { + name = "Morgenstern D"; + } else if (name == "UD-8 Kayser") { + name = "Kayserstrahl"; + } else if (name == "UD-D Kayser") { + name = "Kayserstrahl"; } } @@ -1615,10 +1615,10 @@ char buf[128]; // For displaying ship names in german version in the briefing // since we can't actually just change them outright. // -void lcl_translate_ship_name_gr(char *name) +void lcl_translate_ship_name_gr(SCP_string &name) { - if (!strcmp(name, "GTDR Amazon Advanced")) { - strcpy(name, "GTDR Amazon VII"); + if (name == "GTDR Amazon Advanced") { + name = "GTDR Amazon VII"; } } diff --git a/code/localization/localize.h b/code/localization/localize.h index d41f905db76..1ba79b27aaf 100644 --- a/code/localization/localize.h +++ b/code/localization/localize.h @@ -130,8 +130,8 @@ void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id = nullptr); // translate the specified string based upon the current language int lcl_get_xstr_offset(int index, int res); -void lcl_translate_wep_name_gr(char *name); -void lcl_translate_ship_name_gr(char *name); +void lcl_translate_wep_name_gr(SCP_string &name); +void lcl_translate_ship_name_gr(SCP_string &name); void lcl_translate_brief_icon_name_gr(char *name); void lcl_translate_brief_icon_name_pl(char *name); void lcl_translate_targetbox_name_gr(char *name); diff --git a/code/menuui/techmenu.cpp b/code/menuui/techmenu.cpp index a3be210d41b..b3d81b4ecfa 100644 --- a/code/menuui/techmenu.cpp +++ b/code/menuui/techmenu.cpp @@ -497,9 +497,6 @@ void tech_common_render() memset( buf, 0, sizeof(buf) ); strncpy(buf, Current_list->at(z).name, sizeof(buf) - 1); - if (Lcl_gr && !Disable_built_in_translations) - lcl_translate_ship_name_gr(buf); - font::force_fit_string(buf, 255, Tech_list_coords[gr_screen.res][SHIP_W_COORD]); gr_string(Tech_list_coords[gr_screen.res][SHIP_X_COORD], Tech_list_coords[gr_screen.res][SHIP_Y_COORD] + y, buf, GR_RESIZE_MENU); diff --git a/code/parse/parselo.cpp b/code/parse/parselo.cpp index 729838a305c..7d26d0909cf 100644 --- a/code/parse/parselo.cpp +++ b/code/parse/parselo.cpp @@ -4475,7 +4475,7 @@ void consolidate_double_characters(char *src, char ch) while (*src) { if (*src == ch && *(src + 1) == ch) - dest--; + --dest; ++src; ++dest; @@ -4485,6 +4485,28 @@ void consolidate_double_characters(char *src, char ch) } } +// Goober5000 +// Used for escape sequences: ## to #, !! to !, etc. +void consolidate_double_characters(SCP_string &str, char ch) +{ + auto src = str.begin(); + auto dest = src; + while (src != str.end()) + { + if (*src == ch && *(src + 1) == ch) + --dest; + + ++src; + ++dest; + + if (src != dest && src != str.end()) + *dest = *src; + } + + if (src != dest) + str.resize(dest - str.begin()); +} + char *three_dot_truncate(char *buffer, const char *source, size_t buffer_size) { Assertion(buffer && source, "Arguments must not be null!"); diff --git a/code/parse/parselo.h b/code/parse/parselo.h index f446e690645..6d4aa905498 100644 --- a/code/parse/parselo.h +++ b/code/parse/parselo.h @@ -84,6 +84,7 @@ extern const char *get_pointer_to_first_hash_symbol(const char *src, bool ignore extern int get_index_of_first_hash_symbol(const SCP_string &src, bool ignore_doubled_hash = false); extern void consolidate_double_characters(char *str, char ch); +extern void consolidate_double_characters(SCP_string &str, char ch); // for limiting strings that may be very long; useful for dialog boxes char *three_dot_truncate(char *buffer, const char *source, size_t buffer_size); diff --git a/code/scripting/api/objs/shipclass.cpp b/code/scripting/api/objs/shipclass.cpp index 38df49ddd34..c633c772bbd 100644 --- a/code/scripting/api/objs/shipclass.cpp +++ b/code/scripting/api/objs/shipclass.cpp @@ -697,17 +697,17 @@ ADE_VIRTVAR(AltName, l_Shipclass, "string", "Alternate name for ship class", "st if(idx < 0 || idx >= ship_info_size()) return ade_set_error(L, "s", ""); - if(ADE_SETTING_VAR && newName != NULL) { - if (strlen(newName) >= NAME_LENGTH) - { - LuaError(L, "Cannot set alternate name value to '%s' because it is too long, maximum length is %d!", newName, NAME_LENGTH - 1); - return ade_set_error(L, "s", ""); + if(ADE_SETTING_VAR && newName != nullptr) { + if (newName == Ship_info[idx].name) { + Ship_info[idx].display_name = ""; + Ship_info[idx].flags.remove(Ship::Info_Flags::Has_display_name); + } else { + Ship_info[idx].display_name = newName; + Ship_info[idx].flags.set(Ship::Info_Flags::Has_display_name); } - - strcpy_s(Ship_info[idx].display_name, newName); } - return ade_set_args(L, "s", Ship_info[idx].display_name); + return ade_set_args(L, "s", Ship_info[idx].display_name.c_str()); } ADE_VIRTVAR(VelocityMax, l_Shipclass, "vector", "Ship's lateral and forward speeds", "vector", "Maximum velocity, or null vector if handle is invalid") diff --git a/code/scripting/api/objs/weaponclass.cpp b/code/scripting/api/objs/weaponclass.cpp index 84e8c06f979..6ada8c3f265 100644 --- a/code/scripting/api/objs/weaponclass.cpp +++ b/code/scripting/api/objs/weaponclass.cpp @@ -76,12 +76,16 @@ ADE_VIRTVAR(AltName, l_Weaponclass, "string", "The alternate weapon class name." return ade_set_error(L, "s", ""); if(ADE_SETTING_VAR && s != nullptr) { - auto len = sizeof(Weapon_info[idx].display_name); - strncpy(Weapon_info[idx].display_name, s, len); - Weapon_info[idx].display_name[len - 1] = 0; + if (s == Weapon_info[idx].name) { + Weapon_info[idx].display_name = ""; + Weapon_info[idx].wi_flags.remove(Weapon::Info_Flags::Has_display_name); + } else { + Weapon_info[idx].display_name = s; + Weapon_info[idx].wi_flags.set(Weapon::Info_Flags::Has_display_name); + } } - return ade_set_args(L, "s", Weapon_info[idx].display_name); + return ade_set_args(L, "s", Weapon_info[idx].display_name.c_str()); } ADE_VIRTVAR(TurretName, l_Weaponclass, "string", "The name displayed for a turret if the turret's first weapon is this weapon class.", "string", "Turret name (aka alternate subsystem name), or empty string if handle is invalid") diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 1747a59eaea..8638fb1457f 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -992,7 +992,7 @@ const float AWACS_HELP_HULL_LOW = 0.25f; // percent hull at which ship will a void ship_info::clone(const ship_info& other) { strcpy_s(name, other.name); - strcpy_s(display_name, other.display_name); + display_name = other.display_name; strcpy_s(short_name, other.short_name); species = other.species; class_type = other.class_type; @@ -1735,7 +1735,7 @@ ship_info::ship_info(ship_info&& other) noexcept ship_info::ship_info() { name[0] = '\0'; - display_name[0] = '\0'; + display_name = ""; sprintf(short_name, "ShipClass%d", ship_info_size()); species = 0; class_type = -1; @@ -2125,7 +2125,7 @@ ship_info::~ship_info() const char* ship_info::get_display_name() const { if (has_display_name()) - return display_name; + return display_name.c_str(); else return name; } @@ -2345,15 +2345,24 @@ static void parse_ship(const char *filename, bool replace) } if (new_name) { - if (!sip->flags[Ship::Info_Flags::Has_display_name]) { + if (!sip->has_display_name()) { // if this name has a hash, create a default display name if (get_pointer_to_first_hash_symbol(sip->name)) { - strcpy_s(sip->display_name, sip->name); + sip->display_name = sip->name; end_string_at_first_hash_symbol(sip->display_name); sip->flags.set(Ship::Info_Flags::Has_display_name); } } + // do German translation + if (Lcl_gr && !Disable_built_in_translations) { + if (!sip->has_display_name()) { + sip->display_name = sip->name; + lcl_translate_ship_name_gr(sip->display_name); + sip->flags.set(Ship::Info_Flags::Has_display_name); + } + } + sip->animations.changeShipName(sip->name); sip->cockpit_animations.changeShipName(sip->name); } @@ -2987,7 +2996,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool if (optional_string("$Alt name:") || optional_string("$Display Name:")) { - stuff_string(sip->display_name, F_NAME, NAME_LENGTH); + stuff_string(sip->display_name, F_NAME); end_string_at_first_hash_symbol(sip->display_name, true); consolidate_double_characters(sip->display_name, '#'); sip->flags.set(Ship::Info_Flags::Has_display_name); diff --git a/code/ship/ship.h b/code/ship/ship.h index c4e616027ff..8da1ab6400f 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1146,7 +1146,7 @@ class ship_info { public: char name[NAME_LENGTH]; // name for the ship - char display_name[NAME_LENGTH]; // display another name for the ship + SCP_string display_name; // display another name for the ship char short_name[NAME_LENGTH]; // short name, for use in the editor? int species; // which species this craft belongs to int class_type; //For type table diff --git a/code/weapon/weapon.h b/code/weapon/weapon.h index ba408c535e8..3c14686635d 100644 --- a/code/weapon/weapon.h +++ b/code/weapon/weapon.h @@ -363,7 +363,7 @@ extern SCP_vector Weapon_info; struct weapon_info { char name[NAME_LENGTH]; // name of this weapon - char display_name[NAME_LENGTH]; // display name of this weapon + SCP_string display_name; // display name of this weapon char title[WEAPON_TITLE_LEN]; // official title of weapon (used by tooltips) std::unique_ptr desc; // weapon's description (used by tooltips) char altSubsysName[NAME_LENGTH]; // rename turret to this if this is the turrets first weapon diff --git a/code/weapon/weapons.cpp b/code/weapon/weapons.cpp index c5c2a79258c..884f9d2aa69 100644 --- a/code/weapon/weapons.cpp +++ b/code/weapon/weapons.cpp @@ -875,7 +875,7 @@ int parse_weapon(int subtype, bool replace, const char *filename) // if this name has a hash, create a default display name if (get_pointer_to_first_hash_symbol(wip->name)) { - strcpy_s(wip->display_name, wip->name); + wip->display_name = wip->name; end_string_at_first_hash_symbol(wip->display_name, true); consolidate_double_characters(wip->display_name, '#'); wip->wi_flags.set(Weapon::Info_Flags::Has_display_name); @@ -883,16 +883,17 @@ int parse_weapon(int subtype, bool replace, const char *filename) // do German translation if (Lcl_gr && !Disable_built_in_translations) { - if (!wip->display_name[0]) { - strcpy_s(wip->display_name, wip->name); + if (!wip->has_display_name()) { + wip->display_name = wip->name; + lcl_translate_wep_name_gr(wip->display_name); + wip->wi_flags.set(Weapon::Info_Flags::Has_display_name); } - lcl_translate_wep_name_gr(wip->display_name); } } if (optional_string("$Alt name:") || optional_string("$Display Name:")) { - stuff_string(wip->display_name, F_NAME, NAME_LENGTH); + stuff_string(wip->display_name, F_NAME); wip->wi_flags.set(Weapon::Info_Flags::Has_display_name); } @@ -9350,7 +9351,7 @@ void weapon_info::reset() int i, j; memset(this->name, 0, sizeof(this->name)); - memset(this->display_name, 0, sizeof(this->display_name)); + this->display_name.clear(); memset(this->title, 0, sizeof(this->title)); this->desc = nullptr; memset(this->altSubsysName, 0, sizeof(this->altSubsysName)); @@ -9738,7 +9739,7 @@ void weapon_info::reset() const char* weapon_info::get_display_name() const { if (has_display_name()) - return display_name; + return display_name.c_str(); else return name; } diff --git a/fred2/shipeditordlg.cpp b/fred2/shipeditordlg.cpp index a398fb5a9db..592d66220fa 100644 --- a/fred2/shipeditordlg.cpp +++ b/fred2/shipeditordlg.cpp @@ -1296,14 +1296,14 @@ int CShipEditorDlg::update_ship(int ship) // the display name was precalculated, so now just assign it if (m_ship_display_name == m_ship_name || m_ship_display_name.CompareNoCase("") == 0) { - if (Ships[ship].flags[Ship::Ship_Flags::Has_display_name]) + if (Ships[ship].has_display_name()) set_modified(); Ships[ship].display_name = ""; Ships[ship].flags.remove(Ship::Ship_Flags::Has_display_name); } else { - if (!Ships[ship].flags[Ship::Ship_Flags::Has_display_name]) + if (!Ships[ship].has_display_name()) set_modified(); Ships[ship].display_name = m_ship_display_name; Ships[ship].flags.set(Ship::Ship_Flags::Has_display_name); diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp index 37ccfa9cc9a..c5881284c97 100644 --- a/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/ShipEditor/ShipEditorDialogModel.cpp @@ -722,12 +722,12 @@ bool ShipEditorDialogModel::update_ship(int ship) // the display name was precalculated, so now just assign it if (_m_ship_display_name == _m_ship_name || stricmp(_m_ship_display_name.c_str(), "") == 0) { - if (Ships[ship].flags[Ship::Ship_Flags::Has_display_name]) + if (Ships[ship].has_display_name()) set_modified(); Ships[ship].display_name = ""; Ships[ship].flags.remove(Ship::Ship_Flags::Has_display_name); } else { - if (!Ships[ship].flags[Ship::Ship_Flags::Has_display_name]) + if (!Ships[ship].has_display_name()) set_modified(); Ships[ship].display_name = _m_ship_display_name; Ships[ship].flags.set(Ship::Ship_Flags::Has_display_name);