HeroEngine Forums
Welcome, Guest. Please login or Register for HeroCloud Account.

Author Topic: Chat Over Head  (Read 1919 times)

nocake

  • General Accounts
  • *
  • Posts: 404
    • View Profile
    • BlightMMO
Chat Over Head
« on: Apr 08, 14, 06:40:57 PM »

This is the functionality for creating a command /say that users can enter to have their text display above their head to others.
This is here for others to use and for my own critique.

I only implemented the functionality. There still needs to be a lot of polishing in regards to displaying the text.

I wrote this tutorial as I created this functionality. Revisions are sure to come.


1. First register the new command in the console with:
   
Code: [Select]
register /say script=>"cmdTell"

2. Open Server Side cmdTell script


3. Locate HE_ProcessCommandInput function:

Original Function:
Code: [Select]

shared function HE_ProcessCommandInput( account as NodeRef, input as String )
  start as Integer = FindString( input, " " ) + 1
  end as Integer = FindStringEx( input, " ", start )
  if end < 0
    end = input.length
  .
  //  FindPlayerByName requires the name be all caps
  var name = ToUpper( SubString( input, start, end - start ) )
  var message = SubString( input, end + 1, input.length )
  var player = FindPlayerByName( name )
  if player != 0
    SendTellToPlayer( player, account, message )
  else
    //  The player isn't here, so try again on the world server.
    call area 0 instance 0 cmdTell:FindPlayerForTell( name, account, message )
  .
.


NOTE: We are going to add to this function by adding a partialMatch() case statement that checks command entered by tokenizing the input into a list of strings. Take note of what code gets moved under the case statement and what doesn't.


4. Lets Tokenize() the input to get the command entered
Add:
Code: [Select]
args as List of String
Tokenize( input, args )
Directly after:
Code: [Select]
  if end < 0
    end = input.length
  .


5a. Now add partialMatch() case statement and encapsulate the code for /tell in its own statement.

 Add:
Code: [Select]
partialMatch toLower( args[1] )
to "/tell"
After:
Code: [Select]

args as List of String
Tokenize( input, args )

5b. And close the new patrialMatch() case statement with two "."'s right before the closing "." for the HE_ProcessCommandInput function.





6. Now lets add the case statement for the /say command.

In between these two lines:

 
Code: [Select]
partialMatch toLower( args[1] )
 to "/tell"

Add:
Code: [Select]
to "/say"
      //ATTACH CHAT TO PLAYER
      var message =  SubString( input, start, input.length )
      targetList as List of NodeRef = GetChatTargetsInArea(pc,2)
      pc as NodeRef of Class E_playerCharacter = account.GetMyChar()
      foreach target in targetList
        where target is kindof E_CommonCharacter
          SendChatToPlayer( target.GetCombatantAccount(), pc, message )
        .
      .
    .




NOTE: 
Code: [Select]
where target is kindof E_CommonCharacter
          SendChatToPlayer( target.GetCombatantAccount(), pc, message )
        .
I have implemented my own E_commoncharacter. You will need to validate that what Target represents is actually a player.



7. We are going to want to amend 2 more functions to the cmdTell File:

Code: [Select]
function SendChatToPlayer( player as ID, talker as NodeRef, message as String )
  $CHAT.SendChatToClient( player, talker, message )
.



function GetChatTargetsInArea(character as NodeRef, size as Integer) as List of NodeRef
 
  visChar as NodeRef of Class _characterAppearance = character.GetMyVisibleCharacter()
 
  var targetList = GetTargetsInSphere(true, true, visChar.character_position, size)
 
  return targetList

.

FUNCTIONS EXPLAINED:
SendChatToPlayer - Call the extended function we will add to $CHAT system node in the coming steps.
GetChatTargetsInArea - Uses External Function GetTargetsInSphere to find targets around the player.


DONE WITH cmdTell file. Though you wont be able to compile yet so leave it open.

8. Extend $CHAT system node by creating E_ChatHandler class on the server in the DOM editor if you have not already.
9. Press f5 -> tools -> system node configuration
10. Select the server radio button
11. Select $CHAT in the drop down
12. Enter E_ChatHandler into the input field and hit add
13. Open E_ChatHandlerClassMethods server script and add:

Code: [Select]
method SendChatToClient(account as ID, talker as NodeRef, message as String)
  //-----------------------------------------
  // Play the specified Fx spec on the client
  //-----------------------------------------
 
  var rco = GetChatRemoteCall()
  rco.toPlayer = account
  rco.args["message"] = message
  rco.args["talker"] = talker

  RemoteCallClient(rco)
.



function GetChatRemoteCall() as Class RemoteCallClientOut
  rco as Class RemoteCallClientOut
  rco.toScript = SYSTEM.EXEC.THISSCRIPT
  rco.toFunction = "ChatOverHead"
  rco.failScript = debugUtils
  rco.failFunction = "ClientRMCFail"
 
  return rco
.



14. Open E_ChatHandlerClassMethods client script and amend:
Code: [Select]
remote function ChatOverHead(rci as Class RemoteCallServerIn)
  //--------------------------------
  // Play Fx from server remote call
  //--------------------------------
  message as String
  target as NodeRef

 
  message = rci.args["message"]
  talker as NodeRef = rci.args["talker"]
  talkerNode as NodeRef of Class HBNode
  talkerNode = talker.GetCharacterNode()
  if talkerNode == GetPlayerCharacterNode()
    n as NodeRef of Class ChatBubble = CreateNodeFromClass("ChatBubble")
    n.StartTimerChat($GUI.newChatSelfplate(message))
  else
    while(message.length != 0) 
      n as NodeRef of Class ChatBubble = CreateNodeFromClass("ChatBubble")
      n.StartTimerChat( $GUI.newChatplate(talkerNode, subString(message, 1, 50)) )
      message = subString(message, 51, 999999)
    .
  . 
.




14. Create field chatbubbleGUI  Type: noderef of class GUILabel
15. Create class ChatBubbleTimer add fields chatbubbleGUI and myTimer
16. Open ChatBubbleTimerClassMethods client script and add:
Code: [Select]
function myTimer_tick()
 where me is kindof ChatBubble
  DestroyNode(me.chatbubbleGUI)
  me.myTimer.stop()
 .
.

method StartTimerChat(gui as NodeRef)
  me.chatbubbleGUI = gui
  me.myTimer.script = SYSTEM.EXEC.THISSCRIPT
  me.myTimer.fireRate = 0:00:13
  me.myTimer.realTime = true
  me.myTimer.start()
.

NOTE: E_ChatHandlerClassMethods  has an if statement that checks if the chat command was sent by that user. If so we don't gluetonode the GUI element. Look closely at the two different functions added in step #17


17. Amend two functions to E_GUIClassMethods on the client side:

Code: [Select]

method newChatplate(character as NodeRef of Class HBNode, message as String) as NodeRef of Class GUIControl
//  if hasMethod(me, "HE_newChatplate")
//    //return me.HE_newChatplate(character, name)
//  .

 
  chattag as NodeRef of Class GUILabel = CreateNodeFromPrototype("_label")
  chattag.name = "chattag" + character
  chattag.size.x = 300
  chattag.size.y = 40
  chattag.gluedtonode = character
  chattag.defaultStatePresentation.color.r = 1
  chattag.defaultStatePresentation.color.g = 1
  chattag.defaultStatePresentation.color.b = 1
  chattag.justification = CENTER
  chattag.text = "{b:#0,0,0,.5}{f:#0,1,0,1}"+message+"{/}{/}"
  chattag.displayfont = DEFAULTTEXT
  chattag.fadeDistance = 1
  chattag.build = true
  chattag.autoSetHeight = false
  chattag.visible = true
  chattag.layer = "chatplates"
  chattag.worldspaceoffset = $CHARACTERSYSTEM._GetNametagOffset( GetCharacterSpecification( character ) )
  chattag.worldspaceoffset.y = (chattag.worldspaceoffset.y-.03)

  return chattag
.


method newChatSelfplate(message as String) as NodeRef of Class GUIControl
//  if hasMethod(me, "HE_newChatplate")
//    //return me.HE_newChatplate(character, name)
//  .

 
  chattag as NodeRef of Class GUILabel = CreateNodeFromPrototype("_label")
  chattag.name = "chattagSelf"
  chattag.size.x = 300
  chattag.size.y = 40
  chattag.position.y = 400
  chattag.defaultStatePresentation.color.r = 1
  chattag.defaultStatePresentation.color.g = 1
  chattag.defaultStatePresentation.color.b = 1
  chattag.justification = CENTER
  chattag.text = "{b:#0,0,0,.5}You Said: {f:#0,1,0,1}"+message+"{/}{/}"
  chattag.displayfont = DEFAULTTEXT
  chattag.fadeDistance = 1
  chattag.build = true
  chattag.autoSetHeight = false
  chattag.visible = true
  chattag.layer = "chatplates"


  return chattag
.



18. In E_GUIClassMethods client side script find HE_validGUILayersList()  and add:
 
Code: [Select]
add back "chatplates" to layers
( I added it directly after the nameplates entry )


You should be able to compile and submit all scripts now.
« Last Edit: Apr 08, 14, 07:12:29 PM by nocake »
Logged

nocake

  • General Accounts
  • *
  • Posts: 404
    • View Profile
    • BlightMMO
Re: Chat Over Head
« Reply #1 on: Apr 09, 14, 09:28:51 PM »

This is a simple implementation of this system. You will need to implement more features to consider for your game play.

I had a discussion with user:Keeperofstars about this topic. These are things you will need to consider:


who uses chat anymore anyways, especially in FPS. Not to mention in any game where there is any element of FPS, chat bubbles become a liability. Gamers just never understand that. Don't want your enemy seeing a chat bubble above your head, so you turn them off for opposing sides, or non-friendly, then the issue of having someone's chat bubble messing up your shot, so now they need user adjustable alpha options. The list of problems gets to be insane. Yet gamers never think about it, and just blame the devs / engine.

I just implemented a chat over head and I will be watching for these problems.

I would like to comment on a few things you mentioned though:

There are plenty of ways to handle Chat over head. For instance: I have a fade distance of 1 with a replication size of 2. This means that only people within 1 distance will see the message but everyone 2 distances away will be aware of the GUI node but not actually display it until they are within the 1.

This allows for people to talk closely with out people detecting or seeing the graphic from far away. Or even detecting the extra network information as they still need to be relatively close to even be alerted about the chat bubble.

This chat is not meant to be the main form of communication either but a way to easily communicate if you run into anyone on the field. (open world PVPFFA)

Chat bubbles can also be disabled or EXTREMELY lowered alpha if they are flagged for combat. That way it wont get in the way of your high endurance battle.

I can also limit how much my players can say over head at once, characters, lines, etc.




TO NOT THREAD JACK ANYMORE: I have copied these comments into my tutorial thread for things for people to consider when implementing.

https://community.heroengine.com/forums/index.php/topic,5331.0.html





just a lot of overhead, and rendering that isn't needed.

There is a key line to walk with any feature, while it might be a highly requested feature, to those that are vocal. Need to look at your core player base, not the fanatical side screaming for features, but the core target player.

For example if you are going for any core features driven by guilds, then chat is almost to rarely used, except for coordination between alliances, in this case chat bubbles never will be visible. almost every single guild around will have a voice chat setup, even pick up players use skype now. But between mumble, vent, teamspeak, it's almost never going to have chat via type. Except for dealing with people that aren't in your circle to say. Trade, global chat, cross group / guild chatting.

If you look at trade, well that is a horrible idea, chats get stacked and missed, cause lots of draw call issues. So everyone looks to the chat / trade window, about the only half decent function a chat bubble makes is to help locate trade spammers. In case of a trade you wouldn't want a short range chat bubble anyways, cause would be impossible to use for locating trade. So really it's not a solution or option for use. So chat bubble for trade is never used. So now what cross area group chat, and guild chat for coordination, once again won't be a usable case for chat bubbles. So have yet to find a valid use or time when they would be of any value to the game play at all.

So now lets look on even more evil issues, even at a short range. You will have a ton of extra draw calls potentially more than the players are generating themselves for the chat bubbles. My guild of 100+ who will roll around areas in full force for combat, will have no problem setting our macro programs to spam chat bubbles. which means potential for an additional 100 draw calls a half second. So now you putting in chat spam controls, always helpful but always dangerous to do so. GW2 caught a lot of flak for some of their controls in chat spam protection with them blocking the repeating verbiage. Squad / alliance leaders would say things like follow me 3 times in a row, and bam 1 minute of chat lock out. Was a pain. So now have to balance out chat spam that is created cause guilds know they can drive draw calls on the enemies gpu's to force them to lag. This then starts to become a problem of money wins the game, guilds with more people and more average income of said players for gaming hardware start to pick up an edge. Not to mention even at close range having 10 of us stacked up with blah blah blah appearing can really block your ability to see what we are doing in close combat. So you give option to just turn them off, which everyone does. So once again, chat bubbles fail to add value and cause more headache.

Not to mention you are now managing basically two additional replication groups to just allow for a chat bubble gui to show up, so that is more network traffic and more work load, for feature that 98% of your players will forcefully remove. So the question I ask is what use based scenarios can you lay out that provide / justify the addition of chat bubbles and the answer of players want them, isn't correct. Need to know why they want them and when and how do they plan to use them. If they can't tell you anything beyond cause it adds to "realism" then they don't know either, and are just chirping for a feature that isn't useable.

There is a reason chat bubbles went away. I think people try and bring back things from old, or want to push the envelope on radical GUI concepts etc. When really it's not what the players want, keep in mind each game has unique aspects, that require tweaks. However, gui's, chat systems, group systems, inventories, etc are the way they are cause of 20 years of real world play testing and feedback. The players have had their say for almost 20 years on what they want with GUI's and while some would say the big guys are just lazy, I say it's more we can't tell them as a gamer any better way to do it that makes enough logical sense to warrant retraining a gaming force of millions of players.

The wheel / tire is a good example over a few century man has improved on the wheel as a function of a tire for a mobile unit. during that time frame large improvements were made, as radical new aspects came out, but between those radical improvements there is a long flat line. So when wood was replaced with steel we saw a design and use change, when that was improved on with rubber we saw changes, but since then not a lot of aspects have changed sure we have made options for run flat tires, or redid tread design a billion times, after hundreds of studies, but to the consumer, they go out buy a tire. So till the next super polymer or such is made no need to change the tire. Same thing with your game, till a new form of input is made available design changes in regards to gui etc are goign to be small, if they are to be effective. The last change we saw was cheap voip. Everyone has a dozen means for voip at dirt cheap price options. Since hands are needed for game play, voice chat is the new standard. Only when a barrier to it's effectiveness is presented do we revert back to typed text.

I think the next change we will see will come from more motion driven input, till that is well brought into play though we going to keep the status quo.

Not to hijack a thread or anything. :)

And Cooper nothing wrong with skin tight clothing, granted a few cloth flairs wouldn't of killed them. lol


There are many things that can be added to this functionality to solve every single one of these problems.
« Last Edit: Apr 10, 14, 03:40:23 PM by nocake »
Logged

Voarsh

  • General Accounts
  • *
  • Posts: 56
    • View Profile
Re: Chat Over Head
« Reply #2 on: Jun 01, 14, 12:05:59 PM »

This is the functionality for creating a command /say that users can enter to have their text display above their head to others.
This is here for others to use and for my own critique.

I only implemented the functionality. There still needs to be a lot of polishing in regards to displaying the text.

I wrote this tutorial as I created this functionality. Revisions are sure to come.


1. First register the new command in the console with:
   
Code: [Select]
register /say script=>"cmdTell"

2. Open Server Side cmdTell script


3. Locate HE_ProcessCommandInput function:

Original Function:
Code: [Select]

shared function HE_ProcessCommandInput( account as NodeRef, input as String )
  start as Integer = FindString( input, " " ) + 1
  end as Integer = FindStringEx( input, " ", start )
  if end < 0
    end = input.length
  .
  //  FindPlayerByName requires the name be all caps
  var name = ToUpper( SubString( input, start, end - start ) )
  var message = SubString( input, end + 1, input.length )
  var player = FindPlayerByName( name )
  if player != 0
    SendTellToPlayer( player, account, message )
  else
    //  The player isn't here, so try again on the world server.
    call area 0 instance 0 cmdTell:FindPlayerForTell( name, account, message )
  .
.


NOTE: We are going to add to this function by adding a partialMatch() case statement that checks command entered by tokenizing the input into a list of strings. Take note of what code gets moved under the case statement and what doesn't.


4. Lets Tokenize() the input to get the command entered
Add:
Code: [Select]
args as List of String
Tokenize( input, args )
Directly after:
Code: [Select]
  if end < 0
    end = input.length
  .


5a. Now add partialMatch() case statement and encapsulate the code for /tell in its own statement.

 Add:
Code: [Select]
partialMatch toLower( args[1] )
to "/tell"
After:
Code: [Select]

args as List of String
Tokenize( input, args )

5b. And close the new patrialMatch() case statement with two "."'s right before the closing "." for the HE_ProcessCommandInput function.





6. Now lets add the case statement for the /say command.

In between these two lines:

 
Code: [Select]
partialMatch toLower( args[1] )
 to "/tell"

Add:
Code: [Select]
to "/say"
      //ATTACH CHAT TO PLAYER
      var message =  SubString( input, start, input.length )
      targetList as List of NodeRef = GetChatTargetsInArea(pc,2)
      pc as NodeRef of Class E_playerCharacter = account.GetMyChar()
      foreach target in targetList
        where target is kindof E_CommonCharacter
          SendChatToPlayer( target.GetCombatantAccount(), pc, message )
        .
      .
    .



I got to this stage, and was already confused. It isn't clear where to add the Tokenize() and the patrialMatch() to the cmdTell server script.
Logged