HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

GetUnitOnRect...?

08-20-2010, 07:01 PM#1
Ignitedstar
Is there an easier way to find out whether a unit is on a rect, rather than vice versa?

My current plan is to use rects to track my grid-based movement. I have a 16x16 rect rectangle and I need to find out where units are on the "grid". There's the function RectContainsUnit() but I need to find out whether a unit is on a rect, not whether the rect has the unit in it. While I could use it... I'd have to call this boolean 256 times. It'd get even worse as the grid gets bigger. I planned on having larger grids such as 40x40. I'm not calling that 1600 times. That's kinda ridiculous especially considering that I'd have about 4000 lines of code. lol

This situation leads me to believe that I have to make my own function. However, I don't really where to start. I thought that naming the rects would allow me track them via their coordinate names by using string references, but I don't think it's possible with rects. If it is, please tell me how to do that, because I fear the worse, which is the first idea.

The other idea I had on how to make my life easier is to get the X and Y coordinates of the (00, 00) and find out the distance between grid spaces. I've done that and it's approximately 250 horizontally and vertically (no need for diagonals, it makes more complicated than it should be). Then, I could use those to track where each grid space is... but I still end up having 256 stored variables.

I have dimmed circles of power to indicate grid spaces. Another idea I thought of was tracking each circle of power because it is also a unit. However, unlike units that you can control, the circles of power are just dummy units that act as indicators for grid spacing, so they stay stationary. Wherever there's a circle of power, there's a rect that shares the same exact X/Y coordinates. This doesn't really work though because we can't detect units are walking on top of other units...

There has to be an easier way, right? Currently, all of the rects are labeled by their grid coordinates, so (00, 00) is the name of the rect on the bottom left corner, (15, 15) at the top right, etc. I know that no matter what, I'll end up having to deal with 256-somethings, but I want to find a way that makes this as painless as possible.

Please help.
08-20-2010, 07:42 PM#2
Troll-Brain
You can use one region per rect (the jass type not the gui one) and the enter region event, assuming your rects don't share any points between us.
And use a struck linked to your unit which contains the region which the unit is currently inside it.
Ofc since we don't have GetTriggeringRect but GetTriggeringRegion you will also have to link your rects with the associated region.
08-20-2010, 08:01 PM#3
Ignitedstar
Yes, that's right. None of the 256 rects share any coordinate points.

One of the things I had thought of previously was using the Enter Region event as well. The only problem with the Enter Region event is the fact that preplaced units do not count as "entering a region". I could get rid of them, since I have everything necessary to recreate them with identical stats.

I wonder if I can abuse the Enter Region event. If I move a unit out of it, then move it back in an instant inside the rect it was in, it should still trigger the event because it technically entered and did not start in the space as with preplaced units. Hmm... That's interesting an idea... I should try that.

EDIT: Oh, wait. You're right. There is no GetTriggeringRect. Uugh... Does this mean I'll end up with 256 regions and rects? lol

I did made this as a potential solution to my problem:

Collapse JASS:
function GetUnitOnGrid takes unit c returns rect
    local group g = CreateGroup()
    local rect r
    local real x = GetRectCenterX(gg_rct_00_00)
    local real y = GetRectCenterY(gg_rct_00_00)
    local integer xm = -1 //horizontal space multiplier
    local integer ym = 0 //vertical space multiplier
    local real dx = x + GetRectCenterX(gg_rct_00_01)
    local real dy = y + GetRectCenterX(gg_rct_00_01)
    local boolean b = false
    loop
        
        if xm == 15 then
            set xm = -1
            set ym = ym + 1
        endif
        if xm == -1 then
            set xm = 0
        else
            set xm = xm + 1
        endif
        call GroupEnumUnitsInRange(g, x+dx*xm, y+dy*ym, 100., null)
        if FirstOfGroup(g) == c then
            set b = true
        endif
        
        exitwhen b == true
    endloop
        return Rect(0., 0., 0., 0.)
endfunction

That's great and all, but how do I go from coordinates to a rect that the unit is in? Even then, I don't know how fast it will go through 256 coordinates.
08-20-2010, 09:08 PM#4
Troll-Brain
I see no problems using as many regions as rects, but if you have problems on how to use them friendly and efficiently, let me know.
08-20-2010, 09:43 PM#5
Ignitedstar
Yeah. Am still thinking of work arounds instead of work throughs. I won't be able to work on this again until Sunday afternoon, though. I'll keep thinking about it, of course.

I found something that might work. I started up a test using a rect array and storing numbers based on their UnitUserData. Since this map is a project demonstration for a Grid-Based Strategy RPG, I hope to add a story to it as well. I can do cinematics, but I can't work on them until this is done. It's so tempting because I know how to do that and am stuck with this. Haha
08-21-2010, 01:24 AM#6
Anitarf
There are many easier ways to do this. Working with static rects is the wrong approach.

First of all, you said this was a grid. That means that it is easy to deduce the field a unit is on from the unit's coordinates alone, no need to loop through all the rects, just convert the unit coordinates to grid coordinates (terrain grid points are 128.0 units apart).

You don't need to bother placing circles of power/rects in the editor at all, just use triggers to generate them for you. Again, if it's all a simple square grid, that's a piece of cake. Also, consider whether you need rects and/or circles of power at all.

I can not go into more detail unless you post more details about what you are trying to do. However, if you are placing grid stuff manually in the editor, you are definitely doing it wrong.
08-21-2010, 07:00 AM#7
Ignitedstar
Oh, good. I actually hoped that I would be able to avoid the rect/region thing all together. It'd definitely save me loads of time. I'm going to leave it there, though. Technically, I can still use them if I want to. If not, I can throw them out whenever. I'd actually prefer making them via the trigger editor so I make them and keep track of them as they're being made on the spot rather than making all of them and them trying track all of the points.

The circles of power are dummy units to indicate points on the grid, but they are there for a functional reason. The rects I can do without and would prefer to avoid, but the circles of power have an initial transparency value when no action is being taken any unit. What I exactly need from the grid is being able to find out where the unit is on the grid so I can issue move commands to it and have that unit walk to places according to the grid. The rest follows naturally (attacking, spells, etc.). When orders are issued to units, the CoP's light up to show how far the unit can move and what you can do.

To give you a better idea of what players will see, I'll have to take screenshots to simulate the events. However, that will have to wait until Saturday afternoon.

EDIT: Oh, yeah. And, thank you for your interest, Anitarf.
08-21-2010, 09:29 AM#8
Troll-Brain
It depends the unit movement, by trigger (i mean SetUnitX/Y/Position) or not, the accuracy you need and when/how many times you need to know where the unit is.
Ofc i wasn't talking about creating rects with the terrain editor but with the trigger editor.
Can be done with a simple loop if it's just a grid.

But again it all depends what you want to do exactly, you could have no utility of rects.
08-22-2010, 02:27 AM#9
Ignitedstar
Don't worry, Troll Brain. I appreciate your help as well.

Yeah. After I make a square grid, I can flag the ones I don't want units to go on. I should also be able to flag ones where a special event can occur there.

Okay, so this is what the field looks like in-game. Not necessarily in the world editor, because we can't emit transparency to units in the world editor (at least, I don't know of a way to do this, but it's a minor detail).

Click image for larger version

Name:	Grid - In-Game Screenshot 1.jpg
Views:	16
Size:	380.1 KB
ID:	49559

Just to note, I haven't implemented level-up values and bonusmod for health/mana and potentially armor bonuses, so the current stats of the level 18 Paladin are way off. As you can see, each circle of power is dimmed and has a name to label what the terrain type of the space is. Of course, certain terrain types can give certain units advantages and disadvantages depending on the racial status of the unit. For example, Night Elf units gain the most benefits in forested terrain, Tauren in barren terrain, Naga in water, Undead in blight, etc.

The original units' move/attack issued commands have been removed because they are obsolete and have been replaced with customized versions to do the same in a grid-based atmosphere. The tooltips are as so:

Click image for larger version

Name:	Grid - Tooltip Layout.JPG
Views:	10
Size:	28.8 KB
ID:	49560

Let me explain explain each one in depth. This will give you guys thorough insight into what I have planned.

Move: Replacing the Intelligence attribute is Movement. Basically, how many spaces the unit can move. I needed to replace the three stats because Strength, Agility, and Intelligence are no longer needed due to there being eight stats and should be replaced with the three things that the player needs to know right away about the unit. Mood and Lucky Number are not relevant at the current step of the project.

The Paladin's movement is 5. When you select the Move command, the grid will theoretically look like this:

Click image for larger version

Name:	Grid - Move Command In Action.JPG
Views:	18
Size:	176.9 KB
ID:	49555

Available spaces to move are blue spaces. Red spaces are occupied as we can see in the case of the Archer. In Soul Chess, confirming movement is done by clicking on the circle of power that has been given the ability "Move Here". It should be possible to just simply click on a circle of power to move to the unit there. The way I thought of doing this was to give the casting unit a buff to indicate that a move order has been issued so when another trigger detects the selection of a circle of power, the unit with the buff will be moved. Or, if something like this isn't possible, anyone have ideas? Yes- I did get rid of the initial Move command that lets units move, but I can easily give it back to units.

Once the Paladin has been moved and the order issued has been completed, to prevent units from moving twice, I have resorted to temporarily removing the (custom) Move ability with a passive version of it that says "the unit has moved already". This happens in the same case when units attack.

This ends the unit's Move phase. We don't need to worry about anything else until the Move command is working, but I am going on to explain everything else.

Attack: Attacking an enemy requires-- first and foremost, a weapon or item that can target enemies in the unit's inventory. It is possible to target items in an inventory, so it should also be possible to make actions via triggers utilizing this neat little trick. Let's use a Footman as our dummy enemy and assume that the Paladin is within range to attack him. When you use the Attack command, the UI will ask you to select a valid enemy (if any). Enemies within range will have their grid space highlighted in red. like this:

Click image for larger version

Name:	Grid - Attack Command In Action 1.JPG
Views:	9
Size:	102.7 KB
ID:	49556

Of course, spaces with no enemy count as invalid targets. After an enemy has been selected, the UI will ask you to select a valid weapon within the unit's inventory. The item used to combat the enemy will be moved to the unit's first inventory slot (if it isn't already there) and is regarded as the unit's equipped item. Such as this:

Click image for larger version

Name:	Grid - Attack Command In Action 2.JPG
Views:	9
Size:	20.0 KB
ID:	49557

The Footman (who's on level 1) has the current weapon equipped and retaliates with:

Click image for larger version

Name:	Grid - Attack Command In Action 3.JPG
Views:	6
Size:	16.6 KB
ID:	49558

When an enemy target is decided, a new multiboard in the game opens up and shows you how much damage each unit is going to deal to each other, how many times each unit will attack, the chances of hitting one another, and the critical hit percentage. Click on the same unit that was selected and the battle begins. The attacker deals the first hit, then the attacked unit. If the attacker's Speed exceeds the attacked unit (or vice versa), then the attacker will attack the unit twice. Units take damage or die, experience is awarded if the attacker lived, etc. etc.

This ends the Attack phase. Units that have completely used up their turn will obtain a buff indicator (should be something obvious, but haven't decided on what, yet).

Wait
The Wait command ends the unit's turn for the player's round. You cannot undo Wait commands, so I am hesitant to give it a hotkey since I know people will be pressing it on accident.

Cancel
The Cancel command goes back one step in the unit's current order if it is even being given an order at all. Basically, it allows the player to redo their steps for any reason. Doing that sounds slightly complicated... so I'll have to keep track of each command in steps or it'll probably cancel the whole order. Otherwise it does nothing. It's too bad that I can't use 'Esc' as a hotkey. Oh well.
08-22-2010, 11:55 AM#10
Anitarf
why make clumsy workarounds with buffs? Just store the moving unit to a variable.
08-22-2010, 03:47 PM#11
Ignitedstar
Hey. I'm half-thinking of this as I go. I didn't say that I had finalized any decision, yet. I want moving and actions to be smoother than Soul Chess. I know that much for sure.
08-24-2010, 04:15 AM#12
Ignitedstar
Cool. After a few days, got it working. No need for rects or regions, either! Now I can get rid of them. Thanks for putting me in the right direction, guys.
09-01-2010, 09:14 PM#13
Ignitedstar
First of all, let me just say that I have movement working flawlessly; it's very smooth and works exactly how I envisioned. However, now is the time to start working on the finishing touches... something that I'm lacking and don't really know how to start.

Right now, I'm using a local group variable to store all circles of power within a range of the unit moving. That's exactly how I want it because it shows the movement range of the unit. However, there must be a way to refine what it catches, or making a function that allows me to make a more refined version.

The Paladin's movement is 5, so it should look like this like in the example in my starting post:

Click image for larger version

Name:	Grid - Move Command In Action.JPG
Views:	7
Size:	176.9 KB
ID:	49579

But in my map it looks more sloppy, like this:

Click image for larger version

Name:	Grid - Movement Margins of Error.JPG
Views:	9
Size:	121.6 KB
ID:	49580

I just don't know how to start this. It seems rather difficult to find out where every single circle of power is around the caster in comparison to using the group function that catches everything in a radius of the caster. Soul Chess didn't seem to have to worry about this because units could only move one to two spaces. I can't make a diamond shaped group... Can I?
09-02-2010, 03:50 AM#14
Anitarf
Well, the range enum will pick all units in a circular area; therefore, SquareRoot(dx*dx+dy*dy) will be less than or equal the unit's move range, which is 5 in your case. As a result, points up to seven moves away (4 in the x direction and 3 in the y direction) are considered to be in range. What you want is for dx+dy to be less than or equal to the unit's move range; you need to add that condition to your group enum filter, which will require you to store the origin coordinates to global variables so you can calculate dx and dy in the filter.