1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  3. The Melee Mapping Contest #4: 2v2 - Results are out! Step by to congratulate the winners!
    Dismiss Notice
  4. We're hosting the 15th Mini-Mapping Contest with YouTuber Abelhawk! The contestants are to create a custom map that uses the hidden content within Warcraft 3 or is inspired by any of the many secrets within the game.
    Dismiss Notice
  5. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice

Chat System v1.1.0 (GUI)

Submitted by Belgarath
This bundle is marked as approved. It works and satisfies the submission rules.
This is a pretty basic GUI chat system using the disallowing of ordinary chat messages being displayed (spaces in chat fields) and a lot of substrings.
It allows for dynamic colour chat strings (for example changing player chat colour but not unit colour, while retaining their actual colour for other purposes) should they be wanted (this would have to be added, as the system, at the moment, sets a colour string at map init for each player and uses that for any actions involving a player's colour) and private chat groups, which are added in and easy to use.
It also allows for NPC characters to seem like an actual player, since they will simply be part of the chat system, which would be extremely good for RPGs.

However, of course, there are cons - command confirmations and other game messages will be part of the chat system, limiting the amount of messages inside it, unless they were done in floating texts above the player's unit, which would make sense (partially).
As well as this, the little sound when a player sounds something won't occur in private message groups, as it's recommended that pretty much all chat is kept in private, where players can't use F12 to see chat intended for private.

The system is entirely in GUI and obviously MUI/MPI.
(I made this in UMSWE/newgen pack, but I'm not quite certain if I actually used any of the added triggers)




Triggers - note that they may be hybrid german at times, but it should be understandable using pictures/common sense :/
(Folder - Chat)
  • Initialization
    • Ereignisse
      • Map initialization
    • Bedingungen
    • Aktionen
      • Set ColourString[1] = |CFFFF0303
      • Set ColourString[2] = |CFF0042FF
      • Set ColourString[3] = |CFF1CB619
      • Set ColourString[4] = |CFF540081
      • Set ColourString[5] = |CFFFFFF01
      • Set ColourString[6] = |CFFFE8A0E
      • Set ColourString[7] = |CFF20C000
      • Set ColourString[8] = |CFFE55BB0
      • Set ColourString[9] = |CFF959697
      • Set ColourString[10] = |CFF7EBFF1
      • Set ColourString[11] = |CFF106246
      • Set ColourString[12] = |CFF4E2A04
        • Do Multiple ActionsFor each (Integer A) from 1 to 12, do (Actions)
          • Schleifen - Aktionen
            • Spielergruppe - Add (Player((Integer A))) to PrivateGroup[(Integer A)]
      • Quest - Create a Optional quest titled Chat with the description The game's chat sys..., using icon path ReplaceableTextures\CommandButtons\BTNNeutralManaShield.blp
      • Quest - Create a quest requirement for (Last created quest) with the description to talk to all, use...
      • Quest - Create a quest requirement for (Last created quest) with the description talking to Allies i...
      • Quest - Create a quest requirement for (Last created quest) with the description p to talk to your p...
      • Quest - Create a quest requirement for (Last created quest) with the description removeall - removes...
      • Quest - Create a quest requirement for (Last created quest) with the description addall - adds all p...
      • Quest - Create a quest requirement for (Last created quest) with the description privlist - displays...
      • Quest - Create a quest requirement for (Last created quest) with the description nl/-nlist - display...
      • Quest - Create a quest requirement for (Last created quest) with the description add# - adds the spe...
      • Quest - Create a quest requirement for (Last created quest) with the description remove# - removes t...
      • Quest - Create a quest requirement for (Last created quest) with the description note that the comma...
      • Quest - Create a Optional quest titled Player Numbers with the description Either use this lis..., using icon path ReplaceableTextures\CommandButtons\BTNNeutralManaShieldOff.blp
        • Do Multiple ActionsFor each (Integer A) from 1 to 12, do (Actions)
          • Schleifen - Aktionen
            • Spielergruppe - Pick every player in (Player group((Player((Integer A))))) and do (Actions)
              • Schleifen - Aktionen
                • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                  • 'IF'-Bedingungen
                    • Multiple ConditionsAnd - All (Conditions) are true
                      • Bedingungen
                        • ((Player((Integer A))) controller) Gleich (==) User
                        • ((Player((Integer A))) slot status) Gleich (==) Playing
                  • 'THEN'-Aktionen
                    • Quest - Create a quest requirement for (Last created quest) with the description (ColourString[(Integer A)] + ((Name of (Player((Integer A)))) + (|r - + (String((Player number of (Player((Integer A)))))))))
                  • 'ELSE'-Aktionen




Trigger Comment:
This is quite straightforward. The first part gives the colour strings to be used in the whole game, used in the Chat trigger.
The next part adds each player to his/her own private chat group, so that they can see their own private chat.
The next part is entirely self explanatory. It simply displays commands and features of the system, and their function.
The next part of it is for a "quest" displaying each player's number, to be used for the -add#/-remove# command, for the private chat list. Not everyone knows each player's number, which is why this "quest" exists, as well as the -nl/-nlist command.

  • Chat
    • Ereignisse
      • Spieler - Spieler 1 (Rot) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 2 (Blau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 3 (Blaugrau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 4 (Lila) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 5 (Gelb) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 6 (Orange) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 7 (Grün) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 8 (Rosa) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 9 (Grau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 10 (Hellblau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 11 (Dunkelgrün) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 12 (Braun) types a chat message containing <Leerer String> as Ein Teil-String
    • Bedingungen
    • Aktionen
      • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • 'IF'-Bedingungen
          • Multiple ConditionsAnd - All (Conditions) are true
            • Bedingungen
              • (Substring((Entered chat string), 1, 7)) Ungleich (!=) -remove
              • (Substring((Entered chat string), 1, 4)) Ungleich (!=) -add
              • (Substring((Entered chat string), 1, 9)) Ungleich (!=) -privlist
              • (Substring((Entered chat string), 1, 6)) Ungleich (!=) -nlist
              • (Substring((Entered chat string), 1, 3)) Ungleich (!=) -nl
        • 'THEN'-Aktionen
          • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Bedingungen
              • (Substring((Entered chat string), 1, 2)) Gleich (==) -p
            • 'THEN'-Aktionen
              • Spiel - Display to PrivateGroup[(Player number of (Triggering player))] the text: ((|cffD6AE12(Private) |r + ColourString[(Player number of (Triggering player))]) + ((Name of (Triggering player)) + (|r: + (Substring((Entered chat string), 3, 90)))))
            • 'ELSE'-Aktionen
              • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • 'IF'-Bedingungen
                  • (Substring((Entered chat string), 1, 1)) Gleich (==) .
                • 'THEN'-Aktionen
                  • Spiel - Display to (All players) the text: ((|cffD6AE12(All) |r + ColourString[(Player number of (Triggering player))]) + ((Name of (Triggering player)) + (|r: + (Substring((Entered chat string), 2, 90)))))
                • 'ELSE'-Aktionen
                  • Spiel - Display to (All allies of (Triggering player)) the text: ((|cffD6AE12(Allies) |r + ColourString[(Player number of (Triggering player))]) + ((Name of (Triggering player)) + (|r: + (Entered chat string))))
        • 'ELSE'-Aktionen




Trigger Comment:
The triggering of this system is quite simple.
Event: obvious. When any player types anything, the trigger runs a check if the message did not have any of the messages in the string (substrings referring to a part of a string, ie. sub of a string. The substring takes the first number and begins the substring there and ends at the second number). Should this pass, the first check is if the message contains a -p at the start (first two letters), which is the command for a private chat. Should this condition be true, the message displayed to the players inside the variable player group PrivateGroup[number of triggering player(ie. person saying something)] will be the following 'formula':

[ |cffD6AE12(Private) - |r ] - the first concenate string (concenate strings are a series of strings). The text in front of the bracket is the colour code in which (Private) will be coloured, (Private) refers to the fact that this is a private message. |r displays the end of the colour string.
[ + ColourString[(Player number of (Triggering player))]) ] - the second concenate string. This will decide the colour of the player's name in the private message. ColourString[] is an array variable (ie a 'stack' of variables of the same type) which has 12 values. Each of these 12 values is set at the very beginning of the game, player 1 (red)'s, [1], is set to red, player 2 (blue)'s, [2], is set to blue and so on. This way, the trigger takes the player's number and puts it into this array, giving the correct colour for the player name.
[ + Name of (Triggering player) ] - this is the third part. The name of the player in the message will be decided by it - its colour already decided by the string before this one.
[ + (|r: ] - this is a simple colour string stop and :, displaying the end of the player name (and the colour of it) and the start of the message, with a : and a space (giving a less cluttered look)
[ + Substring(Entered chat string), 3, 90) ] - this is the most important part of the action. It displays the actual message.
Continued: (last line) ... [ + Substring(Entered chat string), 3, 90) ] - this is the most important part of the action. It displays the actual message. ...
This message will be displayed to the player group already decided and explained earlier on, along with the rest of the string, the 3, 90 is deciding the start and end of the message. 3 is after -p (which are 1, 2) and 90 is the maximum amount of characters allowed in a message, thus giving a quite long message length limit.

The All and Allies chat follow the same rule, with some exceptions:
- Player group displayed to - All displays the message to, well, ALL players. Allies only displays it to allies of the typist.
- The last part of the concenate strings (Substring(Entered chat string), #, #) - varies as in the All chat, only a . is needed to cause this, while allies is the default chat and displays all characters entered. (last line) ... [ + Substring(Entered chat string), 3, 90) ] - this is the most important part of the action. It displays the actual message. ...
This message will be displayed to the player group already decided and explained earlier on, along with the rest of the string, the 3, 90 is deciding the start and end of the message. 3 is after -p (which are 1, 2) and 90 is the maximum amount of characters allowed in a message, thus giving a quite long message length limit.

The All and Allies chat follow the same rule, with some exceptions:
- Player group displayed to - All displays the message to, well, ALL players. Allies only displays it to allies of the typist.
- The last part of the concenate strings (Substring(Entered chat string), #, #) - varies as in the All chat, only a . is needed to cause this, while allies is the default chat and displays all characters entered.



Not displaying chat in game: As you may notice, the system also does not display normal WC3 chat in game. This is because of the fact that in Advanced > Game Interface, the 5 Chat Recipient message fields have been filled with spaces, this many:
' '
If you wish to export this system to a different map, you will need to change those fields as well.

  • Commands with command confirmation
    • Ereignisse
      • Spieler - Spieler 1 (Rot) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 2 (Blau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 3 (Blaugrau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 4 (Lila) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 5 (Gelb) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 6 (Orange) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 7 (Grün) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 8 (Rosa) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 9 (Grau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 10 (Hellblau) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 11 (Dunkelgrün) types a chat message containing <Leerer String> as Ein Teil-String
      • Spieler - Spieler 12 (Braun) types a chat message containing <Leerer String> as Ein Teil-String
    • Bedingungen
    • Aktionen
      • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • 'IF'-Bedingungen
          • (Substring((Entered chat string), 1, 4)) Gleich (==) -add
        • 'THEN'-Aktionen
          • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Bedingungen
              • (Substring((Entered chat string), 5, 7)) Gleich (==) all
            • 'THEN'-Aktionen
              • Do Multiple ActionsFor each (Integer A) from 1 to 12, do (Actions)
                • Schleifen - Aktionen
                  • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • 'IF'-Bedingungen
                      • ((Player((Integer A))) slot status) Gleich (==) Playing
                      • ((Player((Integer A))) controller) Gleich (==) User
                    • 'THEN'-Aktionen
                      • Spielergruppe - Add (Player((Integer A))) to PrivateGroup[(Player number of (Triggering player))]
                    • 'ELSE'-Aktionen
              • Spiel - Display to (Player group((Triggering player))) the text: |cffD6AE12All playe...
            • 'ELSE'-Aktionen
              • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • 'IF'-Bedingungen
                  • Multiple ConditionsAnd - All (Conditions) are true
                    • Bedingungen
                      • (Integer((Substring((Entered chat string), 5, 5)))) Größer als (>) 0
                      • (Substring((Entered chat string), 5, 5)) Ungleich (!=) (String((Player number of (Triggering player))))
                • 'THEN'-Aktionen
                  • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • 'IF'-Bedingungen
                      • Multiple ConditionsAnd - All (Conditions) are true
                        • Bedingungen
                          • ((Player((Integer((Substring((Entered chat string), 5, 5)))))) slot status) Gleich (==) Playing
                          • ((Player((Integer((Substring((Entered chat string), 5, 5)))))) controller) Gleich (==) User
                    • 'THEN'-Aktionen
                      • Spielergruppe - Add (Player((Integer((Substring((Entered chat string), 5, 5)))))) to PrivateGroup[(Player number of (Triggering player))]
                      • Spiel - Display to (Player group((Triggering player))) the text: ((ColourString[(Integer((Substring((Entered chat string), 5, 5))))] + (Name of (Player((Integer((Substring((Entered chat string), 5, 5)))))))) + |r |cffD6AE12has been succesfully added to your private chat list.|r)
                    • 'ELSE'-Aktionen
                • 'ELSE'-Aktionen
        • 'ELSE'-Aktionen
          • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Bedingungen
              • (Substring((Entered chat string), 1, 7)) Gleich (==) -remove
            • 'THEN'-Aktionen
              • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • 'IF'-Bedingungen
                  • (Substring((Entered chat string), 8, 10)) Gleich (==) all
                • 'THEN'-Aktionen
                  • Do Multiple ActionsFor each (Integer A) from 1 to 12, do (Actions)
                    • Schleifen - Aktionen
                      • Spielergruppe - Remove (Player((Integer A))) from PrivateGroup[(Player number of (Triggering player))]
                      • Spielergruppe - Add (Player((Player number of (Triggering player)))) to PrivateGroup[(Player number of (Triggering player))]
                  • Spiel - Display to (Player group((Triggering player))) the text: |cffD6AE12All playe...
                • 'ELSE'-Aktionen
                  • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • 'IF'-Bedingungen
                      • (Substring((Entered chat string), 8, 8)) Ungleich (!=) (String((Player number of (Triggering player))))
                      • (Integer((Substring((Entered chat string), 8, 8)))) Größer als (>) 0
                    • 'THEN'-Aktionen
                      • Spielergruppe - Remove (Player((Integer((Substring((Entered chat string), 8, 8)))))) from PrivateGroup[(Player number of (Triggering player))]
                      • Spiel - Display to (Player group((Triggering player))) the text: ((ColourString[(Integer((Substring((Entered chat string), 8, 8))))] + (Name of (Player((Integer((Substring((Entered chat string), 8, 8)))))))) + |r |cffD6AE12has been succesfully removed from your chat list.|r)
                    • 'ELSE'-Aktionen
            • 'ELSE'-Aktionen
              • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • 'IF'-Bedingungen
                  • Multiple ConditionsOr - Any (Conditions) are true
                    • Bedingungen
                      • (Substring((Entered chat string), 1, 9)) Gleich (==) -privlist
                • 'THEN'-Aktionen
                  • Spiel - Display to (Player group((Triggering player))) the text: |cffD6AE12Your priv...
                  • Spielergruppe - Pick every player in PrivateGroup[(Player number of (Triggering player))] and do (Actions)
                    • Schleifen - Aktionen
                      • Spiel - Display to (Player group((Triggering player))) the text: ((- + ColourString[(Player number of (Picked player))]) + ((Name of (Picked player)) + |r))
                • 'ELSE'-Aktionen
                  • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • 'IF'-Bedingungen
                      • Multiple ConditionsOr - Any (Conditions) are true
                        • Bedingungen
                          • (Substring((Entered chat string), 1, 3)) Gleich (==) -nl
                          • (Substring((Entered chat string), 1, 6)) Gleich (==) -nlist
                    • 'THEN'-Aktionen
                      • Spiel - Display to (Player group((Triggering player))) the text: |cffD6AE12Player's ...
                        • Do Multiple ActionsFor each (Integer A) from 1 to 12, do (Actions)
                          • Schleifen - Aktionen
                            • Spielergruppe - Pick every player in (Player group((Player((Integer A))))) and do (Actions)
                              • Schleifen - Aktionen
                                • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                  • 'IF'-Bedingungen
                                    • ((Player((Integer A))) slot status) Gleich (==) Playing
                                    • ((Player((Integer A))) controller) Gleich (==) User
                                  • 'THEN'-Aktionen
                                    • Spiel - Display to (Player group((Triggering player))) the text: ((- + ColourString[(Player number of (Picked player))]) + ((Name of (Picked player)) + (|r - + (String((Player number of (Picked player)))))))
                                  • 'ELSE'-Aktionen
                    • 'ELSE'-Aktionen



Trigger Comment:
NOTE: The only difference between the normal Commands trigger and this trigger is that this one returns a confirmation message when an -add or -remove command is carried out.

The triggering of this is extremely simple as well. If you know anything about substrings, it will come quite naturally, if not, the Chat comments should be enough.
The first command is the -add: -add1-12 or -addall. -addall is the first check, and will simply take all players from 1-12 and do the actions that if they are playing and are a user (ie, not computer), they will be added to the triggering player's PrivateGroup, to which private chat is displayed with -p. -add# is simply that it will take the 5th letter of the chat command, the number being added, and run the checks for playing & user and then add this player to the triggering player's group. The next action is simply the confirmation return message, which also displays the player's colour, should the person make a mistake.
The next part, -remove, is the precise same, other than the fact that it removes players instead of adding them. During the -removeall command, ALL players are first removed, then the triggering player is put back, so that this player can see their own chat once again. For the single number removal, it also checks that the number is not the triggering player's number.
-privlist takes all the players in the triggering player's PrivateGroup and displays a message with their colour string & name to the triggering player, showing all the people in their private chat list.
-nl/-nlist simply displays the 2nd quest during Initialization.
And that's already it! :- )!

(in the actual system, there is an alternate trigger to this one which will not display a confirmation to -add/-remove)


V.1.1.0: fixed two leaks!

Keywords:
chat, system, gui, rpg, players
Contents

Chat System v1.1.0 (Map)

Reviews
Moderator
14:47, 4th Feb 2010 The_Reborn_Devil: The triggers look good and it's working correctly. Quite handy for RPG's. Status: Approved Rating: Recommended
  1. 14:47, 4th Feb 2010
    The_Reborn_Devil:
    The triggers look good and it's working correctly. Quite handy for RPG's.

    Status: Approved
    Rating: Recommended
     
  2. Crazed_seal2

    Crazed_seal2

    Joined:
    Jan 5, 2009
    Messages:
    824
    Resources:
    2
    Tutorials:
    2
    Resources:
    2
    Put the triggers in the discription
     
  3. Belgarath

    Belgarath

    Joined:
    Sep 22, 2007
    Messages:
    2,626
    Resources:
    10
    Maps:
    2
    Spells:
    1
    Tutorials:
    7
    Resources:
    10
    i'll get right on that then
     
  4. Cihparg

    Cihparg

    Joined:
    Jul 4, 2008
    Messages:
    958
    Resources:
    1
    Models:
    1
    Resources:
    1
    What kind of editor have you been using when you triggered this?
     
  5. YourNameHere

    YourNameHere

    Joined:
    Apr 29, 2007
    Messages:
    745
    Resources:
    4
    Maps:
    1
    Spells:
    2
    JASS:
    1
    Resources:
    4
    It's some sort of german editor, I don't really now where these Equal(==) things come from. (Maybe UMSWE or whatever)
     
  6. HaniBon

    HaniBon

    Joined:
    Apr 25, 2009
    Messages:
    464
    Resources:
    0
    Resources:
    0
    This is really usefull!
    +rep for a good system!

    (Do you want a full review of this? Send a PM!)
     
  7. Cihparg

    Cihparg

    Joined:
    Jul 4, 2008
    Messages:
    958
    Resources:
    1
    Models:
    1
    Resources:
    1
    Well, The thing why I asked what kind of editor he has is that...
    You can't do this in normal editor:
     
  8. MortAr

    MortAr

    Joined:
    Feb 25, 2009
    Messages:
    1,770
    Resources:
    7
    Icons:
    6
    Packs:
    1
    Resources:
    7
    Oh you can.
     
  9. YourNameHere

    YourNameHere

    Joined:
    Apr 29, 2007
    Messages:
    745
    Resources:
    4
    Maps:
    1
    Spells:
    2
    JASS:
    1
    Resources:
    4
  10. Belgarath

    Belgarath

    Joined:
    Sep 22, 2007
    Messages:
    2,626
    Resources:
    10
    Maps:
    2
    Spells:
    1
    Tutorials:
    7
    Resources:
    10
    oh, i forgot that thw doesn't allow for massive amounts of spaces.
    just go into the map and get it from there

    i used umswe/newgen pack to make this
     
  11. AnektoD

    AnektoD

    Joined:
    Oct 2, 2010
    Messages:
    72
    Resources:
    0
    Resources:
    0
    Good Job.

    But why i cant delete normal messages ?

    [​IMG]
     
  12. Noobsmoke

    Noobsmoke

    Joined:
    Nov 20, 2007
    Messages:
    631
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Read this from step 1. It explains better
     
  13. gorillabull

    gorillabull

    Joined:
    Jul 17, 2011
    Messages:
    1,368
    Resources:
    2
    Spells:
    2
    Resources:
    2
    nice nice