The goal is to keep things at the utmost possible simplicity. This is just a proof of concept anyway, if it's not going to be very fun to play, so what. Important is, it should be fun to '''create'''.
This will most notably carry the simplicity. There are no dialogs or any sort of GUI elements, except a fixed bar of buttons to the left. The left bottom will have an overview map. The right part will be the main screen, with the game map.
Left clicking will be the way to interact with buttons, and to perform actions in the main screen. Right clicking will be used to scroll.
- Following up on the simplicity, there is a fixed layout for all buttons. There are 4 tabs, each has 8 buttons. for 8 panels. The 4 tabs are: Buildings, Settings, Statistics, System
- Buildings: A button for each building to build it with the next mouse click into the map. Each of the 8 panels holds 4 building buttons.
- Settings: The 8 panels are: Waypoint, Carrier Priorities, Food Priorities, Industry Priorities, ...
- Waypoint: This panel allows commands for the currently selected waypoint. This includes:
- Carrier: This panel allows to change the priority in which item orders are handled. It has an icon of each possible item, and there order can be modified.
- Food: Distribution of fish/bread/ham to coal/iron/gold/stone mine, water to baker/pig farm, grain to mill/pig farm.
- Industry: Distribution of boards to construction sites or boat builder, logs to woodcutter/char burner, coal to gold/iron melter, weapon/tool smith, iron bars to weapon/tool smith
Just a simple square-grid of hexagons.
Maps are currently created randomly.
Fish, Water, Coal, Stone, Iron, Gold
- The following units are planned
- wine grower
- pig rancher
- gold melter
- iron melter
- weapon smith
- tool smith
- boat builder
- gold miner
- coal miner
- iron miner
- stone miner
- water worker
- There are several states of units:
- New units are created in castles or residences, and start in the created state. It means, they have no profession yet, and merely exist as a number.
- When a new unit is needed somewhere, first the residence with an existing profession is searched for, and else a new unit is created and leaves the residence.
- Once it is possible to leave the building, the unit gets an active unit. From now on, it will actively execute its code.
- When a unit is no longer needed, it returns to a residential building, and becomes a sleeping unit - it is not visible, but in the queue of sleeping units of its building.
- If a residential building is destroyed, all units within die. Units not in the building are not affected.
- If a unit wants to go to a residential building, but no room is left, it dies.
- A unit can never change its profession.
- Some units require tools for their profession. When a new unit is created in a residential building, for these units, the tool must first be delivered to the residential building.
- The base of a settlement is the roads net:
- Each building and each waypoint is a node in the roads net.
- Each active unit has a road. For carriers, this road connects two waypoints.
For all others, it connects a waypoint and their workplace.
- Pathes are only found inside the road system. If a unit goes from point A to
point B, both A and B must be nodes in the roads system. Same if an item is
shipped from A to B. Found pathes are cached on the assumption that the path
finder might take up lots of performance. Any modification to the roads
system invalidates the complete cache though.
- Items can only be transported along the roads.
- When a road is removed, not much happens, just:
- It is erased from the map.
- It is removed from the roads map, any cached pathes are invalidated.
- The unit assigned to the road is sent home.
New pathes calculated from now on will not consider the road - but there may
still be units walking a path containing the removed road, and goods tagged
with a path over the now missing part.
In the former case, units will try to find another path.
/!\ TODO: /!\ details? what if no other path? what if a unit is on a road while it is
In the case of items, the items should be retagged with a new path, or sent
back, and a replacement order placed.
TODO: details? how is it detected?
- Caching - the same path may get used multiple times before waypoints are removed/added -
so might want to cache pathes. Removing or adding any road invalidates all
- The following items are planned:
- gold ore
- iron ore
- gold bar
- iron bar
- pick axe
- fishing pole
- All items have a destination path tagged to them. Carriers will look for items at both of their waypoints, and transport them accordingly.
- For each item type, there will be a list of providers, and users. Each
provider and user will fill in the amount they have in stock, and the amount
required. For example, a sawmill might have 3 boards in stock, and a
construction site might need 3 boards. Therefore, a path is searched in the
roads net, and the boards are tagged with it.
This can deal well enough with permanent request. E.g. a sawmill can store 6
logs, so it will only have a request placed for the missing storage. Once it has
ordered 7 logs, it will be removed from the users list, and then only order a
new log again when it has processed one of the 6 logs first.
Providers are castles and storage halls, and also other producers. E.g. a
sawmill may provide up to 6 boards on its associated waypoint.
Castles and storage halls also are users for all goods - but as a special
case, only for goods from actual produces, and only if nobody needs them.
Priorities can be set if the same good is needed by different professions.
E.g. coal can be develivered to both the iron and the gold melter. This is
handled by a global priority system. The user can set a ratio, e.g. 1:2 for
coal which goes to gold and iron melter. A global counter of delivered goods
is used to keep the ratio at the set value. When the ratio is changed,
counters are all reset, and the ratio is used for all subsequent deliveries.
Apart from the priorities, orders are processed on a first-in, first-out
basis. Once an order to process is found, the nearest source of the item in
question is found, and the item is tagged with the path.
- The priorities could be realized by assigning each newly arrived order a priority.
For example, assume fish is set to priority 10 for gold mines, and priority 1 for iron
mines. If now a gold mine orders a fish, that order is inserted with priority 10, the
other with 1. (If priority for something is set to 0, the order isn't even processed.)
When processing orders, now only those with priority 10 are considered. All others
only have their priority increased by 1. Therefore, if there's a big stock of fish
available, and gold and iron mine will constantly place orders, the gold mine will
have it's orders all processed at one, while the iron mine's orders will be
processed only for each 10th time a gold mine order is processed.
For each player in the game some things need to be managed.
- Roads. Each player has their own roads system. See Roads. Because each building is also a waypoint in the roads system, this also includes all buildings.
- Active units. All the units of this player which are currently active in the game.
- Scheduled units. A queue of all the units to be sent out, either builders or carriers ordered directly by the user, or other units ordered because of different events (e.g. woodcutter, when the woodcutter hut is finished). A unit is removed from this list as soon as it enters the road. If it can't reach its destination for whatever reason, it should probably order a replacement. Alternatively, un-occupied buildings could be checked for from time to time.
- Active objects. E.g. trees or fields to grow.
Networked games run on each client, and use a server for synchronization. The server will do nothing but accepting commands from the clients, and then broadcasting the commands again. But it will make sure that the exact same commands and in the same order arrive for each client, in the same game tick.
A client must run synchronized. That is, it will have a function game.tick, which is run every time the server sends a TICK command. This probably would be fast enough with happening only around 10 times a second - so will need a way to separate game logic from actual animation. For example, if an animation is one second long, that would be only 10 game ticks. But still, it might move something by 100 pixels pixel-by-pixel.
Initial client commands:
- PLAY name - Ask to join the game.
- STAR size, seed - The game host starts the game on a map with the given parameters.
- TOAI id - The given player is an AI player (host only)
- READ - This client is ready
Initial server commands:
- WELC player/seed/size - A newly joining clients gets this. The first is the player number it gets assigned, and then information so it can create a new random map from the given random number generator seed and map size.
- PLAY id, name - A new player joins the game.
- STAR size, seed - Game starts. After this, no more new players can join.
- TOAI id - given player is an AI player
- READ id - given player is ready
- BUIL type/x/y - Place a building of the given type at the given location.
- ROAD x/y/x/y... - Build a road over all the given positions.
- REMB x/y - Destroy building at x/y.
- REMR x/y - Destroy the road leading over x/y, or starting there, if it is the only.
- GEOL x/y - Send out a geologist.
- TOOL a/b/c/... - Modify production settings.
- RECR a/b/c/... - Modify recruitment settings.
- DIST a/b/c/... - Modify distribution settings.
- ATTA x/y - Attack given location.
- BUIL id/type/x/y
- ROAD id/x/y/x/y...
- REMB id/x/y
- REMR id/x/y
- GEOL id/x/y
- TOOL id/a/b/c/...
- RECR id/a/b/c/...
- DIST id/a/b/c/...
- ATTA id/x/y
- LEFT id - A player left the game.
- TICK n - Next game tick marker.
Lag hopefully isn't that big a problem. Even your own action will go to the server before being processed, but nothing in this game should be time critical.
A central server is used, so cheating attempts by clients could be detected there (e.g. if anything is different than on the host player, we know there has been cheating). A cheating player could be disconnected.
Actually, even for clients it should be possible to detect cheating. Any packet in the chain of packets not possible would mean cheating (or desynchronization). So basically, any tampering with the game state will lead to desynchronization, and the game will end.
Another form of cheating would be to access hidden information. For example, a client could run with the whole map visible, and could eavesdrop on any settings and commands issued by other clients. There's not much that can be done to prevent this.
Being an RTS game, sooner or later you will encounter enemies. To attack them, barracks must be built. They can hold multiple military units, which will attack enemies in the vincinity. A challenged unit will not run away, but await the attacker once challenged. To give not too much advantage to the attacker that way, fights are always one-on-one.
Bot or player?
There are generally two ways to do AI players:
- Have AIs simple work locally on the game and control their '''Player'''. The AIs would run on each client, and directly update game state, without any network access.
- Make AIs send out client commands to the server, just like other players. This means, each AI needs as much network bandwith as a human player. But, the AI could simply re-use all the player functions and wouldn't need separate direct-update methods.
Maybe also a hybrid could be found, where AIs behave like clients, but also simulate the server and don't actually send anything over the network.
This is a hard part. Generally, the AI will do this:
- Select a starting location.
- Build up an army.
- Kill all enemy players.
The starting location should not be too far from mountains (=resources), and also have some initial woods, and stone. Maybe a lake for fish, and some berries.
After that, buildings must be built. Roads must be built. Locations must be chosen.
Finally, other players must be analyzed (it could cheat here by accessing internal info) and attacked.
When a game is saved, the complete game state must be serialized and written. This includes the state of the map, the roads system, queued buildings/units/items, players, units, items. Possibly python could be used to just pickle all the relevant objects.
The Woosls code is written in Python, and follows the Python style guide. That is, 4 space indentation, no spaces around ( and ), and so on.
When using Python for small scripts, you never need care much about code organization, it simply will be readable. For a project like this, there are some self-imposed guidelines:
- Put all variables at proper place, that is, local, class level, or module level. Python has no global variables, so the problems of C++ with that can't arise.
- Next, initialize all variables in the __init__ function of the class. This somehow violates the ability to have fully dynamic objects, but for Woosls, it is good as documentation. For example, when the game is saved and reloaded, it's enough to check that every variable in the __init__ methods is saved/reloaded. Of course the pickle module could be used in that specific case, bu still - it's nice to have an overview of all variables, even if usually you just add properties to an object where they arise.
- Never make a file with more than 1000 lines. 500 lines already is really bad. And creating a file of 1 line for a very simple class is ok. The reason is, this serves again as documentation. If each unit class has its file, then everything is clear. Of course you could just put 3 or 4 units inherited from another in one file.. but there's no advantage.
- Related to the previous, never put many unrelated source files into the same directory. E.g. all units go into their own directory.
That's about it, I may add more later. But it's all Python code, so there really shouldn't be much possibility to mess up completely, like in most other languages who '''even ignore whitespace''' |)
This does several things (maybe should split it?):
- Dispatch events and ticks to server, client, GUI
- Hold global accessible items, e.g. the screen or the current client, so they can be accessed by other modules without having to pass around references everywhere
The map module simply contains the representation of the hexagon map.
The game module contains the complete game state. This means map and players.
The server module has the game server. It handles all client requests and sends out the game ticks.
This is where user input is collected and transformed into requests to the server. Replies by the server are received and passed on to the game module.
This module describes a player. A player has units, a roads net, various settings and other things.
Implementation of a single unit.
some minor design decisions
- All waypoints in the roads system are simply buildings, where a signpost also is a building. (And each building is a subclass of an object.)
- There can be only one road between two waypoints.