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: 424
An excellent addition to your achievements. Better than DivideString? Even better! That alone was perfect for RP maps and the like.
 
> 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.
 
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.
 
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.
 
> What about the other way around?
I like to do resources one at a time. :p

I guess I could though... Probably today even. :)
 
> 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.
 
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)
 
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. :)
 
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 .
 
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>
 
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.
 
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. :)
 
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.
 
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
 
Took me a while to figure out what this was for but now I can see how useful it is! GJ! :thup:
 
> 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. :)
 
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.
  • Varine Varine:
    And since almost all of my programming experience is with defunct shit now, I figure my best place is helping preserve legacy stuff. Which I don't know how to do necessarily, but I need some kind of a hobby and figuring out how older things worked is the only shit that really interests me. Well soldering and restoration is fun too, but no one is bringing me new stuff to fix and restore, so it's mostly old shit, and I LOVE OG Xbox so much. I want to make sure it can function as long as possible, until someone can effectively emulate it at least. I have like 15 I was going to fix over the winter and didn't get to.
    +1
  • Varine Varine:
    I also have a couple OG gameboys, but idk if I can do that without like, manufacturing new parts that no one makes anymore and I can't do that right now
  • tom_mai78101 tom_mai78101:
    Currently in the middle of getting the probate process going. We're doing the informal probate process.
    +3
  • Varine Varine:
    A probate is usually done with a will, yes? If so I am sorry for your loss
    +1
  • The Helper The Helper:
    Yeah Tom, me too sorry for your loss buddy my mom told me she finds out her olds friend died from Google searching them. She had not talked to one of her old friends in a year and found out she died from Google. Also another one in the same session. RIP all of them my sincere condolences Tom
    +1
  • Varine Varine:
    We have some elderly guests that regularly come hang out at the bar at the end of the night, and every once in a while we don't see someone for a few weeks and then someone shows up with their obituary.
  • Varine Varine:
    We usually let them do their memorials there in the morning if they want to and I'll make them some snacks and drinks. There was one guy named Tom that came in like every night and would sit by himself and get a bunch of soup and a glass of wine. idk why but he LOVED our fucking soup, like he would order a fucking quart of it at a time and would always get so sad when we stop doing it for the summer.
    +1
  • Varine Varine:
    But he also loved our calamari, which is another thing I hate but it sells super well so I can't change it. There was one day he came in and was asking me how to make it, because he tried to at home once in the off season when we stop running it and he really wanted it lol
  • Varine Varine:
    I think he's one of the only people I've made recipes for for free because he really wanted a broccoli cheddar, and it was like dude I don't have a recipe, it's just whatever I have, but here, this is how you do it
  • Varine Varine:
    I don't think he ever figured out how to do the calamari in a pan though, like idk how to do that either. He was afraid of the at home deep fryers though and it's like yeah, that's fair, I am too
  • Varine Varine:
    He was just such a sweet old man, we had two servers pregnant and they held a baby shower together, he was soooooo fucking excited to get to see a baby. Unfortunately he died a month or so before they were born
  • The Helper The Helper:
    So I decided to Google some people that I had not seen or heard from in a while and sure enough one of my old best friends, we had a falling out years ago but whatever, find out he died of Pancreatic Cancer in January. I have also lost a few of my closer acquaintances from growing up the last year. Getting old - people die - I kinda thought it was going to be this way a few years ago....
    +2
  • The Helper The Helper:
    Forum running super slow again
  • Ghan Ghan:
    Not really clear from the stats as to what is causing the slowness.
  • Ghan Ghan:
    We get a lot of guest traffic so it may just be the load is getting too high and not from any particular source.
  • Ghan Ghan:
    Looks like the server is maxed out on CPU.
  • Ghan Ghan:
    Oh it looks like a lot of the traffic is Silkroad Forums. That domain isn't protected by Cloudflare.
  • Ghan Ghan:
    But the old Silkroad site is still on its own server. I just had a test site set up on this server for it.
  • Ghan Ghan:
    I just disabled that test site. Let's see if that helps the load.
  • Ghan Ghan:
    Looks much better already.
  • The Helper The Helper:
    I had actually forgot about the Silkroad site. I had asked
  • The Helper The Helper:
    SD Ryoko about it and he said the couple of people left on there really like it, that was a few years ago, maybe I should check back
  • jonas jonas:
    I guess when you're getting old, and the last day of soup season draws near, you start wondering
  • jonas jonas:
    will I make it to the start of the next season? or was this the last time I'll ever have my favorite dish?
  • The Helper The Helper:
    I am doing my first Vibe Coding project. In installed the environment and tools according to instructions but it is all chat doing this for me at my direction. It is fun really and holy shit I might finish in 2 hours what it would have taken a day to in my Access and this would be an electron app complete new

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials
      Top