• 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.

[vJASS] Merchant system not working properly

Status
Not open for further replies.
Alright, i have a ssytem that is designed to simulate trade routes between allied countries in my map. It is meant to work like this:

*Every x seconds, the script groups your allies and sorts them in an array based on how many ports they have. Ally with most ports is 1, ally with second most ports is 2, etc.

*The system groups all your ports and orders the first one in the group to send a trade ship towards the closest port owned by the first ally in the list. The second port will send to the second player in the list, and so on. If there are more ports then allies, it will start over from the top.

*The map is divided into two "theatres", one asian and one european. For this reason, the system has to run once for each theatre to make sure that no ships attempt to cross the border. You will see how this works in the script.

*If a player has no allies with ports in the same theatre, he will not send any trade ships.

Here is the code:

JASS:
library TradeSys initializer Init requires GetClosestWidget
    //Credits to this library goes to Spinnaker.
    globals
        private integer          handler  = 0 //this variable is used for deciding which theatre to use - ports in the asian theatre are stored in "ports[i+handler]"
        private timer            timers   = CreateTimer()
        private force            traders  = CreateForce()
        private trigger          trig     = CreateTrigger()
        public integer array     numports
        public group array       ports
        private player array     players
        
        private constant integer HARBOR_ID = 'h00R'
        private constant integer SHIP_ID   = 'h01B' //ID of the trade ship that will be spawned
        private constant real    INTERVAL  = 20. //Interval between the shipments
    endglobals

    ////////////////////////////////////////////////////
    
    private function Swap takes player a, player b returns nothing
        local player p = a
        set a = b
        set b = p
        set p = null
    endfunction
    
    private function SelectSort takes integer n returns nothing //This trigger sorts the allies depending on ammount of ports.
        local integer i = 0
        local integer j
        local integer m
        loop
            exitwhen i > n
            set m = i
            set j = i + 1
            loop
                exitwhen j > n
                if numports[GetPlayerId(players[j])+handler] < numports[GetPlayerId(players[m])+handler] then
                    set m = j
                endif
                set j = j + 1
            endloop
            call Swap(players[m], players[i])
            set i = i + 1
        endloop
    endfunction
        
    private function Execute takes nothing returns nothing
        local unit u
        local unit e
        local integer i = 0
        local integer m = 0
        local integer n = 0
        local player p = GetEnumPlayer()
        local player q
        loop
            exitwhen i > 11
            set q = Player(i)
            if GetPlayerAlliance(p, q, ALLIANCE_SHARED_VISION) and p != q and numports[GetPlayerId(q)+handler] > 0 then
                set players[n] = Player(i)
                set n = n + 1
            endif                
            set i = i + 1
        endloop
        set m = n - 1
        call SelectSort(m)
        call GroupClear(bj_lastCreatedGroup)
        call ForGroup(ports[GetPlayerId(p)+handler], function AddGroupEnum)
        loop
            set u = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen u == null
            if 0 == m then
                set m = n - 1
            else
                set m = m - 1
            endif
            set bj_lastCreatedUnit = CreateUnit(p, SHIP_ID, GetUnitX(u), GetUnitY(u), 0)
            set e = GetClosestUnitInGroup(GetUnitX(u), GetUnitY(u), ports[m+handler])
            //call IssueTargetOrder(bj_lastCreatedUnit, "channel", u) 
            call IssueTargetOrderById(bj_lastCreatedUnit, OrderId("thunderbolt"), e) //This is just a dummy spell to register when the ship reaches it's destination. 
            call GroupRemoveUnit(bj_lastCreatedGroup, u)
        endloop
        set e = null
        set p = null
        set q = null
        set u = null
    endfunction

    private function CallBack takes nothing returns nothing
        set handler = 0
        call ForForce(traders, function Execute) //For european theatre
        set handler = 12
        call ForForce(traders, function Execute) //For asian theatre
    endfunction
    
    private function SortHarbors takes nothing returns boolean
     //This decides wether a port is in the european or asian theatre. 
     //All units above -2500 are in the european theatre, all below -2000 are in asian. 
     //This trigger is used in the beginning of the game to sort the ports into the array. 
        local unit u = GetFilterUnit()
        local integer i = GetPlayerId(GetOwningPlayer(u))
        if GetUnitTypeId(u) == HARBOR_ID then
            if GetUnitY(u) <= -2000 then
                set numports[i] = numports[i] + 1
                call GroupAddUnit(ports[i], u)
            elseif GetUnitY(u) >= -2500 then
                set numports[i+12] = numports[i+12] + 1
                call GroupAddUnit(ports[i+12], u)
            endif
        endif
        set u = null
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        local integer i = 0
        local player p
        loop
            exitwhen i > 11
            set p = Player(i)
       //     if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
       //     I commented above statement out in order to try the system in single player

                call ForceAddPlayer(traders, p)
                if ports[i] == null then
                    set ports[i] = CreateGroup()
                endif
                set handler = i + 12
                if ports[handler] == null then
                    set ports[handler] = CreateGroup()
                endif
          //  endif

            call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, Filter(function SortHarbors))
            set i = i + 1
        endloop
        call TimerStart(timers, INTERVAL, true, function CallBack)
        set p = null
    endfunction
endlibrary

And here is a snippet from the code for capturing ports - not that it is of any interrest:

JASS:
            if GetUnitTypeId(u) == 'h00R' then
              if GetUnitY(u) <= -2000 then
                  set TradeSys_numports[b] = TradeSys_numports[b] + 1
                  set TradeSys_numports[a] = TradeSys_numports[a] - 1
                  call GroupAddUnit(TradeSys_ports[b], u)
                  call GroupRemoveUnit(TradeSys_ports[a], u)
              else
                  set a = a + 12
                  set b = b + 12
                  set TradeSys_numports[b] = TradeSys_numports[b] + 1
                  set TradeSys_numports[a] = TradeSys_numports[a] - 1
                  call GroupAddUnit(TradeSys_ports[b], u)
                  call GroupRemoveUnit(TradeSys_ports[a], u)
              endif
            endif

Here is a shot of the minimap, for better understanding:

minimap.png


Now, my problem is, that some players will not send their ships - Japan and USA both send no ships whatsoever (they spawn, but don't move), while for other players, some individual ports won't send, like Arkhangel'sk, Singapore, etc.
Another problem is that some axis players send ships to allied players, even though they are enemies. For instance, finish port in Petsamo sends ships to russian Murmansk.

The ones that work flawlessly are:

GB Port Morseby to USSR Vladivostok - this trade route functions perfectly.

Chineese port below Changsha to USSR vladivostok - also this works as planned

USSR Vladivostok to US Pearl Harbour - this works well, but since US player won't send ships, they cluster up, and hence the USSR ships can't get past.

GB London to US New York - same as above

All german ports somehow insist on sending their ships to USSR port in Arkhangel'sk, which is unallied

Finnish ports in Petsamo and Oulu both insists on sending ships to unallied USSR port in Murmansk

Please note that this is in the beginning when n ports had been captured. I was playing as USSR.

Players are the following:

USSR (player 0)
USA (player 1)
China (player 3)
Great Britain (player 5)
Italy (player 6)
Germany (player 8)
Finland (player 10)
Japan (player 11)

Player slots not mentioned are not in use.


I seriously have no idea why this is happening, the system is confusing enough as it is with all it's loops and sorting algorithms, so can anyone help me with this?
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
Download link

You may want to wait until tomorrow night though, we will be uploading ver 1.2 with various bugfixes and such, not to mention v2 missiles, new german bomber model, some new terrain, etc.

Wait, what? Come on bro i know you better than that (even if you may not know me) . Your acting like a jan 2011... Post the trigger (or atleast tell us where to look), tell us whats wrong with it, and we can help "Merchant System not working properly" than linking to your map doesnt help
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
OMG!! ok, sorry, i just posted this in the wrong thread!

Sorry dude, i had several windows open, intended to post that as a reply for a guy who waws asking for a download link for world in flames.

The code will be up in 20 seconds!

lol..?
 
Level 12
Joined
Mar 28, 2005
Messages
160
reading convoluted code like that made by someone else can be tough

so I took a stab using the approach I would have taken

un-compiled or tested, take it for what its worth

the idea was

loop through all players in a given theatre
sort their allies based on port count
loop through all of their ports
sending one ship from each to one port from each sorted ally (which ever port is closest), starting at 1, and returning to 1 if the total number of allies is exceeded

JASS:
struct portData // one struct per player housing all ports data
    integer array ports // each port that we have is saved when it is acquired
    // ports are identified by a numeric value establish on mapInit
    integer count // total number of ports a player has

// run a function periodically for each theatre available
local integer i=0
local integer j
local integer k
local integer l
local integer m
local integer n
local real dist
local force f=CreateForce()
local force ff=CreateForce()
local player p=null
local player pp=null
local player array ppp
local integer c=0
local integer ports=0
local portData d
local portData dd

loop
    exitwhen i>TOTAL_PLAYERS // loop through all players    
    call ForceEnumAllies(f,Player(i),null) // get all allies of the player
    call ForceEnumAllies(ff,Player(i),null) // you will see why we need two of the same force
    
    set c=0
    set p=null
    loop // loop until all allies have been sorted
        set ports=0 // reset highest ports total value
        set pp=null
        
        loop // loop through all allies who have not been sorted        
            set p=ForcePickRandomPlayer(f) // grab random player in allies force
            exitwhen p==null // all remaining players have been checked for next highest ports total
            call ForceRemovePlayer(f,p)
            
            set d=GetPlayerPortData(p) // however you want to store it, in a global array, hashtable, whatever
            if p.count>ports then // new next highest ports total found
                set pp=p // update current highest player this iteration
                set ports=p.count // update current highest port count this iteration
            endif            
        endloop
        
        set c=c+1 // found the next highest ports count player
        set ppp[c]=pp // store that player
        call ForceRemovePlayer(ff,pp) // don't count that player again              
        exitwhen ForcePickRandomPlayer(ff)==null // every player has been sorted
        call ForceClear(f) 
        call ForceAddForce(ff,f) // restore our original ally force, minus the players who have already been sorted
        // so we don't loop through them again
        // its a shame this native does not exist :(, try to  make it work just like GroupAddGroup  
    endloop
    
    // we now have have a sorted array ppp which houses all players ranked based on port totals
    // ideally we only do this once, and keep this list saved (that is ofcourse if alliances are fixed)
    // the way I coded this assumes they are not
    // now we need to start sending ships out
    // loop through all of our players ports, and send a ship to the allies in circular order, based on their ports distances
    // the following is only run assuming c>0
    
    if c>0 then // we have allies, and atleast one of them has a port!!!!!
        set j=1
        set l=1
        set d=GetPlayerPortData(Player(i))
        loop
            exitwhen j>d.count // loop through all of this players available ports
            
            set k=d.ports[j] // grab this ports number
            // again these stored values reference a particular port
            // maybe d.ports[1]=12, and port 12 is china, or something like that
            set l=1
            set dd=GetPlayerPortData(ppp[l]) // next player in sorted progression
            set m=1
            set dist=9999.
            loop // loop through this ordered players ports to find the closest port to port d.ports[j]
                exitwhen m>dd.count // exitwhen we have checked each of this players ports
                    if DistanceBetweenPorts(PORTS[k],PORTS[dd.ports[m]])<dist then // check distance between ports
                        // closer port found, store it
                        set dist=DistanceBetweenPorts(PORTS[k],PORTS[dd.ports[m]])
                        set n=dd.ports[m] // and its distance so we can compare to all others
                    endif
                set m=m+1
            endloop
            
            // send ship from port PORTS[k] to port PORTS[n] for Player(i)
            // where PORTS is an array that stores our port location data
            call SendShipForPlayer(Player(i),PORTS[k],PORTS[n])
            
            set j=j+1 // eventually this will exceed our total number of ports for Player(i)
            set l=l+1 // if this exceeds the total number of allies
            if l>c then
                set l=1 // we need to start back over at the one with the highest number of ports we found earlier
            endif
        endloop
    endif
endloop
seems logical enough to me, and theoretically does what you want, in a whole lot less code then you were using before

heavily commented so hopefully you can follow my thought process...

was not sure if you wanted to avoid sending ships to ports you had already targeted - this does not do that
 
Bah, just started tampering with it.. and i noticed, it is so sketchy, it is almost metacode!

No wonder your code is shorter, when half the related functions aren't present. It is very hard to read and implement when i will have to try and read the code forwards and backwards to see what things you have left out.

I really appreciate you taking the time to write this down for me, but for this to be of any use to me, i would need to know for instance how you solved the issue with two theatres (you just say "run this stuff once for each theatre"), and why you are using integers to represent the different ports? would i have to link each port to an integer AND save the location of each in a separate array?

Do it again and do it properly, or more preferably, make something that could fit into the foundations used in my old script, and i will give you +4.
 
Ah, i see it now, that is true! Using a variable as parameter only passes the value, not the variable itself. Instead i should be using the indexes as parameters and use the player ararys inside the function.

Well spotted, +rep!

EDIT: So, i changed it to this:

JASS:
    private function Swap takes integer a, integer b returns nothing
        local player p = players[a]
        set players[a] = players[b]
        set players[b] = p
        set p = null
    endfunction

But to no effect. Theese are the problems i am still experiencing:

*USA and Japan aren't sending their ships.
*Japan is spawning ships even though they have no allies in the pacific theatre.
*Axis countries in the european theatre are sending their ships to enemy ports, even though friendly ports are closer.

I think the error might be either here:

JASS:
    private function SelectSort takes integer n returns nothing //This trigger sorts the allies depending on ammount of ports.

//Is there any chance that somewhere in this loop, a "friendly" player index is recpaced with one of an "enemy"?
        local integer i = 0
        local integer j
        local integer m
        loop
            exitwhen i > n
            set m = i
            set j = i + 1
            loop
                exitwhen j > n
                if numports[GetPlayerId(players[j])+handler] < numports[GetPlayerId(players[m])+handler] then
                    set m = j
                endif
                set j = j + 1
            endloop
            call Swap(players[m], players[i]) 
            set i = i + 1
        endloop
    endfunction

or here:

JASS:
set m = n - 1 //?
        call SelectSort(m)
        call GroupClear(bj_lastCreatedGroup)
        call ForGroup(ports[GetPlayerId(p)+handler], function AddGroupEnum)
        loop
            set u = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen u == null
            if 0 == m then
                set m = n - 1 //Why is this being done?
            else
                set m = m - 1 //And this?
            endif
            set bj_lastCreatedUnit = CreateUnit(p, SHIP_ID, GetUnitX(u), GetUnitY(u), 0)
            set e = GetClosestUnitInGroup(GetUnitX(u), GetUnitY(u), ports[m+handler])
            //call IssueTargetOrder(bj_lastCreatedUnit, "channel", u)
            call IssueTargetOrderById(bj_lastCreatedUnit, OrderId("thunderbolt"), e) //This is just a dummy spell to register when the ship reaches it's destination.
            call GroupRemoveUnit(bj_lastCreatedGroup, u)
        endloop

Read my comments in the code and say what you think.
 
Last edited:
Level 12
Joined
Mar 28, 2005
Messages
160
Bah, just started tampering with it.. and i noticed, it is so sketchy, it is almost metacode!

No wonder your code is shorter, when half the related functions aren't present. It is very hard to read and implement when i will have to try and read the code forwards and backwards to see what things you have left out.

I really appreciate you taking the time to write this down for me, but for this to be of any use to me, i would need to know for instance how you solved the issue with two theatres (you just say "run this stuff once for each theatre"), and why you are using integers to represent the different ports? would i have to link each port to an integer AND save the location of each in a separate array?

Do it again and do it properly, or more preferably, make something that could fit into the foundations used in my old script, and i will give you +4.

i don't know what metacode is, but I will take that as a compliment....

you are being a bit melodramatic, I mean, honestly.....the only things that are left out are the things that are incredibly simple (and I assumed you had custom functions to perform anyway)

GetPlayerPortData(p) - > this can be as complicated as return portData[GetPlayerId(p)]

DistanceBetweenPorts() - > this is a simple distance algorithm, there is even a bj one if you don't have your own

SendShipForPlayer() - > Create a ship at the first port, order it to move to the second....two lines

aside from that, as far as theatres are concerned (which I assumed were different map areas) - I assumed you had two different map areas, and different ports located in each - depending on which theatre you are checking for in this instance, just double check as you loop through a player's ports to send a ship from, or as you loop through the other players ports to send ships to, or even as you are counting the number of ports in a theatre (if that is important too - in this case we would need an additional loop inside the port count ordering loop, to loop through a players ports and check to see if its in the correct theatre before it was counted towards that players total - hell if this is the case, simple have separate player port structs, one for each theatre in question, and only check the struct from the theatre of interest) to make sure that these ports are in the correct theatre (a unitgroup would be very simple, so long as the global array PORTS are units, if its say a location, check that its loc in in the theatres rect), I neglected to include that in my script on accident

the way I identify ports was just the first way that popped into my head, maybe not the best, but its far from difficult to understand

for instance, the global array PORTS

PORTS[1]=China
PORTS[2]=America
PORTS[3]=Europe

or whatever - these ports could probably just be units that you have stored in the array, or locations, or however you reference them, either way works equally well

when we store an acquired port to the players struct

set this.count=this.count+1
set this.ports[count]=PORTS[#]

where # is the acquired port, which ever it is

now we know how many ports each player has, along with which ones they have

you can extend that to check and see if a player as a particular port

loop through their ports from index=1->this.count

if this.ports[index]==PORTS[whatever]

the player as the port of interest!!

see how easy?

my script wasn't meant to be an end all be all solution (otherwise I would have compiled and tested it) - it was meant to be an example to help you get thinking on the right track, or to spur some unique ideas to help you solve your problem, or as simply a second approach at a solution

to be honest your response was incredibly un-appreciative, especially considering I spent a good 45 minutes working out all the loop logic to get things to work just for the sake of trying to help you, and leaves me little desire to help out anymore
 
I am sorry about sounding unappreciative, as i said, i respect the time you have taken to figure that out, aswell as the time you just took to explain it to me. It's just that i get the impression that your suloution is more cumbersome and less thought-out than the one i already have, having to store a bunch of structs, forces and locations, and other things that are disregarding many of the foundations of my code that are already woven in to related libraries, like for instance - there is no need to use a struct to combine the two variables that already existed in the original code as separate arrays. There is also no need for the routine of getting the closest port, that, i DO have a GetClosestUnitInGroup function for. You could also have left out the player browsing, since i could easily just place your code in the spot of the "execute" function. Your variable names are also confusing, for instance, having two arrays named "ports" makes it very hard for me to keep track of what is what. All of this would just force me to rewrite all your code, or at least makes it difficult to fit it into my existing framework.

The way my current code works is this:

*The ports for each player are all stored in a group array called Ports[]. Ports in the pacific theatre are those from 0 to 11, and the ports of the european theatre are those from 12 and forwards. To run for both theatres, the "execute" function is run twice, with a "handler" value set before each time. This handle value is basically the offset used in the array.

*The number of ports of each player is listed in an integer array canned numports[]. This is so that we don't have to recount the ammount of ports everytime we run the loop.

*On initialization, all harbours are grouped and placed in respective group. numport value for each player is calculated. Ports in this game never die, they are captured.

*A periodic timer runs, and on expiration, it runs the callback function, which in turn runs the "execute" function once for each theatre (using the handle variable).

*The script sorts all players allied to the enum player and adds them to an array. It then sorts the array in order of what player has most ports.

*Finally, it loops through the ports of the enum player, and for each port, picks whatever player is next in the list, picks the port that is closest of the ones in that players port group, and sends a ship to it. One port can recieve ships from several different players.

One part of this is not working.
You have a different sort method that might be better than the one i am using. However, it is written with different pretenses. Is there any chance, that you can explain it to me in ways and with variables that could be integrated with my existing library? Like, in the place of the "execute" function?

I am sorry if i came out harsh in my response. Your code was well commented and your effort was more than most people would do to help out. i was frustrated that it seemed like you had not looked at my code in the first place, which in turn meant i would have to do a lot of extra work changing other systems that used the same variables etc.


EDIT: I just realized, from reading my own explanation, why some ports are still spawning ships despite having no allies with ports in the same theatre. The "numports" integer array contains the total ammount of ports in both theatres, not in the current.
 
Level 12
Joined
Mar 28, 2005
Messages
160
*The script sorts all players allied to the enum player and adds them to an array. It then sorts the array in order of what player has most ports.
i think my player sort method is pretty good, if you dynamically adjust a force for each player with their allies, then you can cut out the part where I grab their allies, and use your forces variable in its place, but the rest is applicable

*Finally, it loops through the ports of the enum player, and for each port, picks whatever player is next in the list, picks the port that is closest of the ones in that players port group, and sends a ship to it. One port can recieve ships from several different players.
this seems to be your main culprit

next time I get a few minutes I will rewrite my "this part" using your variables

I had hoped you could apply my principles to your variables and such
 
Thank you, that sounds fair.

I had hoped you could apply my principles to your variables and such

Although i don't question the use of one-letter variables, i feel that it makes it hard to keep track on other peoples calculations. Also, i never understood the purpouse of using an integer array to store units, why not simply use a unit array instead, and skip the combersome work of attaching an ID to each and every port? Sure, i have both an indexer and the ability to simply use GetHandleId, but it seemed strange to me regardless.
 
Level 12
Joined
Mar 28, 2005
Messages
160
try this one on for size

and as a bonus, it compiles

I had to invent a few variable names....

JASS:
globals 
    //all the ports on the map, you said 0-11 is the Pacific, and >11 is Europe
    unit array AllPorts
    // Ports a player currently has
    group array Ports
    //total number of ports in the game, I imagine you have this stored somehow
    integer TotalPorts=22 // ???
    // ports in the pacific
    group PacificPorts=CreateGroup()
    //ports in europe
    group EuropePorts=CreateGroup()
    //are we looping through the Pacific, or through Europe?  which theatre?
    boolean Pacific=true
    //number of ports a player has, index is their player id
    integer array Numports
    //periodic timer for execution
    timer Timer=CreateTimer()
    
    force TempForce=null //just a little force we need between functions
endglobals

function HasPorts takes nothing returns boolean
    //we only want players with some port, right?
    return Numports[GetPlayerId(GetFilterPlayer())]>0
endfunction
function RestoreAllies takes nothing returns nothing
    call ForceAddPlayer(TempForce,GetEnumPlayer())
endfunction
function LetsDoThisShit takes nothing returns nothing
    local integer i=0 //counter
    local integer currAlly=0 //counter
    local integer portsInTheatre=0 //temp int
    local force allies=CreateForce()
    local force storage=CreateForce()
    local player array p //temp player storage
    local player array sorted //sorted player array
    local integer playerCount=0 //total players in sorted array
    local integer portsMax=0 //temp integer storage
    local group g=CreateGroup() //temp group
    local unit tempPort=null //temp unit
    local group theatreGroup //which theatre we are in
    
    if Pacific then
        set theatreGroup=PacificPorts
    else
        set theatreGroup=EuropePorts
    endif
    set Pacific=not Pacific
    
    loop
        exitwhen i>12 //total number of players
        
        call ForceEnumAllies(allies,Player(i),Condition(function HasPorts))
        call ForceEnumAllies(storage,Player(i),Condition(function HasPorts))
        
        loop //loop until all allies have been sorted
            
            loop //loop through all allies who have not been sorted
                set p[0]=ForcePickRandomPlayer(allies) //grab random player in allies force
                exitwhen p[0]==null //all remaining players have been checked for next highest ports total
                call ForceRemovePlayer(allies,p[0])
                
                call GroupClear(g)
                call GroupAddGroup(Ports[GetPlayerId(p[0])],g)
                loop
                    set tempPort=FirstOfGroup(g)
                    exitwhen tempPort==null //loop through the player ports
                    call GroupRemoveUnit(g,tempPort)
                    if IsUnitInGroup(tempPort,theatreGroup) then //if this port in is the correct theatre
                        set portsInTheatre=portsInTheatre+1 //then lets count it
                    endif
                endloop                
                
                if portsInTheatre>portsMax then //new next highest ports total found
                    set p[1]=p[0] //update current highest player this iteration
                    set portsMax=portsInTheatre //update current highest port count this iteration
                endif      
                set portsInTheatre=0
            endloop
            // now we have the player with the highest port count still in the allies force
            
            set playerCount=playerCount+1 //found the next highest ports count player
            set sorted[playerCount]=p[1] //store that player
            call ForceRemovePlayer(storage,p[1]) //don't count that player again
            exitwhen ForcePickRandomPlayer(storage)==null //every player has been sorted
            set TempForce=allies
            call ForForce(storage,function RestoreAllies)
            
            set portsMax=0
            set p[1]=null
        endloop
        
        if playerCount>0 then // we have allies to send ships to     
            call GroupClear(g)
            call GroupAddGroup(Ports[GetPlayerId(Player(i))],g)
            loop
                set tempPort=FirstOfGroup(g)
                exitwhen tempPort==null
                call GroupRemoveUnit(g,tempPort) //loop through Player(i) ports
                
                if IsUnitInGroup(tempPort,theatreGroup) then //port is in the correct theatre 
                    //use your custom function to find the closest port to tempPort in group Ports[GetPlayerId(sorted[currAlly])]
                    //and however you do it, send a ship from tempPort to this closest port
                endif
                
                set currAlly=currAlly+1 // if this exceeds the total number of allies
                if currAlly>playerCount then
                    set currAlly=1 // we need to start back over at the one with the highest number of ports we found earlier
                endif
            endloop
        endif
        
        set i=i+1
    endloop
    
    //clean up
    call DestroyForce(allies)
    call DestroyForce(storage)
    call DestroyGroup(g)
    set tempPort=null
endfunction

//onInit
function SetupPortsGroups takes nothing returns nothing
    local integer i=0
    
    //create initial ports groups for players
    loop
        exitwhen i>12 //total players
        set Ports[i]=CreateGroup()
        set i=i+1
    endloop
    
    //group ports
    loop
        exitwhen i>TotalPorts
        //store ports in two seperate groups based on their theatre
        if i<12 then
            call GroupAddUnit(PacificPorts,AllPorts[i])
        else
            call GroupAddUnit(EuropePorts,AllPorts[i])
        endif
        call GroupAddUnit(Ports[GetPlayerId(GetOwningPlayer(AllPorts[i]))],AllPorts[i]) // store port in Ports group of owner
        set i=i+1
    endloop
    
    //periodically send out transport ships, 15. is a total guess here
    call TimerStart(Timer,15.,true,function LetsDoThisShit)
endfunction
 
Last edited:
Ah, that looks much better! I can understand a lot more of the code now, and it looks much more methodical. Only thing i find redundant is having a global array with all the ports in it, aswell as another global for counting them, when you are only using them in the onInit function! I think you had just misunderstood my code, with the +12 stuff, but that's ok. Those are things that are very easy for me to fix though, this is how i would do it:

JASS:
private function onEnum takes nothing returns boolean
    if GetUnitY(GetFilterUnit()) > EUROPE then
        call GroupAddUnit(EuropePorts, GetFilterUnit())
    elseif GetUnitY(GetFilterUnit()) < PACIFIC then
        call GroupAddUnit(PacificePorts, GetEnumUnit())
    endif
    call GroupAddUnit(Ports[GetPlayerId(GetOwningPlayer(GetFilterUnit()))],GetFilterUnit())
    
    return false
endfunction

private function Init takes nothing returns nothing
    local group g = CreateGroup()
    call GroupEnumUnitsOfType(g, UnitId2String(HARBOR_ID), Filter(function onEnum))
    call DestroyGroup(g)
    call TimerStart(Timer, INTERVAL, true, function LetsDoThisShit)
    set g = null
endfunction

EDIT: Alright, i implemented the code, and so far.. it have not managed to make it send ships at all. It might ofcourse be the way i implemented it, this is how the ship-sending part looks:

JASS:
        if playerCount>0 then // we have allies to send ships to
            call GroupClear(g)
            call GroupAddGroup(Ports[GetPlayerId(Player(i))],g)
            loop
                set tempPort=FirstOfGroup(g)
                exitwhen tempPort==null
                call GroupRemoveUnit(g,tempPort) //loop through Player(i) ports
                
                if IsUnitInGroup(tempPort,theatreGroup) then //port is in the correct theatre
                    //use your custom function to find the closest port to tempPort in group Ports[GetPlayerId(sorted[currAlly])]
                    //and however you do it, send a ship from tempPort to this closest port
                    set bj_lastCreatedUnit = CreateUnit(Player(i), SHIP_ID, GetUnitX(tempPort), GetUnitY(tempPort), 0)
                    set target = GetClosestUnitInGroup(GetUnitX(tempPort), GetUnitY(tempPort), Ports[GetPlayerId(sorted[currAlly])]) //"target" is another local i created
                    call IssueTargetOrderById(bj_lastCreatedUnit, OrderId("thunderbolt"), target) 
                endif
                
                set currAlly=currAlly+1 // if this exceeds the total number of allies
                if currAlly>playerCount then
                    set currAlly=1 // we need to start back over at the one with the highest number of ports we found earlier
                endif
            endloop
        endif

I haven't had all too much time to debug it yet, but i will try when i get back home.
 
Level 12
Joined
Mar 28, 2005
Messages
160
i find things, especially this complex, rarely every work on the first go around without some fixing up.

the first thing to check would be that our players are sorted correctly in the player array sorted - assuming that is the case

you could remove the target variable, and just call the function where you currently are using target

need to debug to see if things are what we think they are
 
I did some fixing, and now it does send ships properly! Even Japan and US now send their ships, as i was hoping.
However, there is just one small flaw.. the script seems to completely disregard alliances. US ships go to japan, japanese ships go to USSR, USSR ships appear to go... to themselves! The ships just spawn, and then dissapear (ass they do when they reach their port of destination). In other words, i have largely the same problems as i had with the old script.

I think this can partially be explained with a bug in wc3, that perhaps only is present in map testing (but nonetheless), that somehow returns all CPU players as allies. I had this problem in another map too.
I will try to do some multiplayer testing to see how that works. In the mean time i will still have to consider this unsolved.
 
Level 12
Joined
Mar 28, 2005
Messages
160
check to see who is present in alliances that you know to be between only certain players

my script should never send a ship to itself

unless for some reason players are considered allies of themselves, in which case remove them before sorting the allies


what I would do is write a demo script to shuffle alliances, display what they are, then send ships out to see how it works out
 
Status
Not open for further replies.
Top