• 0

    posted a message on Castle Fight: The Starcraft II Remake

    I've attached the map data files containing text from castle fight 1.14b. It contains all of the unit tips and so forth, but you will need to extract them. For example, you can find all of the unit tips inside the .w3u file, but it's mixed with other data and meant to be read by a program instead of a person.

    I didn't have a good listfile on hand, so some of the files are just called Unknown[HashOfName]. The map didn't provide a correct list file because it is protected. (Actually, it says its list file is in an invalid location among many other strategies to try to prevent the data from being explored.)

    Posted in: Project Workplace
  • 0

    posted a message on Galaxy++ editor

    @SBeier:

    Well, I was messing around trying to get something to work. It was 'compiled' code, so I wasn't commenting it. I'll comment it below.

    The basic trick is to use a trigger pointing to a custom method as your function pointer. Arguments and results are stored in globals. The 'main' argument has to be stored indirectly, because the caller does not know what type of instance owns the method it is trying to call. In the example it is stored as an array index, and the custom method knows which array to access. So:

    • A delegate is an indirect 'pointer' to an instance (a Dynamic) and a trigger
    • Callers store pointer to instance (an array index or bank key) in a common global
    • Callers store arguments in globals, based on their types
    • Callers execute the associated trigger once they have stored the argument data
    • Triggers point to compiler generated methods
    • The generated methods retrieve the instance pointer and uses it to get the instance
    • The generated methods retrieve the other arguments out of globals, based on their types
    • The generated methods pass those values into the intended function
    • The generated methods stor the result in a global, chosen based on its expected type
    • Callers pull the result from the result global, chosen based on the expected type

    The 'Dynamic' code I posted is the machinery necessary to store the indirect pointers to instances. It ensures the indirect pointers don't outlive what they point to and that they count as a reference to the instance.

    class C { int i; }
    int F() {
        C x1 = new C(0);
        Dynamic x2 = Dynamic.From(x1);
        C x3 = x2.As(C);
        int x4 = x3.i;
        return x4;
    }
    
    //////////////////////// Compiled Code ////////////////
    
    void Halt(string message) {
        int i;
        TriggerDebugOutput(0, StringToText(message), true);
        i = 1 / 0;
    }
    
    int[1] args_int;
    
    //=== Dynamic Class ===
    trigger[100] Dynamic_field_Cleaner; // called just before a Dynamic is destroyed (target stored in args_int[0])
    int[100] Dynamic_field_Type; // the type of object pointed to by the Dynamic instance
    int[100] Dynamic_field_Pointer; // points to the object, assuming you know the type-specific access stuff like which arrays contain the fields
    int[100] Dynamic_refs; // reference count
    int[100] Dynamic_alloc_list; // linked list of de-allocated instances, '0' is end of list
    int Dynamic_alloc_count = 0; // total number of allocated instances
    int Dynamic_constructor(int instance, int type, trigger cleaner) {
        // allocate an unused index
        int me = Dynamic_alloc_list[0];
        Dynamic_alloc_count += 1;
        if (me == 0) { me = Dynamic_alloc_count; }
        Dynamic_alloc_list[0] = Dynamic_alloc_list[me];
    
        // initialize values
        Dynamic_field_Type[me] = type;
        Dynamic_field_Pointer[me] = instance;
        Dynamic_field_Cleaner[me] = cleaner;
        Dynamic_refs[me] = 0;
    
        return  me;
    }
    /// Casts the Dynamic to the given type (note: in practice may compile to multiple methods based on pointer type)
    int Dynamic_method_As(int me, int type) {
        if (type != Dynamic_field_Type[me]) { Halt("Type Mismatch"); }
        return Dynamic_field_Pointer[me];
    }
    void Dynamic_destructor(int me) {
        // Fire the 'being destroyed' event, allowing holder to cleanup uncounted reference
        if (Dynamic_field_Cleaner[me] != null) {
            args_int[0] = me;
            TriggerExecute(Dynamic_field_Cleaner[me], true, true);
            Dynamic_field_Cleaner[me] = null;
        }
        // de-allocate index
        Dynamic_alloc_count -= 1;
        Dynamic_alloc_list[me] = Dynamic_alloc_list[0];
        Dynamic_alloc_list[0] = me;
    }
    void Dynamic_GainRef(int me) {
        Dynamic_refs[me] += 1;
    }
    void Dynamic_LoseRef(int me) {
        Dynamic_refs[me] -= 1;
        if (Dynamic_refs[me] == 0) { Dynamic_destructor(me); }
    }
    
    //==== C Class ====
    int[100] C_dyn; // lazily-initialized cached 'dynamic instance' pointing to the C instance
    int[100] C_field_i; // value stored in C
    int[100] C_refs; // reference count
    int[100] C_alloc_list; // linked list of available C indexes ('0' is end of list)
    int C_alloc_count = 0; // number of allocated C instances
    int C_constructor(int i) {
        // allocate index
        int me = C_alloc_list[0];
        C_alloc_count += 1;
        if (me == 0) { me = C_alloc_count; }
        C_alloc_list[0] = C_alloc_list[me];
    
        // initialize values
        C_field_i[me] = i;
        C_refs[me] = 0;
        C_dyn[me] = 0;
    
        return me;
    }
    void C_destructor(int me) {
        // Destroy any associated dynamic instance
        if (C_dyn[me] != 0) {
            Dynamic_field_Cleaner[C_dyn[me]] = null; // prevent re-entrant destroy
            Dynamic_destructor(C_dyn[me]);
            C_dyn[me] = 0;
        }
    
        //de-allocate index
        C_alloc_count -= 1;
        C_alloc_list[me] = C_alloc_list[0];
        C_alloc_list[0] = me;
    }
    void C_GainRef(int me) {
        C_refs[me] += 1;
    }
    void C_LoseRef(int me) {
        C_refs[me] -= 1;
        if (C_refs[me] == 0) { C_destructor(me); }
    }
    
    // event catcher that forwards C's dynamic instance being destroyed to C losing a reference and nulling its cached dynamic
    trigger C_dyn_cleaner = TriggerCreate("C_dyn_cleaner_impl");
    bool C_dyn_cleaner_impl(bool b1, bool b2) {
        int dyn = args_int[0];
        int me = Dynamic_field_Pointer[dyn];
        if (C_dyn[me] != 0) {
            C_dyn[me] = 0;
            C_LoseRef(me);
        }
        return true;
    }
    
    // 'Casts' a C instance to a Dynamic instance by creating a cached Dynamic and returning it
    int C_method_ToDynamic(int me) {
        if (C_dyn[me] == 0) {
            C_dyn[me] = Dynamic_constructor(me, 1, C_dyn_cleaner);
            C_GainRef(me);
        }
        return C_dyn[me];
    }
    
    // ===== F method =====
    int F() {
        int x1;
        int x2;
        int x3;
        int x4;
        int r;
    
        x1 = C_constructor(23);
        C_GainRef(x1);
        x2 = C_method_ToDynamic(x1);
        Dynamic_GainRef(x2);
        x3 = Dynamic_method_As(x2, 1);
        C_GainRef(x3);
        x4 = C_field_i[x3];
        r = x4; // result
    
        // variables go out of scope
        C_LoseRef(x1);
        Dynamic_LoseRef(x2);
        C_LoseRef(x3);
    
        return r;
    }
    
    Posted in: Third Party Tools
  • 0

    posted a message on Galaxy++ editor

    I was going through the documentation and I noticed there was no support for inheritance. There's also no support for closures. I also noticed that delegates compile into If-Else sequences, instead of something constant time.

    I have a method to do all of these things efficiently. At least, assuming a trigger execute is efficient...

    The first thing we need is a reasonable 'Dynamic' type. Dynamic stores a pointer to another type, in this case an array index, and the type that the pointer corresponds to. I created a manual compilation example, where a class with reference counting is cast to Dynamic and back:

    class C
        field i as int
    end class
    func F() as int
        val x1 = new C(0)
        val x2 = Dynamic.From(x1)
        val x3 = x2.As(C)
        val x4 = x3.i
        return x4
    
    -- compiles to --
    
    void Halt(string message) {
        int i;
        TriggerDebugOutput(0, StringToText(message), true);
        i = 1 / 0;
    }
    
    int[1] args_int;
    
    trigger[100] Dynamic_field_Cleaner;
    int[100] Dynamic_field_Type;
    int[100] Dynamic_field_Pointer;
    int[100] Dynamic_refs;
    int[100] Dynamic_alloc_list;
    int Dynamic_alloc_count = 0;
    int Dynamic_constructor(int instance, int type, trigger cleaner) {
        int me = Dynamic_alloc_list[0];
        Dynamic_alloc_count += 1;
        if (me == 0) { me = Dynamic_alloc_count; }
        Dynamic_alloc_list[0] = Dynamic_alloc_list[me];
    
        Dynamic_field_Type[me] = type;
        Dynamic_field_Pointer[me] = instance;
        Dynamic_field_Cleaner[me] = cleaner;
        Dynamic_refs[me] = 0;
        return  me;
    }
    int Dynamic_method_As(int me, int type) {
        if (type != Dynamic_field_Type[me]) { Halt("Type Mismatch"); }
        return Dynamic_field_Pointer[me];
    }
    void Dynamic_destructor(int me) {
        if (Dynamic_field_Cleaner[me] != null) {
            args_int[0] = me;
            TriggerExecute(Dynamic_field_Cleaner[me], true, true);
            Dynamic_field_Cleaner[me] = null;
        }
        Dynamic_alloc_count -= 1;
        Dynamic_alloc_list[me] = Dynamic_alloc_list[0];
        Dynamic_alloc_list[0] = me;
    }
    void Dynamic_GainRef(int me) {
        Dynamic_refs[me] += 1;
    }
    void Dynamic_LoseRef(int me) {
        Dynamic_refs[me] -= 1;
        if (Dynamic_refs[me] == 0) { Dynamic_destructor(me); }
    }
    
    int[100] C_dyn;
    int[100] C_field_i;
    int[100] C_refs;
    int[100] C_alloc_list;
    int C_alloc_count = 0;
    int C_constructor(int i) {
        int me = C_alloc_list[0];
        C_alloc_count += 1;
        if (me == 0) { me = C_alloc_count; }
        C_alloc_list[0] = C_alloc_list[me];
    
        C_field_i[me] = i;
        C_refs[me] = 0;
        return me;
    }
    void C_destructor(int me) {
        if (C_dyn[me] != 0) {
                        Dynamic_field_Cleaner[C_dyn[me]] = null;
            Dynamic_destructor(C_dyn[me]);
            C_dyn[me] = 0;
        }
    
        C_alloc_count -= 1;
        C_alloc_list[me] = C_alloc_list[0];
        C_alloc_list[0] = me;
    }void C_GainRef(int me) {
        C_refs[me] += 1;
    }
    void C_LoseRef(int me) {
        C_refs[me] -= 1;
        if (C_refs[me] == 0) { C_destructor(me); }
    }
    trigger C_dyn_cleaner = TriggerCreate("C_dyn_cleaner_impl");
    bool C_dyn_cleaner_impl(bool b1, bool b2) {
        int dyn = args_int[0];
        int me = Dynamic_field_Pointer[dyn];
        C_dyn[me] = 0;
        C_LoseRef(me);
        return true;
    }
    int C_method_ToDynamic(int me) {
        if (C_dyn[me] == 0) {
            C_dyn[me] = Dynamic_constructor(me, 1, C_dyn_cleaner);
            C_GainRef(me);
        }
        return C_dyn[me];
    }
    
    int F() {
        int x1;
        int x2;
        int x3;
        int x4;
        int r;
    
        x1 = C_constructor(23);
        C_GainRef(x1);
        x2 = C_method_ToDynamic(x1);
        Dynamic_GainRef(x2);
        x3 = Dynamic_method_As(x2, 1);
        C_GainRef(x3);
        x4 = C_field_i[x3];
        r = x4;
    
        C_LoseRef(x1);
        Dynamic_LoseRef(x2);
        C_LoseRef(x3);
    
        return r;
    }
    

    Although I used reference counting here, it works just as well with manual destruction. The semantics here are a bit tricky, because destroying the underlying instance automatically destroys the Dynamic instance but 'destroying' the Dynamic instance at best only decreases the underlying instance's reference count.

    Now that we have dynamic, we can implement delegates pretty easily. A delegate is a function pointer with its first argument optionally specified. When the delegate is invoked, cast the target instance to dynamic and store it in a global. The 'function pointer' is implemented as a trigger that calls a helper method. The helper method passes the globals into the desired method, and stores the result in a global. Then control returns to the caller, who gets the result from the global.

    Example of reducing delegates to code using dynamic:

    class Vector
        val x as int
        val y as int
        func Dot(other as Vector) as int
            return me.x * other.x + me.y * other.y
    end class
    func G() as int
        Vector v = new Vector(1, -2)
        Func(Vector, int) f = v.Dot
        return f.Invoke(new Vector(3, 4))
    
    -- simplifies to --
    
    class Func_Vector_int
        field static _inst as Dynamic
        field static _arg1 as Vector
        field static _res as int
        field Pointer as trigger
        field Inst as Dynamic
        func Invoke(arg1 as Vector)
            mytype._inst = me.inst
            mytype._arg1 = arg1
            TriggerExecute(me.Pointer, true, true)
            return mytype._res
    
        field static Vector_Dot_Trig = TriggerCreate("Func_Vector_int___Vector_Dot_Impl")
        func static Vector_Dot_Impl(bool checkConditions, bool runActions) as bool
            val inst = _inst.As(Vector)
            _res = inst.Dot(_arg1)
            return true
    end class
    func G() as int
        val v = new Vector(1, -2)
        val f = new Func_Vector_int(v.AsDynamic(), Func_Vector_int.Vector_Dot_Trig)
        return f.Invoke(new Vector(3, 4))
    

    Now that we have delegates, interfaces are trivial. An interface is just a class with delegate members. An instance of class C is cast to interface D by creating delegates for each of C's methods implementing D's functionality.

    Posted in: Third Party Tools
  • 0

    posted a message on [Ability] Lurker Attack

    Try setting the turret to 'spin'. I think that means it can face in a direction the unit is not facing.

    Doing that solved an issue I had with a unit having to face its attackers to shoot down missiles with pdd laser. I think.

    Posted in: Data
  • 0

    posted a message on Sc2 - Basic Driving System

    Quote from Shakeslol:
    How would I implement a WASD system for this?


    I'd recommend against keyboard control. Mouse is way better, because of the latency. For example, you have to start turning before you reach the corner, to compensate for latency. This feels incredibly unnatural with a keyboard but with a mouse you just click inside the corner.

    I made a racing game for warcraft 3 (Lordaeron Grand Prix) with more physics (ramping, bouncing, slowed down by water, rolling down hills, collisions and so forth) and as the speed went up it became unplayable with the keyboard but easy with the mouse.

    I also recommend remembering where a player has clicked and turning the car until it is facing that point from its current position, NOT position at time of order. This compensates even more for the lag, because if there's a spike of lag on your order your car will turn harder and naturally compensate.


    As for the system, it's pretty nice but very simple. A lot of the math would be better represented by a vector type with dot/cross/etc, but I guess galaxy makes that difficult.

    I think your physics are a bit off with respect to friction/braking. You need to detect motion hitting 0 along the axis of a frictional force during a tick. Otherwise a super-strong brake will make the car jump backwards and forwards. Getting that math right is incredibly (INCREDIBLY) finicky.

    Posted in: General Chat
  • 0

    posted a message on Difficulties with a unit morphing ability (like siege mode)

    @Kueken531:

    Ah, so it was a stupid mistake. Thanks for the fresh eyes.

    Once the transition was fixed the actor problems were easy to notice and fix.

    I'm still having a problem, though. If the MPDD has blocked any shots (i.e. attacked) it refuses to transform back to carry mode!

    I'd also like to attach a normal PDD model to float above the cover mode actor and be the origin of the weapon beams but don't know where to start.

    Posted in: Data
  • 0

    posted a message on Difficulties with a unit morphing ability (like siege mode)

    @Kueken531:

    No, I don't mind attaching it. This is like the first thing I've tried to do, so it's already just a bunch of units standing around for testing.

    The unit in question is the MPDD. Just filter by the map data and it should be the only non-melee-or-campaign stuff.

    Posted in: Data
  • 0

    posted a message on Difficulties with a unit morphing ability (like siege mode)

    I want to create a unit which can switch between moving and being a stationary point defense drone.

    So I created two units, one for the mobile form and one for the stationary form. Each of them works individually, being able to move and block shots respectively. But I can't get the morphing ability to work!

    What I've done:

    • Created the two custom units.
    • Created two morph abilities, one for mobile-to-stationary and one for the reverse. I used the siege tank abilities as a comparison, but I don't see any important differences anymore.
    • Added 'morphto/from -> create/destroy' events to the actors of my custom units. I don't think the actors are the problem, because the issue is not purely visual.
    • Given my custom units their respective transition ability and added a command card button for the abilities.

    What happens:

    • Trying to morph from mobile to stationary results in the unit turning and pausing for a few seconds (with the button dulled as if active) then going back to normal (instead of morphing). As if I had cancelled the morph.
    • Trying to morph from stationary to mobile is the same, except if any shots have been blocked the button stays dulled until I tell the unit to stop.

    I have no idea what I've done wrong. I'm new to the data editor, so it's probably something trivial.

    Posted in: Data
  • 0

    posted a message on [video] Power Towers

    I made the wc3 power towers. I played this, expecting it to be terrible. You know how TDs and remakes can be... put them together...

    Instead, I was pleasantly surprised. Most of what I considered the important mechanics are present, and the gameplay feels essentially the same.

    What I liked:

    • The towers are unique (mostly not interchangeable). I feel like I need to play slightly differently to use the various types most effectively.
    • Sell has a full refund, encouraging experimentation.
    • SC2 shows mana bars on the towers!
    • 'capacitance' instead of 'capacity'. It's a bit less clear, but oh so fitting.
    • The power system appears well done. I don't know how well it handles the corner cases I worried about, but it worked well when I played.

    I did notice some problems:

    • The energy board has '0' as the header of the two last columns. Looks like total energy and maybe current energy change?
    • The elemental tower taints don't overwrite each other (on purpose?). A fire tainted unit attacked by water remains fire tainted. This isn't wrong, but I feel it makes the elemental bonus too easy to get. Instead of setting up careful chains of fire-water-nature-fire-... or spamming low fires and high water, you just put one low level fire in front and all the high level water behind it you want.
    • The raised waypoint is hard to maze, because the path behind the raised area is gone and ramps aren't buildable. You can't make a cut between it and the side.
    • The before-last waypoint (beside the corner water) is almost impossible to maze. In particular, runners can bypass attempts to wall along the shore by hugging the shoreline.
    • The water is so deep it's hard to place generators.
    • You can keep runners running in circles by juggling. Make two long paths, joining at a waypoint. Block one of the paths at the join. When runners get close to the end of the unblocked path, open up the blocked path and block the previously unblocked path. Now they have to backtrack to reach the other path, and you can repeat indefinitely. The waypoint is always reachable, so the current anti-wall stuff fails. The wc3 version prevented juggling by detecting backtracking and blinking runners that did so progressively further (because juggling causes them to run backwards).
    • I can't see tower stats on my built towers. How much power is this generator making? How much power can this bridging tower (or whatever) transfer? How much energy will this tower use when it's attacking? These values are only available in the upgrade tooltips.
    • If runners happen to cross the middle due to being mazed, they disappear and you don't lose a life. They should run right over it if it's not their current destination.

    And some minor nitpicks:

    • Can I block players from building in my area? They can really mess up your mazes.
    • The minimap doesn't show the waypoints, making it look extremely bare.
    • The loading screen has a ton of information thrown around. Don't waste this chance to prepare new players for what they need to do when the game starts. Put the basic tips for new players (like "make a generator") at the top and in decently large font. Special thanks and so forth should be off to the side, small, and dimmer.
    • The leaderboards are a bit clunky. When both are open they overflow the screen. Maybe one board with show/hide columns? I dunno. Players who have left should probably be removed from the boards.
    • Players should be able to build while modes are being chosen. At least give them the builder and no money, so new players can learn what is buildable now instead of when they feel even more rushed.
    • In the wc3 version all the generators had a 'fuel' downside (furnaces consumed slowly-regrowing grass, water wheels needed out-of-the-way water, graveyards needed corpses) and it feels weird for that to be absent.
    • The power transfer effects are based on the sender's upgrade level instead of the amount being transferred at the moment. (In wc3 power towers you could put 1 energy into a cycle of bridging towers and watch it go around, or make more complicated visual effects like a silly blinking pentagram)
    • The map has the same major flaw wc3 power towers had: rounds become really REALLY long. A lot of people find that incredibly boring, because you're not really doing anything during the rounds.
    • There's a noticeable pause when you sell several towers at the same time.
    • SC2 has a lot of features I think you can take advantage of. When a runner is selected, can you show the path it is taking in the same way queued orders appear in melee? Can 'sell' show a +minerals popup like the mineral boxes in the campaign? What about modifying the reaper cliff-jump ability to work exactly once, for an interesting runner?
    • Towers can't attack at all without power, instead of at significantly reduced rates. But they don't really complain about not being powered, they just do nothing.
    • The values shown on the board have too many digits. If I have 253 454 energy stored... I don't really care about the 454.
    Posted in: Map Feedback
  • 0

    posted a message on Is this autotrain stuff useful?

    <<reply 208370="">>

    I didn't want to post something useless into the resources section.

    The cancel last queued function is used by the other actions. You can't know the unit type you are queuing until after-the-fact, so the system works by doing add-check-oops-cancel add-check-oops-cancel add-check-yay!

    Posted in: Triggers
  • 0

    posted a message on Is this autotrain stuff useful?

    I started making a map and, before becoming extremely discouraged by obstacles like the popularity system (meaning I can't do random playtests), I made an autotraining system better than the stuff in the maps I've seen.

    How is it better? Well:

    • Doesn't periodically queue units, which is nice because it doesn't force players to empty the ever-filling queue in order to do actions like research upgrades.
    • Independent of unit type. You don't have to make changes every time you add or modify a building, just give the building a behavior.
    • Simple to understand. Autotrain buildings start training something as soon as they are constructed, and requeue anything that finishes. That's it.

    In any case, I thought I should dump it somewhere. The hard part was writing an "Issue Unit Training Order" action, and the rest is just topping. I'm actually not sure how to export it in a usable format.

    Triggers follow.

    **Issue Unit Training Order**
    Options: Action
    Return Type: (None)
    Parameters
        trainer = No Unit <Unit>
        primaryType = No Game Link <Game Link - Unit>
    Grammar Text: Order (trainer) to train a unit of type (primaryType)
    Hint Text: Orders a unit to begin training a particular unit type.
    Custom Script Code
    Local Variables
        abilIndex = (Get Training Ability Index(trainer)) <Integer>
        commandIndex = 0 <Integer>
        itemIndex = 0 <Integer>
        availableSlotCount = (Training queue Slots Available of trainer) <Integer>
    Actions
        General - If (Conditions) then do (Actions) else do (Actions)
            If
                availableSlotCount == 0
            Then
                General - Skip remaining actions
            Else
        General - For each integer commandIndex from 0 to 29 with increment 1, do (Actions)
            Actions
                Unit - Order trainer to (Ability Command to Order((((trainer ability abilIndex), commandIndex)))) (Replace Existing Orders)
                General - If (Conditions) then do (Actions) else do (Actions)
                    If
                        (Training queue Slots Available of trainer) < availableSlotCount
                    Then
                        General - For each integer itemIndex from 1 to (Number of items in trainer training queue slot ) with increment 1, do (Actions)
                            Actions
                                General - If (Conditions) then do (Actions) else do (Actions)
                                    If
                                        (Unit type of trainer training queue slot (Training queue Slots Used of trainer) item itemIndex) == primaryType
                                    Then
                                        General - Skip remaining actions
                                    Else
                        Order (trainer) to cancel its last queued training order
                    Else
    
    **Ability Command to Order**
    Options: Function
    Return Type: Order
    Parameters
        abilcommand = No Ability Command <Ability Command>
    Grammar Text: Ability Command to Order(abilcommand)
    Hint Text: Converts an ability command to an order.
    Custom Script Code
    Local Variables
    Actions
        General - Return Order(lp_abilcommand)
    
    **Get Training Ability Index**
    Options: Function
    Return Type: Integer
    Parameters
        trainer = No Unit <Unit>
    Grammar Text: Get Training Ability Index(trainer)
    Hint Text: Returns the index of a unit's training ability, or -1 if there is no such ability.
    Custom Script Code
    Local Variables
        abilIndex = 0 <Integer>
    Actions
        General - For each integer abilIndex from 0 to (Number of abilities on trainer) with increment 1, do (Actions)
            Actions
                General - If (Conditions) then do (Actions) else do (Actions)
                    If
                        (Class of (trainer ability abilIndex)) == Train
                    Then
                        General - Return abilIndex
                    Else
        General - Return -1
    
    **Cancel Last Queued Training Order**
    Options: Action
    Return Type: (None)
    Parameters
        trainer = No Unit <Unit>
    Grammar Text: Order (trainer) to cancel its last queued training order
    Hint Text: Orders a unit to cancel the last training order in its queue.
    Custom Script Code
    Local Variables
        i = 0 <Integer>
    Actions
        General - For each integer i from 1 to (Number of abilities on trainer) with increment 1, do (Actions)
            Actions
                General - If (Conditions) then do (Actions) else do (Actions)
                    If
                        (Class of (trainer ability i)) == Queue
                    Then
                        Unit - Order trainer to (Ability Command to Order((((trainer ability i), 0)))) (Replace Existing Orders)
                    Else
    
    **Issue Any Training Order**
    Options: Action
    Return Type: (None)
    Parameters
        trainer = No Unit <Unit>
    Grammar Text: Order (trainer) to train something arbitrary.
    Hint Text: Orders a unit to begin training one of the things it can train.
    Custom Script Code
    Local Variables
        abilIndex = (Get Training Ability Index(trainer)) <Integer>
    Actions
        Unit - Order trainer to (Ability Command to Order((((trainer ability abilIndex), 0)))) (Replace Existing Orders)
    
    **On Enter Map**
    Events
        Unit - Any Unit Enters (Entire map)
    Local Variables
    Conditions
        ((Triggering unit) has AutoTrain) == true
    Actions
        Order ((Triggering unit)) to train something arbitrary.
    
    **On Constructed**
    Events
        Unit - Any Unit construction progress is Completed
    Local Variables
    Conditions
        ((Triggering progress unit) has AutoTrain) == true
    Actions
        Order ((Triggering progress unit)) to train something arbitrary.
    
    **On Training Complete**
    Events
        Unit - Any Unit training progress is Completed
    Local Variables
    Conditions
        ((Triggering unit) has AutoTrain) == true
        curTrainer != (Triggering unit)
    Actions
        ------- The curTrainer stuff is necessary to avoid double-queueing when training multi-spawn-units (like zerglings), which trigger the training event multiple times.
        Variable - Set curTrainer = (Triggering unit)
        Order ((Triggering unit)) to train a unit of type ((Triggering progress unit type))
    
    **Clear Training Complete**
    Events
        Unit - Any Unit training progress is Started
    Local Variables
    Conditions
        ((Triggering unit) has AutoTrain) == true
    Actions
        Variable - Set curTrainer = No Unit
    
    Posted in: Triggers
  • 0

    posted a message on Validation fails on campaign map

    @Torrak: Go

    Is there a list of all the known reasons? That would be a really useful resource.

    Posted in: Miscellaneous Development
  • 0

    posted a message on Validation fails on campaign map

    I want to modify the "All In" campaign map so that instead of being single player it is mutliplayer. Just something simple, like having a second player and using shared control. As a starting point, I wanted to see how it would behave if I just made no changes (except to the name) and played it like a custom map.

    Unfortunately, when I tried to upload it I got an error saying (essentially) validation failed. No explanation, no reason, just "validation failed". Where should I be making changes to make this error go away? I have another map, which is custom, but get the same error.

    This is extremely frustrating. I know something is wrong, but am not given any clue as to what. Where should I be looking? What should I be changing? How can the system possibly be this BAD?

    My best guess is it has to do with game attributes, but there's so many settings there it would take days just to stumble across a 'correct' variation by making random changes.

    Posted in: Miscellaneous Development
  • To post a comment, please or register a new account.