| 10-23-2008, 02:42 AM | #1 |
This is a library taken from my Jass Framework I use for my map project Nights of Kalimdor. DESCRIPTION This system is a string parser, that enable us to create very easily "tables" to roll "things". These "things" could be war3's items (for a looting system), or just numbers (integers, reals) or any other data that could be retrieved from a string. THOUGHTS The (theorical) main problem that could be encountered is the (big ?) amount of strings generated by the parsing system, but I don't really know how bad it could be (if someone has answers I miss..) In the past, I made a very simple roll system based on Blizzard's one, but it couldn't be able to manage priorities or conditional behaviours between table's items so I decided to try using strings. Even it appears to be harmful for the game's integrity due to the amount of created strings, I hope you'll find it enough interesting to give a feedback and improvement ideas ;) CODE JASS:library FW //Extract a string from an other string. //Example: FW_SubString( "Hello World", 1, 4 ) //return "ello" // public function Substring takes string s, integer start, integer length returns string if( start<0 )then set start = 0 endif return SubString( s, start, start+length ) endfunction //Find a needle in a string and return the starting position of the first occurence. //Return -1 if the needle is not found. //Example: FW_FindString( "Hello World", "Wo" ) // return 6 // public function FindString takes integer start, string s, string needle, boolean caseSensitive returns integer local integer i = start local integer L = StringLength(needle) if( s!="" and needle!="" and (StringLength(needle)<=StringLength(s)) )then if( not caseSensitive )then set s = StringCase(s,false) set needle = StringCase(needle,false) endif if( i<0 )then set i=0 endif loop exitwhen( i>StringLength(s) ) if( SubString(s,i,i+L)==needle )then return i endif set i=i+1 endloop endif return -1 endfunction //Find and replace all occurences of a needle in a string. public function StringReplace takes string s, string needle, string replacedMsg, boolean caseSensitive returns string local integer f loop set f = FindString( 0, s, needle, caseSensitive ) exitwhen( f==-1 ) set s = Substring(s,0,f) + replacedMsg + Substring(s,f+StringLength(needle),-1) endloop return s endfunction //Removes all spaces or linebreaks from a string. public function ClearSpaces takes string s returns string set s = StringReplace( s, " ", "", false ) set s = StringReplace( s, "|n", "", false ) return s endfunction scope FWRollTable //=========================================================================== //CUSTOM: private function definedRollTables takes string name returns string if( name=="testtable" )then return "" else endif return "" endfunction //=========================================================================== //STRUCTURE'S DEFINITION: private struct s_rolltable private string table private integer pos = 0 private real array chances[100] private string array values[100] private integer count = 0 private string dataReadValue = "" private real dataReadChance = 100. private boolean dataQueued = false //*********************************************************** //Reading methods private method readRandom takes nothing returns string local string char = SubString( .table, .pos, .pos+1 ) local boolean A = false local boolean isReal = false local integer recordPos local integer aI = 0 local integer bI = 0 local real aR = 0 local real bR = 0 local string temp if( char=="(" )then set recordPos = .pos + 1 loop set .pos = .pos + 1 set char = SubString( .table, .pos, .pos+1 ) exitwhen( char==")" ) if( char==null )then return "" //missing closing parenthesis elseif( char==";" )then if( not A )then set A = true //read a-value set temp = SubString(.table,recordPos,.pos) set isReal = (FW_FindString( 0, temp, ".", true )>-1) if( isReal )then set aR = S2R( temp ) else set aI = S2I( temp ) endif set recordPos = .pos + 1 else return "" //wrong random definition endif endif endloop //Read 2nd value set temp = SubString(.table,recordPos,.pos) if( not isReal )then if( FW_FindString(0,temp,".",true)>-1 )then set isReal = true set aR = I2R(aI) set bR = S2R( temp ) else set bI = S2I( temp ) endif else set bR = S2R( temp ) endif //return random value set .pos = .pos + 1 if( isReal )then return R2S( GetRandomReal(aR,bR) ) else return I2S( GetRandomInt(aI,bI) ) endif endif return "" endmethod private method readValue takes nothing returns string local string char = SubString( .table, .pos, .pos+1 ) local integer recordPos = .pos local boolean inBracket = (char=="[") local string r = "" loop //call BJDebugMsg( " value char= "+ char) if( char=="(" )then if( recordPos<.pos )then set r = r + SubString( .table, recordPos, .pos ) +.readRandom() else set r = r + .readRandom() endif set recordPos = .pos//update record position elseif( char=="{" )then if( recordPos<.pos )then set r = r + SubString( .table, recordPos, .pos ) + .readTable() else set r = r + .readTable() endif set recordPos = .pos//update record position elseif( char=="#" )then if( recordPos<.pos )then set r = r + SubString( .table, recordPos, .pos ) endif set .pos = .pos + 1 set r = r + .readTableNamed(.readValue()) set recordPos = .pos//update record position endif set char = SubString( .table, .pos, .pos+1 ) exitwhen( char==":" or char=="&" or (char=="," and not inBracket) or (char=="]" and not inBracket) or char==null ) set .pos = .pos + 1 set char = SubString( .table, .pos, .pos+1 ) endloop if( recordPos<.pos )then set r = r + SubString( .table, recordPos, .pos ) endif return r endmethod private method readTableNamed takes string name returns string local s_rolltable rt local string r = "" set name = definedRollTables(name) if( name!="" )then set rt = s_rolltable.create( name ) set r = rt.Process() call rt.destroy() endif return r endmethod private method readTable takes nothing returns string local integer start = .pos + 1 local integer end = -1 local integer c = 0 local string r local s_rolltable rt //Find the end of the table ("}"char) loop set .pos = .pos + 1 set r = SubString( .table, .pos, .pos+1 ) exitwhen( r==null ) if( r=="{" )then set c = c + 1 elseif( r=="}" )then if( c==0 )then set end = .pos exitwhen(true) else set c = c - 1 endif endif endloop //Check if( r==null )then set .pos = end return "" endif //Process table set rt = s_rolltable.create( SubString(.table,start,end) ) set r = rt.Process() //Clean struct and return result set .pos = end + 1 call rt.destroy() return r endmethod //*********************************************************** //Main private method saveRead takes boolean increase returns boolean local boolean result = false if( .dataReadValue!="" )then if( .dataQueued )then //call BJDebugMsg( ":: Random queued '"+.dataReadValue+"', chance= "+R2S(.dataReadChance) ) if( FW_MathRoll(.dataReadChance) )then if( .values[.count]=="" )then //call BJDebugMsg( " ->added (new)" ) set .values[.count] = .dataReadValue set .chances[.count] = 100. else //call BJDebugMsg( " ->added (update)" ) set .values[.count] = .values[.count] + "&" + .dataReadValue endif set result = true else //call BJDebugMsg( " ->failed" ) endif else set .values[.count] = .dataReadValue set .chances[.count] = .dataReadChance set result = true endif if( increase and .values[.count]!="" )then //call BJDebugMsg( " ## INCREASE ##" ) set .count = .count + 1 set .values[.count] = "" set .chances[.count] = 100. endif endif //call BJDebugMsg( "|cffffcc00stored: "+.values[.count]+"|r" ) set .dataReadValue = "" set .dataReadChance = 100. return result endmethod method Process takes nothing returns string local integer i = 0 local real sum = 0 local string char local real rand //Parse table loop set char = SubString( .table, .pos, .pos+1 ) if( char==null or char=="" )then call .saveRead(true) set .dataQueued = false exitwhen( true ) elseif( char=="," )then call .saveRead(true) set .dataQueued = false set .pos = .pos + 1 elseif( char=="&" )then set .dataQueued = true call .saveRead(false) set .pos = .pos + 1 elseif( char=="#" )then set .pos = .pos + 1 set .dataReadValue = .readTableNamed(.readValue()) elseif( char=="{" )then set .dataReadValue = .readTable() elseif( char==":" )then set .pos = .pos+1 set .dataReadChance = S2R(.readValue()) else set .dataReadValue = .readValue() endif endloop //Roll results if( .count>0 )then set rand = GetRandomReal( 0.001, 100. ) //call BJDebugMsg( "===========================" ) //call BJDebugMsg( " rand: "+R2S(rand) ) loop exitwhen( i==.count ) //call BJDebugMsg( " value #"+I2S(i)+" : "+.values[i]+" chance: "+R2S(RMinBJ(100.-sum,.chances[i]))+"%" ) set sum = sum + .chances[i] if( rand<=sum )then //call BJDebugMsg( "RESULT : "+.values[i] ) return .values[i] endif set i = i + 1 endloop endif return "" endmethod //*********************************************************** //Constructor static method create takes string table returns s_rolltable local s_rolltable this = s_rolltable.allocate() set this.table = FW_ClearSpaces( table ) set this.values[0] = "" set this.chances[0] = 100. return this endmethod endstruct //=========================================================================== //Process a "roll table" and return the result as a string. function FW_RollTable takes string tableDef returns string local s_rolltable rt local string r = "" if( tableDef!="" )then set rt = s_rolltable.create( tableDef ) set r = rt.Process() call rt.destroy() endif return r endfunction endscope endlibrary EXPLANATIONS Note: The result of a processed table is a string, that might be parsed to be "translated" by your own systems into "game's actions".Using it is very simple once you understood the simple syntax, then it'll be pretty easy for you to build complex roll tables. You can process a table with a single function function FW_RollTable takes string tableDef returns string Structure of <tableDef> A simple table definition is a string that matches the following structure : Custom/Composed return value If you need to return a custom string containing a syntax character (like ,) just enclose it with [ ]. Random definitions The previous example can be simplified with the use of a random definition, the syntax is ( minValue ; maxValue ) . Inner tables Sometime you might want to use kinds of "random definition" but with unequipotential chances to be picked, the simplest way to do that is to use an other rolltable INSIDE the current table ! Results concatenation You may need to return more than one item from a single table, for example if you are working on a looting system, the roll can return multiple item codes. External tables Different roll tables might share a common part of data, so instead of repeating it as many time as needed, you can include an external string defined in the "definedRollTables" function. I hope that I don't made any mistake, and wait for your opinion about this system. |
| 10-23-2008, 03:09 AM | #2 | |
Quote:
|
| 10-23-2008, 10:53 AM | #3 |
In fact, the FW scope is used for a large group of functions/structures and not only these 3, but in order to keep the prefix of these functions i also kept this scope ;) |
| 10-23-2008, 04:41 PM | #4 |
scope takes your stuff back to the dark age. You have functions that are supposed to be called from another place... use a library man. |
| 10-23-2008, 05:41 PM | #5 |
You right, the real structure of my data is : library FW scope FWRollTable endscope endlibrary I'll modify my first post right now. Any idea about how bad would be the strings generation for a map? |
| 10-28-2008, 08:54 PM | #6 |
Does my question deserve any answer or is this a lame question.. or ? |
