Is this code right?

The place to post your WML questions and answers.

Moderator: Forum Moderators

Forum rules
  • Please use [code] BBCode tags in your posts for embedding WML snippets.
  • To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
Post Reply
picass_dilly
Posts: 79
Joined: September 3rd, 2012, 3:31 am

Is this code right?

Post by picass_dilly »

Okay, so this isn't a bug, exactly, but I was looking at the macros, specifically side-utils.cfg, and I think this might be an error in the coding for {RECALL_AI_SIDE SIDE}. Here's the full code:
Spoiler:
now specifically, this part:

Code: Select all

                        [gold]
                            side=2
                            amount=-20
                        [/gold]
seems wrong to me, and I think it should be

Code: Select all

[gold]
    side={SIDE}
    amount=-20
[/gold]
Now, I'll readily admit I might be missing something here. There's actually a few other things that I don't quite get about this macro (It looks like it would keep recalling units until the recall list is empty or the side is out of gold, regardless of whether there are actually still castle tiles available for those units to be recalled on, but I'm sure there must be some reason why that's not the case), but those are more of a "I probably just don't properly understand how the coding works" type-stuff.
Anyway, I just wanted to bring this to the developer's attention. Apologies if this isn't the correct forum, or way of reporting this.

(edit- just to be clear, this isn't a macro I'm building. It's part of the core macros (at least in v.1.10.4). I'm just curious whether that's a typo, or I'm just mistaken about it being incorrect)

(edit- new question)
Anyway, now I've got a question about ai.
I'm working on a scenario pair, where in the first scenario you fight until you've defeated one enemy leader, then the other one runs away. In the next scenario, you have to wipe out the rest of the units. I've managed to get those units saved and put on the recall list for the next scenario (I've even figured out a way to bolster the recall list if there aren't a significant number of units left-over from the first scenario), but the ai simply won't recall any of the units. This is doubly baffling since, when I switch my own side to ai, the first thing it does is recall all its available units.
Now the macro I've mentioned above provides a framework for manually recalling all the units, but if at all possible, I'd like the ai to do the recalling. So is there any way to add a recall CA to the ai (preferably one that doesn't involve building the formula up from scratch, as my efforts to look into ai modification have left me very confused and done very little to further my understanding of the subject)?
User avatar
Lord_Kata
Posts: 16
Joined: October 11th, 2012, 9:04 pm

Re: Is this code right?

Post by Lord_Kata »

Are you sure that there is a problem with the code, exactly? I have not ran into any problems with it so far?
Lord Kata

Coder, Roller coaster enthusiast, king of the world...
I'm looking to hire artists for my campaign. If interested, please PM me.
picass_dilly
Posts: 79
Joined: September 3rd, 2012, 3:31 am

Re: Is this code right?

Post by picass_dilly »

do you mean the core macro, or the ai-recall question? After I slept on it, I think my recall issue might be related to the test-map, in that the keeps are close enough together for one leader to attack the other. So if the leader evaluates the attack CA first, it might just be attacking before it gets a chance to recall or recruit (which my leader wouldn't be doing, because it's much lower level as I've just been skipping straight to the scenarios I'm trying to test). I'm gonna be enlarging my test map and trying it out like that to see if it has an effect on anything. I'll let you know how that works out.
With the core macro, I'm not totally sure if it's wrong or not. But it seems to me that if you're recalling ai from a side that isn't side 2, when the macro triggers the recalling of all the units, it's gonna keep charging side 2, even if the ai doing the recalling is say side 3 or 4. And I'm also a bit unsure as to how the core macro would know when to stop recalling units because there's no more castle tiles to recall them on (although checking for castle tiles might be part of the [recall] tag if there's no specified x,y. It doesn't say so in the wiki, but that doesn't mean it's not the case).
User avatar
Dugi
Posts: 4960
Joined: July 22nd, 2010, 10:29 am
Location: Carpathian Mountains
Contact:

Re: Is this code right?

Post by Dugi »

IMO, that is an obvious bug - this would make side 2 pay for all recalls of the side selected, and because it checks for the correct side (supposing it is not side 2), it would recall anything that that side has on its 'recall list', and the gold of side 2 would possibly drop deep bellow 0.

And it has nothing to do with Candidate Actions, it just makes units get recalled before AI does a thing, not allowing the AI to recruit anything.
User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: Is this code right?

Post by zookeeper »

picass_dilly
Posts: 79
Joined: September 3rd, 2012, 3:31 am

Re: Is this code right?

Post by picass_dilly »

well that's a good feeling (about the bug-fix). I'm still mildly confused about the core macro, still, because I don't quite understand how it makes sure the ai side doesn't recall units after it's run out of castle tiles for those units to be recalled on.

The thing about CA's is relatively unrelated. I wasn't trying to use the macro to recall units, I was just hoping the ai would do it. From what I can gather, though, it seems like the ai doesn't consider it's recall list (although a human turned droid does, possibly) during the recruitment CA (I expanded my test map and that didn't fix anything- see above for more details). But the problem might also be that I didn't construct the recall list right (I can see that it's there when I turn droid off, but maybe it doesn't show up until after the ai has run it's recruitment ca, or maybe there's some other reason why it's not 'seen' by the ai). I figure I'll dig into the code for "The Legend of Wesmere" (where Landar splits sides from Kalenz and that ai seems to make use of it's recall list) to see if I can figure how that works and what I'm doing different. If worst comes to worst, I'll just create some variation of the core macro to do the recalling manually (or just use the macro. Just because I don't understand why it would stop recalling once the appropriate castle tiles are filled doesn't mean it won't).
mattsc
Developer
Posts: 1173
Joined: October 13th, 2010, 6:14 pm
Location: Hidden on the hex behind Fred

Re: Is this code right?

Post by mattsc »

picass_dilly: I just ran a little test in my own 2-scenario test campaign and in my case the AI definitely recalls units from the previous scenario (I made the AI side persistent, spammed a couple high-level units for that side in Scenario 1, ended the scenario, and at the beginning of the next scenario the very first thing the AI did was recalling those units). So this is not a general problem, but must be specific to your scenario(s). If you post your code and/or a savefile, I can try to have a look and see what's going on.
JaMiT
Developer
Posts: 511
Joined: January 22nd, 2012, 12:38 am

Re: Is this code right?

Post by JaMiT »

picass_dilly wrote:There's actually a few other things that I don't quite get about this macro (It looks like it would keep recalling units until the recall list is empty or the side is out of gold, regardless of whether there are actually still castle tiles available for those units to be recalled on, but I'm sure there must be some reason why that's not the case),
That appears to be a correct assessment to me. As for a reason, maybe it is because the AI can recall normally, so this macro allows the AI side to break some rules? Or maybe it is an old (and bugged) macro that made up for a deficiency that has since been fixed? Not sure. It does not seem like a particularly useful macro to me.
picass_dilly wrote:I've managed to get those units saved and put on the recall list for the next scenario (I've even figured out a way to bolster the recall list if there aren't a significant number of units left-over from the first scenario), but the ai simply won't recall any of the units.
How are you saving these units? I think it should be just a matter of making the side persistent and giving it a save_id.

I'll echo the request for the scenario code. Or at least the definitions for this side in the two scenarios and whatever code you came up with for transferring the recallable units.
picass_dilly
Posts: 79
Joined: September 3rd, 2012, 3:31 am

Re: Is this code right?

Post by picass_dilly »

well one thing's for sure, upon looking back at my code, I know I misspelled "persistent" as "persistant." Fixing that, at least allowed me to create a recall list without starting the side out as human-controlled and switching. I'm also going back to the previous scenario to see if that carries over the recall list. I haven't tried it yet (well I did, but that was with "persistant" set, so I'm going to retry) but to carry over the recall list, I just switch all the units from both enemy sides to the surviving side using:

Code: Select all

[store_unit]
    variable=survivors
        [filter]
            side=2,3
        [/filter]
[/store_unit]
{FOREACH survivors i}
{VARIABLE survivors[$i].side 2} ## or 3, depending on who got defeated first
## I know it's redundant filtering for 2 when I'm changing the unit to side 2, 
## but I use $survivors.length in the next scenario to figure out how many 
## extra units I want to add to the recall list, so I store those ones as well
[unstore_unit]
    variable=survivors[$i]
[/unstore_unit]
{NEXT i}
The method I used originally was to [store_unit] the survivors, then in the next scenario, use the {FOREACH} part of the above code, only in addition to switching sides, I added "x,y=recall,recall" to the [unstore_unit] portion. I think it's pretty likely the "persistant" issue is the main source of my problem, but I'm going to have to go back and change some of the code (it got a bit convoluted trying to create the recall list, and otherwise trying to get the ai to recall) from the first of the two scenarios, and then re-test things. If I'm still having problems at that point, I'll post my [side] definitions and other related bits of code. These typo issues, unfortunately, aren't an uncommon source of bugs for me, as some of you who've helped me with earlier problems are probably aware ("ajacent" springs to mind).

(edit-solved)-Okay, from the way things look at this point, the main problem was the misspelling of persistent. AI recalls units normally now. I'll have to do a bit more testing when I actually design the next scenario, but I'm fairly confident it'll work out. Also, probably gonna wanna go through my code and make sure all the loose ends from my previous attempts to work this out are taken care of. Apologies for taking up your time because I didn't notice the typo earlier.

(edit-something else I noticed)- While perusing the core macros (a habit of mine that helps with my understanding of WML), I also noticed that {LIMIT_CONTEMPORANEOUS_RECRUITS} could potentially re-allow recruiting of units disallowed by {LIMIT_RECRUITS}, specifically this event in {LIMIT_CONTEMPORANEOUS_RECRUITS}:
Spoiler:
appears to have the potential for conflicting with the functionality of {LIMIT_RECRUITS} in the way I mentioned above. For example, if you wanted to allow one side to only recruit a total of three Revenants, as well as only being able to have two Revenants at the same time, the above event would allow the recruiting of Revenants, once the total number dropped below the limit number even if 3 had already been recruited. To fix this conflict in function, I've created the macro {LIMIT_RECRUITS_TRUE}:
Spoiler:
I also added a victory event to clear the variables created by the {LIMIT_RECRUITS} macro (which includes a pre-start event to do the same thing, but leaves the variables if there's not a {LIMIT_RECRUITS} for the appropriate side in the next scenario).
No real question here (although I haven't tested this macro, yet, so i would appreciate if anyone who might see a reason why it wouldn't work or who finds a problem with it in their testing could inform me), but I thought this might be something useful for anyone else who might want to apply both macros to the same unit-type.
JaMiT
Developer
Posts: 511
Joined: January 22nd, 2012, 12:38 am

Re: Is this code right?

Post by JaMiT »

picass_dilly wrote:I also noticed that {LIMIT_CONTEMPORANEOUS_RECRUITS} could potentially re-allow recruiting of units disallowed by {LIMIT_RECRUITS}, specifically this event in {LIMIT_CONTEMPORANEOUS_RECRUITS}:
Hmm... that appears to be an accurate assessment. I would guess that it was not planned for both of those macros to be in use, although there should not be a reason to assume that. Your approach of piggy-backing on the core macro looks good, but it might be better to fix the core macro. Here's something I've come up with but have not tested yet:
Replacement for LIMIT_CONTEMPORANEOUS_RECRUITS

Code: Select all

#define LIMIT_CONTEMPORANEOUS_RECRUITS SIDES TYPES LIMIT_NUMBER
    # Limit the number of units of the specified type(s) that a side
    # can have simultaneously. When the number of matching units on a
    # side reaches or exceeds LIMIT_NUMBER, that side is prevented from
    # recruiting more until the number of units of that type(s) drops
    # below LIMIT_NUMBER again.
    #
    # Allow sides 2 and 3 no more than 2 Troll Rocklobbers at a time
    #! {LIMIT_CONTEMPORANEOUS_RECRUITS 2,3 "Troll Rocklobber" 2}
    #
    # While this can technically be used within events, it is usually placed
    # within the toplevel scenario tag. In particular, this can have a bad
    # interaction with LIMIT_RECRUITS if used after a player has a chance to
    # recruit.

    # NOTE: If someone wants to limit the combined units on the specified
    #       sides, copy this macro and change each occurrence of
    #       "$side_number" to "{SIDES}".
    [event]
        name=side turn
        first_time_only=no

        [filter_side]
            side={SIDES}
        [/filter_side]

        [if]
            [have_unit]
                side=$side_number
                type={TYPES}
                count="{LIMIT_NUMBER}-99999"
            [/have_unit]

            [then]
                [disallow_recruit]
                    side=$side_number
                    type={TYPES}
                [/disallow_recruit]
            [/then]

            [else]
                [allow_recruit]
                    side=$side_number
                    type={TYPES}
                [/allow_recruit]
            [/else]
        [/if]
    [/event]

    [event]
        name=recruit
        first_time_only=no

        [filter]
            side={SIDES}
            type={TYPES}
        [/filter]

        [if]
            [have_unit]
                side=$side_number
                type={TYPES}
                count="{LIMIT_NUMBER}-99999"
            [/have_unit]

            [then]
                [disallow_recruit]
                    side=$side_number
                    type={TYPES}
                [/disallow_recruit]
            [/then]
        [/if]
    [/event]
#enddef
Replacement for LIMIT_RECRUITS

Code: Select all

#define LIMIT_RECRUITS SIDES TYPES LIMIT_NUMBER
    # Limit the total number of units of the given types that the given
    # sides (combined) can recruit in the scenario.
    # (Use this macro multiple times to separately limit multiple sides.)
    #
    # Allow side 2 no more than 1 Draug in the entire scenario
    #! {LIMIT_RECRUITS 2 Draug 1}
    #
    # Creates start events, so it must be placed either within the
    # toplevel scenario tag or within a prestart event.

    [event]
        name=start

        # We shall be tracking the number of units of matching types recruited
        # in an array. Each invocation of this macro gets its own index.

        # First, discover which index to use.
        {VARIABLE LIMIT_RECRUITS_index $LIMIT_RECRUITS_array.length}

        # Keep track of how many units of these types have been recruited.
        {VARIABLE LIMIT_RECRUITS_array[$LIMIT_RECRUITS_index|].count 0}

        # To aid debugging:
        {VARIABLE LIMIT_RECRUITS_array[$LIMIT_RECRUITS_index|].side {SIDES}}
        {VARIABLE LIMIT_RECRUITS_array[$LIMIT_RECRUITS_index|].type {TYPES}}

        # Every time the side recruits this given type(s), we increment the
        # counter, and if it matches or exceeds the limit, we disallow recruiting
        # more of those units.
        [event]
            name=recruit
            first_time_only=no
            delayed_variable_substitution=no

            [filter]
                side={SIDES}
                type={TYPES}
            [/filter]

            {VARIABLE_OP LIMIT_RECRUITS_array[$LIMIT_RECRUITS_index|].count add 1}
            [if]
                [variable]
                    name=LIMIT_RECRUITS_array[$LIMIT_RECRUITS_index|].count
                    greater_than_equal_to={LIMIT_NUMBER}
                [/variable]

                [then]
                    [disallow_recruit]
                        side={SIDES}
                        type={TYPES}
                    [/disallow_recruit]

                    # This event is to support the limiting of both total and
                    # contemporaneous recruits (it cancels an [allow_recruit]
                    # that might be issued by LIMIT_CONTEMPORANEOUS_RECRUITS).
                    [event]
                        name=side turn
                        first_time_only=no

                        [filter_side]
                            side={SIDES}
                        [/filter_side]

                        [disallow_recruit]
                            # Remember that we have one layer of non-delayed
                            # variable substitution.
                            side=$|side_number
                            type={TYPES}
                        [/disallow_recruit]
                    [/event]
                [/then]
            [/if]
        [/event]

        # Clear our temporary variable.
        {CLEAR_VARIABLE LIMIT_RECRUITS_index}
    [/event]

    [event]
        name=victory

        # Clear our array.
        {CLEAR_VARIABLE LIMIT_RECRUITS_array}
    [/event]

#enddef
I replaced both macros in an attempt to make them more robust, even though only the latter macro needs to be changed to fix this particular issue. If someone wants to test these out, it would save me the effort. (Otherwise I do not know when I would get around to it.)


One thing I noticed when editing these is that the current "contemporaneous" macro allows limitations per side, but does not allow limitations for combined sides. (For example, it does not support limiting sides 2-4 to a total of 5 contemporaneous trolls; it supports limiting sides 2-4 to 5 contemporaneous trolls each.) Is this a good thing? The benefit is that you can set limits for all sides with one line, instead of needing one line per side. That does not look like a good cost-benefit ratio to me, but changing this behavior would break compatibility. So it might be not worth changing. Still, it seems like functionality that someone might want.
Post Reply