• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Creating a custom repair ability

Status
Not open for further replies.
Hello guys! I'm working on a custom resource harvesting system which replaces the standard wc3 resources with custom ones, to support more than 2 resources. Naturally, this also requires a custom way to enforce costs when producing units, which i have also managed to do.

Now, my final problem is that repairing and assisting construction (aka. powerbuilding) will also have to use the custom resources. Unfortunately, it appears that the standard repair ability is channeling, and there is no way of tracing when an individual batch of hit points has been added to the unit. I need to find some kind of workaround for this. Here are the ideas i've been throwing around so far:

1. Replacing the repair ability with a custom one, with cooldown instead of channeling. Intercept when a unit starts casting it, and start a timer which will repeatedly order it to cast it until either ordered otherwise, or the unit is complete. For every time that the ability is cast, resoruces are deducted and the construction state/health amount is increased.

Cons: the build animation will not display properly when the constructon state is set manually. Lots of work overall.

2. Start a timer with 1 second repeating interval everytime a unit starts casting the standard repair spell. Deduct the cost every cycle. If the unit starts casting some other ability, the timer is paused. If the player runs out of resources, the unit is ordered to stop.

Cons: it feels kinda wonky to not know how many hit points have actually been added, and just deduct the resources "on faith". If the player runs out of resources, all units engaged in construction will stop and not resume once more resources are gained, unless explicitly ordered to do so.

Which way do you think would be the best? Is there a third, better way? Are there any other problems with these methods?
 
I like the second one more, to work with the in-build repaire ability.

It sounds good make a struct,
- adding a new instance onRepairStart with certain members (builder, playerId, maybe resource usage)
- having a periodic loop through all instances with destroy() if no resource is here
- for all builders of all instances a method that fires destroy() method once they get a new order while repairing

sorry, could you re-phrase it what you mean with "on faith" with the 2nd way you posted

Creating a whole new ability from scratch might work as well, but I assume it to be a lot more work to get it work correctly.
 
By "on faith" i mean that there's no way to guarantee that the unit is actually repairing while the resources are being deducted, or that the amount of hp repaired is consistent with the cost. For instance, the repair time is specified in the object editor. To simplify things, i intended to make it so that the repair cost is a constant factor of the build cost. If the repair time is long, more resources will be spent. Still better than having no support for repair at all though, i guess.

I started working on applying this but realized that in order to get the correct amount of resources to reduce per second, i need to know how long the building takes to construct. Is there any way of retrieving this through triggers, or will users have to register the construction time of each building separately?
 
Possibly, but there is no way of guaranteeing that the heal is the result of repair. Besides that, his system is quite comprehensive, and also requires me to implement his GUI unit indexer which would amount to a large amount of overhead code to solve a very small problem.

Another problem i realized with method 2 is that it will be possible to hack your way into repairing units even though you don't have the funds. Essentially, since the timer that deducts the resources runs once every second, and only then stops the unit from repairing, it is possible to spam-click the target to make the worker repair small bits at the time before being stopped. Considering this, i'm not really sure if it's worth bothering with this method?

EDIT: Here is the code i made so far, for anyone who is interested:

JASS:
    struct Repair extends array
        static group repairGroup
        static integer count = 0
        static timer updateTimer
        
        private static method enumRepairing takes nothing returns nothing
            local Resource r = Resource.first
            local Resource array res_list
            local integer unitid = GetUnitTypeId(LoadUnitHandle(Hashtbl, 0, GetHandleId(GetEnumUnit())))
            local integer array cost_list
            local integer playerid = GetPlayerId(GetOwningPlayer(GetEnumUnit()))
            local integer cost = 0
            local integer n = 0
            local boolean apply = true
            
            loop
                exitwhen r == 0

                set cost = GetCost(unitid, r) * (1/GetUnitBuildTime(unitid))
                
                if cost > 0 then
                    if PlayerResource[playerid][r] >= cost then
                        set res_list[n] = r
                        set cost_list[n] = cost
                        set n = n+1
                    else
                        call IssueImmediateOrder(GetEnumUnit(), "stop")
                        set apply = false
                    endif
                endif
                
                set r = r.next
            endloop
            
            if apply then
                loop
                    set n = n-1
                    set PlayerResource[playerid][res_list[n]] = PlayerResource[playerid][res_list[n]] - cost_list[n]
                    exitwhen n == 0 
                endloop
            endif
            
            set target = null
        endmethod
        
        private static method update takes nothing returns nothing
            call ForGroup(thistype.repairGroup, function enumRepairing)
        endmethod
        
        static method remove takes unit whichunit returns nothing
            if IsUnitInGroup(whichunit, thistype.repairGroup) then
                call RemoveSavedHandle(Hashtbl, 0, GetHandleId(whichunit))
                set .count = .count-1
                call GroupRemoveUnit(thistype.repairGroup, whichunit)
                if .count == 0 then
                    call PauseTimer(thistype.updateTimer)
                endif
            endif
        endmethod
        
        static method add takes unit caster, unit target returns boolean
            if not IsUnitInGroup(caster, thistype.repairGroup) then
                call GroupAddUnit(thistype.repairGroup, caster)
                set .count = .count+1
                call SaveUnitHandle(Hashtbl, 0, GetHandleId(caster), target)
                
                if .count == 1 then
                    call TimerStart(thistype.updateTimer, 1, true, function thistype.update)
                endif
            else
                return false
            endif
            
            return true
        endmethod
        
        static method onUnitOrdered takes nothing returns boolean
            local integer orderid = GetIssuedOrderId()
            
            if orderid == OrderId("repair") then
                call thistype.add(GetTriggerUnit(), GetSpellTargetUnit())
            else
                call thistype.remove(GetTriggerUnit())
            endif
            
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            
            set thistype.updateTimer = CreateTimer()
            set thistype.repairGroup = CreateGroup()
            
            loop
                exitwhen i == bj_MAX_PLAYER_SLOTS
                call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
                call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
                call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
                set i = i+1
            endloop
            
            call TriggerAddCondition(t, Condition(thistype.onUnitOrdered)
        endmethod
    endstruct
 
When ordered "smart" or "repair" towards a a building you can check for current resource and then re-order the unit not to repair the building.

But yes, you might need to loop like each 0.2 seconds to check if the unit is still repairing, if not remove the instance.

For the resouece/second amount ... there maybe could be set one default and constant value for all buildings.
Let's just say 2 resource/second.
And the user doesn't need to change it, but can also set his own resource/second amount for a building type.
For example set resoruce[UNIT_TYPE_TOWER] = 3 resource/second.

When a peon gets order "repair" or "smart" towards a unit you add it to the loop group and also save the resource[UNIT_TYPE] as member of instance.
In loop group you periodicly check for current order and if it stills equals repair...

I've not analyzed your code yet, sorry, but would it be possible you also provide also the resource library so I can just copy your code and test a bit by myself?

Btw, also don't forget only to pay attention onNewOrder, but also onDeath. I think it's needed, too.
 
Actually, i am linking the whole test map for you here to try out: LINK

Note that i have disabled the repair part of my script, but you can enable it if you want to try it. I have yet to make it discriminate between constructing structures (which should not cost extra to repair) and damaged ones. It also uses a GetUnitBuildTime() function which currently returns the default value 60. I tried making it so that units would be prevented from repairing if the player is short on resources, but this would cause an infinite loop since units with repair autocast will insistently try to repair units that are damaged nearby.

I'm personally quite proud of the system as it is, it's been a long time in the works and is turning out pretty cool.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Yay a resource system :D
...
O wait a resource system -_-

I can guarantee you that your system will not be able to support my favorite resource... I won't tell you which it is though :D

Anyway about repair.
It is a horrible idea to use any detection (except spellcast) to work with in this situation.
If you replace the repair action in the first place, then why not heal the target as well?

The second solution is almost exactly the same as I do these things... ussually.
You have a channel based ability that has a 1,000,000,000 seconds Follow Through Time.
(Universal spell and preferably replacing the repair button but if not then also not visible.)
Cast range 100 (more or less).
And ofcourse not disabling other abilities.
And there is NO heal from the normal repair when you use this ability.
I don't see why you people talk about detecting how much has been healed when you cast a dummy ability on a target.

When your unit casts that ability, you start a (0.5s) timer (or when you have really low intervals, you can add the unit to a list of current repairers).
Each iteration, you check if the unit's current order is "myrepairorder" (whatever you set for the ability) and you then heal the structure.
Then based on how much you healed the structure divided by how much you wanted to heal the structure:
set resourceCostFactor = (newHealth - oldHealth ) / repairValue
And substract your resources by what you would normally substract per iteration multiplied by resourceCostFactor.
When resourceCostFactor is lower than 1, you also stop the unit. (Lower than 1 means that the building is completely healed.)
You also need a check if the building was dead at the beginning of the iteration.

To find out some OE data that are kind of inaccessible by trigger... normally :D
you can try to use Object Data Extractor which works perfectly as it should do.
Using a simple external call, you can load all data that you want to have:
//! LoadUnitData fields=ubld -defaults
So you can get the value per unit type like this:
local integer buildTime = GetUnitTypeBuildTime('hbar')
(I suggest you to make the ability in external calls as well.)

The idea of "when the unit is ordered to do anything else" sounds good but I don't know what happens when he is stunned or dies.

And... I don't know how good you are with triggering, but ForGroup, multiple get calls for variables, hashtables over arrays, and... WHO?! set target = null
I suppose structs is nice, but apart from that, there are quite a few things that you can improve on performance.
 
Level 10
Joined
Apr 4, 2010
Messages
509
Cool system, but you cant force them to return resource early, they will try to return the resource, but after a few seconds they turn around to harvest again. Also there is a spelling mistake in the multi-board :d
It would be cool to see this animation for meat harvesting.
attachment.php
 

Attachments

  • GIF.gif
    GIF.gif
    245.1 KB · Views: 261
The second solution is almost exactly the same as I do these things... ussually.
You have a channel based ability that has a 1,000,000,000 seconds Follow Through Time.
(Universal spell and preferably replacing the repair button but if not then also not visible.)
Cast range 100 (more or less).
And ofcourse not disabling other abilities.
And there is NO heal from the normal repair when you use this ability.
I don't see why you people talk about detecting how much has been healed when you cast a dummy ability on a target.

When your unit casts that ability, you start a (0.5s) timer (or when you have really low intervals, you can add the unit to a list of current repairers).
Each iteration, you check if the unit's current order is "myrepairorder" (whatever you set for the ability) and you then heal the structure.
Then based on how much you healed the structure divided by how much you wanted to heal the structure:
set resourceCostFactor = (newHealth - oldHealth ) / repairValue
And substract your resources by what you would normally substract per iteration multiplied by resourceCostFactor.
When resourceCostFactor is lower than 1, you also stop the unit. (Lower than 1 means that the building is completely healed.)
You also need a check if the building was dead at the beginning of the iteration.

To find out some OE data that are kind of inaccessible by trigger... normally :D
you can try to use Object Data Extractor which works perfectly as it should do.
Using a simple external call, you can load all data that you want to have:
//! LoadUnitData fields=ubld -defaults
So you can get the value per unit type like this:
local integer buildTime = GetUnitTypeBuildTime('hbar')
(I suggest you to make the ability in external calls as well.)

The problem with this kind of half-way approach is that units will still need the normal repair ability in order to construct buildings normally. Otherwise, you'll need to script this too and accept the fact that building birth animations will be improperly displayed. Another issue is that you will not have autocast where workers automatically seek to repair damaged structures (which is a big thing). Just applying the heal is very simple - i've already done a system like this for my World Domination map.

The object data extractor seems really cool, is that a part of the standard JNGP grimoire package? It would very much simplify things for users.

And... I don't know how good you are with triggering, but ForGroup, multiple get calls for variables, hashtables over arrays, and... WHO?! set target = null
I suppose structs is nice, but apart from that, there are quite a few things that you can improve on performance.

Hmpf, i don't know if you are in any position to question my programming skills here... it is perfectly acceptable to use ForGroup in this situation (since it is not possible to use FoG iteration for this). Having a group allows you to quickly check whether a unit is included or not, while a struct-based solution would require you to link the struct to the unit, which would either need hashtables or force you to use an indexer. Not to mention that the rate at which units change orders would require the structs to be frequently created/destroyed.

Multiple variable get calls is hardly an issue here, AFAIK there has been no benchmarking tests proving that you'd prefer a local variable before two native calls. Regardless of which, this is only present in the repair struct which i hastily threw together.

Hashtables are very fast. If you have an array implementation that could replace this, i'd love to see it (i'm guessing that you don't).

Cool system, but you cant force them to return resource early, they will try to return the resource, but after a few seconds they turn around to harvest again. Also there is a spelling mistake in the multi-board :d
It would be cool to see this animation for meat harvesting.
attachment.php

Haha omg, thanks for noticing the spelling error... must have slipped on the key. :ogre_hurrhurr:

Units will indeed continue working if you order them to return the resource - this is how the vanilla resource system of wc3 normally works... you'll just have to give them a new order right after they've returned the resource. The meat anim looks neat, to bad the peasant model doesn't have it.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Not for me to question your programming skills?
Don't make me laugh... even though I don't have any emotions, that seems to be quite a challange.

In any case, I just made the repair for you :D
One thing that you will notice is that the resources SUCK.
I mean they use integers instead of reals for getters and setters.
Which basically mean that when you take a farm:
Build time: 35s.
Gold cost: 80.
Lumber cost: 20.

With an interval duration of 0.5s

Will result in
80/35 * 0.5 (rounded to integer) will be 1 gold
20/35 * 0.5 (rounded to integer) will be 0 lumber

a farm is extremely cheap to repair.

I hope that you use proper reals to save the data (showing the data can be done with integers idc)
And that you have all resources properly listed so you can loop through them.

I am thinking about placing both the owner and unit type variables inside the arrays as well but I think that I am too much of an efficiency nerd.
In any case, I gotta find a proper way of avoiding double or even triple repairs from the same unit.
Gonna be nice :D
 

Attachments

  • CustomRepair 0.1.w3x
    51.6 KB · Views: 40
Try the attached map.

I created a new sheet and named it "Resource Repair Module New" and yours "Resource Repair Module Original".
Else I did not touch any other triggers by you, so you might just test the script.

What is also new is the unit indexer in "Required" folder.

I hope I did not make too many mistakes, and that the comments make the wirtten code understandable:).

This is new code:

JASS:
library RepairModule requires ResourceLibrary, UnitIndexer
struct CustomRepair

    private integer resourceAmount
    private unit builder
    private unit building
    private integer playerId
    private integer buildingType
    private boolean enabled // if enabled it will cost resources
    
    private static hashtable hash = InitHashtable()
    private static timer tim = CreateTimer()
    private static constant real TIMEOUT = 1.
    
    private thistype next
    private thistype prev
    private static thistype first = 0
    
    private static trigger orderTrigger = CreateTrigger()
    
    private static thistype array list
    private static boolean array isWorking
    private static boolean array isConstructing
    private static integer array workerCount
    
    // Loop through all resources and check if player has enough for the building.
    private static method checkResources takes integer playerId, integer buildingType returns boolean
        local Resource this = Resource.first
        local integer cost
        loop
            exitwhen (this == null)

            set cost = R2I(I2R(GetCost(buildingType, this)) * (1/GetUnitBuildTime(buildingType)))
                
            if cost > 0 then
                if PlayerResource[playerId][this] < cost then
                    return false
                endif
            endif
                
            set this = this.next
        endloop
        return true
    endmethod
    
    // this method will substract the costs from a building from player's resources
    private method ressourceUsage takes nothing returns nothing
        local Resource that = Resource.first
        local integer cost
        loop
            exitwhen (that == null)

            set cost = R2I(I2R(GetCost(this.buildingType, that)) * (1/GetUnitBuildTime(this.buildingType)))
                
            if cost > 0 then
                set PlayerResource[this.playerId][that] = PlayerResource[this.playerId][that] - cost
            endif
                
            set that = that.next
        endloop
    endmethod
    
    // loop through all instances
    private static method callback takes nothing returns nothing
        local thistype this = thistype.first
        loop
            exitwhen (this == null)
            
            if GetUnitCurrentOrder(this.builder) ==  852024 or GetUnitCurrentOrder(this.builder) == 851971 then //repair and smart
                
                if (this.enabled) then 
                    if(checkResources(this.playerId, this.buildingType)) then
                        //  Success
                        call this.ressourceUsage()
                        call BJDebugMsg("usage")
                    else
                    
                        call IssueImmediateOrder(this.builder, "stop")
                        set thistype.workerCount[GetUnitUserData(this.building)] = thistype.workerCount[GetUnitUserData(this.building)] - 1
                        call this.destroy()
                        call BJDebugMsg("Not enough resources no more.")
                    endif
                endif
            else
                    
                set thistype.workerCount[GetUnitUserData(this.building)] = thistype.workerCount[GetUnitUserData(this.building)] - 1
                call this.destroy()
                call BJDebugMsg("Unit stopped working.")
            endif
            
            set this = this.next
        endloop
    endmethod
    
    private static method create takes unit u, unit b, boolean e returns thistype
        local thistype this = thistype.allocate()
        local integer index = GetUnitUserData(u)
        set this.builder = u
        set this.building = b
        set this.buildingType = GetUnitTypeId(b)
        set this.playerId = GetPlayerId(GetOwningPlayer(u))
        set enabled = e
        set thistype.isWorking[index] = true
        set thistype.list[index] = this
        
        if (thistype.first == 0) then
            call TimerStart(tim, TIMEOUT, true, function thistype.callback)
        endif
        set this.next = thistype.first
        set thistype.first.prev = this
        set thistype.first = this
        set this.prev = 0
        return this
    endmethod
    
    method destroy takes nothing returns nothing
        local integer index = GetUnitUserData(builder)
        set this.builder = null
        set this.building = null
        set thistype.list[index] = 0
        set this.isWorking[index] = false
        call this.deallocate()
    
        if (this == thistype.first) then
            set thistype.first = this.next
        endif
        set this.next.prev = this.prev
        set this.prev.next = this.next
            
        if (thistype.first == 0) then
            call PauseTimer(tim)
        endif

    endmethod
    
    // check if a unit is currently repairing/working
    private static method isUnitWorking takes unit u returns boolean
        return thistype.isWorking[GetUnitUserData(u)]
    endmethod
    
    static method getWorkerInstance takes unit u returns thistype
        return thistype.list[GetUnitUserData(u)]
    endmethod
    
    private static method onCast takes nothing returns boolean
        local integer abilityId = GetSpellAbilityId()
        local unit builder = GetTriggerUnit()
        local unit building = GetSpellTargetUnit()
        local integer index = GetUnitUserData(building)
        
        // Repair abilities
        if  (abilityId == 'Ahrp' or abilityId == 'Arep' or abilityId == 'Arst' or abilityId == 'Aren') then
            
            // Check if player has enough resources
            if thistype.checkResources(GetPlayerId(GetTriggerPlayer()), GetUnitTypeId(building)) then
                
                // One more worker is working on this building
                
                set thistype.workerCount[index] = thistype.workerCount[index] + 1
                call thistype.create(builder, building, thistype.isConstructing[index] and thistype.workerCount[index] != 1)
                
                if thistype.isConstructing[index] and thistype.workerCount[index] == 1 then
                    call BJDebugMsg("Start - normal")
                else
                    call BJDebugMsg("Start - Powerbuild [" + I2S(thistype.workerCount[index]) + "]")
                endif
    
            else
                call BJDebugMsg("Force stop")
                // disable onOrder trigger before stop
                call DisableTrigger(orderTrigger)
                call PauseUnit(builder, true)
                call IssueImmediateOrder(builder, "stop")
                call PauseUnit(builder, false)
                call EnableTrigger(orderTrigger)
                
            endif
        endif
        set builder = null
        set building = null
        return false
    endmethod
    
    // if unit gets ordered we remove the instance
    private static method onOrder takes nothing returns boolean
        local thistype this
       if (thistype.isUnitWorking(GetTriggerUnit())) then
            call BJDebugMsg("NewOrder.")
            set this = getWorkerInstance(GetTriggerUnit())
            set thistype.workerCount[GetUnitUserData(this.building)] = thistype.workerCount[GetUnitUserData(this.building)] - 1
            call this.destroy()
        endif
        return false
    endmethod
    
    // Remove a instance if it gets deindexed.
    private static method onDeindex takes nothing returns boolean
        if (thistype.isUnitWorking(GetIndexedUnit())) then
            call thistype.getWorkerInstance(GetIndexedUnit()).destroy()
        endif
        return false
    endmethod
    
    // when a building begins the construcvtion we initialisize members
    private static method onConstructStart takes nothing returns boolean
        set thistype.isConstructing[GetUnitUserData(GetConstructingStructure())] = true
        set thistype.workerCount[GetUnitUserData(GetConstructingStructure())] = 0
        return false
    endmethod
    
    // building finished construction -> now also the 1st worker has to pay for repair
    private static method onConstructFinish takes nothing returns boolean
        set thistype.isConstructing[GetUnitUserData(GetConstructedStructure())] = false
        return false
    endmethod
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local trigger t3 = CreateTrigger()
        local player p
        local integer i = 0
        loop
            exitwhen i == bj_MAX_PLAYER_SLOTS
            set p = Player(i)
            call TriggerRegisterPlayerUnitEvent(orderTrigger, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
            call TriggerRegisterPlayerUnitEvent(orderTrigger, p, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
            call TriggerRegisterPlayerUnitEvent(orderTrigger, p, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
            
            call TriggerRegisterPlayerUnitEvent(t2, p, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
            call TriggerRegisterPlayerUnitEvent(t3, p, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, null)
            
            call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set i = i+1
        endloop
        call TriggerAddCondition(orderTrigger, Condition(function thistype.onOrder))
        call TriggerAddCondition(t, Condition(function thistype.onCast))
        
        call TriggerAddCondition(t2, Condition(function thistype.onConstructStart))
        call TriggerAddCondition(t3, Condition(function thistype.onConstructFinish))
        
        call RegisterUnitIndexEvent(Condition(function thistype.onDeindex), UnitIndexer.DEINDEX)
    endmethod
endstruct
endlibrary
 

Attachments

  • Resource System new.w3x
    689.7 KB · Views: 36
Thanks IcemanBo, that looks really cool!! I like that you've made it so that it counts the number of workers repairing/working on a structure, and that you keep track of what structures are completed (though it appears that structures placed on map start still count as constructing). The problem is the same as with mine though, consider this:

Imagine that you are out of resources, one of your structures is damaged, and you have a worker with repair on autocast. The worker will aquire the target and attempt to repair it, but he is denied because of lack of resources. After he is ordered to stop, he will instantly reaquire the target and attempt to repair it again (resulting in another denial). This will reoccur roughly 30 times per second, and this will cause your framerate to plummit and make the game unplayable.

@Wietlol: Thanks, but you should have read my previous comment first... in your systems, peasants can no longer construct buildings (they are healed, but don't progress). Also, the above problem still stand - this works fine with normal wc3 resources, but with a custom resource system, the worker won't sense that he doesn't have enough resources to repair, and instead constantly attempt to target the structure (and constantly be denied) which causes the simulation to grind to a halt. Regarding resources being stored as reals - yeah, this might be a good idea. To be fair though, the standard wc3 resources can only be accessed as integers (though they are likely stored as reals internally).

I have an idea regarding this though - perhaps it is possible to combine the two ideas? Let's say that if a worker attempts to aquire a structure which it cannot afford to repair. Imagine that instead of ordering it to stop, you order it to cast a dummy spell on the structure with very high follow through time, and check once per second whether it can afford to repair. Only if the structure has finished repairing do we order it to stop. Frankly, in the event of repairing, it should be possible to override the standard repair spell with a custom (non-visible) one, and only use the standard one for initial target aquiring.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Maybe it would be a good idea to filter out buildings that aren't completely constructed yet?
Ofcourse this isn't a native and thus requires some triggers for start and finish constructing but that would work if I am not mistaken.

About the order spam, it doesn't happen in my map because it takes one iteration before the check is applied.
However, this does mean that they do show their work animation which may be a bit odd because it is not repairing anything.

(There seemed to be one peasant that used the normal repair instead of the triggered one as a one on 40 occurance though... dunno what caused that.)
 
You don't understand... the order spam doesn't happen in your map because you are using normal wc3 resources. The repair ability is implemented in such a way that it won't aquire targets that cost resources to repair if you don't have enough of this resource. If you were to set the repair costs to zero, i'm sure you'd also have this problem. Maybe not the part where the game crashes, but at least the worker would appear to be repairing all the time.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
No I dont.
I removed the need of both gold and lumber but instead made a check that would always fail.

The worker does start repairing until the first iteration after that, then he stops and tries again.
This does indeed result in the peasant appearing to repair all the time though.
 
Status
Not open for further replies.
Top