• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Solved] Enable AI smart cast AND disable return to player starting point

The title is self explanatory.
I want my creep spawned to actually cast their damn spells like Neutral Hostile does AND attack players IMMEDIATELY AFTER SPAWNING.
But since the map has a lot of units, I split them into 6 players (from 19 to 24).
I enable them an AI so they use their spells to be more dangerous, but these damn morons returns to the player start position.
I said player start position, not their guard position. YES I disabled their guard position but NO they keep returning to their damn start location like dumbasses.
"Place their start location in the player base duh" NOPE since the paths doesn't cover all players and it will break the map gamedesign.
Yeah, sometimes they group & attack but that's not the behavior I'm waiting for.
I was tempted to use this but the problem is it only selects one player at a time, as I want a kind of seek & destroy logic.
Completely disabling AI doesn't change their "return to their player start location like a puppy" ; removing the player slot disables their alliance.
So if you have any suggestion, I dig.
Here's the map if you want to play around and witness their sheer dumbness.
 

Attachments

  • FlameofInvasion.w3m
    639.3 KB · Views: 2

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,714
Use a Unit Indexer to keep track of your Creep's current destination:
  • Creep Movement Top Left
    • Events
      • Unit - A unit enters Top Left <gen>
    • Conditions
    • Actions
      • Set VariableSet u[0] = (Entering unit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (u[0] belongs to an ally of Player 19 (Mint).) Equal to True
        • Then - Actions
          • Set VariableSet CV = (Custom value of u[0])
          • Custom script: call RemoveLocation(udg_Destination[udg_CV])
          • Set VariableSet Destination[CV] = (Random point in Bottom Left <gen>)
          • Unit - Order u[0] to Attack-Move To Destination[CV]
        • Else - Actions
Now at any point in time you can tell this unit to Attack-Move there again:
  • Events
    • Unit - A unit Stops casting an ability
  • Conditions
    • ((Triggering unit) belongs to an ally of Player 19 (Mint).) Equal to True
  • Actions
    • Set VariableSet u[0] = (Triggering unit)
    • Set VariableSet CV = (Custom value of u[0])
    • Unit - Order u[0] to Attack-Move To Destination[CV]
You could use a Unit Group and a Timer to do this periodically to all of the Units, although, I recommend doing a more Event based approach. It's nice to spread this out so not all 500+ units are told to Attack-Move at the same time.

Also, make sure to use completely unique variables in your Unit Dies trigger(s):
  • Unit Dies
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Set VariableSet uDied[0] = (Dying unit)
      • Custom script: call creepDied(udg_uDies[0])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Level of Spawn creep for uDied[0]) Equal to 1
        • Then - Actions
          • Custom script: call creepSpawnerRecycle(udg_uDied[0])
        • Else - Actions
This Event is different from most other Events and can cause bugs due to it running during the execution of other triggers.


Also, a nice trick I like to use to easily organize my Units when they belong to multiple Computer players is to use hidden passive abilities. For example, copy and paste Storm Hammers and change it's name to Computer (Player). Now add this ability to any Computer-owned Units in the Object Editor and you can check for it in your Conditions:
  • Events
    • Unit - A unit enters some region <gen>
  • Conditions
    • (Level of Computer (Player) for (Triggering unit)) Greater than 0
  • Actions
    • // A computer owned unit entered the region
You can use any number of Abilities to create any number of these pseudo-Classifications:
  • Events
    • Unit - A unit dies
  • Conditions
    • (Level of Computer (Boss Class) for (Triggering unit)) Greater than 0
  • Actions
    • // You killed a boss
 
Last edited:
Thank you for your helpful hints ! These would give more grist to the mill.
Well, I also thought about bruteforcing their order but it is not very performance wise.
The sweet spot would be to disable the "return to home" order.
This Event is different from most other Events and can cause bugs due to it running during the execution of other triggers.
Are death events run async ? That could explain some struggles I had before on GUI maps.
I know that killing a unit mid-trigger would immediatly trigger all the death event ; although you made me learn something ehehe.

I shall post some news when I will dive again into it.
 

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,714
I don't think you'll see any performance issues assuming you handle it properly. An Event based approach should be very lightweight and Unit Indexing is really lightweight as well. I'm not sure if there's any other way to disable the returning home thing but I also have little experience with AI.

From my experience, performance issues come from massive Unit Group enumeration with complex processes, things like Damage Engine which can run 1000's of times per second doing large numbers of calculations, and most of the time just too many Units/Special Effects being created/on the screen at once.

I've created a map (Castle Wars-like with 100's of units clashing at once) and it had all of these performance intensive processes happening at the same time. But even with HD graphics on and an average PC everything seemed to run fine, I get well over 60 fps. Although, the terrain is very small and I use Lua mode so perhaps those help.

Yes, Death Events will run immediately and break the "trigger queue" rule. They insert their trigger's Actions directly into the trigger that caused them to run.
Here's an example of a problem it can cause.

This first trigger is perfectly fine on it's own:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to INSTA KILL
  • Actions
    • Set Variable u[0] = (Casting unit)
    • Set Variable u[1] = (Target unit of ability being cast)
    • Unit - Kill u[1]
    • Player - Add 100 Gold to (Owner of u[0])
But it has problems when we introduce this second trigger:
  • Events
    • Unit - A unit Dies
  • Conditions
  • Actions
    • Set Variable u[0] = (Dying unit)
These two triggers will cause problems for one another because when u[1] dies in the first trigger the second trigger runs because a unit died, and since the Event runs immediately you end up with these results:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to INSTA KILL
  • Actions
    • Set Variable u[0] = (Casting unit)
    • Set Variable u[1] = (Target unit of ability being cast)
    • Unit - Kill u[1]
    • Set Variable u[0] = (Dying unit) <----- THIS WAS INSERTED FROM OUR SECOND TRIGGER!
    • Player - Add 100 Gold to (Owner of u[0])
Originally, our (Casting unit) was getting 100 gold, but because of our second trigger the u[0] variable has changed and now the (Target unit of ability being cast) is the one that gets the 100 gold. This is why it can cause all sorts of bugs. As far as I know this is the only Event with this issue but for a safety measure I try to use unique variables for each Event that I think can run during another trigger.
 
Last edited:
Well, by forcing their behavior I managed to do what I wanted to do.
The trick is to detect when the unit has a point order, check if this order is within its targeted rect, if not order it again.
Here's the map if someone want to play around. I might update it later and make a proper topic, but this is for another time.
Problem solved
  • Force Order
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
    • Actions
      • Set VariableSet u[0] = (Ordered unit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (u[0] belongs to an ally of Player 19 (Mint).) Equal to True
        • Then - Actions
          • Set VariableSet l[0] = (Target point of issued order)
          • Custom script: set udg_rect=creepGetDestination(udg_u[0])
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (rect contains l[0]) Equal to False
            • Then - Actions
              • Custom script: call creepForceDestination(udg_u[0])
            • Else - Actions
          • Custom script: call RemoveLocation(udg_l[0])
        • Else - Actions
  • Creep Movement Center
    • Events
      • Unit - A unit enters Center <gen>
    • Conditions
    • Actions
      • Set VariableSet u[0] = (Entering unit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (u[0] belongs to an ally of Player 19 (Mint).) Equal to True
        • Then - Actions
          • Set VariableSet x[0] = (Random integer number between 1 and 4)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • x[0] Equal to 1
            • Then - Actions
              • Custom script: call creepSetDestination(udg_u[0],gg_rct_Top_Left)
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • x[0] Equal to 2
            • Then - Actions
              • Custom script: call creepSetDestination(udg_u[0],gg_rct_Center_Left)
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • x[0] Equal to 3
            • Then - Actions
              • Custom script: call creepSetDestination(udg_u[0],gg_rct_Top_Right)
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • x[0] Equal to 4
            • Then - Actions
              • Custom script: call creepSetDestination(udg_u[0],gg_rct_Center_Right)
            • Else - Actions
        • Else - Actions
JASS:
function creepGetDestination takes unit creep returns rect
    return LoadRectHandle(hashFoI,GetHandleId(creep),2)
endfunction

function creepForceDestination takes unit creep returns nothing
    local rect r=creepGetDestination(creep)
    call IssuePointOrder(creep,"attack",GetRectCenterX(r),GetRectCenterY(r))
    set r=null
endfunction

// When the unit is created, I just have to call this once to the center region and we are good.
// I might replace it with a random point, but it is good as it is.
function creepSetDestination takes unit creep, rect destination returns nothing
    call SaveRectHandle(hashFoI,GetHandleId(creep),2,destination)
    call creepForceDestination(creep)
endfunction
 

Attachments

  • FlameofInvasion 0.1.w3m
    640.3 KB · Views: 0
Top