From 9586f21780a80d76007b4f34ad3f6a2e8b8f4f54 Mon Sep 17 00:00:00 2001 From: Naomi Coffman Date: Sat, 16 Sep 2023 10:24:40 -0400 Subject: [PATCH 1/6] Add team filters to messages --- code/mission/missionmessage.cpp | 22 ++++++++++++++++++---- code/mission/missionmessage.h | 1 + 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/code/mission/missionmessage.cpp b/code/mission/missionmessage.cpp index 3fdfd7f6c70..e593fe7666e 100644 --- a/code/mission/missionmessage.cpp +++ b/code/mission/missionmessage.cpp @@ -338,6 +338,7 @@ int add_wave( const char *wave_name ) void message_filter_clear(MessageFilter& filter) { filter.species_bitfield = 0; filter.type_bitfield = 0; + filter.team_bitfield = 0; } void message_filter_parse(MessageFilter& filter) { @@ -374,13 +375,24 @@ void message_filter_parse(MessageFilter& filter) { stuff_string(buf, F_NAME); int type = ship_type_name_lookup(buf.c_str()); if (type < 0) { - Warning(LOCATION, "Unknown type %s in messages.tbl", buf.c_str()); + Warning(LOCATION, "Unknown ship type %s in messages.tbl", buf.c_str()); } else if (type >= 32) { Warning(LOCATION, "Type %s is index 32 or higher and therefore cannot be used in a message filter", buf.c_str()); } else { filter.type_bitfield |= (1 << type); } } + while (optional_string("+Team:")) { + stuff_string(buf, F_NAME); + int team = iff_lookup(buf.c_str()); + if (team < 0) { + Warning(LOCATION, "Unknown team %s in messages.tbl", buf.c_str()); + } else if (team >= 32) { + Warning(LOCATION, "Team %s is index 32 or higher and therefore cannot be used in a message filter", buf.c_str()); + } else { + filter.team_bitfield |= (1 << team); + } + } } void handle_legacy_backup_message(MissionMessage& msg, SCP_string wing_name) { @@ -550,7 +562,7 @@ void message_parse(MessageFormat format) { handle_legacy_backup_message(msg, "Delta"); } else if (!stricmp(msg.name, "Epsilon Arrived")) { handle_legacy_backup_message(msg, "Epsilon"); - } if (get_builtin_message_type(msg.name) == MESSAGE_NONE) { + } else if (get_builtin_message_type(msg.name) == MESSAGE_NONE) { Warning(LOCATION, "Unknown builtin message type %s in messages.tbl", msg.name); } } @@ -2017,7 +2029,8 @@ bool has_filters(MessageFilter& filter) { || !filter.class_name.empty() || !filter.wing_name.empty() || filter.species_bitfield - || filter.type_bitfield; + || filter.type_bitfield + || filter.team_bitfield; } bool filter_matches(SCP_string value, SCP_vector& filter) { @@ -2045,7 +2058,8 @@ bool filters_match(MessageFilter& filter, ship* it) { && filter_matches(hud_get_ship_class(it), filter.class_name) && filter_matches(wing_name, filter.wing_name) && filter_matches(Ship_info[it->ship_info_index].species, filter.species_bitfield) - && filter_matches(Ship_info[it->ship_info_index].class_type, filter.type_bitfield); + && filter_matches(Ship_info[it->ship_info_index].class_type, filter.type_bitfield) + && filter_matches(it->team, filter.team_bitfield); } } diff --git a/code/mission/missionmessage.h b/code/mission/missionmessage.h index 33fa91b4649..7969ac1ee57 100644 --- a/code/mission/missionmessage.h +++ b/code/mission/missionmessage.h @@ -148,6 +148,7 @@ typedef struct MessageFilter { SCP_vector wing_name; int species_bitfield; int type_bitfield; + int team_bitfield; } MessageFilter; typedef struct MissionMessage { From 04a5778235ffe427911e414fb8e4edae673e8d61 Mon Sep 17 00:00:00 2001 From: Naomi Coffman Date: Sat, 16 Sep 2023 12:21:25 -0400 Subject: [PATCH 2/6] Allow filtering builtin messages by nearby ships --- code/mission/missionmessage.cpp | 41 +++++++++++++++++++++++++++++++++ code/mission/missionmessage.h | 2 ++ 2 files changed, 43 insertions(+) diff --git a/code/mission/missionmessage.cpp b/code/mission/missionmessage.cpp index e593fe7666e..5b0be4fbeda 100644 --- a/code/mission/missionmessage.cpp +++ b/code/mission/missionmessage.cpp @@ -21,6 +21,7 @@ #include "iff_defs/iff_defs.h" #include "io/timer.h" #include "localization/localize.h" +#include "math/vecmat.h" #include "mission/missiontraining.h" #include "mission/missiongoals.h" #include "mod_table/mod_table.h" @@ -553,6 +554,16 @@ void message_parse(MessageFormat format) { message_filter_clear(msg.subject_filter); } + msg.outer_filter_radius = -1; + if (optional_string("$Filter by other ship:")) { + if (optional_string("+Within range of sender:")) { + stuff_int(&msg.outer_filter_radius); + } + message_filter_parse(msg.outer_filter); + } else { + message_filter_clear(msg.outer_filter); + } + if (format == MessageFormat::TABLED) { if (!stricmp(msg.name, "Beta Arrived")) { handle_legacy_backup_message(msg, "Beta"); @@ -2063,6 +2074,25 @@ bool filters_match(MessageFilter& filter, ship* it) { } } +bool outer_filters_match(MessageFilter& filter, int range, ship* sender) { + for (auto i: list_range(&Ship_obj_list)) { + auto obj = &Objects[i->objnum]; + if (obj->flags[Object::Object_Flags::Should_be_dead]) { + continue; + } + if ((range >= 0) && (vm_vec_dist(&obj->pos, &Objects[sender->objnum].pos) > range)) { + continue; + } + if (sender->objnum == i->objnum) { + continue; + } + if (filters_match(filter, &Ships[obj->instance])) { + return true; + } + } + return false; +} + bool excludes_current_mood(int message) { for (SCP_vector::iterator iter = Messages[message].excluded_moods.begin(); iter != Messages[message].excluded_moods.end(); ++iter) { if (*iter == Current_mission_mood) { @@ -2134,6 +2164,17 @@ int get_builtin_message_inner(int type, int persona, ship* sender, ship* subject } } + // Ditto with outer filters, although they're more complicated + if (has_filters(Messages[i].outer_filter)) { + if (outer_filters_match(Messages[i].outer_filter, Messages[i].outer_filter_radius, sender)) { + // Boost messages that have at least one filter + match_level |= BUILTIN_MATCHES_FILTER; + } else { + // Ignore messages where any filter doesn't match + continue; + } + } + if (match_level == best_match_level) { matching_builtins.push_back(i); } else if (match_level > best_match_level) { diff --git a/code/mission/missionmessage.h b/code/mission/missionmessage.h index 7969ac1ee57..becf693770b 100644 --- a/code/mission/missionmessage.h +++ b/code/mission/missionmessage.h @@ -161,6 +161,8 @@ typedef struct MissionMessage { MessageFilter sender_filter; MessageFilter subject_filter; + MessageFilter outer_filter; + int outer_filter_radius; // unions for avi/wave information. Because of issues with Fred, we are using // the union to specify either the index into the avi or wave arrays above, From f13a5b3736af5cc7f9d146dfbd399500d76cdd88 Mon Sep 17 00:00:00 2001 From: Naomi Coffman Date: Sat, 16 Sep 2023 12:36:53 -0400 Subject: [PATCH 3/6] Allow specifying tiebreakers between builtin messages --- code/mission/missionmessage.cpp | 29 +++++++++++++++++++++++------ code/mission/missionmessage.h | 1 + 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/code/mission/missionmessage.cpp b/code/mission/missionmessage.cpp index 5b0be4fbeda..305a80694db 100644 --- a/code/mission/missionmessage.cpp +++ b/code/mission/missionmessage.cpp @@ -62,6 +62,16 @@ builtin_message Builtin_messages[] = { #undef X }; +#define BUILTIN_BOOST_LEVEL_ONE 1 +#define BUILTIN_BOOST_LEVEL_TWO 2 +#define BUILTIN_MATCHES_TYPE 4 +#define BUILTIN_MATCHES_FILTER 8 +#define BUILTIN_MATCHES_MOOD 16 +#define BUILTIN_MATCHES_SPECIES 32 +#define BUILTIN_MATCHES_PERSONA 64 + +#define BUILTIN_BOOST_LEVEL_THREE (BUILTIN_BOOST_LEVEL_ONE | BUILTIN_BOOST_LEVEL_TWO) + int get_builtin_message_type(const char* name) { for (int i = 0; i < MAX_BUILTIN_MESSAGE_TYPES; i++) { if (!stricmp(Builtin_messages[i].name, name)) { @@ -564,6 +574,16 @@ void message_parse(MessageFormat format) { message_filter_clear(msg.outer_filter); } + if (optional_string("$Prefer this message very highly")) { + msg.boost_level = BUILTIN_BOOST_LEVEL_THREE; + } else if (optional_string("$Prefer this message highly")) { + msg.boost_level = BUILTIN_BOOST_LEVEL_TWO; + } else if (optional_string("$Prefer this message")) { + msg.boost_level = BUILTIN_BOOST_LEVEL_ONE; + } else { + msg.boost_level = 0; + } + if (format == MessageFormat::TABLED) { if (!stricmp(msg.name, "Beta Arrived")) { handle_legacy_backup_message(msg, "Beta"); @@ -2103,12 +2123,6 @@ bool excludes_current_mood(int message) { } int get_builtin_message_inner(int type, int persona, ship* sender, ship* subject, bool require_exact_persona_match) { - static const int BUILTIN_MATCHES_TYPE = 0; - static const int BUILTIN_MATCHES_FILTER = 1; - static const int BUILTIN_MATCHES_MOOD = 2; - static const int BUILTIN_MATCHES_SPECIES = 4; - static const int BUILTIN_MATCHES_PERSONA = 8; - const char* name = Builtin_messages[type].name; SCP_vector matching_builtins; int match_level, best_match_level = 0; @@ -2128,6 +2142,9 @@ int get_builtin_message_inner(int type, int persona, ship* sender, ship* subject match_level = BUILTIN_MATCHES_TYPE; } + // Apply "Prefer this message" flags + match_level |= Messages[i].boost_level; + if (Current_mission_mood == Messages[i].mood) { // Boost messages that match the current mood match_level |= BUILTIN_MATCHES_MOOD; diff --git a/code/mission/missionmessage.h b/code/mission/missionmessage.h index becf693770b..fd9cc18ce20 100644 --- a/code/mission/missionmessage.h +++ b/code/mission/missionmessage.h @@ -163,6 +163,7 @@ typedef struct MissionMessage { MessageFilter subject_filter; MessageFilter outer_filter; int outer_filter_radius; + int boost_level; // unions for avi/wave information. Because of issues with Fred, we are using // the union to specify either the index into the avi or wave arrays above, From 0811c7037b9b3f817725a8aacf1d266c36e50be8 Mon Sep 17 00:00:00 2001 From: Naomi Coffman Date: Sat, 16 Sep 2023 13:06:37 -0400 Subject: [PATCH 4/6] Allow (easily) requiring an exact mood match --- code/mission/missionmessage.cpp | 67 ++++++++++++++++----------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/code/mission/missionmessage.cpp b/code/mission/missionmessage.cpp index 305a80694db..9b87a299d99 100644 --- a/code/mission/missionmessage.cpp +++ b/code/mission/missionmessage.cpp @@ -45,6 +45,7 @@ bool Allow_generic_backup_messages = false; float Command_announces_enemy_arrival_chance = 0.25; +#define DEFAULT_MOOD 0 SCP_vector Builtin_moods; int Current_mission_mood; @@ -426,6 +427,16 @@ void handle_legacy_backup_message(MissionMessage& msg, SCP_string wing_name) { strcpy(msg.name, backup); } +int lookup_mood(SCP_string& name) { + for (auto i = Builtin_moods.begin(); i != Builtin_moods.end(); ++i) { + if (*i == name) { + return (int) std::distance(Builtin_moods.begin(), i); + } + } + Warning(LOCATION, "Message.tbl has an entry for mood type %s, but this mood is not in the #Moods section of the table.", name.c_str()); + return -1; +} + // parses an individual message void message_parse(MessageFormat format) { MissionMessage msg; @@ -509,45 +520,31 @@ void message_parse(MessageFormat format) { } } + bool require_exact_mood_match; if (optional_string("$Mood:")) { - SCP_string buf; - bool found = false; - - stuff_string(buf, F_NAME); - for (SCP_vector::iterator iter = Builtin_moods.begin(); iter != Builtin_moods.end(); ++iter) { - if (*iter == buf) { - msg.mood = (int)std::distance(Builtin_moods.begin(), iter); - found = true; - break; - } - } - - if (!found) { - // found a mood, but it's not in the list of moods at the start of the table - Warning(LOCATION, "Message.tbl has an entry for mood type %s, but this mood is not in the #Moods section of the table.", buf.c_str()); - } - } - else { - msg.mood = 0; + SCP_string buf; + stuff_string(buf, F_NAME); + msg.mood = lookup_mood(buf); + require_exact_mood_match = optional_string("+Require exact match"); + } else { + msg.mood = DEFAULT_MOOD; + require_exact_mood_match = false; } - if (optional_string("$Exclude Mood:")) { - SCP_vector buff; - bool found = false; - - stuff_string_list(buff); - for (SCP_vector::iterator parsed_moods = buff.begin(); parsed_moods != buff.end(); ++parsed_moods) { - for (SCP_vector::iterator iter = Builtin_moods.begin(); iter != Builtin_moods.end(); ++iter) { - if (!stricmp(iter->c_str(), parsed_moods->c_str())) { - msg.excluded_moods.push_back((int)std::distance(Builtin_moods.begin(), iter)); - found = true; - break; - } + if (require_exact_mood_match) { + for (auto i = Builtin_moods.begin(); i != Builtin_moods.end(); ++i) { + int mood = (int) std::distance(Builtin_moods.begin(), i); + if (mood != msg.mood) { + msg.excluded_moods.push_back(mood); } - - if (!found) { - // found a mood, but it's not in the list of moods at the start of the table - Warning(LOCATION, "Message.tbl has an entry for exclude mood type %s, but this mood is not in the #Moods section of the table.", parsed_moods->c_str()); + } + } else if (optional_string("$Exclude Mood:")) { + SCP_vector buf; + stuff_string_list(buf); + for (auto i = buf.begin(); i != buf.end(); ++i) { + int mood = lookup_mood(*i); + if (mood >= 0) { + msg.excluded_moods.push_back(mood); } } } From 15db3b17bc0aadeb11691139ea1280f3f0bec9cb Mon Sep 17 00:00:00 2001 From: Naomi Coffman Date: Sun, 24 Sep 2023 11:09:31 -0400 Subject: [PATCH 5/6] Respond to PR feedback --- code/mission/missionmessage.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/code/mission/missionmessage.cpp b/code/mission/missionmessage.cpp index 9b87a299d99..d738cecb1e0 100644 --- a/code/mission/missionmessage.cpp +++ b/code/mission/missionmessage.cpp @@ -427,7 +427,7 @@ void handle_legacy_backup_message(MissionMessage& msg, SCP_string wing_name) { strcpy(msg.name, backup); } -int lookup_mood(SCP_string& name) { +int lookup_mood(SCP_string const& name) { for (auto i = Builtin_moods.begin(); i != Builtin_moods.end(); ++i) { if (*i == name) { return (int) std::distance(Builtin_moods.begin(), i); @@ -2094,15 +2094,19 @@ bool filters_match(MessageFilter& filter, ship* it) { bool outer_filters_match(MessageFilter& filter, int range, ship* sender) { for (auto i: list_range(&Ship_obj_list)) { auto obj = &Objects[i->objnum]; + // Ignore dying ships if (obj->flags[Object::Object_Flags::Should_be_dead]) { continue; } - if ((range >= 0) && (vm_vec_dist(&obj->pos, &Objects[sender->objnum].pos) > range)) { + // Ignore the sender itself + if (sender->objnum == i->objnum) { continue; } - if (sender->objnum == i->objnum) { - continue; - } + // If a range was specified, ignore anything out of range + if ((range > 0) && (vm_vec_dist(&obj->pos, &Objects[sender->objnum].pos) > range)) { + continue; + } + // If we got this far, we can check our filters if (filters_match(filter, &Ships[obj->instance])) { return true; } From 2bcedc34cc163121150a5e7625560cbd1f508021 Mon Sep 17 00:00:00 2001 From: Naomi Coffman Date: Mon, 16 Oct 2023 12:36:48 -0400 Subject: [PATCH 6/6] Respond to PR feedback --- code/mission/missionmessage.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/code/mission/missionmessage.cpp b/code/mission/missionmessage.cpp index d738cecb1e0..926b01f0cf9 100644 --- a/code/mission/missionmessage.cpp +++ b/code/mission/missionmessage.cpp @@ -63,15 +63,15 @@ builtin_message Builtin_messages[] = { #undef X }; -#define BUILTIN_BOOST_LEVEL_ONE 1 -#define BUILTIN_BOOST_LEVEL_TWO 2 -#define BUILTIN_MATCHES_TYPE 4 -#define BUILTIN_MATCHES_FILTER 8 -#define BUILTIN_MATCHES_MOOD 16 -#define BUILTIN_MATCHES_SPECIES 32 -#define BUILTIN_MATCHES_PERSONA 64 +constexpr int BUILTIN_BOOST_LEVEL_ONE = 1; +constexpr int BUILTIN_BOOST_LEVEL_TWO = 2; +constexpr int BUILTIN_MATCHES_TYPE = 4; +constexpr int BUILTIN_MATCHES_FILTER = 8; +constexpr int BUILTIN_MATCHES_MOOD = 16; +constexpr int BUILTIN_MATCHES_SPECIES = 32; +constexpr int BUILTIN_MATCHES_PERSONA = 64; -#define BUILTIN_BOOST_LEVEL_THREE (BUILTIN_BOOST_LEVEL_ONE | BUILTIN_BOOST_LEVEL_TWO) +constexpr int BUILTIN_BOOST_LEVEL_THREE = (BUILTIN_BOOST_LEVEL_ONE | BUILTIN_BOOST_LEVEL_TWO); int get_builtin_message_type(const char* name) { for (int i = 0; i < MAX_BUILTIN_MESSAGE_TYPES; i++) { @@ -429,7 +429,7 @@ void handle_legacy_backup_message(MissionMessage& msg, SCP_string wing_name) { int lookup_mood(SCP_string const& name) { for (auto i = Builtin_moods.begin(); i != Builtin_moods.end(); ++i) { - if (*i == name) { + if (lcase_equal(*i, name)) { return (int) std::distance(Builtin_moods.begin(), i); } } @@ -2094,8 +2094,8 @@ bool filters_match(MessageFilter& filter, ship* it) { bool outer_filters_match(MessageFilter& filter, int range, ship* sender) { for (auto i: list_range(&Ship_obj_list)) { auto obj = &Objects[i->objnum]; - // Ignore dying ships - if (obj->flags[Object::Object_Flags::Should_be_dead]) { + // Ignore dead/dying ships + if (obj->flags[Object::Object_Flags::Should_be_dead] || Ships[obj->instance].flags[Ship::Ship_Flags::Dying]) { continue; } // Ignore the sender itself