• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Reputation System

Level 23
Joined
Apr 16, 2012
Messages
4,041
Fit my needs and maybe some of you too.
Also I want to know if there are any ways(there ever are) to improve this
JASS:
library ReputationSystem requires Table, UnitIndexer //by edo494
/*      
        Table by Bribe - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
        UnitIndexer by Nestharus - http://www.hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/

        This is wrote mainly for my own needs
        
        This system allows you to create factions(reputations) for instance shops with requirements of some reputation,
        gain reputation when we kill some monster and also gain some reputation when for isntance quests are done
        
        You can also deincrease reputation to punish not good done job and other stuff.
        
        Together with custom units and techtree, you can even have shops with items which require you to have some amount
        of reputation in order to buy those items.
        
        note: When I say in API reputation I most likely mean reputation as currency and reputation itself is
        referred as faction.


*/
//------------------------------------------------------------------------
/*
    
API: First is struct API, if you are too lazy to use it, under it is a normal funcion API

struct reputation

Normal API:
    
    (s - static)function(arguments) > return type(if any)            description
    
    
    (s).create(string) > Reputation                             Creates a new faction with name
    
    (s).createEx(string, integer) > Reputation                  Creates a new faction with name at starting value of reputation
    
    .remove()                                                   Destroys a given faction
    
    (s).setRank(integer rank, string, integer tonextrank)       Creates a new rank(currently globally), like hostile, friendly, revered ...
    
    .increase(integer howmany, integer forplayer)               Adds reputation in given faction for a player
    
    .decrease(integer howmany, integer forplayer)               Lowers reputation in given faction for a player
    
    .getName() > string                                         returns a name of a faction
    
    .repS2I(string name) > integer                          turns a name of a faction into an integer(id of a faction), if invalid returns 0
                                                            This method can be used to get a non referenced reputation by knowing its name.
    
    .getRep(integer ofplayer) > integer                     returns a current standing of a player to faction(not Rank but value)
    
    .getTypeById(integer id) > string                       returns a name of a rank by id(this is not bound to faction)
    
    .getRankTypeOfPlayer(integer ofplayer) > string         returns name of a current rank in given faction of player
    
    .getRankOfPlayer(integer ofplayer) > integer            returns a current rank in given faction of player(the same as GetCurrentRepTypeOfPlayer but as integer)

Trigger API:
    
    global private constant boolean wanttoregister = true       if you want to use TriggerRegisterRepChange, let it true, otherwise set to false
                                                                to shorten the code a little bit
    
    .registerRepChangeEvent(code c, integer rankchange, boolean upside)     registers a rankchange to trigger, the boolean tells
                                                                            if it is upside or downside, whenever the reputation rank is changed
                                                                            all functions registered to that reputation are executed regardless of how much
                                                                            times the same function is there
    
Single Player API:
    
    Those commands shouldnt be used in multiplayer games because they are not changed for each player differently
    and doing so could desync the game.
            
    .addToFaction(unit toadd) > boolean                     Bounds a unit to that faction. One unit can be in any number of factions
                                                            but that may cause problems in changing aggresivity.
                                                            If successful returns true, if already saved or invalid unit(null) returns false.
    
    .removeFromFaction(unit toremove) > boolean             Removes a unit from a faction. If successful returns true, otherwise returns false.
    
    .defineHostility(integer rank)                          Defines at which rank the units start to be friendly/hostile in that faction. Default is: friendly at all time.
    
    .changeFaction(boolean good)                            Immedietally forces the system to swtich all units in given faction to
                                                            friendly(true) or hostile(false)

    (s).getOriginalOwner(unit u) > player                   returns a original owner of a unit bound to faction
    
    (s).getOriginalOwnerId(unit u) > integer                returns Id of original owner of a unit bound to faction

                                                        
    In case you dont like to use structs or methods, there is library under this one with a function API for this system.
    
    
*/
//------------------------------------------------------------------------
/*
    Change note:
    
        Version Log:
            
            121107  - fixed a bug with CreateNewRepEx(prior to 121107 createEx) not
                      setting name of the reputation
                    - removed local variable j from remove method
                    - renamed all methods(check API to see new names)
                    - moved function API into separated library
                    - reworked how repS2I works (O(n) -> O(1) )
            
            121104  - remoworked how .ChangeFaction() works in AddRep and LowerRep methods
                    - added 2 new methods: GetOriginalOwner and GetOriginalOwnerId as well as 2 functions
                      with the same name
            
            121103  - Intial release
*/
//------------------------------------------------------------------------

    globals
        private constant boolean wanttoregister = true  //If you plan on using RegisterRepChangeEvent let this true, otherwise set to false
    endglobals



    struct Reputation
        private string reptypename
        private static Table repname
        private static integer maxreptype = 0
        private static TableArray tab
        private static TableArray tac
        private static integer maxstack = 1
        private static integer array pointtoname        //sololy for .getName() so we can point to every instance, otherwise
                                                        //method repS2I would be O(n)
        private static integer array repreq
        private static integer array reqxp
        
        /*  
            why all arrays instead of normal variables as reptypename may you ask?
            I like array syntax more then struct syntax, also I dont need to declare another
            integer(thistype) in every function Im passing integers not reputation or without allocation
            and still can access the desired index. Example is in setRank
            If I didnt have arrays I would have to declare another integer = waste of memory
        */
        
        static method create takes string name returns thistype
            local integer i = 0
            local thistype this = .allocate()
            set repname[StringHash(name)] = this
            set pointtoname[this] = StringHash(name)
            loop
                set tac[i][this] = 0
                exitwhen i == 15
                set i = i + 1
            endloop
            set maxstack = this
            return this
        endmethod
        
        static method createEx takes string name, integer startingrep returns thistype
            local integer i = 0
            local thistype this = .allocate()
            set repname[StringHash(name)] = this
            set pointtoname[this] = StringHash(name)
            loop
                set tac[i][this] = startingrep
                exitwhen i == 15
                set i = i + 1
            endloop
            return this
        endmethod
        
        method remove takes nothing returns nothing
            local integer i = 0
            set repname.string[this] = ""
            set pointtoname[this] = 0
            loop
                set reqxp[tac[i][this]] = 0
				set tab[i][this] = 0
                exitwhen i == 15
                set i = i + 1
            endloop
            set repreq[this] = 0
            call .deallocate()
        endmethod
        
        static method setRank takes integer rank, string name, integer xpto returns nothing
            
            //to make sure we can use struct syntax (something.something_else)
            
            local thistype this = rank
            
            //protection
            
            if repreq[rank] == -1 then
                return
            endif
            
            set repreq[rank] = -1
            set this.reptypename = name
            
            // to ensure that even when we get over the rep needed it will show the correct rep
            
            set this = this + 1
            set this.reptypename = name
            
            
            set reqxp[rank] = xpto
            set maxreptype = maxreptype + 1
        endmethod
        
        method changeFaction takes boolean good returns nothing
            local unit u
            local integer i = 1
            loop
                set u = GetUnitById(saver[this][i])
                if good then
                    call SetUnitOwner(u, Player(15), true)
                else
                    call SetUnitOwner(u, Player(12), true)
                endif
                exitwhen i == hunit[this]
                set i = i + 1
            endloop
        endmethod
        
        method increase takes integer howmany, integer forplayer returns nothing
            local integer a = tab[forplayer][this]
            local integer b = tac[forplayer][this]
            set a = a + howmany
            loop
                exitwhen a < reqxp[b] or maxreptype == b
                if a > reqxp[b] then
                    set a = a - reqxp[b]
                    set b = b + 1
                endif
            endloop
            if b > hostile[this] and not changed[this] then
                call .changeFaction(true)
                set changed[this] = true
            endif
        static if wanttoregister then
            if b > (tac[forplayer][this]) then
                if regist[this].trigger[b*2-1] != null then
                    call TriggerEvaluate(regist[this].trigger[b*2-1])
                endif
            endif
        endif
            set tab[forplayer][this] = a
            set tac[forplayer][this] = b
        endmethod
        
        method decrease takes integer howmany, integer forplayer returns nothing
            local integer a = tab[forplayer][this]
            local integer b = tac[forplayer][this]
            set a = a - howmany
            loop
                exitwhen a > 0 or b == 0
                if b <= 0 and a < 0 then
                    set a = 0
                endif
                set a = a + reqxp[b]
                set b = b - 1
            endloop
            if b < hostile[this] and changed[this] then
                call .changeFaction(false)
                set changed[this] = false
            endif
        static if wanttoregister then
            if b < (tac[forplayer][this]) then
                if regist[this].trigger[b*2] != null then
                    call TriggerEvaluate(regist[this].trigger[b*2])
                endif
            endif
        endif
            set tab[this][forplayer] = a
            set tac[forplayer][this] = b
        endmethod
        
        method getName takes nothing returns string
            return repname.string[pointtoname[this]]
        endmethod
    
        static method repS2I takes string name returns integer
            return repname[StringHash(name)]
        endmethod
        
        method getRep takes integer playerindex returns integer
            return tab[playerindex][this]
        endmethod
        
        static method getTypeById takes integer id returns string
            local thistype this = id
            return this.reptypename
        endmethod
        
        method getRankTypeOfPlayer takes integer forplayer returns string
            local thistype what = tac[forplayer][this]
            return what.reptypename
        endmethod
        
        method getRankOfPlayer takes integer ofplayer returns integer
            return tac[ofplayer][this]
        endmethod
        
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        
        private static method onInit takes nothing returns nothing
            set tab = TableArray[16]
            set tac = TableArray[16]
            set saver = TableArray[0x2000]
            set repname = Table.create()
        static if wanttoregister then
            set regist = TableArray[0x2000]
        endif
        endmethod
        
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
    
    //Single Player commands:
    
        private static TableArray saver
        private static integer array hostile
        private static integer array hunit
        private static player array origown
        private static boolean array changed
        
        private static method update takes nothing returns nothing
            local thistype i = 1
            local integer forplayer = 0
            local integer q
            loop
                set forplayer = 0
                set q = tac[forplayer][i]
                loop
                    if hostile[i] > q and not changed[i] then
                        call i.changeFaction(true)
                    elseif hostile[i] < q and changed[i] then
                        call i.changeFaction(false)
                    endif
                    exitwhen forplayer == 15
                    set forplayer = forplayer + 1
                endloop
                exitwhen i == maxstack
                set i = i + 1
            endloop
        endmethod
        
        method addToFaction takes unit u returns boolean
            
            //double save protection
            
            if saver[this][GetUnitUserData(u)] != GetUnitUserData(u) and u != null then
                set saver[this][GetUnitUserData(u)] = GetUnitUserData(u)
                set origown[GetUnitUserData(u)] = GetOwningPlayer(u)
                set hunit[this] = hunit[this] + 1
                call .update()
                return true
            else
                return false
            endif
        endmethod
        
        method removeFromFaction takes unit u returns boolean
            
            //double free protection
            
            if saver[this][GetUnitUserData(u)] == 0 or u == null then
                return false
            else
                set saver[this][GetUnitUserData(u)] = 0
                set hunit[this] = hunit[this] - 1
                set origown[this] = null
                return true
            endif
        endmethod
        
        method defineHostility takes integer rank returns nothing
            set hostile[this] = rank
            call .update()
        endmethod
        
        // Register command:
        
    static if wanttoregister then
        private static TableArray regist
    
        method registerRepChangeEvent takes code c, integer rankchange, boolean upside returns nothing
            if upside then
                if regist[this].trigger[rankchange*2-1] == null then
                    set regist[this].trigger[rankchange*2-1] = CreateTrigger()
                endif
                call TriggerAddCondition(regist[this].trigger[rankchange*2-1], Condition(c))
            else
                if regist[this].trigger[rankchange*2] == null then
                    set regist[this].trigger[rankchange*2] = CreateTrigger()
                endif
                call TriggerAddCondition(regist[this].trigger[rankchange*2], Condition(c))
            endif
        endmethod
    endif
        
        static method getOriginalOwner takes unit u returns player
            return origown[GetUnitUserData(u)]
        endmethod
        
        static method getOriginalOwnerId takes unit u returns integer
            return GetPlayerId(origown[GetUnitUserData(u)])
        endmethod
    
    endstruct
    
endlibrary

library ReputationSystemFunctionAPI requires ReputationSystem //by edo494
    
/*
    Im not going to say what each function does, if you want to know, please reffer to their counterparts in struct
    API:
        
           function(arguments) -> return type(if any)...................................what the function does
        
        CreateNewReputation(string name) -> reputation                              return Reputation.create(name)
        
        CreateNewReputationEx(string name, integer startingrep) -> reputation       return Reputation.createEx(name, startingrep)
        
        RemoveReputation(Reputation rep)                                            call rep.remove()
        
        SetReputationRank(integer rank, string name, integer xpto)                  call Reputation.setRank(rank, name, xpto)
        
        IncreaseReputation(Reputation rep, integer value, integer forplayer)        call rep.increase(value, forplayer)
        
        LowerReputation(Reputation rep, integer value, integer forplayer)           call rep.decrease(value, forplayer)
        
        GetReputationName(Reputation rep) -> string                                 return rep.getName()
        
        ReputationS2I(string s) -> integer                                          return Reputation.repS2I(s)
        
        GetCurrentReputation(Reputation rep, integer playerindex) -> integer        return rep.getRep(playerindex)
        
        GetRepTypeById(integer id) -> string                                        return Reputation.getTypeById(id)
        
        GetCurrentRepTypeOfPlayer(Reputation rep, integer playerindex) -> string    return rep.getRankTypeOfPlayer(playerindex)
        
        GetCurrentRankOfPlayer(Reputation rep, integer playerindex) -> integer      return rep.getRankOfPlayer(playerindex)

//----------------------------------------------------------

    Single Player API(Should not be used in multiplayer for 100% correct result)
        
        ChangeFaction(Reputation rep, boolean flag)         call rep.changeFaction(flag)
        
        AddUnitToFaction(Reputation rep, unit u)            call rep.addToFaction(u)
        
        RemoveUnitFromFaction(Reputation rep, unit u)       call rep.removeFromFaction(u)
        
        DefineHostility(Reputation rep, integer rank)       call rep.defineHostility(rank)
                
        GetOriginalOwner(unit u) -> player                  return Reputation.getOriginalOwner(u)
    
        function GetOriginalOwnerId(unit u) -> integer      return Reputation.getOriginalOwnerId(u)

//----------------------------------------------------------

    Trigger API, only works with global called wanttoregister == true
        
        RegisterRepChangeEvent(reputation rep, code c, integer rank, boolean good)  call rep.registerRepChangeEvent(c, rank, flag)
*/

    // Function API:
    
    function CreateNewReputation takes string name returns Reputation
        return Reputation.create(name)
    endfunction
    
    function CreateNewReputationEx takes string name, integer startingrep returns Reputation
        return Reputation.createEx(name, startingrep)
    endfunction
    
    function RemoveReputation takes Reputation rep returns nothing
        call rep.remove()
    endfunction
    
    function SetReputationRank takes integer rank, string name, integer xpto returns nothing
        call Reputation.setRank(rank, name, xpto)
    endfunction
    
    function IncreaseReputation takes Reputation rep, integer value, integer forplayer returns nothing
        call rep.increase(value, forplayer)
    endfunction
    
    function LowerReputation takes Reputation rep, integer value, integer forplayer returns nothing
        call rep.decrease(value, forplayer)
    endfunction
    
    function GetReputationName takes Reputation rep returns string
        return rep.getName()
    endfunction
    
    function ReputationS2I takes string s returns integer
        return Reputation.repS2I(s)
    endfunction
    
    function GetCurrentReputation takes Reputation rep, integer playerindex returns integer
        return rep.getRep(playerindex)
    endfunction
    
    function GetRepTypeById takes integer id returns string
        return Reputation.getTypeById(id)
    endfunction
    
    function GetCurrentRepTypeOfPlayer takes Reputation rep, integer playerindex returns string
        return rep.getRankTypeOfPlayer(playerindex)
    endfunction
    
    function GetCurrentRankOfPlayer takes Reputation rep, integer playerindex returns integer
        return rep.getRankOfPlayer(playerindex)
    endfunction
    
    function ChangeFaction takes Reputation rep, boolean flag returns nothing
        call rep.changeFaction(flag)
    endfunction
    
    function AddUnitToFaction takes Reputation rep, unit u returns nothing
        call rep.addToFaction(u)
    endfunction
    
    function RemoveUnitFromFaction takes Reputation rep, unit u returns nothing
        call rep.removeFromFaction(u)
    endfunction
    
    function DefineHostility takes Reputation rep, integer rank returns nothing
        call rep.defineHostility(rank)
    endfunction

    static if wanttoregister then
        function RegisterRepChangeEvent takes Reputation rep, code c, integer rank, boolean flag returns nothing
            call rep.registerRepChangeEvent(c, rank, flag)
        endfunction
    endif
    
    function GetOriginalOwner takes unit u returns player
        return Reputation.getOriginalOwner(u)
    endfunction
    
    function GetOriginalOwnerId takes unit u returns integer
        return Reputation.getOriginalOwnerId(u)
    endfunction
    
endlibrary
 
Last edited:
Sorry about that, I will review your script now.

edit
Okay, from what I can see, this is the kind of resource that would be better off as a Spell resource because it's not something one would associate with systems like Event, PriorityEvent, UnitIndexer, RegisterPlayerUnitEvent, Table and the like. It's something you would associate with Streak systems, Recipe systems, Hero-picking systems, etc...
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
Yes, but well That requires picture :D Ill post it there then, I think too that this doesnt really fit here because its usefulness is at some different level then things like Table, UnitIndexer, TimerUtils ...
Also no need to apologies, its not been that long :D
 
Some code improvements would involve some name changes and API modifications ;P

struct Reputation
Because in JASS, the convention for struct names is FooBar.

static method CreateNewRep
static method CreateNewRepEx
Reputation.CreateNewRep() is a pretty wordy call because we already know that we're going to be creating "NewRep" :p
It's a convention to use "create" for the constructor names in structs.
Respectively, you could use createEx (camelCased of course)

static method RemoveRep
static method SetReputationRank
static method AddRep
static method LowerRep
-> remove()
-> setRank()
-> add()
-> subtract()
Actually, subtract is bad name, so you can call it decrease(), but then, add() would become increase() ;D

Note: These are the shortest names that make the most sense and are the most intuitive.

All of the struct methods need to be camelCased (meaning they would be written similarly to how I just wrote camelCased ;D)

My last comment concerning function naming involves TriggerRegisterRepChange.
A better alternative to this name would be:
registerRepChangeEvent()
This is because to the user, the method has nothing to do with triggers, but only a code, because he's passing in a code argument.

As for algorithms, I have one thing to say about your RepStringToInteger method for now.
It's using an O(n) method (iteration over a list/array) when it could be O(1). (constant execution speed)
You can achieve this by using a hashtable. (Or a Table in this case :D)
Using an instance of Table with a StringHash call should be good enough I guess.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
Updated a little bit

Ill post it in spells section once I found a way how to light method update a little bit, currently it is O(O(n)) and Its really bad because every time method addToFaction is called, the update is called as well

Also upon posting to spells section Ill separate the function API library and put it into another [code=jass][/code] tags
 
Top