|
+ Show Spoiler +I'm a programmer (not a pro-gamer) working on a new tool which will drastically expand the capabilities of UMS maps as well as make authoring way easier (no more trigger bullshit). It doesn't use EUD, doesn't require client mods and can't be patched out. The tool is quickly becoming usable and I need someone to help me with testing and feedback. Let me know in this thread if you're interested to help. Experience with SCMDraft 2 and/ or the Blizzard editor as well as (really) basic programming skills are required.
EDIT: Initial version has been released. Check out this post for info - http://www.teamliquid.net/forum/brood-war/521681-custom-map-ums-creators-wanted#19
|
if you have source code on on a github repository (either private or public) maybe i can take a look. What language did you write this tool with?
|
I'm curious but skeptical, especially of claims that you can "drastically expand the capabilities of UMS maps". We at staredit.net have pretty well pushed UMS mapping to its limits already. That said, if you can back up your claims, I'll help with both testing and feedback.
|
On April 25 2017 13:24 integral wrote: I'm curious but skeptical, especially of claims that you can "drastically expand the capabilities of UMS maps". We at staredit.net have pretty well pushed UMS mapping to its limits already. That said, if you can back up your claims, I'll help with both testing and feedback.
I've created a programming language, IR and a compiler. It takes the source code + a .scx and produces a new .scx. In its essence it's a virtual machine running on triggers. It doesn't offer any new features per se (like EUDs) but you can create much more complicated behaviors and logic than with hand-made triggers as it's a real programming language with variables, expressions, loops, functions, etc.
Here is a simple example I can compile and run right now (image because the forum software messes up the formatting):
And by the way the resources on staredit.net (especially the wiki) have been absolutely essential for creating this, I am so grateful to your community!
On April 25 2017 10:57 biryusky wrote: if you have source code on on a github repository (either private or public) maybe i can take a look. What language did you write this tool with?
Give me your GitHub username and I'll add you to the repo. The compiler is written in modern C++.
As a sidenote, I will open source everything in the coming weeks (days), as soon as it's in a semi-finished state.
|
Looking forward to this !! Please, make poker defense on steroids
|
After some more work today stuff like this works now:
global allowedSwaps = 3;
bring(Player1, AtLeast, 1, TerranMarine, "TestLocation2") => { if (allowedSwaps == 2) { print("Sorry, you have no more swaps left."); return; } allowedSwaps--; kill(TerranMarine, Player1, 1, "TestLocation2"); spawn(ProtossZealot, Player1, 1, "TestLocation"); print("Here is your zealot."); }
bring(Player1, AtLeast, 1, ProtossZealot, "TestLocation2") => { if (allowedSwaps == 2) { print("Sorry, you have no more swaps left."); return; } allowedSwaps--; kill(ProtossZealot, Player1, 1, "TestLocation2"); spawn(TerranMarine, Player1, 1, "TestLocation"); print("Here is your marine."); }
fn main() { spawn(TerranMarine, Player1, 1, "TestLocation"); while (true) { poll_events(); } }
|
check your pm, ive mailed you my github username
|
Sounds interesting. I'd like to be apart of this.
|
Interesting. A lot of that code looks very similar to current triggers. I'm also concerned about how flexible those functions are, I change conditions and actions constantly when I'm making a map. Adding a single condition and action to a particular function seems like it's going to be the same as writing triggers, correct me if I am wrong. A programmatic approach is helpful still, maybe even required for modern maps, so anyone who isn't using something like that will benefit tremendously. I'm currently using a simple string generator, but I suspect with similar results:
One thing I haven't done yet though is write macros to generate the triggers necessary for complex math between two death counts. Multiplication, division, comparison, etc. Currently I'm generating each calculation separately. If you do something like this in your program, I'm sold. This alone is useful enough to make it all worth it.
Another thing I need to do but haven't done yet is figure out how to use death counts as memory for multiple values/variables. Not having any form of memory without death counts sucks and I'm already low on death counts for a lot of maps. I'm not excited trying to think about how to store multiple values in a death count and then extract those values with addition and subtraction. Maybe you're a genius and can do this easily, here's hoping.
|
The code similarity to current triggers is intentional (to ease the transition for map creators). Don't be mistaken though, this is a full-blown programming language.
I'm very sleepy now (5 am here) so forgive me if I miss some important detail but here's a short explanation of how it works:
- You write your code in a high level language not unlike C++ or JavaScript. It supports local & global variables, arithmetic, complex expressions, function calls with arguments and return value, event callbacks, you name it. This all goes into a handwritten parser which outputs an AST. - From the AST linearized IR code (basically a list of low level instructions) is generated. - The IR language has all the instructions you'd expect to be able to implement the higher level one. Stuff like push, pop, call, ret, set, add, sub, etc + integration with the game like spawning/ killing units, checking locations, setting resources. So a piece of code gets translated to something like:
- This list of instructions is fed into the compiler backend which produces a bunch of triggers and creates the new map file. The actual codegen was the hard part of all this but I have it working properly (and pretty efficiently) now.
- For map makers this means calling the .exe with the map and code file which "combines" them into the final map which you can run in the game.
I feel like I'll be able to release something in a few days or a week tops with the progress I'm making. For the future there is a lot of work that can be done on optimizing the AST and IR once I'm done with all the major features.
To answer your questions specifically:
I'm also concerned about how flexible those functions are, I change conditions and actions constantly when I'm making a map
Everything can be changed instantly, recompilation takes milliseconds. You can go from code to testing in the game in 10 seconds. Also there are preprocessor macros (#define for C/ C++ people) and global variables so you can change stuff in a lot of places at once.
Adding a single condition and action to a particular function seems like it's going to be the same as writing triggers, correct me if I am wrong Well, kind of. With the exception that you can write most stuff once and then reuse it. You can even have multiple code files so you can make a library of common functions for all your maps. Also I'm considering some kind of meta-programming (template) support in the language to make managing a lot of event handlers even less painful.
Multiplication, division, comparison, etc. Currently I'm generating each calculation separately. If you do something like this in your program, I'm sold Yeah, you can do stuff like ((x + 15) * foo()). You can go crazy with the expressions until you blow the stack.
Another thing I need to do but haven't done yet is figure out how to use death counts as memory for multiple values/variables. I use the death counts for player 8 as general purpose memory. It would be possible to pack more than one value into a single "register" to save on memory, though I'm not sure if you'd need that many registers (there are ~220 available) if you're not doing the triggers by hand. The compiler is pretty good at figuring out what to throw out when it's not necessary anymore. We'll have to test the tool with a non-trivial map and see what happens and figure it out from there.
I'll be back tomorrow to talk more. Anyone PM me GitHub username if you want access to the work in progress stuff.
|
Sounds good.
Yeah, you can do stuff like ((x + 15) * foo()). You can go crazy with the expressions until you blow the stack. Just to be 100% clear, because this is sounding slightly too good to be true, I need the triggers themselves to do the calculating. As in, I need a function that generates the entire subtract/add trees necessary to multiply numbers using triggers. Using complex expressions to generate triggers is nice, but using your program to generate complex expressions *as calculated by triggers* is where the money is.
+ Show Spoiler + Trigger("All players"){ Conditions: Deaths("Current Player", "Left Wall Flame Trap", At least, 1024);
Actions: Set Deaths("Current Player", "Right Wall Missile Trap", Add, 1024); Set Deaths("Current Player", "Left Wall Flame Trap", Subtract, 1024); Preserve Trigger(); }
//-----------------------------------------------------------------// Trigger("All players"){ Conditions: Deaths("Current Player", "Left Wall Flame Trap", At least, 512);
Actions: Set Deaths("Current Player", "Right Wall Missile Trap", Add, 512); Set Deaths("Current Player", "Left Wall Flame Trap", Subtract, 512); Preserve Trigger(); }
etc. until 0, then take result (right wall missile trap) and subtract it back into left wall flame trap while multiplying or dividing by another value somewhere else, usually hardcoded since you can't exactly have a condition for every possibility without exceeding trigger limit. This is the sort of thing I haven't coded a way to cleanly generate yet, and until I see evidence (as in trigger output) of this function existing in your program I won't believe you've done it either.
I use the death counts for player 8 as general purpose memory. It would be possible to pack more than one value into a single "register" to save on memory, though I'm not sure if you'd need that many registers (there are ~220 available) if you're not doing the triggers by hand. The compiler is pretty good at figuring out what to throw out when it's not necessary anymore. We'll have to test the tool with a non-trivial map and see what happens and figure it out from there.
I'm using almost all already in most of my maps, to the point where I need to start recycling them sometimes where possible. If I had the ability to use more per register I would be able to do some pretty neat stuff with triggers that no one has done before. As an example, I have plans for a primitive AI that would be able to make attack/retreat decisions on the basis of counting non-arbitrary units per non-arbitrary location. I would use ~40 death counts per location, with calculations to extract those values and compare them, if this weren't prohibitively difficult to code.
|
Just to be 100% clear, because this is sounding slightly too good to be true, I need the triggers themselves to do the calculating. As in, I need a function that generates the entire subtract/add trees necessary to multiply numbers using triggers.
Exactly what I've done. The expressions are emitted as trigger chains and evaluated at runtime inside the game (as in your example). And you can do much more than just multiply two numbers. Here is the result of compiling one of my examples from the previous posts opened in SCMDraft (you don't actually need a map editor for writing the triggers, the compiler reads/ writes to the map file directly).
I would use ~40 death counts per location, with calculations to extract those values and compare them, if this weren't prohibitively difficult to code.
This would be quite possible to code, especially after I implement arrays which is more or less next on my list.
I'm using almost all already in most of my maps, to the point where I need to start recycling them sometimes where possible. That's possibly because you use a register (death count) only for one thing. The language I'm making is block scoped and reuses "dead" values, so you can do more with less registers. Also temporaries from expression calculations are reused.
|
Croatia9357 Posts
This looks pretty interesting. Good work!
Btw, you can use [code]some code here[./code] (without a dot) to post code here in the forum, which preserves formatting.
|
The expressions are emitted as trigger chains and evaluated at runtime inside the game (as in your example).
k you're a genius, I'm sold
And you can do much more than just multiply two numbers.
keep going I'm close
That's possibly because you use a register (death count) only for one thing. The language I'm making is block scoped and reuses "dead" values, so you can do more with less registers. Also temporaries from expression calculations are reused.
Ok, I was skeptical of you, so you can be skeptical of me! Totally fair. Most are in continual use, actually, and I'm using a lot of the heroes and buildings in the map so I just can't use those. I'm more or less at the limit on everything - ccmu, strings, death counts, locations, switches, death counts, even # of triggers that can run without lag. So yeah I'm definitely at the point where I'd be needing to reuse death counts if I want to add things. What I want to do is probably an edge case though, so probably not where you'll want to focus when you're still in development.
This would be quite possible to code, especially after I implement arrays which is more or less next on my list. One value per unit type per location and I get ~5 locations without having to reuse stuff, so I haven't even tried to mess with this. Plus I run out of triggers doing a count for every unit type. Until recently I've been pretty sure what I wanted to do is just outside the range of what's possible with the current engine, but if I (we?) can figure out a clever way to store multiple values, I can do some really neat things.
|
accidental double post. I'll use it to ask some more questions:
Who are you? Where did you come from? (Who the fuck writes a programming language for triggers?) Why are you doing this? What help do you need?
|
Who are you? I'm Alex, I ... mod games I guess. I've participated in many modding communities and started some myself.
Where did you come from? I used to make UMS maps with the blizzard editor when I was like 10. I think it may have helped me become a programmer. Also I played a lot of competitive SC2 a few years ago, now I'm back to Brood War.
Who the fuck writes a programming language for triggers? People who should be working for their job instead. Also it just happens I had to write a bunch of code parsers recently so the knowledge is still fresh in my mind. The IR + codegen part is new to me so don't expect LLVM or GCC levels of sophistication, at least to start.
Why are you doing this? I love modding and I love writing tools so it was a natural consequence. The actual decision to make this came a few days ago after I got annoyed at the limitations of a UMS map I play a lot.
What help do you need?
Testing, feedback and writing documentation. I'll setup avenues for all of this as soon as I decide to release the initial version in a few days.
|
Some new stuff to show. This code compiles and runs now.
global foo = 15;
fn do_some_stuff() { return 42; }
fn main() { var bar = do_some_stuff(); if (bar == 42) { set_resource(Player1, Minerals, foo + bar); } }
|
Looks sick. Hopefully, you'll figure out a way to go past the limitations.
|
|
Awesome. I will give it a shot.
|
|
|
|