Author Topic: [patch submission] Persona-specific promotion debriefings  (Read 1742 times)

0 Members and 1 Guest are viewing this topic.

Offline Yarn

  • 210
[patch submission] Persona-specific promotion debriefings
Since FSPort has promotion debriefings that differ from the other FSPort debriefing personas, I decided to make a patch that allows for different personas to have different promotion text. Here's the patch:
Code: [Select]
Index: code/missionui/missiondebrief.cpp
===================================================================
--- code/missionui/missiondebrief.cpp (revision 10046)
+++ code/missionui/missiondebrief.cpp (working copy)
@@ -916,10 +916,8 @@
 // Goober5000
 // V sez: "defaults to number 9 (Petrarch) for non-volition missions
 // this is an ugly, nasty, hateful way of doing this, but it saves us changing the missions at this point"
-void debrief_choose_voice(char *voice_dest, char *voice_base, int default_to_base = 0)
+void debrief_choose_voice(char *voice_dest, char *voice_base, int persona_index, int default_to_base = 0)
 {
- // see if we have a persona
- int persona_index = debrief_find_persona_index();
  if (persona_index >= 0)
  {
  // get voice file
@@ -1001,11 +999,19 @@
  debrief_choose_medal_variant(buf, Rank_medal_index, Promoted);
  Rank_bitmap = bm_load(buf);
 
- Promotion_stage.text = Ranks[Promoted].promotion_text;
+ // see if we have a persona
+ int persona_index = debrief_find_persona_index();
+
+ // use persona-specific promotion text if it exists; otherwise, use default
+ if (Ranks[Promoted].promotion_text.find(persona_index) != Ranks[Promoted].promotion_text.end()) {
+ Promotion_stage.text = Ranks[Promoted].promotion_text[persona_index];
+ } else {
+ Promotion_stage.text = Ranks[Promoted].promotion_text[-1];
+ }
  Promotion_stage.recommendation_text = "";
 
  // choose appropriate promotion voice for this mission
- debrief_choose_voice(Promotion_stage.voice, Ranks[Promoted].promotion_voice_base);
+ debrief_choose_voice(Promotion_stage.voice, Ranks[Promoted].promotion_voice_base, persona_index);
 
  debrief_add_award_text(Ranks[Promoted].name);
  }
@@ -1016,11 +1022,19 @@
  debrief_choose_medal_variant(buf, Player->stats.m_badge_earned, Player->stats.medal_counts[Player->stats.m_badge_earned] - 1);
  Badge_bitmap = bm_load(buf);
 
- Badge_stage.text = Medals[Player->stats.m_badge_earned].promotion_text;
+ // see if we have a persona
+ int persona_index = debrief_find_persona_index();
+
+ // use persona-specific badge text if it exists; otherwise, use default
+ if (Ranks[Promoted].promotion_text.find(persona_index) != Ranks[Promoted].promotion_text.end()) {
+ Badge_stage.text = Medals[Player->stats.m_badge_earned].promotion_text[persona_index];
+ } else {
+ Badge_stage.text = Medals[Player->stats.m_badge_earned].promotion_text[-1];
+ }
  Badge_stage.recommendation_text = "";
 
  // choose appropriate badge voice for this mission
- debrief_choose_voice(Badge_stage.voice, Medals[Player->stats.m_badge_earned].voice_base);
+ debrief_choose_voice(Badge_stage.voice, Medals[Player->stats.m_badge_earned].voice_base, persona_index);
 
  debrief_add_award_text(Medals[Player->stats.m_badge_earned].name);
  }
@@ -1082,7 +1096,7 @@
 // }
 
  // Goober5000
- debrief_choose_voice(stagep->voice, traitor_voice_file, 1);
+ debrief_choose_voice(stagep->voice, traitor_voice_file, debrief_find_persona_index(), 1);
 
  required_string("$Recommendation text:");
  stuff_string( stagep->recommendation_text, F_MULTITEXT, NULL);
Index: code/stats/medals.cpp
===================================================================
--- code/stats/medals.cpp (revision 10046)
+++ code/stats/medals.cpp (working copy)
@@ -190,10 +190,13 @@
  kills_needed = m.kills_needed;
  memcpy(voice_base, m.voice_base, MAX_FILENAME_LEN);
 
- if (m.promotion_text)
- promotion_text = vm_strdup(m.promotion_text);
- else
- promotion_text = NULL;
+ promotion_text.clear();
+ SCP_map<int, char*>::const_iterator it;
+ for (it = m.promotion_text.begin(); it != m.promotion_text.end(); it++) {
+ if (it->second) {
+ promotion_text[it->first] = vm_strdup(it->second);
+ }
+ }
 }
 
 // assignment operator
@@ -200,10 +203,13 @@
 const medal_stuff &medal_stuff::operator=(const medal_stuff &m)
 {
  if (this != &m) {
- if (promotion_text) {
- vm_free(promotion_text);
- promotion_text = NULL;
+ SCP_map<int, char*>::iterator it;
+ for (it = promotion_text.begin(); it != promotion_text.end(); it++) {
+ if (it->second) {
+ vm_free(it->second);
+ }
  }
+ promotion_text.clear();
  clone(m);
  }
 
@@ -340,6 +346,7 @@
  // this medal is a badge and should be treated specially
  if ( optional_string("+Num Kills:") ) {
  char buf[MULTITEXT_LENGTH];
+ int persona;
  stuff_int( &temp_medal.kills_needed );
 
  if (optional_string("$Wavefile 1:"))
@@ -351,9 +358,23 @@
  if (optional_string("$Wavefile Base:"))
  stuff_string(temp_medal.voice_base, F_NAME, MAX_FILENAME_LEN);
 
- required_string("$Promotion Text:");
- stuff_string(buf, F_MULTITEXT, sizeof(buf));
- temp_medal.promotion_text = vm_strdup(buf);
+ while (check_for_string("$Promotion Text:")) {
+ required_string("$Promotion Text:");
+ stuff_string(buf, F_MULTITEXT, sizeof(buf));
+ persona = -1;
+ if (optional_string("+Persona:")) {
+ stuff_int(&persona);
+ if (persona < 0) {
+ Warning(LOCATION, "Debriefing text for %s is assigned to an invalid persona: %i (must be 0 or greater).\n", temp_medal.name, persona);
+ continue;
+ }
+ }
+ temp_medal.promotion_text[persona] = vm_strdup(buf);
+ }
+ if (temp_medal.promotion_text.find(-1) == temp_medal.promotion_text.end()) {
+ Warning(LOCATION, "%s medal is missing default debriefing text.\n", temp_medal.name);
+ temp_medal.promotion_text[-1] = "";
+ }
  }
 
  Medals.push_back(temp_medal);
Index: code/stats/medals.h
===================================================================
--- code/stats/medals.h (revision 10046)
+++ code/stats/medals.h (working copy)
@@ -35,7 +35,7 @@
 
  //If this is a badge (kills_needed > 0)
  char voice_base[MAX_FILENAME_LEN];
- char *promotion_text;
+ SCP_map<int, char*> promotion_text;
 
  medal_stuff() {
  name[0] = '\0';
@@ -45,14 +45,17 @@
  version_starts_at_1 = false;
  kills_needed = 0;
  voice_base[0] = '\0';
- promotion_text = NULL;
+ promotion_text.clear();
  }
 
  ~medal_stuff() {
- if (promotion_text) {
- vm_free(promotion_text);
- promotion_text = NULL;
+ SCP_map<int, char*>::iterator it;
+ for (it = promotion_text.begin(); it != promotion_text.end(); it++) {
+ if (it->second) {
+ vm_free(it->second);
+ }
  }
+ promotion_text.clear();
  }
 
  medal_stuff(const medal_stuff &m) { clone(m); }
Index: code/stats/scoring.cpp
===================================================================
--- code/stats/scoring.cpp (revision 10046)
+++ code/stats/scoring.cpp (working copy)
@@ -58,7 +58,7 @@
 {
  atexit(scoreing_close);
  char buf[MULTITEXT_LENGTH];
- int rval, idx;
+ int rval, idx, persona;
 
  // open localization
  lcl_ext_open();
@@ -86,11 +86,25 @@
  stuff_string( Ranks[idx].bitmap, F_NAME, MAX_FILENAME_LEN );
  required_string("$Promotion Voice Base:");
  stuff_string( Ranks[idx].promotion_voice_base, F_NAME, MAX_FILENAME_LEN );
- required_string("$Promotion Text:");
- stuff_string(buf, F_MULTITEXT, sizeof(buf));
- drop_white_space(buf);
- compact_multitext_string(buf);
- Ranks[idx].promotion_text = vm_strdup(buf);
+ while (check_for_string("$Promotion Text:")) {
+ required_string("$Promotion Text:");
+ stuff_string(buf, F_MULTITEXT, sizeof(buf));
+ drop_white_space(buf);
+ compact_multitext_string(buf);
+ persona = -1;
+ if (optional_string("+Persona:")) {
+ stuff_int(&persona);
+ if (persona < 0) {
+ Warning(LOCATION, "Debriefing text for %s rank is assigned to an invalid persona: %i (must be 0 or greater).\n", Ranks[idx].name, persona);
+ continue;
+ }
+ }
+ Ranks[idx].promotion_text[persona] = vm_strdup(buf);
+ }
+ if (Ranks[idx].promotion_text.find(-1) == Ranks[idx].promotion_text.end()) {
+ Warning(LOCATION, "%s rank is missing default debriefing text.\n", Ranks[idx].name);
+ Ranks[idx].promotion_text[-1] = "";
+ }
  idx++;
  }
 
@@ -1513,8 +1527,13 @@
 
 void scoreing_close()
 {
+ SCP_map<int, char*>::iterator it;
  for(int i = 0; i<NUM_RANKS; i++) {
- if(Ranks[i].promotion_text)
- vm_free(Ranks[i].promotion_text);
+ for (it = Ranks[i].promotion_text.begin(); it != Ranks[i].promotion_text.end(); it++) {
+ if (it->second) {
+ vm_free(it->second);
+ }
+ }
+ Ranks[i].promotion_text.clear();
  }
 }
Index: code/stats/scoring.h
===================================================================
--- code/stats/scoring.h (revision 10046)
+++ code/stats/scoring.h (working copy)
@@ -63,7 +63,7 @@
 
 typedef struct rank_stuff {
  char name[NAME_LENGTH]; // name of this rank
- char *promotion_text; // text to display when promoted to this rank
+ SCP_map<int, char*> promotion_text; // text to display when promoted to this rank
  int points; // points needed to reach this rank
  char bitmap[MAX_FILENAME_LEN]; // bitmap of this rank medal
  char promotion_voice_base[MAX_FILENAME_LEN];


Here's an example of how this feature is utilized:
Code: [Select]
$Name: Lieutenant
$Points: 10000
$Bitmap: medal12b.pcx
$Promotion Voice Base: rank_b.wav

$Promotion Text:
XSTR("After reviewing your service record and the recommendations of your superior officers, Allied Command has authorized your promotion. Congratulations, Lieutenant.", 2931)
$end_multi_text

$Promotion Text:
XSTR("Per GTAR 64-2a you are hereby promoted to the rank of Lieutenant.  Congratulations.", -1)
$end_multi_text
+Persona: 10

In this example, the first $Promotion Text entry looks the same as before, making it the default text. The second entry, however, is immediately followed by +Persona: 10, which indicates that this text should be used instead of the default text if the debriefing persona is 10.

Any number of $Promotion Text entries may be used as long as the following conditions are adhered to: 1) There is a default entry (i.e., one without a +Persona field), and 2) no two entries of a rank/medal are assigned as default or to the same persona. Entries may appear in any order.

This feature may be used with ranks in rank.tbl as well as the Ace medals in medals.tbl.


I have created a mod that lets you try this out. It includes two campaigns that are identical save for the debriefing persona. Each mission has a non-moving enemy fighter that you can destroy for an Ace medal. (I reduced the thresholds of the Aces to 1, 2, and 3 kills, so they should be easy to test.) At the end of each mission, you will be automatically promoted.

Here is the mod: https://dl.dropboxusercontent.com/u/89353583/FreeSpace/PersonaSpecificDebriefings.zip


(I know that there's also traitor.tbl, I have not yet been successful in doing the same with that. Since its format is different, it would probably deserve its own patch anyway.)
"Your fighter is running out of oil.  Please check under the hood and add more if necessary"
--strings.tbl, entry 177

"Freespace is very tired.  It is shutting down to get some rest."
--strings.tbl, entry 178

 

Offline niffiwan

  • 211
  • Eluder Class
Re: [patch submission] Persona-specific promotion debriefings
I've reviewed this, and once I got past my surprise at seeing [-1] (it's a map, NOT an array :)) I think it all looks good.  Committed in r10124.
Creating a fs2_open.log | Red Alert Bug = Hex Edit | MediaVPs 2014: Bigger HUD gauges | 32bit libs for 64bit Ubuntu
----
Debian Packages (testing/unstable): Freespace2 | wxLauncher
----
m|m: I think I'm suffering from Stockholm syndrome. Bmpman is starting to make sense and it's actually written reasonably well...

  

Offline mjn.mixael

  • Cutscene Master
  • 212
  • Chopped liver
    • Steam
    • Twitter
Re: [patch submission] Persona-specific promotion debriefings
Be sure to wiki how to do this... cause it's neat.

I would hate for this feature to be lost to the forum history like a few others I've found. :)
Cutscene Upgrade Project - Mainhall Remakes - Between the Ashes
Youtube Channel - P3D Model Box
Between the Ashes is looking for committed testers, PM me for details.
Freespace Upgrade Project See what's happening.