Snippet Explode String

Romek

Super Moderator
Reaction score
963
Explode String
By Romek

SetPlayerColour Command

Introduction:
A remake of one of my old snippets. (Search "Divide String" if you care). This one uses less strings, and doesn't leak as much. And this one supports multiple separators next to eachother. :)

Usage:
To use it, you do the following:
JASS:
local ExpString name = ExplodeString(source, separator, maxamount)

'ExpString' is a dynamic array type.
'name' is the variable name.
'source' is the string you want to explode
'separator' is the character that determine where to break the string. This will default to " " if an invalid string is specified.
And 'maxamount' is the amount of array slots that will be filled.

Note that if 'separator' is more than one character long, only the first character will be used.

Note that ExpString's will need to be destroyed after you're finished with them. They don't require nulling though.
To destroy it, just use:
JASS:
call name.destroy()


The final array slot (maxamount) will contain the rest of the string if it wasn't fully divided.
JASS:
ExplodeString("Hello, my name is Romek", " ", 5)

Will give the following results:
Code:
index[0] => "Hello,"
index[1] => "my"
index[2] => "name"
index[3] => "is"
index[4] => "Romek"

The following will give identical results:
JASS:
ExplodeString("Hello,%my%name%is%Romek", "%", 5)

As will this:
JASS:
ExplodeString("Hello,^^^^^^my^^^name^^is^^^^Romek", "^", 5)


However, changing the maximum to 2 will result in this:
Code:
index[0] => "Hello,"
index[1] => "my name is Romek"

An Example:
JASS:
// Player Chat Event.
// Sends a message to a given player
function Action takes nothing returns nothing
    local ExpString result = ExplodeString(GetEventPlayerChatString(), " ", 3)
    // Split the chat string into multiple words.
   
    // "red" == "ReD", etc.
    set result[0] = StringCase(result[0], false)
    set result[1] = StringCase(result[1], false)
    // However, we want the message to retain capitalization.
   
    // result[0] will be the first word.
    if result[0] == "-message" or result[0] == "-send" then
    // We can use many alternatives to the 'command' very, very easily.
       
        // result[1] will be the second word
        if result[1] == "red" or result[1] == "1" or result[1] == GetPlayerName(Player(0)) then
        // We will check if the user typed either the colour, number or name of the player
       
            // result[2] will hold the rest of the string. In our case, this is the message.
            call DisplayTimedTextToPlayer(Player(0), 0., 0., 10., result[2])
           
        // Rinse and repeat <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin    :D" loading="lazy" data-shortname=":D" />
        elseif result[1] == &quot;blue&quot; or result[1] == &quot;2&quot; or result[2] == GetPlayerName(Player(1)) then
            call DisplayTimedTextToPlayer(Player(1), 0., 0., 10., result[2])
        endif
        // Etc.. I could go up to Player(11), but I think this explains enough.
       
    endif
   
    // We need to destroy the ExpString as it is a dynamic array. Sorry!
    call result.destroy()
    // It doesn&#039;t require nulling. :]
endfunction


The Code:
JASS:
library ExplodeString
//  ________________________________________
// +----------------------------------------+
// |      E X P L O D E   S T R I N G       |
// +----------------------------------------+
// |            By Romek - v3               |
// |________________________________________|
// +----------------------------------------+

    struct ExpString
        //  Change the [10] to the maximum amount of substrings
        private string array S [10]
        
        readonly integer size
        
        method operator [] takes integer i returns string
            return .S<i>
        endmethod
        
        static method create takes string source, string separator, integer maxamount returns ExpString
            local ExpString this = ExpString.allocate()
            local integer i = 0
            local integer last = 0
            local integer first = 0
            local integer length
            local boolean insep = true
            if maxamount &lt; 1 then
                set maxamount = 1
            endif
            set maxamount = maxamount - 1
            if separator == &quot;&quot; or separator == null then
                set separator = &quot; &quot;
            else
                set separator = SubString(separator, 0, 1)
            endif
            set .size = 0
            set source = separator + source
            set length = StringLength(source)
            loop
               exitwhen i &gt; length
                    if SubString(source, i, i + 1) == separator or i == length then
                        if not insep then
                            set last = i + 1
                            set insep = true
                            if .size  == maxamount then
                                set .S[.size ] = SubString(source, first, length)
                                exitwhen true
                            endif
                            set .S[.size ] = SubString(source, first, last - 1)
                            set .size  = .size  + 1
                        endif
                    elseif insep then
                        set insep = false
                        set first = i
                    endif
                set i = i + 1
            endloop
            return this
        endmethod
    endstruct
    
    function ExplodeString takes string source, string separator, integer maxamount returns ExpString
        return ExpString.create(source, separator, maxamount)
    endfunction

endlibrary</i>


.size will be the amount of array slots used.

...This must be the longest post for a snippet ever... :p

The Map:
 

Attachments

  • [Snippet] ExplodeString.w3x
    17 KB · Views: 306

Azlier

Old World Ghost
Reaction score
461
An excellent addition to your achievements. Better than DivideString? Even better! That alone was perfect for RP maps and the like.
 

Romek

Super Moderator
Reaction score
963
> That alone was perfect for RP maps and the like.
That worked only with single spaces.
Here you can define your own seperator, and this will still work if there are multiple seperators next to each other. :)

> This could definitely be useful for some.
I thought so too.
Thanks.
 

Vestras

Retired
Reaction score
248
Cool, except I think you should put some documentation or examples of where it could be useful.
Atleast I couldn't figure out ^_________^

EDIT: Oshi-, you already did. Fail.
 

Romek

Super Moderator
Reaction score
963
For those of you who are finding it difficult to see how it's useful:
Lets say you wanted to make a resource command. So the player could do:
-give gold red 600

Which would obviously work with all player colours, numbers and names.
And for lumber. And an alt. name, 'wood'.
Also, we might want to use 'send' or 'transfer' instead of 'give'.

You'd need an awful lot of code to make something like that normally. And a lot of substring usage and string leaks, to account for the different resource name lengths, and play colour lengths. (Lumber, gold. Blue, red, darkgreen).

However, if you used this, you could just explode the chat string,
and compare index[0] to "-send", "-transfer", etc. Anything really.
Same applies for the resources. No need for substrings at all actually. :)

I might submit a little snippet like that once this has been approved, to further demonstrate the usage.
 

Romek

Super Moderator
Reaction score
963
> What about the other way around?
I like to do resources one at a time. :p

I guess I could though... Probably today even. :)
 

AceHart

Your Friendly Neighborhood Admin
Reaction score
1,494
> if SubString(source, i, i + 1) == seperator

Why not + <string length of separator>? (note the spelling too).
You sure this works with separators that have more than 1 character?

> set source = seperator + source

Hm... looks more like an evil hack than an actual plan.



You have an integer called arrays? Great... :p
If maxamount passed is less than 1, strange things may happen.
 

GooS

Azrael
Reaction score
154
This looks sweet, like the PHP explode function ;)

I can thinks of a few uses that I have for this, thx Romek!

//==GooS

PS:

If maxamount passed is less than 1, strange things may happen.

Cutting strings into smaller pieces to put in 0 boxes, hopefully not many people will do that or even think about doing it. :)

But anyway couldn't there be a failcheck that disables the explode is the passed maxamount is <1, giving them a gentle nudge ingame when they're testing.
(I was thinking a great red fade filter going from 0-100% 5 times a second)
 

Romek

Super Moderator
Reaction score
963
Updated. You can only have 1 character as the separator now. There was no need for it to allow multiple characters anyway.

> (note the spelling too).
It's one of those words I don't know how to spell. :D

> Hm... looks more like an evil hack than an actual plan.
Nothing wrong with that, right? :p

> You have an integer called arrays? Great...
It's descriptive. :)

> If maxamount passed is less than 1, strange things may happen.
Added safety.

> This looks sweet, like the PHP explode function
Yeah, I got the name from the PHP function. :)
 

WolfieeifloW

WEHZ Helper
Reaction score
372
Just fully read the thread over.
Again, very nice job.
I'll probably be using this when I start to make my AoS.

Looks like that "useful for some" is me :p .
 

Romek

Super Moderator
Reaction score
963
Here's a fairly large code I made to demonstrate the use of this snippet.
And for everyone to use if they wish. :)

This allows players to use a chat command to change a players colour. The colour will only change for them.

'-colour reset' resets all the colours of the players to the default ones.
'-colour NAME COLOUR' can change the colour of NAME to COLOUR.
NAME can be either the player number, the default colour, or the player name.
Colour can be any colour from red to dark brown.

'allies' can be used as NAME to change the colour of all the players allies.
'enemies' can be used as above, but for the enemies.
'all' will change the colour of all the players.
'me' will change the colour of the triggering player.

'-color' can be used instead of '-colour'
'dg' can be used instead of 'darkgreen'
'lb' can be used instead of 'lightblue'
'gray' can be used instead of 'grey'
'myself' can be used instead of 'me'
'friends', 'ally', 'friend' or 'team' can be used instead of 'allies'
'enemy' can be used instead of 'enemies'
'everybody' and 'everyone' can be used instead of 'all'

The command is not case sensitive. :)

JASS:
scope SetPlayerColour initializer Init
//  ____________________________________________________
// +----------------------------------------------------+
// |        S E T   P L A Y E R   C O L O U R           |
// +----------------------------------------------------+
// |                 By Romek v1.0                      |
// |____________________________________________________|
// +----------------------------------------------------+
// | Commands that can be used to change the colour of  |
// | all players, as well as groups of players for only |
// | one player. So only the typing player will see     |
// | the colour changes.                                |
// |____________________________________________________|
// +----------------------------------------------------+
// |          C O N F I G U R A T I O N :               |
// |____________________________________________________|
// +----------------------------------------------------+
    
    // The command the player needs to type to change the players colours.
    // Example: &#039;COMMAND worldedit red&#039; will set WorldEdits colour to red.
    private constant function COMMAND takes string s returns boolean
        return s == &quot;-colour&quot; or s == &quot;-color&quot;
    endfunction
    
    // The command used to reset the colours.
    private constant function RESET takes string s returns boolean
        return s == &quot;reset&quot;
    endfunction

    // The command used to refer to the typing player.
    private constant function SELF takes string s returns boolean
        return s == &quot;myself&quot; or s == &quot;me&quot; 
    endfunction
    
    // Group name for allies.
    private constant function ALLIES takes string s returns boolean
        return s == &quot;allies&quot; or s == &quot;friends&quot; or s == &quot;ally&quot; or s == &quot;friend&quot; or s == &quot;team&quot;
    endfunction
    
    // Group name for enemies.
    private constant function ENEMIES takes string s returns boolean
        return s == &quot;enemies&quot; or s == &quot;enemy&quot;
    endfunction
    
    // Group name for everybody.
    private constant function ALL takes string s returns boolean
        return s == &quot;all&quot; or s == &quot;everyone&quot; or s == &quot;everybody&quot;
    endfunction

    globals
        // The error message shown to the triggering player when an invalid colour is specified
        private constant string INVALIDCOLOUR = &quot;|cffffcc00Invalid Colour|r&quot;
        
        // The error message shown to the triggering player when an invalid player or group is specified.
        private constant string INVALIDGROUP = &quot;|cffffcc00Invalid Player or Group|r&quot;
        
        // The sound played to the triggering player when an error message is displayed.
        private constant sound ERROR = CreateSoundFromLabel(&quot;InterfaceError&quot;, false, false, false, 10, 10)
    endglobals
    
//  ______________________________________________________
// +------------------------------------------------------+
// |        E N D   O F   C O N F I G U R A T I O N       |
// |______________________________________________________|
// +------------------------------------------------------+

    private struct Data extends array
        string Colour
        string AltColour
        string Name
        string Number
        
        playercolor C
    endstruct
    
    globals
        private group G = CreateGroup()
    endglobals
    
    private function True takes nothing returns boolean
        return true
    endfunction
    
    private function SetUnitsColour takes player p, playercolor c returns nothing
        local unit u
        call GroupEnumUnitsOfPlayer(G, p, Filter(function True))
        loop
            set u = FirstOfGroup(G)
            exitwhen u == null
            call GroupRemoveUnit(G, u)
            call SetUnitColor(u, c)
        endloop            
    endfunction
    
    private function ColourReset takes player p returns nothing
        local integer i = 0
        if GetLocalPlayer() == p then
            loop
                exitwhen i &gt; 11
                call SetPlayerColor(Player(i), Data<i>.C)
                call SetUnitsColour(Player(i), Data<i>.C)
                set i = i + 1
            endloop
        endif
    endfunction
    
    private function ColourPlayer takes player p, player target, integer colour returns nothing
        if GetLocalPlayer() == p then
            call SetPlayerColor(target, Data[colour].C)
            call SetUnitsColour(target, Data[colour].C)
        endif
    endfunction
    
    private function ColourGroup takes player p, integer who, integer colour returns nothing
        local integer i = 0
        if GetLocalPlayer() == p then
            if who == 0 then // Allies
                loop
                    exitwhen i &gt; 11
                    if IsPlayerAlly(p, Player(i)) then
                        call SetPlayerColor(Player(i), Data[colour].C)
                        call SetUnitsColour(Player(i), Data[colour].C)
                    endif
                    set i = i + 1
                endloop
            elseif who == 1 then // Enemies
                loop
                    exitwhen i &gt; 11
                    if IsPlayerEnemy(p, Player(i)) then
                        call SetPlayerColor(Player(i), Data[colour].C)
                        call SetUnitsColour(Player(i), Data[colour].C)
                    endif
                    set i = i + 1
                endloop
            else 
                loop
                    exitwhen i &gt; 11
                    call SetPlayerColor(Player(i), Data[colour].C)
                    call SetUnitsColour(Player(i), Data[colour].C)
                    set i = i + 1
                endloop
            endif                         
        endif
    endfunction

    private function Act takes nothing returns nothing
        local ExpString es = ExplodeString(StringCase(GetEventPlayerChatString(), false), &quot; &quot;, 3)
        local integer i = 0
        local integer colour
        local player p = GetTriggerPlayer()
        if COMMAND(es[0]) then
            // &quot;reset&quot;
            if RESET(es[1]) then
                call ColourReset(p)
                call es.destroy()
                return
            endif
            
            // Check colour:
            loop
                exitwhen i &gt; 11
                    if es[2] == Data<i>.Colour or es[2] == Data<i>.AltColour then
                        set colour = i
                        exitwhen true
                    endif
                set i = i + 1
            endloop
            if i &gt; 11 then
                call es.destroy()
                if GetLocalPlayer() == p then
                    call StartSound(ERROR)
                endif
                call DisplayTimedTextToPlayer(p, 0., 0., 1., INVALIDCOLOUR)
                return
            endif
            
            // Individual Players:
            if SELF(es[1]) then
                call ColourPlayer(p, p, colour)
                call es.destroy()
                return
            endif
            
            set i = 0
            loop
                exitwhen i &gt; 11
                    if es[1] == Data<i>.Name or es[1] == Data<i>.Colour or es[1] == Data<i>.AltColour or es[1] == Data<i>.Number then
                        call ColourPlayer(p, Player(i), colour)
                        call es.destroy()
                        return
                    endif
                set i = i + 1
            endloop
            
            // Groups of Players:
            if ALLIES(es[1]) then
                call ColourGroup(p, 0, colour)
            elseif ENEMIES(es[1]) then
                call ColourGroup(p, 1, colour)
            elseif ALL(es[1]) then
                call ColourGroup(p, 2, colour)
            else
                if GetLocalPlayer() == p then
                    call StartSound(ERROR)
                endif
                call DisplayTimedTextToPlayer(p, 0., 0., 1., INVALIDGROUP)
            endif
        
        endif
        call es.destroy()
    endfunction

    private function Cond takes nothing returns boolean
        return SubString(GetEventPlayerChatString(), 0, 1) == &quot;-&quot;
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
            exitwhen i &gt; 11
            call TriggerRegisterPlayerChatEvent(t, Player(i), null, false)
            set Data<i>.Name = StringCase(GetPlayerName(Player(i)), false)
            set Data<i>.Number = I2S(i + 1)
            set Data<i>.C = ConvertPlayerColor(i)
            set i = i + 1
        endloop
        call TriggerAddCondition(t, Filter(function Cond))
        call TriggerAddAction(t, function Act)
        call Filter(function True) // Figure out why this is here, and you get a cookie. :]
        
        set Data[0].Colour = &quot;red&quot;
        set Data[1].Colour = &quot;blue&quot;
        set Data[2].Colour = &quot;teal&quot;
        set Data[3].Colour = &quot;purple&quot;
        set Data[4].Colour = &quot;yellow&quot;
        set Data[5].Colour = &quot;orange&quot;
        set Data[6].Colour = &quot;green&quot;
        set Data[7].Colour = &quot;pink&quot;
        set Data[8].Colour = &quot;grey&quot;
        set Data[9].Colour = &quot;lightblue&quot;
        set Data[10].Colour = &quot;darkgreen&quot;
        set Data[11].Colour = &quot;brown&quot;
        
        set Data[8].AltColour = &quot;gray&quot;
        set Data[9].AltColour = &quot;lb&quot;
        set Data[10].AltColour = &quot;dg&quot;
    endfunction

endscope
</i></i></i></i></i></i></i></i></i></i></i>
 

GooS

Azrael
Reaction score
154
Is it possible that you may extend the library, adding ExplodeAtIndex(string, index, repeat, maxamount), as I think that would be a good addition, unless it already is possible in some odd way o_O

Anyways if you don't get what I mean,

JASS:
local ExpString result = ExplodeAtIndex(TestString, 6, false, 4)
local string TestString = &quot;GooS is not epic at JASS&quot;
local integer Index = 0

loop
        call DisplayTextToForce(GetPlayersAll(), result[Index])
        set Index = Index + 1
        exitwhen Index == 4
endloop


The repeat says if it should cut again at Index behind last Index cut.

Will display:

Code:
GooS i
s not epic at JASS

(With repeat true)

Code:
GooS i
s not 
epic a
t JASS


//==GooS

PS: I would need one of these function although I think I can make it myself ;) But if it was added to the library I'd appreciate it since I'm using the rest of the snippet.
 

Romek

Super Moderator
Reaction score
963
You can do that with pretty basic substring usage.
The first one would be SubString( thestring, 0, index) and SubString(thestring, index, length).

Second one would be similar, but using a loop.

Glad to see another person is using this. :)
 

Sevion

The DIY Ninja
Reaction score
413
Is there a reason this isn't approved? It's probably too pwnage to get approved :D Admins/Mods just can't touch it. They get burned. LOL.
 

Romek

Super Moderator
Reaction score
963
Thanks Sevion. :)
Anyway, I updated it.

It now uses a struct instead of a dynamic array. Believe it or not, it's backwards compatible.
The reason I changed it? '.size' now works as it should. It becomes the amount of array indices used, not the total possible amount. :D
 

Ghostwind

o________o
Reaction score
172
Took me a while to figure out what this was for but now I can see how useful it is! GJ! :thup:
 

Romek

Super Moderator
Reaction score
963
> Took me a while to figure out what this was for but now I can see how useful it is! GJ!
It is indeed a useful snippet. Thanks. :)
 

SerraAvenger

Cuz I can
Reaction score
234
this is absolutely bugged for seperators that are longer than one char.
You even cut it to length 1. If you can't handle length n seperators, please post that. You even say it would work with "char(s)".
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top