Difference between revisions of "AI War 2:Custom Text Messages To The Player"

From Arcen Wiki
Jump to navigation Jump to search
 
(2 intermediate revisions by the same user not shown)
Line 4: Line 4:
 
What is presented below is an older form of "journal entry," which is just embedded in the chat log.  It can be useful to do that sort of thing at various points as well, but those are... less robust.  The journal entries that we're referring to at the above link is instead like uncovering little wiki entries, almost, that show up in a dedicated tab in your sidebar as you play.
 
What is presented below is an older form of "journal entry," which is just embedded in the chat log.  It can be useful to do that sort of thing at various points as well, but those are... less robust.  The journal entries that we're referring to at the above link is instead like uncovering little wiki entries, almost, that show up in a dedicated tab in your sidebar as you play.
  
= Original Q&A =  
+
= Revised Q&A =  
  
 
'''Q:''' Do you have sample code for how to emit different journal entries? For example, I'd like a permanent journal entry at game start when you have allied marauders that basically explain how marauders work, and that they can build Raiders if they have their own planets.
 
'''Q:''' Do you have sample code for how to emit different journal entries? For example, I'd like a permanent journal entry at game start when you have allied marauders that basically explain how marauders work, and that they can build Raiders if they have their own planets.
  
'''A:''' Sure -- it's super easy, thankfully, and just piggybacking on our existing code for sending players messages.
+
'''A:''' This has changed a LOT! 
  
1. First off, to have something show up in the sidebar log for a second and then NOT go to the journal (aka behavior up until now), you need to do this sort of thing:
+
=== Where Do Do That Sort Of Permanent Logging ===
  
            World_AIW2.Instance.QueueLogMessageCommand( "Not enough energy to place " + typeToPlace.DisplayName, JournalEntryImportance.NeverLogCentrally, "CannotDoThatThing", context );
+
Now you will find the new instructions for that in the [[AI_War_2:_Journal_Entries#Q.26A_On_How_To_Log_Journal_Entries_From_Custom_Code|Q&A On How To Log Journal Entries From Custom Code]].
  
There are several overloads, but it's the new NeverLogCentrally that matters.
+
=== Why We'd Use This Instead ===
  
2. Taking a step up from that, let's log it to the journal but consider it no more important than the average chat message.  So if the players are chatting heavily in multiplayer via text chat for some reason, this will get knocked off the list fast.  These are any of the overloads of the method, but along these lines:
+
So we've established at this point that nothing we do here is actually permanent, correct?
  
            World_AIW2.Instance.QueueLogMessageCommand( "Human Resistance Fighters arriving to reinforce " + targetPlanet.Name, JournalEntryImportance.Normal, "HumanResistanceFightersWarpInNeutralPlanet" , Context );
+
Okay. So, our purpose here is just to write to the chat log. This means a little popup on the right side of the screen for 10 seconds, and then it's visible in the chat window until 50 more messages are loggedOnly the last 50 chat log messages are kept, whereas all journal entries are kept forever.
  
They have an importance of normal.
+
The goal of using something like this is probably to tell people about something that is happening right now ("hey, marauders are back for the 80th time, just thought you should know") in a conversational way that really makes them notice it, but without logging it forever (seriously, a journal full of 80 marauder attacks would be so overstuffed as to be incomprehensible.
  
3. Next step up is stuff to "keep longer."  When it culls the list of journal entries down to 50, it first clears all the normal ones, then only starts clearing the oldest of these keeplonger ones if there are still more than 50 messagesSo these should be used for mid-tier importance stuffSomething that's important maybe for a few hours, I guess, but not an absolute turning point in the game.
+
One thing you should NOT do is use this AND a journal entry call for permanent logging. Why?  Journal entries have chat_text built right in as a field on them, and they will already send a message to the chat log when they arrive, if the journal entry author feels that is warrantedTrust the journal entry authorIf you do both, probably players will get double messages.
  
Actually, your thing that explains how marauders work I feel like falls under this category -- it probably won't disappear in a regular game, at least not for a lot of hours (think of the flow of messages here, you'd have to have more than 50 keeplonger or neverdiscard messages all in there clogging stuff up for this to ever go away.
+
So here are some use cases for this sort of thing:
  
You could even make the argument that you might just want to use Normal importance, so that it disappears if 50 more messages or chats get sent.  How long is this really relevant?  Etc.  But I think that for the intro message at least, it being keeplonger might make sense.  Most other events that aren't absolutely pivotal to the game (even things like a spire relic being captured) should probably just be normal events.  Unless they're the sort of things a player might look back on and go "where did I hit X milestones in my quest?"
+
==== Local-Only ====
  
            World_AIW2.Instance.QueueLogMessageCommand("A Scourge Nemesis has spawned in the galaxy!", JournalEntryImportance.KeepLonger, null);
+
1. First off, to have something show up in the sidebar log for a second and then NOT go to the chat log -- and while we're at it, only go to the local player:
  
4. The strongest type is never discard, which stays absolutely forever. You won the game, the game majorly majorly changed in a way that happens almost never, etc.  Ideally there would not be more than maybe 10 of these in a game so there's still 40 journal spots for the other things. Also ideally, the total of keeplonger and neverdiscard would never be more than 25-30 in a game, or else I'll need to let the  journal be longer than it is now.  We can figure that out as we go.
+
            World_AIW2.Instance.QueueChatMessageOrCommand( "Not enough energy to place " + typeToPlace.DisplayName, ChatType.ShowLocallyOnly, "CannotDoThatThing", context );
  
But an example:
+
There are several overloads, but it's the new ChatType.ShowLocallyOnly that matters.
  
             World_AIW2.Instance.QueueLogMessageCommand( "The AI Civil War has begun", JournalEntryImportance.NeverDiscard, "", Context );
+
==== Main Chat For Everyone ====
 +
 
 +
2. Taking a step up from that, let's tell everyone and dump that in the general chat log:
 +
 
 +
             World_AIW2.Instance.QueueChatMessageOrCommand( "Human Resistance Fighters arriving to reinforce " + targetPlanet.Name, ChatType.LogToCentralChat, "HumanResistanceFightersWarpInNeutralPlanet", Context );
 +
 
 +
= The Various Method Overloads And How To Use Them =
 +
 
 +
There are several different overloads to World_AIW2.Instance.QueueChatMessageOrCommand(), and all of them are safe to be called from any thread on any machine.  Please DO bear in mind that these will generate a GameCommand that then gets executed on everyone's machine... UNLESS you have it set to ChatType.ShowLocallyOnly.  So if you are in sim code that happens for everybody in multiplayer, you really want to wrapper it like this:
 +
 
 +
                    if ( Engine_Universal.GetIsHostMode() )
 +
                    {
 +
                        //your code in here
 +
                    }
 +
 
 +
You have no idea how many clients there are, but there is always one host.  In single player there is one host and no clients.  And all code definitely runs on the host, but not all code runs on clients (some code, like the long-term planning threads, is host-only).  If you DON'T wrapper it in this way, then you'll get multiple copies of the journal entry -- one per player in the game at the moment.
 +
 
 +
==Using This For GUI Feedback ==
 +
 
 +
This example from above typifies it:
 +
 
 +
            World_AIW2.Instance.QueueChatMessageOrCommand( "Not enough energy to place " + typeToPlace.DisplayName, ChatType.ShowLocallyOnly, "CannotDoThatThing", context );
 +
 
 +
Essentially, you want to show the current player that they are not allowed to do the thing that they just did, and play a little buzzer sound.  None of this should go to other players, and it should not be in the chat log even locally.  It just needs to pop up on the side of the screen there so that players know why their click just failed.
 +
 
 +
These types of calls to QueueChatMessageOrCommand are VERY specific to the local machine, and you should never be wrappering them in GetIsHostMode() or similar.  If you do, then only the host would get notice of why their clicks fail, etc.  That sort of limitation to just being on the host is for something that happens from all the machines and needs to be broadcast to everyone else.
 +
 
 +
==Using This For Multiplayer Communication ==
 +
 
 +
It is possible that you could also do something like this in response to a GUI click:
 +
 
 +
            World_AIW2.Instance.QueueChatMessageOrCommand( localPlayerName + " would like to bring your attention to " + ship.Typedata.DisplayName + " on  " + ship.Planet.Name + ".  Click here to view it.", ChatType.LogToCentralChat, string.Empty, ship, Context );
 +
 
 +
This would then broadcast to all players and the chat log that you've just indicated some specific ship on a given planet, as an example.  They could then click on the message from you, either while it is popped-up or when it is in the longer-term chat log (only the last 50 messages, still, but this is plenty of time) to see what you were indicating.
 +
 
 +
==Overrides include==
 +
 
 +
* void QueueChatMessageOrCommand( string Message, ChatType Type, ArcenSimContextBase ContextCanBeNull )
 +
** Message
 +
*** The literal text of the message to show.  If you want special colorization or similar, use [http://digitalnativestudios.com/textmeshpro/docs/rich-text/ rich text tags from TextMeshPro].
 +
** Type
 +
*** ChatType.ShowLocallyOnly
 +
**** This is for local UI feedback only.  It goes to no other players and no chat logs, here or otherwise.  No GameCommands are generated.
 +
*** ChatType.ShowToEveryone
 +
**** This is for showing everybody, but just for 10 seconds for some reason.  It goes to all players (on the right side of the screen), but no chat logs. It generates a GameCommand to do so.  Is there really a use for this?  Eh, whatever, it's here in the interest of completeness.
 +
*** ChatType.LogToCentralChat
 +
**** This is for showing everybody, and having it go to the ongoing chat log.  It goes to all players (on the right side of the screen) for 10 seconds at first. It generates a GameCommand to do so.  This is useful for inter-player communication along the lines of what we described above.
 +
** ContextCanBeNull
 +
*** You can pass in a null, but it's better if you pass in an ArcenSimContext.
 +
*** In the event that maybe you are on some sort of background thread, passing in an ArcenSimContext will ensure that your command gets properly queued.
 +
*** Worst case, you pass in a null and are on a background thread, and potentially your main-thread sim queue gets a little scrambled or even throws an error.
 +
 
 +
* void QueueChatMessageOrCommand( string Message, ChatType Type, ArcenSimContext ContextCanBeNull )
 +
** This is identical to the method above, but uses ArcenSimContext rather than ArcenSimContextBase.  If you're reading this and not in the core codebase (ArcenUniversal), then you'll automatically wind up using this version.  But it really doesn't matter.
 +
** The purpose of having two methods is so that some of our stuff that is game-agnostic in ArcenUniversal can still send chat messages to the sidebar, like voice clips.  It's actually a LOT more common for a voice clip to do this than logging to the journal.
 +
 
 +
* void QueueChatMessageOrCommand( string Message, ChatType Type, string SFXItemInternalName, ArcenSimContext Context)
 +
** SFXItemInternalName
 +
*** This string is the name of a sound clip, which can be a sound or a voice clip.  It will play on the appropriate audio bus as defined by the sound clip, and it will avoid playing if the rules of playing it say not to.
 +
*** In most cases this would just be string.Empty, unless you want it to buzz in a "you can't do that" sort of way and use "CannotDoThatThing", or use some sort of happy sound for confirming an action.  In other cases, like one of the examples above, you might have it play a voice clip for everyone.
 +
*** Please do be careful with playing voice clips via these, as in some cases the voice clips will themselves also write a text message to the chat log, stating the subtitles for whatever the voice clip says.
 +
 
 +
* void QueueChatMessageOrCommand( string Message, ChatType Type, string SFXItemInternalName, GameEntity_Squad RelatedEntityOrNull, ArcenSimContext Context)
 +
** RelatedEntityOrNull
 +
*** If this is present, then clicking this chat message will take you to that entity, if it is still alive when the players click on it.

Latest revision as of 16:19, 7 May 2020

Hey!

Do you really mean " Journal Entries", in the sense of those things that are defined in xml and new in version 2.040 of the game and further? If so, click that link. Those are what you should be using for long-term, detailed journal entries. And as a bonus, they don't require any custom code from you.

What is presented below is an older form of "journal entry," which is just embedded in the chat log. It can be useful to do that sort of thing at various points as well, but those are... less robust. The journal entries that we're referring to at the above link is instead like uncovering little wiki entries, almost, that show up in a dedicated tab in your sidebar as you play.

Revised Q&A

Q: Do you have sample code for how to emit different journal entries? For example, I'd like a permanent journal entry at game start when you have allied marauders that basically explain how marauders work, and that they can build Raiders if they have their own planets.

A: This has changed a LOT!

Where Do Do That Sort Of Permanent Logging

Now you will find the new instructions for that in the Q&A On How To Log Journal Entries From Custom Code.

Why We'd Use This Instead

So we've established at this point that nothing we do here is actually permanent, correct?

Okay. So, our purpose here is just to write to the chat log. This means a little popup on the right side of the screen for 10 seconds, and then it's visible in the chat window until 50 more messages are logged. Only the last 50 chat log messages are kept, whereas all journal entries are kept forever.

The goal of using something like this is probably to tell people about something that is happening right now ("hey, marauders are back for the 80th time, just thought you should know") in a conversational way that really makes them notice it, but without logging it forever (seriously, a journal full of 80 marauder attacks would be so overstuffed as to be incomprehensible.

One thing you should NOT do is use this AND a journal entry call for permanent logging. Why? Journal entries have chat_text built right in as a field on them, and they will already send a message to the chat log when they arrive, if the journal entry author feels that is warranted. Trust the journal entry author. If you do both, probably players will get double messages.

So here are some use cases for this sort of thing:

Local-Only

1. First off, to have something show up in the sidebar log for a second and then NOT go to the chat log -- and while we're at it, only go to the local player:

            World_AIW2.Instance.QueueChatMessageOrCommand( "Not enough energy to place " + typeToPlace.DisplayName, ChatType.ShowLocallyOnly, "CannotDoThatThing", context );

There are several overloads, but it's the new ChatType.ShowLocallyOnly that matters.

Main Chat For Everyone

2. Taking a step up from that, let's tell everyone and dump that in the general chat log:

            World_AIW2.Instance.QueueChatMessageOrCommand( "Human Resistance Fighters arriving to reinforce " + targetPlanet.Name, ChatType.LogToCentralChat, "HumanResistanceFightersWarpInNeutralPlanet", Context );

The Various Method Overloads And How To Use Them

There are several different overloads to World_AIW2.Instance.QueueChatMessageOrCommand(), and all of them are safe to be called from any thread on any machine. Please DO bear in mind that these will generate a GameCommand that then gets executed on everyone's machine... UNLESS you have it set to ChatType.ShowLocallyOnly. So if you are in sim code that happens for everybody in multiplayer, you really want to wrapper it like this:

                   if ( Engine_Universal.GetIsHostMode() )
                   {
                       //your code in here
                   }

You have no idea how many clients there are, but there is always one host. In single player there is one host and no clients. And all code definitely runs on the host, but not all code runs on clients (some code, like the long-term planning threads, is host-only). If you DON'T wrapper it in this way, then you'll get multiple copies of the journal entry -- one per player in the game at the moment.

Using This For GUI Feedback

This example from above typifies it:

            World_AIW2.Instance.QueueChatMessageOrCommand( "Not enough energy to place " + typeToPlace.DisplayName, ChatType.ShowLocallyOnly, "CannotDoThatThing", context );

Essentially, you want to show the current player that they are not allowed to do the thing that they just did, and play a little buzzer sound. None of this should go to other players, and it should not be in the chat log even locally. It just needs to pop up on the side of the screen there so that players know why their click just failed.

These types of calls to QueueChatMessageOrCommand are VERY specific to the local machine, and you should never be wrappering them in GetIsHostMode() or similar. If you do, then only the host would get notice of why their clicks fail, etc. That sort of limitation to just being on the host is for something that happens from all the machines and needs to be broadcast to everyone else.

Using This For Multiplayer Communication

It is possible that you could also do something like this in response to a GUI click:

            World_AIW2.Instance.QueueChatMessageOrCommand( localPlayerName + " would like to bring your attention to " + ship.Typedata.DisplayName + " on  " + ship.Planet.Name + ".  Click here to view it.", ChatType.LogToCentralChat, string.Empty, ship, Context );

This would then broadcast to all players and the chat log that you've just indicated some specific ship on a given planet, as an example. They could then click on the message from you, either while it is popped-up or when it is in the longer-term chat log (only the last 50 messages, still, but this is plenty of time) to see what you were indicating.

Overrides include

  • void QueueChatMessageOrCommand( string Message, ChatType Type, ArcenSimContextBase ContextCanBeNull )
    • Message
    • Type
      • ChatType.ShowLocallyOnly
        • This is for local UI feedback only. It goes to no other players and no chat logs, here or otherwise. No GameCommands are generated.
      • ChatType.ShowToEveryone
        • This is for showing everybody, but just for 10 seconds for some reason. It goes to all players (on the right side of the screen), but no chat logs. It generates a GameCommand to do so. Is there really a use for this? Eh, whatever, it's here in the interest of completeness.
      • ChatType.LogToCentralChat
        • This is for showing everybody, and having it go to the ongoing chat log. It goes to all players (on the right side of the screen) for 10 seconds at first. It generates a GameCommand to do so. This is useful for inter-player communication along the lines of what we described above.
    • ContextCanBeNull
      • You can pass in a null, but it's better if you pass in an ArcenSimContext.
      • In the event that maybe you are on some sort of background thread, passing in an ArcenSimContext will ensure that your command gets properly queued.
      • Worst case, you pass in a null and are on a background thread, and potentially your main-thread sim queue gets a little scrambled or even throws an error.
  • void QueueChatMessageOrCommand( string Message, ChatType Type, ArcenSimContext ContextCanBeNull )
    • This is identical to the method above, but uses ArcenSimContext rather than ArcenSimContextBase. If you're reading this and not in the core codebase (ArcenUniversal), then you'll automatically wind up using this version. But it really doesn't matter.
    • The purpose of having two methods is so that some of our stuff that is game-agnostic in ArcenUniversal can still send chat messages to the sidebar, like voice clips. It's actually a LOT more common for a voice clip to do this than logging to the journal.
  • void QueueChatMessageOrCommand( string Message, ChatType Type, string SFXItemInternalName, ArcenSimContext Context)
    • SFXItemInternalName
      • This string is the name of a sound clip, which can be a sound or a voice clip. It will play on the appropriate audio bus as defined by the sound clip, and it will avoid playing if the rules of playing it say not to.
      • In most cases this would just be string.Empty, unless you want it to buzz in a "you can't do that" sort of way and use "CannotDoThatThing", or use some sort of happy sound for confirming an action. In other cases, like one of the examples above, you might have it play a voice clip for everyone.
      • Please do be careful with playing voice clips via these, as in some cases the voice clips will themselves also write a text message to the chat log, stating the subtitles for whatever the voice clip says.
  • void QueueChatMessageOrCommand( string Message, ChatType Type, string SFXItemInternalName, GameEntity_Squad RelatedEntityOrNull, ArcenSimContext Context)
    • RelatedEntityOrNull
      • If this is present, then clicking this chat message will take you to that entity, if it is still alive when the players click on it.