Jump to content


Community Members
  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by DanW58

  1. The PLA. Actually, it's hard to tell. All the movies from the first Century were black and white...
  2. Ok, I will. No; I don't have Windows anymore; I quit cold turkey.
  3. I WAS a programmer, many years ago; decades. Back then I was using Visual Studio. I don't know what to use now; I would need something that can search through the code, tell me all the places a function is called from, etc. Right now I've no idea where to start. Heard about Eclipse, but never used it. Alright, I'm going to look into it right now.
  4. You may be right; it was many years ago, the days of the K6; Athlon was the new thing. I found this, in the code optimization guide for h19 processors (latest Ryzen, etc): So, the return chache is small, and easily polluted if there's multiple return points in each function. The reason a return cache is needed is for speculative execution not to halt at returns. The processor doesn't know where a function begins and ends; all it sees is a stream of instruction, some being return instructions, and needs to know where to return to in its speculative execution, to keep the pipelines full. The more returns, the more entries are used up in the cache. However, I can't see why the compiler can't optimize this. This AMD document is written for Assembly programmers, it seems. https://www.amd.com/system/files/TechDocs/56665.zip EDIT: Are you sure you NEED a square root? 90% of the time square roots are performed unnecessarily. If for comparing distances, for example, which are always positive, A = a.x*a.x + a.y*a.y; B = b.x*b.x + b.y*b.y; return sqrt(A) < sqrt(B) can be replaced by return A < B; or return sqrt(A) < 5 can be replaced by return A < 25; In fact, ieee floats, if they are positive, can be compared in magnitude using integer arithmetic, by reinterpretatively casting them to unsigned ints of the same size. Only useful for sorting big tables, though, as otherwise you incur the cost of passing data from the fp pipeline to the integer pipeline; not worth it for just one or two comparisons. Renormalizing vectors is another area where people feel they need square roots, but in many cases the amount of renormalization is small. Rotating a matrix or vector can introduce tiny errors. In such a case, you only need a quick renormalization. class Cvec3 { float x, y, z; void normalize() { correction = 1.0f / sqrtf( x*x + y*y + z*z ); x*= correction; y*= correction; z*= correction; } void quick_renormalize() { correction = 1.5f - 0.5f*( x*x + y*y + z*z ); x*= correction; y*= correction; z*= correction; } }
  5. You mean for function definitions only? But it's nice to have var declarations and function definitions in a common style. Anyways; not important, really. The Timing Pitfalls and Solutions paper at the end uses a double precision time tracker; I thought that was a sad choice, as the precision of floating point depends on the value magnitude. Floats don't make for good accumulators; they make terrible accumulators. Always use fixed point for accumulation. I'm sure double precision having so much precision it masks the horrible choice, but it is a horrible choice anyhow. I hope you guys haven't adopted that solution verbatim. By the way, my own engine I was working on, 20 years ago, was going to represent the world using integers, and motion through the world was going to be integer-computed as far as translation. Only upon conversion to perspective (once every 64 frames or so) was everything going to be "floatified" (and held in video memory for the next 64 frames). This way I was going to be able to have continuous worlds; the origin of the current "floatified" world geometry was to always be not far from the camera. Unlike the game Strike Commander (Origin) where you could fly far off the map, but the farther you went the more coarse your flight dynamics became. At 200 miles or so, your altitude would change by meters increments/decrements... But none of this is applicable to an RTS; just mumbling... This is about exactly one little bit relevant, in MathUtil.h: #define DEGTORAD(a) ((a) * ((float)M_PI/180.0f)) could be more precisely defined as #define DEGTORAD(a) ((a) * (float)(M_PI/180.0)) Though for performance you'd want to make sure the division is done only once; maybe using a global constant of type float... Too bad C++ doesn't have once functions... once float DEGTORADF () { return (float) M_PI/180.0; } #define DEGTORAD(a) ((a) * DEGTORADF()) EDIT: Every time I svn up, it takes an hour. Are you guys really working so hard? Or are you adding and deleting a space in a Collada header? :-) EDIT 2: This may have a negative impact on performance. The AMD manual on code optimization, which applies to Intel 90% of the time, recommends to avoid having more than one return per function. I can't remember what they said about compilers' troubles optimizing this themselves, but they recommend single return from functions at source, or the result is usually return cache pollution.
  6. There's one gottcha there, for declaring multiple variables... int& a, b, c; //only a is a reference. int &a, &b, &c; //3 references Same goes for pointers. Seems to me it pays to advocate the exact opposite, when accounting for that.
  7. Hey, Loki! Nice to see you! Glad to hear. I worked really hard on it. Remember when the brothers went on vacation and I standardized the formatting and fixed the indentations? Even using a code formatter, that took about a whole month of full-time work; and when they came back from vacation I got treated like a pariah for one tiny mistake I made in one tiny line of code, a typo; which is when... Anyways, I hope you understand my bitterness... Glad to find a good old friend here. :-) EDIT: I'm "Chuck Starchaser", by the way; not sure if you know. Ah, I guess you do; you remembered my love of Eiffel.
  8. I LOVE your code, guys. Never seen anything so clean and thorough and well organized (since last time I looked at MY code, of course ;-)). Also reading the Timing Pitfalls and Solutions document. I hit that wall before; just didn't know how thick it was. PRIVATE NOTE: The engine I worked with, before, was Vegastrike. If you've never looked at it, thank your favorite god and perform the appropriate sacrifices.
  9. Been winning a bit more often... By building walls and turrets galore and researching ranged everything before anything else. Gonna start looking at the code now. What environment do you guys use in Linux? And, do you have project files for the preferred environment? I'm going to be opening files in gedit for now... EDIT: Reading Coding Conventions. Glad you indent {} the way you do; that was my biggest fear... EDIT2: R.e. "Prefer global variables over singletons, because then they're not trying to hide their ugliness"... The purpose of using a singleton is to ensure proper order of initialization, even during static initializations; not a cosmetic concern. I like the solution packaged in the Eiffel language... Bear with me, because understanding how another language solved a problem sometimes clarifies what the problem is... In Eiffel there's a class of function called "the once function", which only computes its result the first time it is invoked, and subsequently always returns the same value it returned that first time. They are used to replace constants (and I know that globals and constants are not the same thing, but MOST globals are constants). But the difference between once functions and simple constants is that once functions are NOT computed in just any arbitrary order; they are invoked following an initialization tree. Many once functions compute their results on the basis of other once functions, to arbitrary levels of depth. In other words, constants in Eiffel benefit from "lazy initialization". In C or C++ the order in which constants are initialized is undefined. If a constant requires another constant, but is being initialized first, the other constant may be assumed to be 0. The only thing in C++ that resembles lazy initializations is template resolution. However, templates resolve at compile time; NOT static time. Eiffel's once functions resolve at static time, meaning that they CAN be computed on the basis of, say, hardware attributes. EDIT3: What I was driving at is three things: 1) There's a role for globals; and it makes code more readable and execution faster to use them, rather than passing them on the stack millions of times a second over a philosophy concern. Use without abuse should be the beacon. 2) It is important to distinguish between global constants and variables. The latter can be very dangerous; not usually the former. Where global variables seem to be needed, it is often the case that a "header inversion" idiom is screaming to be implemented. 3) The problem with global constants depending on other global constants across translation units is that order of initialization is not guaranteed in the language. Singletons should not be demonized, as they offer a solution to that, if a bit clunky... They are the closest thing to Eiffel once functions C++ has to offer.
  10. Ahh, gottcha. Actually, I rebooted and run 0ad before anything else and didn't have the problem. My computer is a bit old; I bought it used; 4 gigs of RAM, quad Intel processor, cheap, old gforce video card. I made the chickens pay.
  11. I just finished building from svn, non-debug, non-commit, and it passes the tests and runs. However, 1) Display has slowdown considerably. When the game opened, the frames around windows took about 10 seconds to show. I think it's a texture loading issue. I started a game, and the initial map display built up slowly, as if one class of object at a time. It could be my computer is out of memory; I'll reboot and report back. 2) Deer have become hard to kill, to the point that my cavalry ends up galloping half way across the map before the deer dies, which is so far away it's not even worth processing the meat. I manually sent my cavalry to kill a deer closer to the base, and the same thing happens again: The deer escapes fast and its health comes down slowly and my cavalry ends up almost in enemy territory before the deer dies. Had to give up on deer hunting altogether. Which is actually okay; I'm vegetarian, and I hate the obligation to play as if I'm not; but according to the tutorial videos, hunting is mandatory...
  12. I'm in, but not sure what to do. I expected to find a lot of discussion topics, though; I just have a blank page with "(No Topic Set)", telling me I've joined.
  13. Ah, I didn't know they cost less; and didn't notice they built faster. Still, even if it's a zero sum, it distracts you twice as often.
  14. So many things it's embarrassing. I just tried a couple IRC clients. One of them, "circle" something, installed, but didn't put itself in the applications menu; so I uninstalled it. I know it is probably in usr/bin or one of those folders, but it's a nightmare for me to try to find things in unix. I know, I installed it; no dual boot, but I'm an eternal newbie in unix. Another I tried is "quassel" or "loque", I could not find how to list servers or channels, and had no help and no manual. Uninstalled it. About to try xchat.... Installs, puts itself in the menu, but I select it from the menu and nothing happens. And I just had another killer Very Easy experience. I was attacked by a horde of like 25 calvary. Exit game. Not fun at all. Someone was saying that playing with an ally can teach me, but how can I learn when the ally is in the fog of war? Then again, I heard there's a way to not have the fog of war, but I looked everywhere; couldn't find it. Just like I couldn't find the ceasefire. Just like I don't know how to reply to an enemy offering neutrality. A lot of things are not intuitive.
  15. Gottcha. Okay, I'll look into an IRC client, for starters (it's been many years...)
  16. Another problem: With most civilizations, a house supports 10 units; but if you are Indian, a house supports only 5 people. You have to spend so much time building houses you almost can't do anything else. Of course it makes sense that there be differences between civilizations; but a change for the worse with an item that borders on being a pure irritant by a whopping factor of two is a bit much. Could it be made to support 8 units, maybe? Otherwise, it's like start a game with random civ, but if India comes up, quit and start again. There's also the consideration of real world consistency. Are people in India more isolated than other civilizations? Do they really accomodate less people in each house than Persians, Greeks, Romans or Egyptians? I can't see this being the case. My feeling is that this number, 5, was totally randomly picked.
  17. Alright, here you lost me completely; I've no idea what thread pools and local management are. Here I'm lost as well. Okay, this is my first gut feeling how I would organize threads: Kernel (a director thread using millisecond interrupts, manages simulation speed, quality, queues and thread balancing, static memory). Map, resources, updates, fog of war (singleton thread with clients, all memory allocated at game initialization). Owns path-finder lib. Unit iterator thread (visits all empires, groups and units at every sim step; calls AI client libraries, see below). Projectiles, spear tips and sword edges (perhaps a separate thread from the unit iterator, as these are smaller sim objects) User input, user interface (menus, etc), camera movement, positional sound (as in Starcraft, a request hint ;-) ), video synch. If necessary, depending on test results, I might split threads 3 and/or 4 into two threads, and assign new units to them always to the thread with lesser load, to keep the balance Client libraries to those threads would be: Unit and group "vision"(&hearing?)/awareness Path-finding Collision detection, accel/decel motion modifiers, for movement realism Empire AI, Group AI, Unit AI Client libraries would be pure, statatic-free functions, to avoid thread-safety issues. Such threads are only "modifiable" in the sense that units are born and die; groups are defined or undefined, etc.; but that's only a quantitative mod that may affect load balancing a bit... not much at all. If I'm understanding correctly, what you are doing, instead, is assigning groups of units to separate threads, all threads running the same code... Yes? A horizontal breakdown, rather than a vertical one. If so, I'd suggest a vertical approach, as a horizontal one has lots of problems. One of them you are aware of, namely that units die (if I'm understanding correctly), affecting load balance. But there are other problems with the horizontal approach: Each thread has essentially access to all of the memory, and so every thread suffers the same L1 data cache pollution as a single thread version. Additionally, if L2 cache is shared between cores, the L2 will be hotly contended. In a vertical approach, each thread has its own private, smaller memory and can read and write to swapping buffers like I described in the last post of previous page. Each thread is essentially running all of the code, ok for L2, but making L1 code cache utilization far less efficient than it could be. Not using thread pipelining and swap buffers between them, allows too much freedom, leading to confusion about previous vs current vs next unit state when threads are reading or modifying each other's data, as well as confusion in how to make class variables thread-safe. Horizontal or vertical, having threads is a way to exploit multiple cores, primarily; both approaches achieve division of labor. But the vertical approach achieves additional efficiencies by reducing the amount of code AND data each thread runs or accesses, using L1 cache more efficiently, reducing contentions for L2 cache, and enforcing a cleaner organization of the code. EDIT: Talking about path-finding, there's a "bug" in the game (I'd call it that) whereby wood cutters search for the "nearest" depot to deliver wood to, but where nearest means sqrt( dx^2 + dy^2 ), rather than the pathfinder-nearest. Sometimes they walk crazy distances to go to a depot across a wall or chasm before you notice and correct the situation. You might say it adds a challenge to the game, but the challenges that add to the game experience are those that seem logical and realistic. Nobody in the real world would be so dumb. EDIT2: I suggested a vertical threading approach when I was working on my own engine, back in 2000, but my partner insisted "that's not the way it's done!" After the project was dead, an article appeared in some magazine, about John Carmack (ID software, Doom) independently discovering the vertical approach was the way to go; and so I was right. I was also right about my suggestion in 2000 that time be treated as data; and that was another thing John discovered and wrote about in another article... EDIT3: Another problem with the game is that enemies are psychic. They clearly have chrystal balls through which they find out I'm building a CC anywhere in the vecinity of their territory, and by the time the building is 10% complete or so they come and attack with full force. Yet another game I have to Exit. Super-AI's are one thing, but psychic power really has no place in games ... unless the game is about psychic powers, or explicitly involves psychers, or unless it explicitly says "civilization Z is telepathic; beware!" (as was the case with the Elerians in MOO2). But this business of "knowing without seeing" is anti-immersive, unfair and abominable. Is visual field computed? Per unit or per group? Realistic or hacky? Are there hacks to get around visual field for some particular purpose? EDIT4: Actually, I just won a game, somehow.
  18. Sounds like a good, quick solution. I know that if I wait until I have 4 cavalry, before exploring, and send them all together, they usually survive with but little damage; so halving their health and attack damage that's over-all 1/4 of the strength, so a single cavalry could then survive. It's not all the animals that are so bad. The crocodiles and bears are maybe worth halving their strength; not quartering it. I think the worst is wolves, as they appear to be harder to hit than lions and tigers, and so take a good dozen javelin throws to kill. Another solution that might be good to have generally is a combat modality whereby you command your soldiers to be agressive, but to retreat when injured. Then one could send a single cavalry to explore without having to watch over it not getting itself killed.
  19. Thanks for the thorough answer. I just had a random poke at the D2857 folder. If I understand anything, it seems you're trying to let the compiler write SSE for you. When I worked with SIMD (that was not SSE; it was 3DNow! by AMD, which preceded Intel; but it's basically the same thing, as Intel basically copied AMD's 3D Now! with a few changes here and there), I did it all by hand, in Assembler. My .h files were all pure C++, using templates and all; but in my .cpp file, every function went like { #asm: {SIMD code} #endasm } It was a 3D vector, matrix and quaternion math library. No compiler touched SIMD back then, but I wouldn't have used it if it had. The most surprising thing was when I wrote code to test the functions, and discovered that in the entire library there was just one single bug; a typo, at that. Usually the make-believe is that higher level languages are supposed to reduce errors in code; I found the exact opposite. In terms of performance it was lightning fast; not 4 times faster; more like 15 times faster than non-SIMD code. Probably 25 times faster when looping it over arrays, thanks to prefetching; but I didn't test that.
  20. Another thing that could be done to vastly improve the game difficulty is to make wild animals easier to kill. I just started a game (Alpine Mountains (3) at Very Easy), with one enemy and one ally. After only 2 or 3 minutes, I've already lost the game, for all intents and purposes, because of the ridiculous amounts of time I have to devote to keep my calvary alive while fighting dozens of wolves; I haven't done anything at all to grow my colony; any map where there's wolves or tigers or lions are basically maps where a beginner is GUARANTEED to lose. The outcome is 100% predetermined. I have to manually retreat and attack again roughtly 4 to 6 times for each wolf; it takes forever and I can't deal with production and units and research while doing that; or have an option for no wild animals, perhaps; I HATE THEM, personally; I don't see what they add to the game, except frustration. EDIT: Please see my previous post also; the last post in previous page. That one is a coding offer. EDIT2: Started another game, another map. In this one, right at the start of the game, I get a Roman soldier out of nowhere and killed half my women in the time it took me to figure out WHAT was attacking me WHERE; I could not even see the attack; only hear the horn. Exit game again. It is pointless... Perhaps it is true that using an allied AI should make things easier; but to have an allied AI you have to use a 3-player map, and so far the 3-player maps are all impossible to play. The first one I tried did not have enough flat terrain to build farms. The second was infested with wolves. The third one has passing Roman soldiers that totally destroy you from the start.... This is entirely pointless. Let me guess: Things like wolves and random Roman soldiers are parts of the map. Right? So they fall out of the jurisdiction of game difficulty setting? Not good... If you want to make magical maps, maps owned and operated by Gaia, you have to make them customizable, and hook Gaia into the game settings.
  21. Watched the interview. Is the multi-threading done, or is it still open? One thing I did back in early 2000 was a per-thread memory allocator and an inter-thread memory rotation object. I was using Boost Threads, which back then was not part of the official Boost; it was in early development. And the idea came from an article in Dr Dobbs, which said basically that if you have a common allocator to all threads, each allocation call has to pass through a mutex, which is a real drag. Another unrelated Dr Dobbs article talked about how much speed-up you can get by running array allocators under the standard allocator, and the speed-up was significant already then (should be far more significant today). So I combined these Ideas. First I coded a fast array allocator for single thread access; no mutex; put it into a class. Then I allocated one object of this class per thread at runtime. Now, you might ask, what about when one thread is a producer, and another is a consumer? No problem: I made allocated memory objects "remember" their producer thread; then coded the virtual dtor to return the object to its producer thread, for destruction or recycling. So the same thread that produced an object and allocated it to a fast array, would be the thread to destroy it, guaranteed. The inter-thread memory rotator was an idea that came from a friend back in the 1980's who designed a traffic simulator using several microcontrollers working as a pipeline. Between the microcontrollers were memory banks that "rotated" at each iteration, such that controller A wrote output to the memory bank, and controller B read the memory bank also, but they were actually accessing separate halves of the memory bank. Once both controllers signalled task completion, the hardware swapped the two halves of the memory bank, so that controller B could now read what controller A was writing in the previous iteration. I implemented the same idea into an inter-thread communication object, with a size_t and two threads as template parameters, whereby the threads accessed a "common memory" (or rather, what might seem to be common memory). This memory was actually duplicated, and the two copies could be "secretly" (internally) swapped. So the two threads could read and write without the penalty of mutexes, and when done would call the im_done() function; and when the object was sure that both threads were done, it internally swapped (the internal pointers to) the two copies of memory. So, information could actually flow both ways between the threads, as safe as queues, but much faster, since you can write megs of data if you want, without mutexes, and then safely swap. Even the swap was spared having to have a mutex, as each thread blocked as they hit the im_done() function, waiting for swap confirmation. The logic was inherently thread-safe. Besides gains in speed, this scheme could have beneficial side-effects in two general areas: Exposing memory leaks, since you are in control of allocation and therefore can easily place code for debugging or monitoring what's going on. Exposing thread balancing problems. Promoting a clear inter-thread data flow graph, if inter-thread data objects are used exclusively, and mutexes are avoided entirely.
  22. Good answers, all, thanks. @Nifa, I was not suggesting an exact copy of MOO2 diplomacy; I was simply describing it. Indeed, the first stage would be rather short in 0ad. In MOO2, meeting a civilization is a bit of a big deal; it's a first message delivery; an establishment of diplomatic relations; you typically stop hitting the turn key, and read about your opponent's personality, check their fleets, economy and technology tree. A good tactic is to see what items they've researched, and what research items are denied to them, that you might have researched already, and offer them a technology swap. This puts the love/hate bar from 55% or whatever is the default, all the way to 80%. But that's a different game, of course, and it is turns-based. I think the most important aspect that would be applicable to 0ad is the making of alliance versus enmity something dynamic; something that is affected by your actions in-game, rather than written in stone during game setup. An action such as building a tower right next to their territory could be interpreted by the AI as an act of war, while things like trade might trickle confidence into the relationship. Then again, being too weak relative to an opponent could be an invitation for a sneak attack without formal war declaration. In MOO2, if you attack someone without first declaring war formally, your relationships with all AI players deteriorate; but it happens some times, with some races more than with others. On the other hand, if you go to war with a party that is in an alliance with another, the other party is OBLIGED to go to war with you. And they may still choose not to, but breaking the alliance in that way may lead to war with their ally. There's all kinds of possibilities. The main advantage, at least for me, would be to see whatever goes on as conceivably justified. This business of getting attacked for no reason whatsoever kind of detracts from game immersion... at least for me it does... In MOO2 you could typically analyze the strategic and diplomatic situation in retrospect and figure out what you did or failed to do that resulted in a war that you didn't want at a particular time; so even after you finished playing a game you are still partly immersed. And this kind of AI is nothing requiring neural networks or dojos; just simple rules for the most part. By the way, what language are you guys writing this in? I have C and C++ experience, ASM with SIMD, as well as GLSL.
  23. I heard about the ceasefire option, but I haven't tried it yet. I looked for it, but not too hard. In MOO2 there's actually many ways to choose diplomacy over war (or the opposite). One strategy I used which was absolutely hilarious, and it was my own invention, to get two races to go to war with each other: I sent two colony ships into their territories, one each, timed to arrive to unsettled systems at the same time. When the ships arrived, I told them to build a colony in a planet. This would get ME into big trouble, if I left it at that; but what I did at the very next turn was to go to the diplomacy screen and GIVE these colonies to them, but such that... imagine they were the Darlok and the Meklar, I give the new base in Meklar territory to the Darlok, and the base in Darlok territory to the Meklar. They are incapable of controlling themselves and not attack these new bases in their midst, and so they end up at war with each other. It could work with only one base; but I used two bases for extra insurance. Yeah, the game never defaults to a war status, so if your race is not 'repulsive', and you make sure you have a respectable navy, and you use diplomacy to trade, and/or to exchange technologies, you can avoid war for the most part, if you want. But it's not for sure; sometimes espionage causes war, even if you were not spying, because sometimes the spies of another opponent manage to frame you. If you are already at war, you can use the diplomacy screen to OFFER or SUGGEST a ceasefire; but they may say NO. The more or the sooner you ask, the worse the result is. Knowing when to sue for peace is an art in MOO2. You have to put yourself in the shoes of your opponent sometimes. If someone else declares war on them, that's a good time to sue for peace, for sure. But generally, the best ingredient is to be winning the war. If you are winning and you sue for peace, you usually get it. Sometimes your enemy may put a condition, like "give me system such and so as an incentive".
  24. Cool. Good to hear, and glad to be understood. For now I guess I'll go back to Sandbox and for a challenge just try to win faster. EDIT: I suppose you guys never played Masters of Orion II? I picture you all much younger than me; MOO2 is from the days of .. DOS. In MOO2 you're conquering planetary systems, rather than terrain, and riding space-ships rather than horses, and researching quantum displacement devices rather than iron swords, but it's the same thing otherwise. And that was a turn-based game, though it kept you on the edge of your chair anyhow. The diplomatic paradigm was interesting, though, and even if it's not traditional in Age of Empire type games, I see no reason why it could not be imported or used as inspiration. Basically, 1) At the beginning of a game you haven't met your opponents yet, so you have NO diplomacy with them; your diplomatic screen is blank. 2) As you expand through the galaxy, you begin to meet your "opponents", usually one at a time. When you do, they fill a slot in your diplomatic screen, and now you can talk to them, offer them gifts or demand gifts from them; you can declare war, sue for peace, or suggest an alliance; you can send spies to their territory and tell them to steal technologies, or to do sabotage, or to hide. In this screen, you can also see how an opponent "feels about you" (in a love to hate scale); though their feelings are not always a sign of upcoming war or peace. They may hate you, but if you know that they fear you more than they hate you, you know they won't be declaring war any time soon; and viceversa: if you are weak, even if they love you and feel really bad about conquering you, they might do it anyways if it makes sense strategically. 3) Some races are more likable than others. The game allows you to create your own race, and one of the items is "repulsive", which makes your race automatically hated by everyone; but the value of this attribute is a whopping negative 6 points that you can spend on positive attributes. And there's a research item you can queue up that gives you extra points in diplomacy. 4) Offering gifts of gold or technologies or systems to a race makes them like you more, of course. Demanding tribute or gifts, and spying on them makes them like you less. Occupying a system too close to their territory makes them like you less in a big way, unless you are in a formal alliance with them. 5) When a race declares war on you, they do it verbally. The message is not always formal; for example "Our troops really need some target practice; so I've instructed them to try taking some of your systems. If we like the result, then it will be WAR.", though the status shows 'WAR' right away. The fact that war is not ON by default, but has to be declared, is the core of the difference. I'm bringing this up because something along such lines would fit in well with 0ad, offering several advantages, namely, a) No attacks at the beginning b) When attacks come, you'll know why (declaration of war) c) You can choose trade and diplomacy over war, *** in-game *** d) Alliances are also possible in-game, but also may shift in-game, adding a new angle Saying, because when I get attacked, in 0ad, I always ask "WHY is this happening?", and there is no answer. It just is. EDIT 2: Just realized what people will ask: Don't you have to eventually have war in order to win? Not necessarily in MOO2. There were 3 ways to win a game in MOO2: 1) Conquering the whole "galaxy", system by system (lots of war) 2) Conquering the world of the Antarans, a transdimensional race that's always present in every game; but it's difficult, and you need to build a dimensional gate to get there... 3) Getting voted emperor of the universe (no war necessary, though it can happen in times of war). Every 50 years or so (game time) there is a vote, where all the races vote for one of two randomly selected contenders (sometimes you are a contender; sometimes not). To win, a contender has to get 2/3 of the votes, which is not easy. The number of votes each race gets is per the number of systems they have settled; so if you have 2/3 of the systems, you can vote for yourself and win. Otherwise, you can improve your chances by having good relationships. But most players DON'T want to win this way, and the way to avoid this win (or the war that may result from voting for the wrong opponent, as they threaten you sometimes), is to abstain. If you abstain, it is VERY rare that you'll be voted in anyways, though it happens occasionally. Most of the time it just prevents all parties from achieving a 2/3 majority, and thus the game continues.
  • Create New...