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.)
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;
}
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.
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.
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.
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.
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!
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
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.
0
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.)
0
@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:
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.
0
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:
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:
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.
0
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.
0
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.
0
@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.
0
@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.
0
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:
What happens:
I have no idea what I've done wrong. I'm new to the data editor, so it's probably something trivial.
0
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:
I did notice some problems:
And some minor nitpicks:
0
<<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!
0
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:
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.
0
@Torrak: Go
Is there a list of all the known reasons? That would be a really useful resource.
0
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.