Merge lp:~widelands-dev/widelands/seafaring-ai into lp:widelands
- seafaring-ai
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 7399 | ||||||||
Proposed branch: | lp:~widelands-dev/widelands/seafaring-ai | ||||||||
Merge into: | lp:widelands | ||||||||
Diff against target: |
3672 lines (+1690/-534) 22 files modified
src/ai/ai_help_structs.h (+58/-13) src/ai/defaultai.cc (+946/-109) src/ai/defaultai.h (+57/-2) src/economy/fleet.cc (+7/-0) src/economy/portdock.cc (+20/-0) src/logic/game.cc (+2/-2) src/logic/game.h (+2/-1) src/logic/playercommand.cc (+34/-5) src/logic/playercommand.h (+4/-3) src/logic/productionsite.cc (+1/-1) src/logic/ship.cc (+396/-382) src/logic/ship.h (+21/-2) src/logic/warehouse.cc (+53/-4) src/logic/warehouse.h (+7/-0) src/notifications/note_ids.h (+2/-1) src/wui/shipwindow.cc (+5/-5) test/maps/ship_transportation.wmf/scripting/init.lua (+17/-0) test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua (+54/-0) tribes/atlanteans/bread/conf (+1/-1) tribes/empire/bread/conf (+1/-1) tribes/empire/meal/conf (+1/-1) tribes/empire/ration/conf (+1/-1) |
||||||||
To merge this branch: | bzr merge lp:~widelands-dev/widelands/seafaring-ai | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
SirVer | Approve | ||
GunChleoc | Approve | ||
TiborB | Needs Resubmitting | ||
Review via email: mp+242271@code.launchpad.net |
Commit message
Description of the change
Hi, after some time this is ready for review, read the branch info for more info.
Tested a lot as AI-only games, I would welcome a human players tests and feedback.
TiborB (tiborb95) wrote : | # |
GunChleoc (gunchleoc) wrote : | # |
I will have to look at this in detail later, my brain's not working properly today.
Codecheck still comes up with an error, could you please always run codecheck over the complete code base before submitting a merge request? This way, we won't have cosmetic changes in the diff in the future.
I also saw that there are commented out lines of code in the diff, they should go before a merge.
TiborB (tiborb95) wrote : | # |
@GunChleoc - yes, there was one error left unfixed in defaultai.cc - fixed
cosmetic changes happened on files out of ai directory (like ship.cc) that I had not touched before. And were caused by clang-format. Next time somebody will work on them and run clang-format on them, there will be no cosmetic changes, of course.
TiborB (tiborb95) wrote : | # |
next time just put a comment into diff here whenever you see someting that needs fixing - it will be easier for me to get what you mean
GunChleoc (gunchleoc) wrote : | # |
Running clang-format is good :) Doesn't work on my system though :(
GunChleoc (gunchleoc) wrote : | # |
> next time just put a comment into diff here whenever you see someting that
> needs fixing - it will be easier for me to get what you mean
I have used my browser to search for "// " and added diff comments. I hope that I have found them all.
When I temporarily comment something out, I usually add a NOCOM to make sure I won't forget to delete it before a merge request. Otherwise, I would forget all the time.
GunChleoc (gunchleoc) wrote : | # |
I have added some diff comments. I find the for loops with the iterators hard to read, so I got stuck there at some point - if you change these to range-based loops (example below), I will have another go at this.
TiborB (tiborb95) wrote : | # |
@GunChleoc - re observers - there is one point why vectors are suitable - they are ordered and have a front member that can be moved to the back. This is used when doing various kinds of check - like check one building every 10 seconds.
Usually a player has couple of ships, but quite a lot of buildings, but most of all - a lot of roads and flags. These would be best candidates for optimization, but these also needs an order, because they are accessed similarly like buildings.
So this is not that straightforward.
BTW, I started to use unordered_set where suitable (as you probably noticed) - exactly for performance point of view.
TiborB (tiborb95) wrote : | # |
correction: allships is a list, not a vector, as I believed, sorry
GunChleoc (gunchleoc) wrote : | # |
> @GunChleoc - re observers - there is one point why vectors are suitable - they
> are ordered and have a front member that can be moved to the back. This is
> used when doing various kinds of check - like check one building every 10
> seconds.
That's a valid point, so ignore my comment for that :)
TiborB (tiborb95) wrote : | # |
This is mystery to me, see two lines:
//ref_cast<
dynamic_cast<const ConstructionSit
commented out is before, the second one is what I want to use. Before it used to compile, now it complains:
defaultai.
Can you see any problem here?
GunChleoc (gunchleoc) wrote : | # |
> //ref_cast<
> dynamic_cast<const ConstructionSit
>
> commented out is before, the second one is what I want to use. Before it used to compile, now it complains:
>
> defaultai.
>
> Can you see any problem here?
You are casting everything instead of just the construction site. Try this:
dynamic_cast<const ConstructionSit
TiborB (tiborb95) wrote : | # |
So I implemented comments I saw there, merged trunk, resolved conflicts :(
SirVer (sirver) wrote : | # |
Say, Tibor, are you having fun working on Widelands? You seem unhappy in most of your comments about procedure, overhead or comments.
TiborB (tiborb95) wrote : | # |
I have, I like the 'productive' work, but all that overhead, cleaning the code.... and what I like the least is conflict resolution - I am never sure if I make it right - all right it will compile, but will the functionality be exactly preserved? It would take further tens of hours of testing to say...
But generally I like it, I would not be 'here' otherwise. :)
SirVer (sirver) wrote : | # |
I think you can maximize the fun by making smaller branches. They are faster to merge, clean and review.
TiborB (tiborb95) wrote : | # |
I found it yet :)
No more big branches, this is why I enjoyed these two (or three) small branches I did lately (including this one and tha LUA resource think - though this gone wild from one-line change).
I have 2 fixes for AI in may mind (both military-related) but I will wait till this one is merged. Having too many branches and multitasking between them is another think that I dont like much :)
GunChleoc (gunchleoc) wrote : | # |
> No more big branches, this is why I enjoyed these two (or three) small
> branches I did lately (including this one and tha LUA resource think - though
> this gone wild from one-line change).
This has happened to me too and will keep happening - one day I will learn that there are no quick fixes. I sometimes find it frustrating at first as well, but when the branch is finished, I am happy, because the code is better.
GunChleoc (gunchleoc) wrote : | # |
I have done some testing now. I ran into 2 issues:
1. Fellowships: The space is too tight for the AI to manoeuvre the ships. Pathfinding is always tricky though, so I don't think we need to fix it in this branch.
2. Together We're Strong: By the time the AI had build enough military buildings, the port space had already been covered with trees/roads. I think the only good way to solve this would be to let the AI cheat and know where all the port spaces are beforehand. So if an economy has no port yet, building anything else on port spaces (including roads) is blocked. If it is blocked by trees, build a lumberjack nearby.
I have also started working on removing all those iterators (they irk me), but this is something for a separate branch.
TiborB (tiborb95) wrote : | # |
@GunChleoc
Thanks for testing:)
1. In fact the ship algorithm (I mean not part of AI) sometimes incorectly reports islandcircumnav
2. Indeed AI "looks" only on owned spots. So it is not aware of port spots even close beyond border. My approach - few port spaces should be available for every headquarters. If probability of making use of portspace is 50 %, 3-4 portspaces (in reasobale spacing) would be enough.
GunChleoc (gunchleoc) wrote : | # |
2. Your approach means that mapmakers will have to change their maps so the AI can deal with them.
Check out get_port_spaces() in map.h, this will give you a list of all port spaces on the map, so it could be used to let the AI "cheat". Add them to the list of blocked fields for everything else but building a port until the economy has a portdock.
TiborB (tiborb95) wrote : | # |
We have handfull of seafaring-ready maps. So reworking them should not be too lot of work.But mainly we needs new ones.
Your suggestion is viable. But this is still not 100% quarantee of a success. Trees can grow there, or roads can be built there (AI does not control exact layout of roads, it uses underlying algorithm)
Let other say what seem the best solution.
Maybe both solutions are to be used - cheating via blocking all port fields, and setting a 'standard' that one headquarters needs multiple ports nearby.
GunChleoc (gunchleoc) wrote : | # |
I think it is a viable suggestion to work from both ends.
I didn't know about the roads problem - maybe it would help to have some sort of safety margin around the port plots where no flags can be set - or if there's a road there, set the flag and dismantle it, so that the road will disappear? That might leave buildings stranded though - tricky.
GunChleoc (gunchleoc) wrote : | # |
I have added some code cleanup suggestions. They are all small changes that will make the code easier to read. Otherwise, LGTM.
SirVer (sirver) wrote : | # |
The reformatting makes it hard to understand what actually changed. Could you do that in trunk and merge this, so that the functional changes stand out more?
I also had some more nits.
TiborB (tiborb95) wrote : | # |
Added some answers to the comments.
GunChleoc (gunchleoc) wrote : | # |
I added a comment to one of your comments :)
TiborB (tiborb95) wrote : | # |
I cannot see it. What line number?
GunChleoc (gunchleoc) wrote : | # |
Neither can I, seems to be gone or hiding.
It was about changing the enum to an enum class - the change has the advantage that there is less danger of another enum using the same name and the code jumping on the wrong enum. We are planning to move from enums to enum classes in general. https:/
TiborB (tiborb95) wrote : | # |
Alright, I merged the trunk once more and resolved conflicts...
There is one NOCOM as a reminder for myself...
I will change that enum (in defaultai.cc), it is defined within a function so it should not be that dangerous however...
GunChleoc (gunchleoc) wrote : | # |
Thanks, this makes the diff a lot smaller :)
Give us a shout when you're ready again.
TiborB (tiborb95) wrote : | # |
Alright, I fixed what remained. Yet there is one NOCOM with question :)
GunChleoc (gunchleoc) wrote : | # |
//NOCOM - how is it, when I added class here I have to cast everything
Because enough_ships isn't of the enum class' type. You will get rid of the cast if you do something like this:
enum class FleetStatus: uint8_t {kNeedShip = 0, kEnoughShips = 1, kDoNothing = 2 };
FleetStatus enough_ships = FleetStatus:
etc...
Note that I also renamed fleetStatus to FleetStatus, because it's an Object class.
TiborB (tiborb95) wrote : | # |
Oh, I believed uint8_t is the type. Then 'uint8_t' is not needed at all, is it?
GunChleoc (gunchleoc) wrote : | # |
Correct, your type is the enum class name now.
If you don't give the enum class itself an explicit type, it will default to int, which would be fine in this case. You also don't need to assign numbers to the members, this is entirely optional and should only be done when you explicitly depend on a member having a specific value. So, get:
enum class FleetStatus {
kNeedShip,
kEnoughShips,
kDoNothing
};
which will do the job nicely :)
TiborB (tiborb95) wrote : | # |
few words to rev. 7245
I think the crashes of seafaring were due to the command queue, especially when it grows big and it takes too long for a command to be processed. AI does not know whether there is a relevant command in the queue and issues another one based on actual status of the ship. When the first command is executed, the status might have changed and second command might crash the game. So I want to add following check:
http://
I thinks (based on testing) it does the job, but I would like to limit the test only to computer player - I just dont know how to find out if the current player is an AI player.
I think these tests might interfere with GUI control of ships.
SirVer (sirver) wrote : | # |
I think these tests should be done for all players - what you describe can happen for a human player too if he presses the same button twice while the command is still distributed to other players in the game. Can easily happen in slow network games. Your code saveguards against that and should not disturb in any other way.
You can doublecheck by running the regression_test.py - it exercises the UI for ships too.
TiborB (tiborb95) wrote : | # |
"I think these tests should be done for all players" - I am not 100% sure, but this does not bother me now.
I had another crash today - again on the same place in code.
It seems to me that unders some circumstance there can be portdock with no fleet associated to it. I am not sure if the fault is in my changes - might be. But I am going to learn all that object oriented mess related to ports, portdocks and fleets. It will take time...
TiborB (tiborb95) wrote : | # |
short update:)
I just want to inform you that I am hunting that mysterious bug - so many hours of tests and no crash. I added some printfs for case it crashes, but nothing. I dont know, but I am not sure it is gone...
But I would say this bug was not introduced with my changes.
Also I added some statistics on command line - this is intended for map creators when testing performance with AI players for what resources are missing or where are the botlenecks, the output looks like:
3: Buildings: Pr: 39, Ml: 9, Mi: 4, Wh: 1, Po: 0. Missing: stone, corn, bread, meat,
2: Buildings: Pr: 76, Ml: 18, Mi: 6, Wh: 1, Po: 0. Missing: stone, wheat, bread, meat,
5: Buildings: Pr: 72, Ml: 14, Mi: 5, Wh: 2, Po: 1. Missing: coal, log, marble, stone, wheat, grape, bread, meat,
4: Buildings: Pr: 63, Ml: 19, Mi: 6, Wh: 2, Po: 1. Missing: marble, stone, wheat, grape, bread, meat,
7: Buildings: Pr: 68, Ml: 16, Mi: 7, Wh: 1, Po: 0. Missing: coal, marble, stone,
6: Buildings: Pr: 69, Ml: 16, Mi: 6, Wh: 2, Po: 0. Missing: coal, log, wheat, meat,
now src/ai/defaultai.cc must be edited (there is a variable) to have it printed out (by every 60 minutes now), but if you find it usefull, I can imagine command line switch like '--stats'. By default it will be disabled, of course.
TiborB (tiborb95) wrote : | # |
So it seems I am done. I failed to invoke the bug again and I think I am just wasting my time.
I removed printfs and nocoms, so it is again ready for your review.
TiborB (tiborb95) wrote : | # |
So I pushed another small tweak - now it counts mine fields of the same material type in vicinity - when positioning new mines. In other words it pushes mines in the middle of area of the same material. Before, location was more random and mine could be build on the edge of area (it might have been and edge of mountains or so).
I hope no one is just doing the review now...
GunChleoc (gunchleoc) wrote : | # |
I added a NOCOM - there is also a second NOCOM that I don't have an answer for.
I also did some testing, and got no crashes.
- On the Fellowships map, 1/3 AIs managed to start building a shipyard and port about 30 minutes into the game. The shipyard was correctly stopped until the port got finished, but then never restarted. The other 2 AIs built something else on the port space.
- On the Swamp Island map, 2/3 AIs managed to start building a Port around 30 minutes into the game. On of the ports got overrun by the friendly neighbour, the other AI started building ships in a lake. Maybe we can add a check in the future if the port is reachable from the shipyard's body of water.
- Long, long way: I spotted the first Port construction site around 18 minutes into the game. Ships were successfully built, but there was no gold for the expeditions.
- Twin Lagoons: First port got built around 1:30 into the game. Both started an expedition. One of the AIs was successful. The second AI failed to build a second port, because there were some port spaces that both AIs kept rejecting.
I think we should merge this soon and deal with any remaining issues in new, smaller branches.
TiborB (tiborb95) wrote : | # |
so to your comments:
The main problem is that seafaring is based on one-water assumption. There is no scanning
and analysis of map to identify individual distinct waters and making decision which one is worth seafaring. This would have to include cheating probably.
I have couple of maps (mine usually) that are one-water-only and used these for testing.
Ai builds strictly one shipyard only.
If there are 2-3 portspaces available for AI nearby, it will succed to use at least one for port.
This can be improved but there are limitations - like AI not in control of layout of roads and so on.
As unfinished ship decays the building of ship starts when shipyard is fully stocked - you mentioned this problem probably.
If AI finds a portspace it scans the vicinity in some radius - if it runs into other player - the spot is ignored - this was probably what you encountered. The idea behind is that expedition would not be succesfull anyway.
But this can be finetuned as well.
The result of this is that last expedition lasts forever. This is inefficiency, because a stock on ship and ship itself could be used for transport. But easy to fix/modify.
I used my maps spider lake, cristobals sea and soutfall islands for testing - I recommend to use one of them to see seafaring at its bests :)
GunChleoc (gunchleoc) wrote : | # |
My testing was done to see if anything crashes, and to check if behaviour is OK. I think it is OK for now - I just decided to give detailed feedback so we know what can be improved further.
Once the 2 remaining NOCOMS are fixed, I think this branch is ready to be merged :)
TiborB (tiborb95) wrote : | # |
@GunChleoc
first NOCOM is perhaps a mistake??
and second one was discussed with SirVer and I think this is no problem and I might remove it.
But I will wait for another review and comments
SirVer (sirver) wrote : | # |
I'll prioritize looking over this tomorrow. There are some changes outside of ai/ that I'd like to think through before they hit trunk.
TiborB (tiborb95) wrote : | # |
of course
I tested only AI-only games, because I dont have enough time for a real game...
SirVer (sirver) wrote : | # |
I did a codereview now and I identified one bug in the AI: it will not work properly after loading. See code review comments. I think this is not ideal, but not critical. After the NOCOMs are removed this should be merged and a new branch should deal with the design issue. Maybe a new bug can be opened to track the problem.
All the changes outside of ai/ lgtm.
TiborB (tiborb95) wrote : | # |
@SirVer
So I did some simple tests and it is BAD. List of ships is empty after load. So AI will not manage them at all.
But his is analogous to productionsites, militarysites vectors when loading game - so I tested militarysites and these are properly initialized = AI knows about all of them after load. (I tested both single player and mulitiplayer game).
Though, when I removed 'if (player_ == nullptr)' test for NoteImmovable it crashed for netwok game and survived for single player game.
But is seems that there is some mechanism that take care of NoteImmovable messages and is not in effect for NoteShipMessages.
What do you think?
SirVer (sirver) wrote : | # |
> But is seems that there is some mechanism that take care of NoteImmovable messages and is not in effect for NoteShipMessages.
I do not believe that is correct - if it where late_initialization would not be needed. Loading would then just be a very fast way of gaining tons of map objects. I therefore believe that the original designer of the AI code decided to build late_initializa
TiborB (tiborb95) wrote : | # |
I found that when a ship is loaded from savefile no note is sent at all. It is send only when ship is initiated.
with playerimmovable it is called from other place. So I will try to find better place to sent a note about new ship from...
TiborB (tiborb95) wrote : | # |
HEY,
after some painfull debugging I found that on load restored immovables are not sending notes to players, but instead a player is scanning for them as a part of late_initializa
So I am working for similar code to scan for ships and will push it to the branch once it is tested and polished ...
TiborB (tiborb95) wrote : | # |
I fixed most of comments.
And I realized that save/load is quite hurting to AI. A lot of internal info of AI is just lost. Some of them will be restored, but .... I need to do some deeper analysis. But not in this branch.
Would it be possible to save&load some data? Not objects, only f.e. unordered sets of integers? It would help much...
SirVer (sirver) wrote : | # |
> Would it be possible to save&load some data? Not objects, only f.e. unordered sets of integers? It would help much...
Of course you could save state, but it won't help: We support right now that two humans start playing a game, later one of them loads it and fills the other slot with an AI. You will still have the case of late initialization then.
TiborB (tiborb95) wrote : | # |
hi just found a small bug that prevents compilation here, I will push a fix tonight. But I can tell you right now if you are just working on it...
I understand... I will try to circumvent it (in next turn), but it is not good...
TiborB (tiborb95) wrote : | # |
rephrasing :) : I will try to mitigate the hurting effect of save/load cycle, but AI has too many internal variables/data structures...
GunChleoc (gunchleoc) wrote : | # |
I have moved the ScoutingDirection enum into ship.h and fixed up the code so it won't get used as boolean.
Everything else LGTM.
SirVer (sirver) wrote : | # |
ship it! (see what I did there :))
GunChleoc (gunchleoc) wrote : | # |
Houston, we have e problem: test_rip_
TiborB (tiborb95) wrote : | # |
Hi, does it makes a sense - to have a worker in a portdock? workers and wares are transfered port <-> ship, portdock is not involved I think....
TiborB (tiborb95) wrote : | # |
So I looked at your tests - I liked them much, two comments about the issue:
1.) I think this is not AI related - at all
2.) I would say the lua is not correct, and I am not sure what is to be tested. When you extend the sleeps - you can see how the ship picks a worker from port1 and if port2 is destroyed before ships arrives, the worker is returned to port1. But I am not sure if this is what is to be tested.
TiborB (tiborb95) wrote : | # |
I thought these are brand new tests you just created, nevermind... :)
BUT - I compiled trunk, run the tests and they failed on the very test. So no regression here...
SirVer (sirver) wrote : | # |
> BUT - I compiled trunk, run the tests and they failed on the very test. So no regression here...
not what I see - the test passes for me on trunk and hangs forever in seafaring-ai. It could be something in the new logic of re-adding the portdock to a warehouse.
GunChleoc (gunchleoc) wrote : | # |
I see the same as SirVer - tests work in trunk.
TiborB (tiborb95) wrote : | # |
with me it does not hang, just prints out FAIL for that test.
I dont have a good network here and have only some older trunk (half year old about) on harddisk, but tested this trunk and current seafaring and both have similar behaviour, this is my testing session:
$python2.7 ./regression_
Using './widelands' binary.
test/maps/
Running Widelands ... FAIL
=======
FAIL: test/maps/
-------
Traceback (most recent call last):
File "./regression_
self.
File "./regression_
"Widelands exited abnormally. %s" % common_msg
AssertionError: Widelands exited abnormally. Analyze the files in /tmp/widelands_
/tmp/
-------
Ran 1 test in 21.740s
FAILED (failures=1)
Content of log file:
$cat /tmp/widelands_
[.....]
MO(5,portdock): set_need_
MO(5,portdock): ... trigger fleet update
# All Tests passed. <= NOTE HERE
WareList: 1 items of 8 left.
widelands: /usr/include/
I will double check with actual trunk tonight
TiborB (tiborb95) wrote : | # |
so I checked on actual trunk - the same problem....
?
SirVer (sirver) wrote : | # |
Maybe try updating boost? Seems like a boost bug in what you see there.
GunChleoc (gunchleoc) wrote : | # |
I just grabbed myself a fresh trunk and the tests still go through there.
Is there a way to run test_rip_
./widelands --scenario=
SirVer (sirver) wrote : | # |
./regression_
-r searches for the given regular expression in the test name (combination of map + lua file).
TiborB (tiborb95) wrote : | # |
the file is test_rip_
After some investigation I found that folowing command will run just particular script:
./widelands --verbose=true --datadir=. --disable_fx=true --disable_
and based on this I was able to get backtrace from gdb, here it is:
Program received signal SIGABRT, Aborted.
0xb7fdcbcc in __kernel_vsyscall ()
(gdb) bt
#0 0xb7fdcbcc in __kernel_vsyscall ()
#1 0xb7805f47 in raise () from /usr/lib/
#2 0xb7807589 in abort () from /usr/lib/
#3 0xb77ff027 in __assert_fail_base () from /usr/lib/
#4 0xb77ff0ab in __assert_fail () from /usr/lib/
#5 0x0877261f in boost::
#6 0x08775874 in boost::
at /usr/include/
#7 0x08888105 in boost::
this=0xa9b3d08) at /usr/include/
#8 0x08886f63 in boost::
at /usr/include/
#9 0x088c9690 in Widelands:
#10 0x08adc7df in Widelands:
#11 0x088cdcdd in Widelands:
#12 0x088cf2cc in Widelands:
#13 0x0880ba6e in Widelands:
#14 0x0880be0c in Widelands:
#15 0x088cdef2 in Widelands:
#16 0x08841ee2 in Widelands:
#17 0x08841514 in Widelands:
#18 0x08772be8 in Widelands:
#19 0x0882a7a7 in Widelands:
script_
This was trunk...
TiborB (tiborb95) wrote : | # |
boost version: 1.57.0 (installed, it is arch linux, and also widelands reports this version when compiling)
Tino (tino79) wrote : | # |
I am not sure if this is related:
https:/
When i was updating my build environment a few days ago i had to apply this patch to boost 1.5.7 to make it work with widelands.
TiborB (tiborb95) wrote : | # |
Tino,
the patch seems to be aplied...
I can do one test though, insert some printf into warelist.cc and run a test game to see whether that piece of code is used during running of widelands at all.
GunChleoc (gunchleoc) wrote : | # |
I'm still on Boost 1.54
GunChleoc (gunchleoc) wrote : | # |
Test won't run at all with ./regression_
Trying to run: map:scripting/
Trying to run: test/maps/
MO(1,ship): ship_update: No destination anymore.
Forcing flag at (16, 17)
Message: adding (wh) (warehouse) 1
MO(4,port): Setting up port dock fields
MO(4,port): Found 1 fields for the dock
Message: adding (wh) (warehouse) 1
Forcing flag at (13, 17)
Forcing flag at (16, 17)
Clearing for road at (14, 17)
Clearing for road at (15, 17)
Forcing flag at (16, 3)
Message: adding (wh) (warehouse) 1
MO(12,port): Setting up port dock fields
MO(12,port): Found 1 fields for the dock
Forcing flag at (18, 5)
Forcing flag at (18, 5)
Forcing flag at (16, 5)
Clearing for road at (17, 5)
Forcing flag at (16, 5)
Forcing flag at (16, 3)
Clearing for road at (16, 4)
MO(6,fleet): Fleet::act
MO(5,portdock): set_need_ship(true)
MO(5,portdock): ... trigger fleet update
MO(5,portdock): set_need_ship(true)
MO(6,fleet): Fleet::act
MO(6,fleet): Port 5 needs ship
MO(6,fleet): ... ship 1 takes care of it
MO(1,ship): set_destination to 5 (currently 0 items)
MO(1,ship): ship_update: Go to dock 5
Message: removing warehouse (player 1)
MO(23,carrier): init_auto_task: become fugitive
MO(23,carrier): [fugitive]: found a flag connected to warehouse(s)
MO(23,carrier): [fugitive]: try to move to flag
Script requests save to: port1_just_removed
Autosave: save requested : port1_just_removed
Game: Writing Preload Data ... took 7ms
Game: Writing Game Class Data ... took 0ms
Game: Writing Player Info ... took 1ms
Game: Writing Map Data!
Writing Elemental Data ... took 0ms
Writing Player Names And Tribe Data ... took 0ms
Writing Port Spaces Data ... took 1ms
Writing Heights Data ... took 0ms
<snip>
Writing Players Unseen Data ... took 4ms
Writing Scripting Data ... took 8ms
Writing Objective Data ... took 0ms
MapSaver::save() took 36ms
Game: Writing Map Data took 36ms
Game: Writing Player Economies Info ... took 1ms
Game: Writing Command Queue Data ... took 8ms
Game: Writing Interactive Player Data ... took 0ms
GameSaver::save() took 53ms
SaveHandler:
Autosave: save took 62 ms
MO(23,carrier): [gowarehouse]: Got transfer
MO(23,carrier): [transfer]: starting task [movepath] and setting location to road 9
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
MO(1,ship): ship_update: No destination anymore.
Looks like the carrier never reaches the warehouse in line 26 of the lua script, so the port isn't built again.
TiborB (tiborb95) wrote : | # |
Probably boost is involved to some degree, but I started tracking down what is triggering the crash.
It seem this situation is it: when a builder is in shipping state, left on a ship with no ports remaining and following function http://
It need further investigation and also I am not very glad to have to tinker with these parts of code.
TiborB (tiborb95) wrote : | # |
GunChleoc - I test mainly the script *SECOND* not FIRST port with portdock....
But ....FIRST... behaves as you described.
You are still sure that trunk behaves differently? Both compiled with the same environment...
GunChleoc (gunchleoc) wrote : | # |
Yes, trunk runs all the tests successfully (I tested twice and did a fresh compile on a fresh branch and tested again and it's OK, and it works for SirVer as well), so the boost problem you're having must be a different problem. Your test explicitly fails, our test just hangs.
TiborB (tiborb95) wrote : | # |
I spent quite a lot of time on this today, both issues points to workers/carriers. This part of code I am not familiar at all... but I will keep looking at it.
SirVer (sirver) wrote : | # |
Have you tried (temporarily)
> Am 09.02.2015 um 15:57 schrieb TiborB <email address hidden>:
>
> I spent quite a lot of time on this today, both issues points to workers/carriers. This part of code I am not familiar at all... but I will keep looking at it.
> --
> https:/
> You are reviewing the proposed merge of lp:~widelands-dev/widelands/seafaring-ai into lp:widelands.
TiborB (tiborb95) wrote : | # |
I will look at this as well of course, but generally I would like to learn and understand how things work there.
Also boost is a complication here, because it is a black box and I am not able to undestand a backtrace from it...
TiborB (tiborb95) wrote : | # |
OK, I moved forward. I reworked the restoration of portdock, returned a bit to initial design and now test_rip_
Generally it is not a showstopper as in a normal game this is stable (talking about linux).
I will push the changes tonight or tomorrow for your review.
TiborB (tiborb95) wrote : | # |
you can run regression tests again. I will look at this one failing test. Also I am testing the game with AI-only players, I will let you know if everything looks alright.
GunChleoc (gunchleoc) wrote : | # |
All tests now run without problems on my machine, so IMO this branch is good to go now :)
Since the test that's still failing does so only on your machine and for trunk as well, I expect that there's something about your setup that Widelands doesn't like.
TiborB (tiborb95) wrote : | # |
I just completed tests on my debian PC - all passed - I just dont know....
SirVer (sirver) wrote : | # |
Time to merge then.
The one issue that concerns me still in this branch is that there is no new test for a warehouse that needs to recreate it's port dock. I feel this corner case can easily regress without a test. But since this branch has been sitting for so long, I think it should be merged now. Maybe you can add a test in a later branch, tibor?
TiborB (tiborb95) wrote : | # |
SirVer - I added such test: test_rip_
It is bit lost among other tests, because they all uses the word "portdock" when in fact they mean "port"
Though situation when portdock is lost but cannot be restored and port itself has to be destroyed is not tested in any test. I was not sure how to do it - if there was lua interface to remove ownership from field(s) where portdock is - this could be used... or place an enemy military building that would take ownership from fields where portdock is located (and not affect the port building itself) .... so indeed such test would be usefull.
GunChleoc (gunchleoc) wrote : | # |
Bug report, so we won't forget: https:/
SirVer (sirver) wrote : | # |
> SirVer - I added such test: test_rip_
My apologize. I did not remember/see it in the branch. I think this is great then.
The other test case you describe is indeed harder to do. You would need to construct a map with military buildings of another player ordered in such a way that as soon as they are all manned, the port has no more space for its dock.
GunChleoc (gunchleoc) wrote : | # |
Thanks, Tibor. I will take care of the merging :)
Preview Diff
1 | === modified file 'src/ai/ai_help_structs.h' |
2 | --- src/ai/ai_help_structs.h 2014-10-30 20:24:57 +0000 |
3 | +++ src/ai/ai_help_structs.h 2015-02-10 21:28:25 +0000 |
4 | @@ -21,6 +21,7 @@ |
5 | #define WL_AI_AI_HELP_STRUCTS_H |
6 | |
7 | #include <list> |
8 | +#include <unordered_set> |
9 | |
10 | #include "economy/flag.h" |
11 | #include "economy/road.h" |
12 | @@ -30,6 +31,7 @@ |
13 | #include "logic/instances.h" |
14 | #include "logic/map.h" |
15 | #include "logic/player.h" |
16 | +#include "logic/ship.h" |
17 | #include "logic/world/terrain_description.h" |
18 | #include "logic/world/world.h" |
19 | |
20 | @@ -122,6 +124,22 @@ |
21 | } |
22 | }; |
23 | |
24 | +// Looking only for mines-capable fields nearby |
25 | +// of specific type |
26 | +struct FindNodeMineable { |
27 | + bool accept(const Map&, const FCoords& fc) const { |
28 | + |
29 | + return (fc.field->nodecaps() & BUILDCAPS_MINE) && |
30 | + (fc.field->get_resources() == res); |
31 | + } |
32 | + |
33 | + Game& game; |
34 | + int32_t res; |
35 | + |
36 | + FindNodeMineable(Game& g, int32_t r) : game(g), res(r) { |
37 | + } |
38 | +}; |
39 | + |
40 | // Fishers and fishbreeders must be built near water |
41 | struct FindNodeWater { |
42 | FindNodeWater(const World& world) : world_(world) { |
43 | @@ -227,10 +245,9 @@ |
44 | int16_t military_stationed_; |
45 | // stationed (manned) military buildings nearby |
46 | int16_t military_unstationed_; |
47 | - // some buildings must be postponed bit |
48 | - int32_t prohibited_till_; |
49 | - // and then some must be forced |
50 | - int32_t forced_after_; |
51 | + bool is_portspace_; |
52 | + bool port_nearby_; // to increase priority if a port is nearby, |
53 | + // especially for new colonies |
54 | |
55 | std::vector<uint8_t> consumers_nearby_; |
56 | std::vector<uint8_t> producers_nearby_; |
57 | @@ -261,7 +278,9 @@ |
58 | military_loneliness_(1000), |
59 | military_in_constr_nearby_(0), |
60 | military_presence_(0), |
61 | - military_stationed_(0) { |
62 | + military_stationed_(0), |
63 | + is_portspace_(false), |
64 | + port_nearby_(false) { |
65 | } |
66 | }; |
67 | |
68 | @@ -273,9 +292,12 @@ |
69 | bool preferred_; |
70 | |
71 | int32_t mines_nearby_; |
72 | + //this is to provide that a mine is not built on the edge of mine area |
73 | + int32_t same_mine_fields_nearby_; |
74 | |
75 | MineableField(const Widelands::FCoords& fc) |
76 | - : coords(fc), next_update_due_(0), preferred_(false), mines_nearby_(0) { |
77 | + : coords(fc), next_update_due_(0), preferred_(false), mines_nearby_(0), |
78 | + same_mine_fields_nearby_(0) { |
79 | } |
80 | }; |
81 | |
82 | @@ -310,12 +332,14 @@ |
83 | bool plants_trees_; |
84 | bool recruitment_; // is "producing" workers? |
85 | bool is_buildable_; |
86 | - bool need_trees_; // lumberjack = true |
87 | - bool need_stones_; // quarry = true |
88 | - bool mines_water_; // wells |
89 | - bool need_water_; // fisher, fish_breeder = true |
90 | - bool is_hunter_; // need to identify hunters |
91 | - bool is_fisher_; // need to identify fishers |
92 | + bool need_trees_; // lumberjack = true |
93 | + bool need_stones_; // quarry = true |
94 | + bool mines_water_; // wells |
95 | + bool need_water_; // fisher, fish_breeder = true |
96 | + bool is_hunter_; // need to identify hunters |
97 | + bool is_fisher_; // need to identify fishers |
98 | + bool is_port_; |
99 | + bool is_shipyard_; |
100 | bool space_consumer_; // farm, vineyard... = true |
101 | bool expansion_type_; // military building used that can be used to control area |
102 | bool fighting_type_; // military building built near enemies |
103 | @@ -327,7 +351,6 @@ |
104 | |
105 | int32_t mines_; // type of resource it mines_ |
106 | uint16_t mines_percent_; // % of res it can mine |
107 | - |
108 | uint32_t current_stats_; |
109 | |
110 | std::vector<int16_t> inputs_; |
111 | @@ -372,6 +395,28 @@ |
112 | bool enemies_nearby_; |
113 | }; |
114 | |
115 | +struct TrainingSiteObserver { |
116 | + Widelands::TrainingSite* site; |
117 | + BuildingObserver* bo; |
118 | +}; |
119 | + |
120 | +struct WarehouseSiteObserver { |
121 | + Widelands::Warehouse* site; |
122 | + BuildingObserver* bo; |
123 | +}; |
124 | + |
125 | +struct ShipObserver { |
126 | + Widelands::Ship* ship; |
127 | + Widelands::Coords expedition_start_point_; |
128 | + std::unordered_set<uint32_t> visited_spots_; |
129 | + |
130 | + // a ship circumvents all islands in the same direction, the value |
131 | + // is assigned only once |
132 | + Widelands::ScoutingDirection island_circ_direction = Widelands::ScoutingDirection::kClockwise; |
133 | + bool waiting_for_command_ = false; |
134 | + int32_t last_command_time = 0; |
135 | +}; |
136 | + |
137 | struct WareObserver { |
138 | uint8_t producers_; |
139 | uint8_t consumers_; |
140 | |
141 | === modified file 'src/ai/defaultai.cc' |
142 | --- src/ai/defaultai.cc 2015-01-27 20:35:26 +0000 |
143 | +++ src/ai/defaultai.cc 2015-02-10 21:28:25 +0000 |
144 | @@ -31,7 +31,9 @@ |
145 | #include "base/macros.h" |
146 | #include "economy/economy.h" |
147 | #include "economy/flag.h" |
148 | +#include "economy/portdock.h" |
149 | #include "economy/road.h" |
150 | +#include "economy/wares_queue.h" |
151 | #include "logic/constructionsite.h" |
152 | #include "logic/findbob.h" |
153 | #include "logic/findimmovable.h" |
154 | @@ -39,7 +41,9 @@ |
155 | #include "logic/map.h" |
156 | #include "logic/militarysite.h" |
157 | #include "logic/player.h" |
158 | +#include "logic/playercommand.h" |
159 | #include "logic/productionsite.h" |
160 | +#include "logic/ship.h" |
161 | #include "logic/trainingsite.h" |
162 | #include "logic/tribe.h" |
163 | #include "logic/warehouse.h" |
164 | @@ -59,6 +63,13 @@ |
165 | // building of the same building can be started after 25s at earliest |
166 | constexpr int kBuildingMinInterval = 25 * 1000; |
167 | constexpr int kMinBFCheckInterval = 5 * 1000; |
168 | +constexpr int kShipCheckInterval = 5 * 1000; |
169 | +constexpr int kMarineDecisionInterval = 20 * 1000; |
170 | +constexpr int kTrainingSitesCheckInterval = 30 * 1000; |
171 | + |
172 | +//this is intended for map developers, by default should be off |
173 | +constexpr bool kPrintStats = false; |
174 | + |
175 | // Some buildings have to be built close to borders and their |
176 | // priority might be decreased below 0, so this is to |
177 | // compensate |
178 | @@ -81,6 +92,7 @@ |
179 | num_constructionsites_(0), |
180 | num_milit_constructionsites(0), |
181 | num_prod_constructionsites(0), |
182 | + num_ports(0), |
183 | next_road_due_(2000), |
184 | next_stats_update_due_(30000), |
185 | next_construction_due_(1000), |
186 | @@ -88,9 +100,13 @@ |
187 | next_productionsite_check_due_(0), |
188 | next_mine_check_due_(0), |
189 | next_militarysite_check_due_(0), |
190 | + next_ship_check_due(5 * 60 * 1000), |
191 | + next_marine_decisions_due(5 * 60 * 1000), |
192 | next_attack_consideration_due_(300000), |
193 | - next_helpersites_check_due_(180000), |
194 | + next_trainingsites_check_due_(15 * 60 * 1000), |
195 | next_bf_check_due_(1000), |
196 | + next_wares_review_due_(15 * 60 * 1000), |
197 | + next_statistics_report_(30 * 60 * 1000), |
198 | inhibit_road_building_(0), |
199 | time_of_last_construction_(0), |
200 | enemy_last_seen_(-2 * 60 * 1000), |
201 | @@ -107,6 +123,8 @@ |
202 | last_attack_target_( |
203 | std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max()), |
204 | next_attack_waittime_(10), |
205 | + seafaring_economy(false), |
206 | + colony_scan_area_(35), |
207 | spots_(0) { |
208 | |
209 | // Subscribe to NoteFieldPossession. |
210 | @@ -146,6 +164,48 @@ |
211 | out_of_resources_site(*note.ps); |
212 | |
213 | }); |
214 | + |
215 | + // Subscribe to ShipNotes. |
216 | + shipnotes_subscriber_ = |
217 | + Notifications::subscribe<NoteShipMessage>([this](const NoteShipMessage& note) { |
218 | + |
219 | + // in a short time between start and late_initialization the player |
220 | + // can get notes that can not be processed. |
221 | + // It seems that this causes no problem, at least no substantial |
222 | + if (player_ == nullptr) { |
223 | + return; |
224 | + } |
225 | + if (note.ship->get_owner()->player_number() != player_->player_number()) { |
226 | + return; |
227 | + } |
228 | + |
229 | + switch (note.message) { |
230 | + |
231 | + case NoteShipMessage::Message::kGained: |
232 | + gain_ship(*note.ship, NewShip::kBuilt); |
233 | + break; |
234 | + |
235 | + case NoteShipMessage::Message::kLost: |
236 | + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { |
237 | + if (i->ship == note.ship) { |
238 | + allships.erase(i); |
239 | + break; |
240 | + } |
241 | + } |
242 | + break; |
243 | + |
244 | + case NoteShipMessage::Message::kWaitingForCommand: |
245 | + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { |
246 | + if (i->ship == note.ship) { |
247 | + i->waiting_for_command_ = true; |
248 | + break; |
249 | + } |
250 | + } |
251 | + break; |
252 | + default: |
253 | + ; |
254 | + } |
255 | + }); |
256 | } |
257 | |
258 | DefaultAI::~DefaultAI() { |
259 | @@ -241,6 +301,14 @@ |
260 | return; |
261 | } |
262 | |
263 | + if (check_ships(gametime)) { |
264 | + return; |
265 | + } |
266 | + |
267 | + if (marine_main_decisions(gametime)) { |
268 | + return; |
269 | + } |
270 | + |
271 | // Check the mines and consider upgrading or destroying one |
272 | if (check_mines_(gametime)) { |
273 | return; |
274 | @@ -252,6 +320,10 @@ |
275 | return; |
276 | } |
277 | |
278 | + if (check_trainingsites(gametime)) { |
279 | + return; |
280 | + } |
281 | + |
282 | // improve existing roads! |
283 | // main part of this improvment is creation 'shortcut roads' |
284 | // this includes also connection of new buildings |
285 | @@ -260,6 +332,18 @@ |
286 | m_mineable_changed = true; |
287 | return; |
288 | } |
289 | + |
290 | + // once in 15 minutes we increase(or decrease) targets for wares |
291 | + if (next_wares_review_due_ <= gametime) { |
292 | + next_wares_review_due_ = gametime + 15 * 60 * 1000; |
293 | + review_wares_targets(gametime); |
294 | + } |
295 | + |
296 | + //print statistics |
297 | + if (kPrintStats && next_statistics_report_ <= gametime) { |
298 | + print_stats(); |
299 | + next_statistics_report_ += 60 * 60 * 1000; |
300 | + } |
301 | } |
302 | |
303 | /** |
304 | @@ -320,6 +404,7 @@ |
305 | bo.mountain_conqueror_ = bh.is_mountain_conqueror(); |
306 | bo.prohibited_till_ = bh.get_prohibited_till() * 1000; // value in conf is in seconds |
307 | bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds |
308 | + bo.is_port_ = bld.get_isport(); |
309 | if (bh.renews_map_resource()) { |
310 | bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource()); |
311 | } |
312 | @@ -333,8 +418,7 @@ |
313 | |
314 | // Read all interesting data from ware producing buildings |
315 | if (bld.type() == MapObjectType::PRODUCTIONSITE) { |
316 | - const ProductionSiteDescr& prod = |
317 | - dynamic_cast<const ProductionSiteDescr&>(bld); |
318 | + const ProductionSiteDescr& prod = dynamic_cast<const ProductionSiteDescr&>(bld); |
319 | bo.type = bld.get_ismine() ? BuildingObserver::MINE : BuildingObserver::PRODUCTIONSITE; |
320 | for (const WareAmount& temp_input : prod.inputs()) { |
321 | bo.inputs_.push_back(temp_input.first); |
322 | @@ -345,7 +429,7 @@ |
323 | |
324 | if (bo.type == BuildingObserver::MINE) { |
325 | // get the resource needed by the mine |
326 | - if (bh.has_mines()) { |
327 | + if (bh.get_mines()) { |
328 | bo.mines_ = world.get_resource(bh.get_mines()); |
329 | } |
330 | |
331 | @@ -365,6 +449,13 @@ |
332 | } else { |
333 | bo.is_fisher_ = false; |
334 | } |
335 | + |
336 | + if (building_name == "shipyard") { |
337 | + bo.is_shipyard_ = true; |
338 | + } else { |
339 | + bo.is_shipyard_ = false; |
340 | + } |
341 | + |
342 | continue; |
343 | } |
344 | |
345 | @@ -373,8 +464,7 @@ |
346 | // non critical are excluded (see below) |
347 | if (bld.type() == MapObjectType::MILITARYSITE) { |
348 | bo.type = BuildingObserver::MILITARYSITE; |
349 | - const MilitarySiteDescr& milit = |
350 | - dynamic_cast<const MilitarySiteDescr&>(bld); |
351 | + const MilitarySiteDescr& milit = dynamic_cast<const MilitarySiteDescr&>(bld); |
352 | for (const std::pair<unsigned char, unsigned char>& temp_buildcosts : milit.buildcost()) { |
353 | // bellow are non-critical wares |
354 | if (tribe_->ware_index("log") == temp_buildcosts.first || |
355 | @@ -397,6 +487,10 @@ |
356 | |
357 | if (bld.type() == MapObjectType::TRAININGSITE) { |
358 | bo.type = BuildingObserver::TRAININGSITE; |
359 | + const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld); |
360 | + for (const WareAmount& temp_input : train.inputs()) { |
361 | + bo.inputs_.push_back(temp_input.first); |
362 | + } |
363 | continue; |
364 | } |
365 | |
366 | @@ -419,10 +513,32 @@ |
367 | resource_necessity_water_needed_ = true; |
368 | } |
369 | |
370 | - // Add all fields that we own |
371 | Map& map = game().map(); |
372 | + |
373 | + //here we scan entire map for own ships |
374 | + std::set<OPtr<Ship>> found_ships; |
375 | + for (int16_t y = 0; y < map.get_height(); ++y) { |
376 | + for (int16_t x = 0; x < map.get_width(); ++x) { |
377 | + FCoords f = map.get_fcoords(Coords(x, y)); |
378 | + //there are too many bobs on the map so we investigate |
379 | + //only bobs on water |
380 | + if (f.field->nodecaps() & MOVECAPS_SWIM) { |
381 | + for (Bob * bob = f.field->get_first_bob(); |
382 | + bob; |
383 | + bob = bob->get_next_on_field()) { |
384 | + if (upcast(Ship, ship, bob)) { |
385 | + if (ship->get_owner() == player_ && !found_ships.count(ship)) { |
386 | + found_ships.insert(ship); |
387 | + gain_ship(*ship, NewShip::kFoundOnLoad); |
388 | + } |
389 | + } |
390 | + } |
391 | + } |
392 | + } |
393 | + } |
394 | + |
395 | + //here we scan entire map for owned unused fields and own buildings |
396 | std::set<OPtr<PlayerImmovable>> found_immovables; |
397 | - |
398 | for (int16_t y = 0; y < map.get_height(); ++y) { |
399 | for (int16_t x = 0; x < map.get_width(); ++x) { |
400 | FCoords f = map.get_fcoords(Coords(x, y)); |
401 | @@ -434,7 +550,6 @@ |
402 | unusable_fields.push_back(f); |
403 | |
404 | if (upcast(PlayerImmovable, imm, f.field->get_immovable())) { |
405 | - |
406 | // Guard by a set - immovables might be on several nodes at once. |
407 | if (&imm->owner() == player_ && !found_immovables.count(imm)) { |
408 | found_immovables.insert(imm); |
409 | @@ -596,6 +711,34 @@ |
410 | } |
411 | } |
412 | |
413 | + // if curent port is a portspace we need add near fields to a list |
414 | + // of fields prohibited for buildings |
415 | + if (!field.is_portspace_) { // if we know it, no need to do it once more |
416 | + if (player_->get_buildcaps(field.coords) & BUILDCAPS_PORT) { |
417 | + field.is_portspace_ = true; |
418 | + seafaring_economy = true; |
419 | + // blocking fields in vicinity |
420 | + MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(field.coords), 3)); |
421 | + do { |
422 | + const int32_t hash = coords_hash(map.get_fcoords(*(mr.location().field))); |
423 | + if (port_reserved_coords.count(hash) == 0) |
424 | + port_reserved_coords.insert(hash); |
425 | + } while (mr.advance(map)); |
426 | + } |
427 | + } |
428 | + |
429 | + // testing if a port is nearby, such field will get a priority boost |
430 | + uint16_t nearest_distance = std::numeric_limits<uint16_t>::max(); |
431 | + for (const WarehouseSiteObserver& wh_obs : warehousesites) { |
432 | + const uint16_t actual_distance = map.calc_distance(field.coords, wh_obs.site->get_position()); |
433 | + nearest_distance = std::min(nearest_distance, actual_distance); |
434 | + } |
435 | + if (nearest_distance < 15) { |
436 | + field.port_nearby_ = true; |
437 | + } else { |
438 | + field.port_nearby_ = false; |
439 | + } |
440 | + |
441 | // collect information about resources in the area |
442 | std::vector<ImmovableFound> immovables; |
443 | // Search in a radius of range |
444 | @@ -707,7 +850,6 @@ |
445 | field.stones_nearby_ = 0; |
446 | |
447 | for (uint32_t j = 0; j < immovables.size(); ++j) { |
448 | - // const BaseImmovable & base_immovable = *immovables.at(i).object; |
449 | if (immovables.at(j).object->has_attribute(stone_attr)) { |
450 | ++field.stones_nearby_; |
451 | } |
452 | @@ -819,6 +961,13 @@ |
453 | } |
454 | } |
455 | } |
456 | + |
457 | + // 0 is default, and thus indicates that counting must be done |
458 | + if (field.same_mine_fields_nearby_ == 0) { |
459 | + FindNodeMineable find_mines_spots_nearby(game(), field.coords.field->get_resources()); |
460 | + field.same_mine_fields_nearby_ = |
461 | + map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_mines_spots_nearby); |
462 | + } |
463 | } |
464 | |
465 | /// Updates the production and MINE sites statistics needed for construction decision. |
466 | @@ -909,14 +1058,14 @@ |
467 | // - there is an enemy |
468 | // Currently more military buildings are built then needed |
469 | // and "optimalization" (dismantling not needed buildings) is done afterwards |
470 | -bool DefaultAI::construct_building(int32_t gametime) { // (int32_t gametime) |
471 | +bool DefaultAI::construct_building(int32_t gametime) { |
472 | // Just used for easy checking whether a mine or something else was built. |
473 | bool mine = false; |
474 | bool field_blocked = false; |
475 | uint32_t consumers_nearby_count = 0; |
476 | std::vector<int32_t> spots_avail; |
477 | spots_avail.resize(4); |
478 | - // uint16_t const pn = player_number(); |
479 | + Map& map = game().map(); |
480 | |
481 | for (int32_t i = 0; i < 4; ++i) |
482 | spots_avail.at(i) = 0; |
483 | @@ -1022,7 +1171,8 @@ |
484 | } |
485 | |
486 | // testing big military buildings, whether critical construction |
487 | - // material is (not) needed |
488 | + // material is available (at least in amount of |
489 | + // 2/3 of default target amount) |
490 | for (uint32_t j = 0; j < buildings_.size(); ++j) { |
491 | |
492 | BuildingObserver& bo = buildings_.at(j); |
493 | @@ -1040,19 +1190,13 @@ |
494 | |
495 | bo.build_material_shortage_ = false; |
496 | |
497 | - for (EconomyObserver* observer : economies) { |
498 | - // Don't check if the economy has no warehouse. |
499 | - if (observer->economy.warehouses().empty()) { |
500 | - continue; |
501 | - } |
502 | - |
503 | - for (uint32_t m = 0; m < bo.critical_built_mat_.size(); ++m) { |
504 | - WareIndex wt(static_cast<size_t>(bo.critical_built_mat_.at(m))); |
505 | - |
506 | - if (observer->economy.needs_ware(wt)) { |
507 | - bo.build_material_shortage_ = true; |
508 | - continue; |
509 | - } |
510 | + // checking we have enough critical material on stock |
511 | + for (uint32_t m = 0; m < bo.critical_built_mat_.size(); ++m) { |
512 | + WareIndex wt(static_cast<size_t>(bo.critical_built_mat_.at(m))); |
513 | + // using default ware quantity, not the best solution but will do |
514 | + if (get_warehoused_stock(wt) < |
515 | + tribe_->get_ware_descr(wt)->default_target_quantity() * 2 / 3) { |
516 | + bo.build_material_shortage_ = true; |
517 | } |
518 | } |
519 | } |
520 | @@ -1068,10 +1212,6 @@ |
521 | ++i) { |
522 | BuildableField* const bf = *i; |
523 | |
524 | - // if 'buildable field' update is overdue for more then 8 seconds |
525 | - // (= bf has not been updated for about 15 seconds) |
526 | - // skip the bf in evaluation, bacause information |
527 | - // contained in bf are too old |
528 | if (bf->next_update_due_ < gametime - 8000) { |
529 | continue; |
530 | } |
531 | @@ -1111,6 +1251,13 @@ |
532 | continue; |
533 | } |
534 | |
535 | + // testing for reserved ports |
536 | + if (!bo.is_port_) { |
537 | + if (port_reserved_coords.count(coords_hash(bf->coords)) > 0) { |
538 | + continue; |
539 | + } |
540 | + } |
541 | + |
542 | if (time(nullptr) % 3 == 0 && bo.total_count() > 0) { |
543 | continue; |
544 | } // add randomnes and ease AI |
545 | @@ -1408,6 +1555,10 @@ |
546 | } else if (bo.cnt_built_ == 1 && game().get_gametime() > 40 * 60 * 1000 && |
547 | bo.desc->enhancement() != INVALID_INDEX && !mines_.empty()) { |
548 | prio += 10; |
549 | + } else if (bo.is_shipyard_) { |
550 | + if (!seafaring_economy) { |
551 | + continue; |
552 | + } |
553 | } else if (!output_is_needed) { |
554 | continue; |
555 | } else if (bo.cnt_built_ == 0 && game().get_gametime() > 40 * 60 * 1000) { |
556 | @@ -1447,7 +1598,13 @@ |
557 | prio -= bf->space_consumers_nearby_ * 3; |
558 | } |
559 | |
560 | - if (!bo.inputs_.empty()) { |
561 | + else if (bo.is_shipyard_) { |
562 | + // for now AI builds only one shipyard |
563 | + if (bf->water_nearby_ > 3 && bo.total_count() == 0 && seafaring_economy) { |
564 | + prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_; |
565 | + } |
566 | + |
567 | + } else if (!bo.inputs_.empty()) { |
568 | if (bo.total_count() == 0) { |
569 | prio += max_needed_preciousness + kDefaultPrioBoost; |
570 | } |
571 | @@ -1508,10 +1665,10 @@ |
572 | ; // we allow big buildings now |
573 | } else if (bf->unowned_land_nearby_ && |
574 | bo.expansion_type_) { // decreasing probability for big buidlings |
575 | - if (bo.desc->get_size() == 2 && gametime % 15 >= 1) { |
576 | + if (bo.desc->get_size() == 2 && gametime % 2 >= 1) { |
577 | continue; |
578 | } |
579 | - if (bo.desc->get_size() == 3 && gametime % 40 >= 1) { |
580 | + if (bo.desc->get_size() == 3 && gametime % 3 >= 1) { |
581 | continue; |
582 | } |
583 | } |
584 | @@ -1563,9 +1720,19 @@ |
585 | } else if (bo.type == BuildingObserver::WAREHOUSE) { |
586 | |
587 | // exclude spots on border |
588 | - if (bf->near_border_) { |
589 | - continue; |
590 | - } |
591 | + if (bf->near_border_ && !bo.is_port_) { |
592 | + continue; |
593 | + } |
594 | + |
595 | + if (!bf->is_portspace_ && bo.is_port_) { |
596 | + continue; |
597 | + } |
598 | + |
599 | + if (bo.cnt_under_construction_ > 0) { |
600 | + continue; |
601 | + } |
602 | + |
603 | + bool warehouse_needed = false; |
604 | |
605 | // Build one warehouse for ~every 35 productionsites and mines_. |
606 | // Militarysites are slightly important as well, to have a bigger |
607 | @@ -1575,33 +1742,64 @@ |
608 | static_cast<int32_t>(numof_warehouses_) && |
609 | bo.cnt_under_construction_ == 0) { |
610 | prio = 20; |
611 | - } |
612 | - |
613 | - // take care about borders and enemies |
614 | + warehouse_needed = true; |
615 | + } |
616 | + |
617 | + // but generally we prefer ports |
618 | + if (bo.is_port_) { |
619 | + prio += 10; |
620 | + } |
621 | + |
622 | + // special boost for first port |
623 | + if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 && |
624 | + !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) { |
625 | + prio += kDefaultPrioBoost + productionsites.size(); |
626 | + warehouse_needed = true; |
627 | + } |
628 | + |
629 | + if (!warehouse_needed) { |
630 | + continue; |
631 | + } |
632 | + |
633 | + // iterating over current warehouses and testing a distance |
634 | + // getting distance to nearest warehouse and adding it to a score |
635 | + uint16_t nearest_distance = std::numeric_limits<uint16_t>::max(); |
636 | + for (const WarehouseSiteObserver& wh_obs : warehousesites) { |
637 | + const uint16_t actual_distance = |
638 | + map.calc_distance(bf->coords, wh_obs.site->get_position()); |
639 | + nearest_distance = std::min(nearest_distance, actual_distance); |
640 | + } |
641 | + // but limit to 15 |
642 | + const uint16_t max_distance_considered = 15; |
643 | + nearest_distance = std::min(nearest_distance, max_distance_considered); |
644 | + prio += nearest_distance; |
645 | + |
646 | + // take care about and enemies |
647 | if (bf->enemy_nearby_) { |
648 | prio /= 2; |
649 | } |
650 | |
651 | - if (bf->unowned_land_nearby_) { |
652 | + if (bf->unowned_land_nearby_ && !bo.is_port_) { |
653 | prio /= 2; |
654 | } |
655 | |
656 | - // TODO(unknown): introduce check that there is no warehouse nearby |
657 | - // to prevent too close placing |
658 | - |
659 | } else if (bo.type == BuildingObserver::TRAININGSITE) { |
660 | |
661 | + if (virtual_mines < 5) { |
662 | + continue; |
663 | + } |
664 | + |
665 | // exclude spots on border |
666 | if (bf->near_border_) { |
667 | continue; |
668 | } |
669 | |
670 | - if (virtual_mines<3) { |
671 | + if (virtual_mines < 3) { |
672 | continue; |
673 | } |
674 | |
675 | // build after 20 production sites and then after each 50 production site |
676 | - if (static_cast<int32_t>((productionsites.size() + 30) / 50) > bo.total_count() && |
677 | + if (static_cast<int32_t>((productionsites.size() + 40) / 60) > bo.total_count() && |
678 | bo.cnt_under_construction_ == 0) { |
679 | prio = 4 + kDefaultPrioBoost; |
680 | } |
681 | @@ -1624,11 +1822,23 @@ |
682 | continue; |
683 | } |
684 | |
685 | + // testing also vicinity |
686 | + if (!bo.is_port_) { |
687 | + if (port_reserved_coords.count(coords_hash(bf->coords)) > 0) { |
688 | + continue; |
689 | + } |
690 | + } |
691 | + |
692 | // Prefer road side fields |
693 | prio += bf->preferred_ ? 1 : 0; |
694 | // don't waste good land for small huts |
695 | prio -= (maxsize - bo.desc->get_size()) * 5; |
696 | |
697 | + // prefer vicinity of ports (with exemption of warehouses) |
698 | + if (bf->port_nearby_ && bo.type == BuildingObserver::MILITARYSITE) { |
699 | + prio *= 2; |
700 | + } |
701 | + |
702 | if (prio > proposed_priority) { |
703 | best_building = &bo; |
704 | proposed_priority = prio; |
705 | @@ -1708,6 +1918,13 @@ |
706 | continue; |
707 | } |
708 | |
709 | + // prefer mines in the middle of mine fields of the |
710 | + // same type, so we add a small bonus here |
711 | + // depending on count of same mines nearby, |
712 | + // though this does not reflects how many resources |
713 | + // are (left) in nearby mines |
714 | + prio += (*j)->same_mine_fields_nearby_ / 3; |
715 | + |
716 | // Continue if field is blocked at the moment |
717 | bool blocked = false; |
718 | |
719 | @@ -1765,7 +1982,6 @@ |
720 | block_time = 25 * 1000; |
721 | block_area = 6; |
722 | } |
723 | - Map& map = game().map(); |
724 | |
725 | MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(proposed_coords), block_area)); |
726 | do { |
727 | @@ -1971,7 +2187,38 @@ |
728 | // Increasing the failed_connection_tries counter |
729 | // At the same time it indicates a time an economy is without a warehouse |
730 | EconomyObserver* eco = get_economy_observer(flag.economy()); |
731 | + |
732 | + // there are two special situations which have a bit different treatment |
733 | + bool is_remote_port_csite = false; |
734 | + bool stationed_military = false; |
735 | if (flag.get_economy()->warehouses().empty()) { |
736 | + // first very special case - lonesome port (in the phase of constructionsite) |
737 | + // obviously it has no warehouse/road network to connect to |
738 | + if (upcast(ConstructionSite const, constructionsite, flag.get_building())) { |
739 | + BuildingObserver& bo = get_building_observer(constructionsite->building().name().c_str()); |
740 | + if (bo.is_port_ && |
741 | + remote_ports_coords.count(coords_hash(flag.get_building()->get_position())) > 0) { |
742 | + is_remote_port_csite = true; |
743 | + } |
744 | + } |
745 | + |
746 | + // second exemption is when a military buiding was conquered, it |
747 | + // might be just too far from near connected building |
748 | + if (Building* b = flag.get_building()) { |
749 | + if (upcast(MilitarySite, militb, b)) { |
750 | + if (militb->present_soldiers().size() > 0) { |
751 | + stationed_military = true; |
752 | + // also increasing checkradius a bit |
753 | + checkradius += 4; |
754 | + } |
755 | + } |
756 | + } |
757 | + } |
758 | + |
759 | + if (is_remote_port_csite || |
760 | + (stationed_military && game().get_gametime() % 10 > 0)) { // counter disabled |
761 | + ; |
762 | + } else if (flag.get_economy()->warehouses().empty()) { |
763 | eco->failed_connection_tries += 1; |
764 | } else { |
765 | eco->failed_connection_tries = 0; |
766 | @@ -2017,7 +2264,7 @@ |
767 | |
768 | for (const Coords& reachable_coords : reachable) { |
769 | |
770 | - // first make sure there is an immovable (shold be, but still) |
771 | + // first make sure there is an immovable (should be, but still) |
772 | if (upcast(PlayerImmovable const, player_immovable, map[reachable_coords].get_immovable())) { |
773 | |
774 | // if it is the road, make a flag there |
775 | @@ -2037,7 +2284,7 @@ |
776 | } |
777 | |
778 | // now make sure that this field has not been processed yet |
779 | - const int32_t hash = reachable_coords.x << 16 | reachable_coords.y; |
780 | + const uint32_t hash = coords_hash(reachable_coords); |
781 | if (lookuptable.count(hash) == 0) { |
782 | lookuptable.insert(hash); |
783 | |
784 | @@ -2119,14 +2366,12 @@ |
785 | for (std::vector<NearFlag>::iterator nf_walk_it = nearflags_tmp.begin(); |
786 | nf_walk_it != nearflags_tmp.end(); |
787 | ++nf_walk_it) { |
788 | - int32_t const hash_walk = |
789 | - nf_walk_it->flag->get_position().x << 16 | nf_walk_it->flag->get_position().y; |
790 | + uint32_t const hash_walk = coords_hash(nf_walk_it->flag->get_position()); |
791 | if (lookuptable.count(hash_walk) > 0) { |
792 | - // iterting over nearflags |
793 | + // iterating over nearflags |
794 | for (std::vector<NearFlag>::iterator nf_it = nearflags.begin(); nf_it != nearflags.end(); |
795 | ++nf_it) { |
796 | - int32_t const hash = |
797 | - nf_it->flag->get_position().x << 16 | nf_it->flag->get_position().y; |
798 | + uint32_t const hash = coords_hash(nf_it->flag->get_position()); |
799 | if (hash == hash_walk) { |
800 | // decreasing "cost" (of walking via roads) |
801 | if (nf_it->cost_ > nf_walk_it->cost_) { |
802 | @@ -2491,7 +2736,7 @@ |
803 | } |
804 | |
805 | // remaining buildings without inputs and not supporting ones (fishers only left probably and |
806 | - // huters) |
807 | + // hunters) |
808 | |
809 | if (site.bo->inputs_.empty() && site.bo->production_hint_ < 0 && |
810 | site.site->can_start_working() && !site.bo->space_consumer_ && |
811 | @@ -2539,6 +2784,217 @@ |
812 | return changed; |
813 | } |
814 | |
815 | +// This function scans current situation with shipyards, ports, ships, ongoing expeditions |
816 | +// and makes two decisions: |
817 | +// - build a ship |
818 | +// - start preparation for expedition |
819 | +bool DefaultAI::marine_main_decisions(uint32_t const gametime) { |
820 | + if (gametime < next_marine_decisions_due) { |
821 | + return false; |
822 | + } |
823 | + next_marine_decisions_due += kMarineDecisionInterval; |
824 | + |
825 | + if (!seafaring_economy) { |
826 | + return false; |
827 | + } |
828 | + |
829 | + // getting some base statistics |
830 | + player_ = game().get_player(player_number()); |
831 | + uint16_t ports_count = 0; |
832 | + uint16_t shipyards_count = 0; |
833 | + uint16_t working_shipyards_count = 0; |
834 | + uint16_t expeditions_in_prep = 0; |
835 | + uint16_t expeditions_in_progress = 0; |
836 | + uint16_t territories_count = 1; |
837 | + bool idle_shipyard_stocked = false; |
838 | + |
839 | + // goes over all warehouses (these includes ports) |
840 | + for (const WarehouseSiteObserver& wh_obs : warehousesites) { |
841 | + if (wh_obs.bo->is_port_) { |
842 | + ports_count += 1; |
843 | + if (Widelands::PortDock* pd = wh_obs.site->get_portdock()) { |
844 | + if (pd->expedition_started()) { |
845 | + expeditions_in_prep += 1; |
846 | + } |
847 | + } else { |
848 | + log(" there is a port without portdock at %3dx%3d?\n", |
849 | + wh_obs.site->get_position().x, |
850 | + wh_obs.site->get_position().y); |
851 | + } |
852 | + } |
853 | + } |
854 | + |
855 | + // goes over productionsites and gets status of shipyards |
856 | + for (const ProductionSiteObserver& ps_obs : productionsites) { |
857 | + if (ps_obs.bo->is_shipyard_) { |
858 | + shipyards_count += 1; |
859 | + if (!ps_obs.site->is_stopped()) { |
860 | + working_shipyards_count += 1; |
861 | + } |
862 | + // counting stocks |
863 | + uint8_t stocked_wares = 0; |
864 | + std::vector<WaresQueue*> const warequeues = ps_obs.site->warequeues(); |
865 | + size_t const nr_warequeues = warequeues.size(); |
866 | + for (size_t i = 0; i < nr_warequeues; ++i) { |
867 | + stocked_wares += warequeues[i]->get_filled(); |
868 | + } |
869 | + if (stocked_wares == 16 && ps_obs.site->is_stopped()) { |
870 | + idle_shipyard_stocked = true; |
871 | + } |
872 | + } |
873 | + } |
874 | + |
875 | + // and now over ships |
876 | + for (std::list<ShipObserver>::iterator sp_iter = allships.begin(); sp_iter != allships.end(); |
877 | + ++sp_iter) { |
878 | + if (sp_iter->ship->state_is_expedition()) { |
879 | + expeditions_in_progress += 1; |
880 | + } |
881 | + } |
882 | + |
883 | + // we must verify that all remote ports are still ours (and exists at all) |
884 | + bool still_ours; |
885 | + for (std::unordered_set<uint32_t>::iterator ports_iter = remote_ports_coords.begin(); |
886 | + ports_iter != remote_ports_coords.end(); |
887 | + ++ports_iter) { |
888 | + still_ours = false; |
889 | + FCoords fcoords = game().map().get_fcoords(coords_unhash(*ports_iter)); |
890 | + if (fcoords.field->get_owned_by() == player_number()) { |
891 | + if (upcast(PlayerImmovable, imm, fcoords.field->get_immovable())) { |
892 | + still_ours = true; |
893 | + } |
894 | + } |
895 | + |
896 | + if (!still_ours) { |
897 | + remote_ports_coords.erase(*ports_iter); |
898 | + break; |
899 | + } |
900 | + } |
901 | + territories_count += remote_ports_coords.size(); |
902 | + |
903 | + enum class FleetStatus : uint8_t {kNeedShip = 0, kEnoughShips = 1, kDoNothing = 2 }; |
904 | + |
905 | + // now we must compare ports vs ships to decide if new ship is needed or new expedition can start |
906 | + FleetStatus enough_ships = FleetStatus::kDoNothing; |
907 | + if (static_cast<float>(allships.size()) > |
908 | + static_cast<float>((territories_count - 1) * 0.6 + ports_count * 0.75)) { |
909 | + enough_ships = FleetStatus::kEnoughShips; |
910 | + } else if (static_cast<float>(allships.size()) < |
911 | + static_cast<float>((territories_count - 1) * 0.6 + ports_count * 0.75)) { |
912 | + enough_ships = FleetStatus::kNeedShip; |
913 | + } |
914 | + |
915 | + // building a ship? if yes, find a shipyard and order it to build a ship |
916 | + if (shipyards_count > 0 && enough_ships == FleetStatus::kNeedShip && idle_shipyard_stocked && |
917 | + ports_count > 0) { |
918 | + |
919 | + for (const ProductionSiteObserver& ps_obs : productionsites) { |
920 | + if (ps_obs.bo->is_shipyard_ && ps_obs.site->can_start_working() && |
921 | + ps_obs.site->is_stopped()) { |
922 | + // make sure it is fully stocked |
923 | + // counting stocks |
924 | + uint8_t stocked_wares = 0; |
925 | + std::vector<WaresQueue*> const warequeues = ps_obs.site->warequeues(); |
926 | + size_t const nr_warequeues = warequeues.size(); |
927 | + for (size_t i = 0; i < nr_warequeues; ++i) { |
928 | + stocked_wares += warequeues[i]->get_filled(); |
929 | + } |
930 | + if (stocked_wares < 16) { |
931 | + continue; |
932 | + } |
933 | + |
934 | + game().send_player_start_stop_building(*ps_obs.site); |
935 | + return true; |
936 | + } |
937 | + } |
938 | + } |
939 | + |
940 | + // starting an expedition? if yes, find a port and order it to start an expedition |
941 | + if (ports_count > 0 && enough_ships == FleetStatus::kEnoughShips && expeditions_in_prep == 0 && |
942 | + expeditions_in_progress == 0) { |
943 | + // we need to find a port |
944 | + for (const WarehouseSiteObserver& wh_obs : warehousesites) { |
945 | + |
946 | + if (wh_obs.bo->is_port_) { |
947 | + game().send_player_start_or_cancel_expedition(*wh_obs.site); |
948 | + return true; |
949 | + } |
950 | + } |
951 | + } |
952 | + return true; |
953 | +} |
954 | + |
955 | +// This identifies ships that are waiting for command |
956 | +bool DefaultAI::check_ships(uint32_t const gametime) { |
957 | + if (gametime < next_ship_check_due) { |
958 | + return false; |
959 | + } |
960 | + |
961 | + next_ship_check_due += kShipCheckInterval; |
962 | + |
963 | + if (!seafaring_economy) { |
964 | + return false; |
965 | + } |
966 | + |
967 | + if (!allships.empty()) { |
968 | + // iterating over ships and executing what is needed |
969 | + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { |
970 | + |
971 | + // only two states need an attention |
972 | + if ((i->ship->get_ship_state() == Widelands::Ship::EXP_WAITING || |
973 | + i->ship->get_ship_state() == Widelands::Ship::EXP_FOUNDPORTSPACE) && |
974 | + !i->waiting_for_command_) { |
975 | + if (gametime - i->last_command_time > 180 * 1000) { |
976 | + i->waiting_for_command_ = true; |
977 | + log(" %1d: last command for ship at %3dx%3d was %3d seconds ago, something wrong " |
978 | + "here?...\n", |
979 | + player_number(), |
980 | + i->ship->get_position().x, |
981 | + i->ship->get_position().y, |
982 | + (gametime - i->last_command_time) / 1000); |
983 | + } |
984 | + } |
985 | + // if ships is waiting for command |
986 | + if (i->waiting_for_command_) { |
987 | + expedition_management(*i); |
988 | + } |
989 | + } |
990 | + } |
991 | + |
992 | + // processing marineTaskQueue_ |
993 | + while (!marineTaskQueue_.empty()) { |
994 | + if (marineTaskQueue_.back() == kStopShipyard) { |
995 | + // iterate over all production sites searching for shipyard |
996 | + for (std::list<ProductionSiteObserver>::iterator site = productionsites.begin(); |
997 | + site != productionsites.end(); |
998 | + ++site) { |
999 | + if (site->bo->is_shipyard_) { |
1000 | + if (!site->site->is_stopped()) { |
1001 | + game().send_player_start_stop_building(*site->site); |
1002 | + } |
1003 | + } |
1004 | + } |
1005 | + } |
1006 | + |
1007 | + if (marineTaskQueue_.back() == kReprioritize) { |
1008 | + for (std::list<ProductionSiteObserver>::iterator site = productionsites.begin(); |
1009 | + site != productionsites.end(); |
1010 | + ++site) { |
1011 | + if (site->bo->is_shipyard_) { |
1012 | + for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) { |
1013 | + game().send_player_set_ware_priority( |
1014 | + *site->site, wwWARE, site->bo->inputs_.at(k), HIGH_PRIORITY); |
1015 | + } |
1016 | + } |
1017 | + } |
1018 | + } |
1019 | + |
1020 | + marineTaskQueue_.pop_back(); |
1021 | + } |
1022 | + |
1023 | + return true; |
1024 | +} |
1025 | + |
1026 | /** |
1027 | * checks the first mine in list, takes care if it runs out of |
1028 | * resources and finally reenqueues it at the end of the list. |
1029 | @@ -2643,7 +3099,7 @@ |
1030 | int16_t* max_preciousness, |
1031 | int16_t* max_needed_preciousness) { |
1032 | |
1033 | - // reseting values |
1034 | + // resetting values |
1035 | *output_is_needed = false; |
1036 | *max_preciousness = 0; |
1037 | *max_needed_preciousness = 0; |
1038 | @@ -2712,6 +3168,50 @@ |
1039 | return count; |
1040 | } |
1041 | |
1042 | +// counts produced output in warehouses (only) |
1043 | +// perhaps it will be able to replace get_stocklevel |
1044 | +uint32_t DefaultAI::get_warehoused_stock(WareIndex wt) { |
1045 | + uint32_t count = 0; |
1046 | + |
1047 | + for (std::list<WarehouseSiteObserver>::iterator i = warehousesites.begin(); |
1048 | + i != warehousesites.end(); |
1049 | + ++i) { |
1050 | + count += i->site->get_wares().stock(wt); |
1051 | + } |
1052 | + |
1053 | + return count; |
1054 | +} |
1055 | + |
1056 | +// this function only manipulates with trainingsites' inputs priority |
1057 | +// decreases it when too many unoccupied military buildings |
1058 | +bool DefaultAI::check_trainingsites(int32_t gametime) { |
1059 | + if (next_trainingsites_check_due_ > gametime) { |
1060 | + return false; |
1061 | + } |
1062 | + if (!trainingsites.empty()) { |
1063 | + next_trainingsites_check_due_ = gametime + kTrainingSitesCheckInterval; |
1064 | + } else { |
1065 | + next_trainingsites_check_due_ = gametime + 3 * kTrainingSitesCheckInterval; |
1066 | + } |
1067 | + |
1068 | + uint8_t new_priority = DEFAULT_PRIORITY; |
1069 | + if (unstationed_milit_buildings_ > 2) { |
1070 | + new_priority = LOW_PRIORITY; |
1071 | + } else { |
1072 | + new_priority = DEFAULT_PRIORITY; |
1073 | + } |
1074 | + for (std::list<TrainingSiteObserver>::iterator site = trainingsites.begin(); |
1075 | + site != trainingsites.end(); |
1076 | + ++site) { |
1077 | + |
1078 | + for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) { |
1079 | + game().send_player_set_ware_priority( |
1080 | + *site->site, wwWARE, site->bo->inputs_.at(k), new_priority); |
1081 | + } |
1082 | + } |
1083 | + return true; |
1084 | +} |
1085 | + |
1086 | /** |
1087 | * Updates the first military building in list and reenques it at the end of |
1088 | * the list afterwards. If a militarysite is in secure area but holds more than |
1089 | @@ -2726,7 +3226,7 @@ |
1090 | } |
1091 | |
1092 | // just to be sure the value is reset |
1093 | - next_militarysite_check_due_ = gametime + 5 * 1000; // 10 seconds is really fine |
1094 | + next_militarysite_check_due_ = gametime + 4 * 1000; // 4 seconds is really fine |
1095 | // even if there are no finished & attended military sites, probably there are ones just in |
1096 | // construction |
1097 | unstationed_milit_buildings_ = 0; |
1098 | @@ -2754,7 +3254,7 @@ |
1099 | // look if there are any enemies building |
1100 | FindNodeEnemiesBuilding find_enemy(player_, game()); |
1101 | |
1102 | - // first if there are enemies nearby, check for buildings not land |
1103 | + // first we make sure there is no enemy at all |
1104 | if (map.find_fields(Area<FCoords>(f, vision + 4), nullptr, find_enemy) == 0) { |
1105 | |
1106 | mso.enemies_nearby_ = false; |
1107 | @@ -2803,18 +3303,38 @@ |
1108 | } |
1109 | } else { |
1110 | |
1111 | + int32_t unused1 = 0; |
1112 | + uint16_t unused2 = 0; |
1113 | + |
1114 | mso.enemies_nearby_ = true; |
1115 | |
1116 | - uint32_t const total_capacity = ms->max_soldier_capacity(); |
1117 | - uint32_t const target_capacity = ms->soldier_capacity(); |
1118 | - |
1119 | - game().send_player_change_soldier_capacity(*ms, total_capacity - target_capacity); |
1120 | - changed = true; |
1121 | - |
1122 | - // and also set preference to Heroes |
1123 | - if (MilitarySite::kPrefersHeroes != ms->get_soldier_preference()) { |
1124 | - game().send_player_militarysite_set_soldier_preference(*ms, MilitarySite::kPrefersHeroes); |
1125 | + // yes enemy is nearby, but still we must distinguish whether |
1126 | + // he is accessible (over the land) |
1127 | + |
1128 | + if (other_player_accessible( |
1129 | + vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) { |
1130 | + |
1131 | + uint32_t const total_capacity = ms->max_soldier_capacity(); |
1132 | + uint32_t const target_capacity = ms->soldier_capacity(); |
1133 | + |
1134 | + game().send_player_change_soldier_capacity(*ms, total_capacity - target_capacity); |
1135 | changed = true; |
1136 | + |
1137 | + // and also set preference to Heroes |
1138 | + if (MilitarySite::kPrefersHeroes != ms->get_soldier_preference()) { |
1139 | + game().send_player_militarysite_set_soldier_preference( |
1140 | + *ms, MilitarySite::kPrefersHeroes); |
1141 | + changed = true; |
1142 | + } |
1143 | + } else { // otherwise decrease soldiers |
1144 | + uint32_t const j = ms->soldier_capacity(); |
1145 | + |
1146 | + if (MilitarySite::kPrefersRookies != ms->get_soldier_preference()) { |
1147 | + game().send_player_militarysite_set_soldier_preference( |
1148 | + *ms, MilitarySite::kPrefersRookies); |
1149 | + } else if (j > 1) { |
1150 | + game().send_player_change_soldier_capacity(*ms, (j > 2) ? -2 : -1); |
1151 | + } |
1152 | } |
1153 | } |
1154 | |
1155 | @@ -2966,6 +3486,24 @@ |
1156 | } |
1157 | } |
1158 | |
1159 | +// this is called whenever we gain ownership of a Ship |
1160 | +void DefaultAI::gain_ship(Ship& ship, NewShip type) { |
1161 | + |
1162 | + if (type == NewShip::kBuilt) { |
1163 | + marineTaskQueue_.push_back(kStopShipyard); |
1164 | + } else { |
1165 | + seafaring_economy = true; |
1166 | + } |
1167 | + |
1168 | + allships.push_back(ShipObserver()); |
1169 | + allships.back().ship = &ship; |
1170 | + if (game().get_gametime() % 2 == 0) { |
1171 | + allships.back().island_circ_direction = ScoutingDirection::kClockwise; |
1172 | + } else { |
1173 | + allships.back().island_circ_direction = ScoutingDirection::kCounterClockwise; |
1174 | + } |
1175 | +} |
1176 | + |
1177 | // this is called whenever we lose ownership of a PlayerImmovable |
1178 | void DefaultAI::lose_immovable(const PlayerImmovable& pi) { |
1179 | if (upcast(Building const, building, &pi)) { |
1180 | @@ -3005,13 +3543,242 @@ |
1181 | } |
1182 | } |
1183 | |
1184 | +// walk and search for teritorry controlled by other player |
1185 | +// usually scanning radius is enough but sometimes we must walk to |
1186 | +// verify that an enemy teritory is really accessible by land |
1187 | +bool DefaultAI::other_player_accessible(const uint32_t max_distance, |
1188 | + int32_t* tested_fields, |
1189 | + uint16_t* mineable_fields_count, |
1190 | + const Widelands::Coords starting_spot, |
1191 | + const WalkSearch type) { |
1192 | + Map& map = game().map(); |
1193 | + std::list<uint32_t> queue; |
1194 | + std::unordered_set<uint32_t> done; |
1195 | + queue.push_front(coords_hash(starting_spot)); |
1196 | + PlayerNumber const pn = player_->player_number(); |
1197 | + |
1198 | + while (!queue.empty()) { |
1199 | + // if already processed |
1200 | + if (done.count(queue.front()) > 0) { |
1201 | + queue.pop_front(); |
1202 | + continue; |
1203 | + } |
1204 | + |
1205 | + done.insert(queue.front()); |
1206 | + |
1207 | + Coords tmp_coords = coords_unhash(queue.front()); |
1208 | + |
1209 | + // if beyond range |
1210 | + if (map.calc_distance(starting_spot, tmp_coords) > max_distance) { |
1211 | + continue; |
1212 | + } |
1213 | + |
1214 | + Field* f = map.get_fcoords(tmp_coords).field; |
1215 | + |
1216 | + // not interested if not walkable (starting spot is an exemption. |
1217 | + if (tmp_coords != starting_spot && !(f->nodecaps() & MOVECAPS_WALK)) { |
1218 | + continue; |
1219 | + } |
1220 | + |
1221 | + // sometimes we search for any owned teritory (f.e. when considering |
1222 | + //a port location), but when testing (starting from) own military building |
1223 | + //we must ignore own teritory, of course |
1224 | + if (f->get_owned_by() > 0) { |
1225 | + if (type == WalkSearch::kAnyPlayer || |
1226 | + (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn)) { |
1227 | + *tested_fields = done.size(); |
1228 | + return true; |
1229 | + } |
1230 | + } |
1231 | + |
1232 | + // increase mines counter |
1233 | + // (used when testing possible port location) |
1234 | + if (f->nodecaps() & BUILDCAPS_MINE) { |
1235 | + mineable_fields_count += 1; |
1236 | + }; |
1237 | + |
1238 | + // add neighbours to a queue (duplicates are no problem) |
1239 | + // to relieve AI/CPU we skip every second field in each direction |
1240 | + // obstacles are usually wider then one field |
1241 | + for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { |
1242 | + Coords neigh_coords1; |
1243 | + map.get_neighbour(tmp_coords, dir, &neigh_coords1); |
1244 | + Coords neigh_coords2; |
1245 | + map.get_neighbour(neigh_coords1, dir, &neigh_coords2); |
1246 | + queue.push_front(coords_hash(neigh_coords2)); |
1247 | + } |
1248 | + } |
1249 | + *tested_fields = done.size(); |
1250 | + |
1251 | + return false; // no players found |
1252 | +} |
1253 | + |
1254 | +// this scores spot for potential colony |
1255 | +uint8_t DefaultAI::spot_scoring(Widelands::Coords candidate_spot) { |
1256 | + |
1257 | + uint8_t score = 0; |
1258 | + uint16_t mineable_fields_count = 0; |
1259 | + int32_t tested_fields = 0; |
1260 | + const bool other_player = other_player_accessible(colony_scan_area_, |
1261 | + &tested_fields, |
1262 | + &mineable_fields_count, |
1263 | + candidate_spot, |
1264 | + WalkSearch::kAnyPlayer); |
1265 | + |
1266 | + // if we run into other player |
1267 | + // (maybe we should check for enemies, rather?) |
1268 | + if (other_player) { |
1269 | + return 0; |
1270 | + } |
1271 | + |
1272 | + Map& map = game().map(); |
1273 | + // if the island is too small |
1274 | + if (tested_fields < 50) { |
1275 | + return 0; |
1276 | + } |
1277 | + |
1278 | + // if we are here we put score |
1279 | + score = 1; |
1280 | + if (mineable_fields_count > 0) { |
1281 | + score += 1; |
1282 | + } |
1283 | + |
1284 | + // here we check for surface stones + trees |
1285 | + std::vector<ImmovableFound> immovables; |
1286 | + // Search in a radius of range |
1287 | + map.find_immovables(Area<FCoords>(map.get_fcoords(candidate_spot), 10), &immovables); |
1288 | + |
1289 | + int32_t const stone_attr = MapObjectDescr::get_attribute_id("granite"); |
1290 | + uint16_t stones = 0; |
1291 | + int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree"); |
1292 | + uint16_t trees = 0; |
1293 | + |
1294 | + for (uint32_t j = 0; j < immovables.size(); ++j) { |
1295 | + if (immovables.at(j).object->has_attribute(stone_attr)) { |
1296 | + ++stones; |
1297 | + } |
1298 | + if (immovables.at(j).object->has_attribute(tree_attr)) { |
1299 | + ++trees; |
1300 | + } |
1301 | + } |
1302 | + if (stones > 1) { |
1303 | + score += 1; |
1304 | + } |
1305 | + if (trees > 1) { |
1306 | + score += 1; |
1307 | + } |
1308 | + |
1309 | + return score; |
1310 | +} |
1311 | + |
1312 | +// this is called whenever ship received a notification that requires |
1313 | +// navigation decisions (these notifiation are processes not in 'real time') |
1314 | +void DefaultAI::expedition_management(ShipObserver& so) { |
1315 | + |
1316 | + Map& map = game().map(); |
1317 | + const int32_t gametime = game().get_gametime(); |
1318 | + |
1319 | + // first we put current spot into visited_spots_ |
1320 | + bool first_time_here = false; |
1321 | + if (so.visited_spots_.count(coords_hash(so.ship->get_position())) == 0) { |
1322 | + first_time_here = true; |
1323 | + so.visited_spots_.insert(coords_hash(so.ship->get_position())); |
1324 | + } |
1325 | + |
1326 | + // If we have portspace following options are avaiable: |
1327 | + // 1. Build a port there |
1328 | + if (so.ship->exp_port_spaces()->size() > 0) { // making sure we have possible portspaces |
1329 | + |
1330 | + // we score the place |
1331 | + const uint8_t spot_score = spot_scoring(so.ship->exp_port_spaces()->front()); |
1332 | + |
1333 | + if ((gametime / 10) % 8 < spot_score) { // we build a port here |
1334 | + const Coords last_portspace = so.ship->exp_port_spaces()->front(); |
1335 | + remote_ports_coords.insert(coords_hash(last_portspace)); |
1336 | + game().send_player_ship_construct_port(*so.ship, so.ship->exp_port_spaces()->front()); |
1337 | + so.last_command_time = gametime; |
1338 | + so.waiting_for_command_ = false; |
1339 | + // blocking the area for some time to save AI from idle attempts to built there |
1340 | + // buildings |
1341 | + // TODO(TiborB): how long it takes to build a port? |
1342 | + // I used 5 minutes |
1343 | + MapRegion<Area<FCoords>> mr( |
1344 | + game().map(), Area<FCoords>(map.get_fcoords(so.ship->exp_port_spaces()->front()), 8)); |
1345 | + do { |
1346 | + BlockedField blocked2( |
1347 | + map.get_fcoords(*(mr.location().field)), gametime + 5 * 60 * 1000); |
1348 | + blocked_fields.push_back(blocked2); |
1349 | + } while (mr.advance(map)); |
1350 | + |
1351 | + return; |
1352 | + } |
1353 | + |
1354 | + // decreasing colony_scan_area_ |
1355 | + if (colony_scan_area_ > 15 && gametime % 10 == 0) { |
1356 | + colony_scan_area_ -= 1; |
1357 | + } |
1358 | + } |
1359 | + |
1360 | + // if we are here, port was not ordered above |
1361 | + // 2. Go on with expedition |
1362 | + |
1363 | + if (first_time_here) { |
1364 | + game().send_player_ship_explore_island(*so.ship, so.island_circ_direction); |
1365 | + so.last_command_time = gametime; |
1366 | + so.waiting_for_command_ = false; |
1367 | + |
1368 | + // we was here but to add randomnes we might continue with expedition |
1369 | + } else if (gametime % 2 == 0) { |
1370 | + game().send_player_ship_explore_island(*so.ship, so.island_circ_direction); |
1371 | + so.last_command_time = gametime; |
1372 | + so.waiting_for_command_ = false; |
1373 | + } else { |
1374 | + // get swimable directions |
1375 | + std::vector<Direction> possible_directions; |
1376 | + for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { |
1377 | + |
1378 | + // testing distance of 8 fields |
1379 | + // this would say there is an 'open sea' there |
1380 | + Widelands::FCoords tmp_fcoords = map.get_fcoords(so.ship->get_position()); |
1381 | + for (int8_t i = 0; i < 8; ++i) { |
1382 | + tmp_fcoords = map.get_neighbour(tmp_fcoords, dir); |
1383 | + if (tmp_fcoords.field->nodecaps() & MOVECAPS_SWIM) { |
1384 | + if (i == 7) { |
1385 | + possible_directions.push_back(dir); |
1386 | + break; // not needed but..... |
1387 | + } |
1388 | + } else { |
1389 | + break; |
1390 | + } |
1391 | + } |
1392 | + } |
1393 | + |
1394 | + // we test if there is open sea |
1395 | + if (possible_directions.empty()) { |
1396 | + // 2.A No there is no open sea |
1397 | + game().send_player_ship_explore_island(*so.ship, so.island_circ_direction); |
1398 | + so.last_command_time = gametime; |
1399 | + so.waiting_for_command_ = false; |
1400 | + ; |
1401 | + } else { |
1402 | + // 2.B Yes, pick one of avaiable directions |
1403 | + const Direction final_direction = |
1404 | + possible_directions.at(gametime % possible_directions.size()); |
1405 | + game().send_player_ship_scout_direction(*so.ship, final_direction); |
1406 | + so.last_command_time = gametime; |
1407 | + so.waiting_for_command_ = false; |
1408 | + } |
1409 | + } |
1410 | + return; |
1411 | +} |
1412 | + |
1413 | // this is called whenever we gain a new building |
1414 | void DefaultAI::gain_building(Building& b) { |
1415 | BuildingObserver& bo = get_building_observer(b.descr().name().c_str()); |
1416 | |
1417 | if (bo.type == BuildingObserver::CONSTRUCTIONSITE) { |
1418 | BuildingObserver& target_bo = |
1419 | - get_building_observer(dynamic_cast<ConstructionSite&>(b).building().name().c_str()); |
1420 | + get_building_observer(dynamic_cast<const ConstructionSite&>(b).building().name().c_str()); |
1421 | ++target_bo.cnt_under_construction_; |
1422 | ++num_constructionsites_; |
1423 | if (target_bo.type == BuildingObserver::PRODUCTIONSITE) { |
1424 | @@ -3034,6 +3801,10 @@ |
1425 | productionsites.back().unoccupied_till_ = game().get_gametime(); |
1426 | productionsites.back().stats_zero_ = 0; |
1427 | productionsites.back().no_resources_count = 0; |
1428 | + if (bo.is_shipyard_) { |
1429 | + marineTaskQueue_.push_back(kStopShipyard); |
1430 | + marineTaskQueue_.push_back(kReprioritize); |
1431 | + } |
1432 | |
1433 | for (uint32_t i = 0; i < bo.outputs_.size(); ++i) |
1434 | ++wares.at(bo.outputs_.at(i)).producers_; |
1435 | @@ -3058,8 +3829,20 @@ |
1436 | militarysites.back().checks = bo.desc->get_size(); |
1437 | militarysites.back().enemies_nearby_ = true; |
1438 | |
1439 | + } else if (bo.type == BuildingObserver::TRAININGSITE) { |
1440 | + trainingsites.push_back(TrainingSiteObserver()); |
1441 | + trainingsites.back().site = &dynamic_cast<TrainingSite&>(b); |
1442 | + trainingsites.back().bo = &bo; |
1443 | + |
1444 | } else if (bo.type == BuildingObserver::WAREHOUSE) { |
1445 | ++numof_warehouses_; |
1446 | + warehousesites.push_back(WarehouseSiteObserver()); |
1447 | + warehousesites.back().site = &dynamic_cast<Warehouse&>(b); |
1448 | + warehousesites.back().bo = &bo; |
1449 | + if (bo.is_port_) { |
1450 | + ++num_ports; |
1451 | + seafaring_economy = true; |
1452 | + } |
1453 | } |
1454 | } |
1455 | } |
1456 | @@ -3069,8 +3852,8 @@ |
1457 | BuildingObserver& bo = get_building_observer(b.descr().name().c_str()); |
1458 | |
1459 | if (bo.type == BuildingObserver::CONSTRUCTIONSITE) { |
1460 | - BuildingObserver& target_bo = get_building_observer( |
1461 | - dynamic_cast<const ConstructionSite&>(b).building().name().c_str()); |
1462 | + BuildingObserver& target_bo = |
1463 | + get_building_observer(dynamic_cast<const ConstructionSite&>(b).building().name().c_str()); |
1464 | --target_bo.cnt_under_construction_; |
1465 | --num_constructionsites_; |
1466 | if (target_bo.type == BuildingObserver::PRODUCTIONSITE) { |
1467 | @@ -3126,9 +3909,31 @@ |
1468 | break; |
1469 | } |
1470 | } |
1471 | + } else if (bo.type == BuildingObserver::TRAININGSITE) { |
1472 | + |
1473 | + for (std::list<TrainingSiteObserver>::iterator i = trainingsites.begin(); |
1474 | + i != trainingsites.end(); |
1475 | + ++i) { |
1476 | + if (i->site == &b) { |
1477 | + trainingsites.erase(i); |
1478 | + break; |
1479 | + } |
1480 | + } |
1481 | } else if (bo.type == BuildingObserver::WAREHOUSE) { |
1482 | assert(numof_warehouses_ > 0); |
1483 | --numof_warehouses_; |
1484 | + if (bo.is_port_) { |
1485 | + --num_ports; |
1486 | + } |
1487 | + |
1488 | + for (std::list<WarehouseSiteObserver>::iterator i = warehousesites.begin(); |
1489 | + i != warehousesites.end(); |
1490 | + ++i) { |
1491 | + if (i->site == &b) { |
1492 | + warehousesites.erase(i); |
1493 | + break; |
1494 | + } |
1495 | + } |
1496 | } |
1497 | } |
1498 | |
1499 | @@ -3137,7 +3942,7 @@ |
1500 | } |
1501 | |
1502 | // Checks that supply line exists for given building. |
1503 | -// Recurcsively verify that all inputs_ have a producer. |
1504 | +// Recursively verify that all inputs_ have a producer. |
1505 | // TODO(unknown): this function leads to periodic freezes of ~1 second on big games on my system. |
1506 | // TODO(unknown): It needs profiling and optimization. |
1507 | // NOTE: This is not needed anymore and it seems it is not missed neither |
1508 | @@ -3168,7 +3973,7 @@ |
1509 | |
1510 | bool DefaultAI::consider_attack(int32_t const gametime) { |
1511 | |
1512 | - // we presume that we are not attacking so we extend waitperiod |
1513 | + // we assume that we are not attacking so we extend waitperiod |
1514 | // in case of attack the variable will be decreased below |
1515 | // this is intended to save some CPU and add randomness in attacking |
1516 | // and also differentiate according to type |
1517 | @@ -3251,13 +4056,13 @@ |
1518 | if (genstats.at(j - 1).miltary_strength.empty()) { |
1519 | log("ComputerPlayer(%d): miltary_strength is empty\n", player_number()); |
1520 | player_attackable.at(j - 1) = false; |
1521 | - // Avoid division by zero |
1522 | + // Avoid division by zero |
1523 | } else if (genstats.at(j - 1).miltary_strength.back() == 0) { |
1524 | player_attackable.at(j - 1) = true; |
1525 | any_attackable = true; |
1526 | - // Check threshold |
1527 | + // Check threshold |
1528 | } else if ((genstats.at(pn - 1).miltary_strength.back() * 100 / |
1529 | - genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) { |
1530 | + genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) { |
1531 | player_attackable.at(j - 1) = true; |
1532 | any_attackable = true; |
1533 | } else { |
1534 | @@ -3265,7 +4070,8 @@ |
1535 | } |
1536 | } catch (const std::out_of_range&) { |
1537 | log("ComputerPlayer(%d): genstats entry missing - size :%d\n", |
1538 | - player_number(), static_cast<unsigned int>(genstats.size())); |
1539 | + player_number(), |
1540 | + static_cast<unsigned int>(genstats.size())); |
1541 | player_attackable.at(j - 1) = false; |
1542 | } |
1543 | } |
1544 | @@ -3291,7 +4097,7 @@ |
1545 | |
1546 | for (uint32_t position = gametime % test_every; position < militarysites.size(); |
1547 | position += test_every) { |
1548 | - // checked_own_ms_tmp += 1; |
1549 | + |
1550 | std::list<MilitarySiteObserver>::iterator mso = militarysites.begin(); |
1551 | std::advance(mso, position); |
1552 | |
1553 | @@ -3311,10 +4117,8 @@ |
1554 | for (uint32_t j = 0; j < immovables.size(); ++j) { |
1555 | |
1556 | // skip if in irrelevant_immovables |
1557 | - const uint32_t hash = immovables.at(j).coords.x << 16 | immovables.at(j).coords.y; |
1558 | - if (irrelevant_immovables.count(hash) > 0) { |
1559 | - continue; |
1560 | - } else { |
1561 | + const uint32_t hash = coords_hash(immovables.at(j).coords); |
1562 | + if (irrelevant_immovables.count(hash) == 0) { |
1563 | irrelevant_immovables.insert(hash); |
1564 | } |
1565 | |
1566 | @@ -3337,7 +4141,6 @@ |
1567 | continue; |
1568 | } |
1569 | |
1570 | - // target_buildings.push_back(immovables.at(j)); |
1571 | const int32_t soldiers_difference = |
1572 | player_->find_attack_soldiers(bld->base_flag()) - bld->present_soldiers().size(); |
1573 | |
1574 | @@ -3382,7 +4185,7 @@ |
1575 | } |
1576 | } |
1577 | |
1578 | - // we allways try to attack warehouse first |
1579 | + // we always try to attack warehouse first |
1580 | if (best_wh_target != nullptr && gametime % 2 == 0) { |
1581 | // attacking with all attack-ready soldiers |
1582 | int32_t attackers = player_->find_attack_soldiers(best_wh_target->base_flag()); |
1583 | @@ -3416,31 +4219,65 @@ |
1584 | } |
1585 | } |
1586 | |
1587 | -// This is used for profiling, so usually this is not used :) |
1588 | -void DefaultAI::print_land_stats() { |
1589 | - // this will just print statistics of land size |
1590 | - // intended for AI development only |
1591 | - uint32_t plr_in_game = 0; |
1592 | - uint32_t sum_l = 0; |
1593 | - uint32_t count_l = 0; |
1594 | - uint32_t sum_m = 0; |
1595 | - uint32_t count_m = 0; |
1596 | - PlayerNumber const nr_players = game().map().get_nrplayers(); |
1597 | - iterate_players_existing_novar(p, nr_players, game())++ plr_in_game; |
1598 | - const Game::GeneralStatsVector& genstats = game().get_general_statistics(); |
1599 | - |
1600 | - for (uint8_t j = 1; j <= plr_in_game; ++j) { |
1601 | - log(" player: %1d, landsize: %5d, military strength: %3d\n", |
1602 | - j, |
1603 | - genstats[j - 1].land_size.back(), |
1604 | - genstats[j - 1].miltary_strength.back()); |
1605 | - |
1606 | - sum_l += genstats[j - 1].land_size.back(); |
1607 | - count_l += 1; |
1608 | - sum_m += genstats[j - 1].miltary_strength.back(); |
1609 | - count_m += 1; |
1610 | - } |
1611 | - |
1612 | - assert(count_l > 0 && count_m > 0); |
1613 | - log(" Average: Landsize: %5d, military strength: %3d\n", sum_l / count_l, sum_m / count_m); |
1614 | +// This runs once in 15 minutes, and adjust wares targets based on number of |
1615 | +// productionsites and ports |
1616 | +void DefaultAI::review_wares_targets(int32_t const gametime) { |
1617 | + |
1618 | + player_ = game().get_player(player_number()); |
1619 | + tribe_ = &player_->tribe(); |
1620 | + |
1621 | + // to avoid floats real multiplicator is multiplicator/10 |
1622 | + uint16_t multiplicator = 10; |
1623 | + if ((productionsites.size() + num_ports * 5) > 50) { |
1624 | + multiplicator = (productionsites.size() + num_ports * 5) / 5; |
1625 | + } |
1626 | + |
1627 | + for (EconomyObserver* observer : economies) { |
1628 | + WareIndex nritems = observer->economy.owner().tribe().get_nrwares(); |
1629 | + for (Widelands::WareIndex id = 0; id < nritems; ++id) { |
1630 | + const uint16_t default_target = tribe_->get_ware_descr(id)->default_target_quantity(); |
1631 | + |
1632 | + game().send_player_command(*new Widelands::CmdSetWareTargetQuantity( |
1633 | + gametime, |
1634 | + player_number(), |
1635 | + player_->get_economy_number(&observer->economy), |
1636 | + id, |
1637 | + default_target * multiplicator / 10)); |
1638 | + } |
1639 | + } |
1640 | +} |
1641 | + |
1642 | +// This prints some basic statistics during a game to the command line - |
1643 | +// missing materials and counts of different types of buildings. |
1644 | +// The main purpose of this is when a game creator needs to finetune a map |
1645 | +// and needs to know what resourcess are missing for which player and so on. |
1646 | +// By default it is off (see kPrintStats) |
1647 | +// TODO(tiborb ?): - it would be nice to have this activated by a command line switch |
1648 | +void DefaultAI::print_stats() { |
1649 | + |
1650 | + PlayerNumber const pn = player_number(); |
1651 | + |
1652 | + //we test following materials |
1653 | + const std::vector <std::string> materials = {"coal", "log", "ironore", "marble", |
1654 | + "plank", "water", "goldore", "granite", "fish", "diamond", "stone", "corn", |
1655 | + "wheat", "grape", "quartz", "bread", "meat" }; |
1656 | + std::string summary = ""; |
1657 | + for (uint32_t j = 0; j < materials.size(); ++j) { |
1658 | + WareIndex const index = tribe_->ware_index(materials.at(j)); |
1659 | + if (index == INVALID_INDEX) { |
1660 | + continue; |
1661 | + } |
1662 | + if (get_warehoused_stock(index) > 0) { |
1663 | + continue; |
1664 | + } |
1665 | + summary = summary + materials.at(j) + ", "; |
1666 | + } |
1667 | + log (" %1d: Buildings: Pr:%3d, Ml:%3d, Mi:%2d, Wh:%2d, Po:%2d. Missing: %s\n", |
1668 | + pn, |
1669 | + productionsites.size(), |
1670 | + militarysites.size(), |
1671 | + mines_.size(), |
1672 | + warehousesites.size() - num_ports, |
1673 | + num_ports, |
1674 | + summary.c_str()); |
1675 | } |
1676 | |
1677 | === modified file 'src/ai/defaultai.h' |
1678 | --- src/ai/defaultai.h 2014-10-17 19:13:39 +0000 |
1679 | +++ src/ai/defaultai.h 2015-02-10 21:28:25 +0000 |
1680 | @@ -22,11 +22,13 @@ |
1681 | |
1682 | #include <map> |
1683 | #include <memory> |
1684 | +#include <unordered_set> |
1685 | |
1686 | #include "ai/ai_help_structs.h" |
1687 | #include "ai/computer_player.h" |
1688 | #include "base/i18n.h" |
1689 | #include "logic/immovable.h" |
1690 | +#include "logic/ship.h" |
1691 | |
1692 | namespace Widelands { |
1693 | struct Road; |
1694 | @@ -76,6 +78,9 @@ |
1695 | DEFENSIVE = 0, |
1696 | }; |
1697 | |
1698 | + enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers }; |
1699 | + enum class NewShip : uint8_t {kBuilt, kFoundOnLoad }; |
1700 | + |
1701 | /// Implementation for Aggressive |
1702 | struct AggressiveImpl : public ComputerPlayer::Implementation { |
1703 | AggressiveImpl() { |
1704 | @@ -130,6 +135,18 @@ |
1705 | |
1706 | bool construct_building(int32_t); |
1707 | |
1708 | + uint32_t coords_hash(Widelands::Coords coords) { |
1709 | + uint32_t hash = coords.x << 16 | coords.y; |
1710 | + return hash; |
1711 | + } |
1712 | + |
1713 | + Widelands::Coords coords_unhash(uint32_t hash) { |
1714 | + Widelands::Coords coords; |
1715 | + coords.x = hash >> 16; // is cast needed here??? |
1716 | + coords.y = hash; |
1717 | + return coords; |
1718 | + } |
1719 | + |
1720 | // all road management is invoked by function improve_roads() |
1721 | // if needed it calls create_shortcut_road() with a flag from which |
1722 | // new road should be considered (or is needed) |
1723 | @@ -139,18 +156,35 @@ |
1724 | bool dispensable_road_test(const Widelands::Road&); |
1725 | bool check_economies(); |
1726 | bool check_productionsites(int32_t); |
1727 | + bool check_trainingsites(int32_t); |
1728 | bool check_mines_(int32_t); |
1729 | bool check_militarysites(int32_t); |
1730 | + bool marine_main_decisions(uint32_t); |
1731 | + bool check_ships(uint32_t); |
1732 | + void print_stats(); |
1733 | uint32_t get_stocklevel_by_hint(size_t); |
1734 | uint32_t get_stocklevel(BuildingObserver&); |
1735 | + uint32_t get_warehoused_stock(Widelands::WareIndex wt); |
1736 | uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_ |
1737 | void check_helpersites(int32_t); |
1738 | + void review_wares_targets(int32_t); |
1739 | + |
1740 | + // sometimes scanning an area in radius gives inappropriate results, so this is to verify that |
1741 | + // other player is accessible |
1742 | + // via walking |
1743 | + bool other_player_accessible(uint32_t max_distance, |
1744 | + int32_t* tested_fields, |
1745 | + uint16_t* mineable_fields_count, |
1746 | + const Widelands::Coords starting_spot, |
1747 | + const WalkSearch type); |
1748 | |
1749 | int32_t recalc_with_border_range(const BuildableField&, int32_t); |
1750 | int32_t calculate_need_for_ps(BuildingObserver&, int32_t); |
1751 | |
1752 | void |
1753 | consider_productionsite_influence(BuildableField&, Widelands::Coords, const BuildingObserver&); |
1754 | + // considering wood, stones, mines, water, fishes for candidate for colonization (new port) |
1755 | + uint8_t spot_scoring(Widelands::Coords candidate_spot); |
1756 | |
1757 | EconomyObserver* get_economy_observer(Widelands::Economy&); |
1758 | BuildingObserver& get_building_observer(char const*); |
1759 | @@ -159,6 +193,8 @@ |
1760 | void lose_immovable(const Widelands::PlayerImmovable&); |
1761 | void gain_building(Widelands::Building&); |
1762 | void lose_building(const Widelands::Building&); |
1763 | + void gain_ship(Widelands::Ship&, NewShip); |
1764 | + void expedition_management(ShipObserver&); |
1765 | void out_of_resources_site(const Widelands::ProductionSite&); |
1766 | |
1767 | bool check_supply(const BuildingObserver&); |
1768 | @@ -181,10 +217,14 @@ |
1769 | uint32_t num_constructionsites_; |
1770 | uint32_t num_milit_constructionsites; |
1771 | uint32_t num_prod_constructionsites; |
1772 | + uint32_t num_ports; |
1773 | |
1774 | std::list<Widelands::FCoords> unusable_fields; |
1775 | std::list<BuildableField*> buildable_fields; |
1776 | std::list<BlockedField> blocked_fields; |
1777 | + std::unordered_set<uint32_t> port_reserved_coords; |
1778 | + // to distinquish which ports are on home teritory and which one are remote |
1779 | + std::unordered_set<uint32_t> remote_ports_coords; |
1780 | std::list<MineableField*> mineable_fields; |
1781 | std::list<Widelands::Flag const*> new_flags; |
1782 | std::list<Widelands::Coords> flags_to_be_removed; |
1783 | @@ -193,6 +233,9 @@ |
1784 | std::list<ProductionSiteObserver> productionsites; |
1785 | std::list<ProductionSiteObserver> mines_; |
1786 | std::list<MilitarySiteObserver> militarysites; |
1787 | + std::list<WarehouseSiteObserver> warehousesites; |
1788 | + std::list<TrainingSiteObserver> trainingsites; |
1789 | + std::list<ShipObserver> allships; |
1790 | |
1791 | std::vector<WareObserver> wares; |
1792 | |
1793 | @@ -203,9 +246,13 @@ |
1794 | int32_t next_productionsite_check_due_; |
1795 | int32_t next_mine_check_due_; |
1796 | int32_t next_militarysite_check_due_; |
1797 | + uint32_t next_ship_check_due; |
1798 | + uint32_t next_marine_decisions_due; |
1799 | int32_t next_attack_consideration_due_; |
1800 | - int32_t next_helpersites_check_due_; |
1801 | + int32_t next_trainingsites_check_due_; |
1802 | int32_t next_bf_check_due_; |
1803 | + int32_t next_wares_review_due_; |
1804 | + int32_t next_statistics_report_; |
1805 | int32_t inhibit_road_building_; |
1806 | int32_t time_of_last_construction_; |
1807 | int32_t enemy_last_seen_; |
1808 | @@ -232,13 +279,21 @@ |
1809 | Widelands::Coords |
1810 | last_attack_target_; // flag to abuilding (position) that was attacked last time |
1811 | int32_t next_attack_waittime_; // second till the next attack consideration |
1812 | - int32_t spots_; // sum of buildable fields |
1813 | + bool seafaring_economy; // false by default, until first port space is found |
1814 | + uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory |
1815 | + // it decreases with failed scans |
1816 | + int32_t spots_; // sum of buildable fields |
1817 | + |
1818 | + enum {kReprioritize, kStopShipyard, kStapShipyard }; |
1819 | + |
1820 | + std::vector<int16_t> marineTaskQueue_; |
1821 | |
1822 | std::unique_ptr<Notifications::Subscriber<Widelands::NoteFieldPossession>> |
1823 | field_possession_subscriber_; |
1824 | std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_; |
1825 | std::unique_ptr<Notifications::Subscriber<Widelands::NoteProductionSiteOutOfResources>> |
1826 | outofresource_subscriber_; |
1827 | + std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipMessage>> shipnotes_subscriber_; |
1828 | }; |
1829 | |
1830 | #endif // end of include guard: WL_AI_DEFAULTAI_H |
1831 | |
1832 | === modified file 'src/economy/fleet.cc' |
1833 | --- src/economy/fleet.cc 2014-11-28 16:40:55 +0000 |
1834 | +++ src/economy/fleet.cc 2015-02-10 21:28:25 +0000 |
1835 | @@ -162,6 +162,13 @@ |
1836 | if (BaseImmovable * imm = cur.field->get_immovable()) { |
1837 | if (imm->descr().type() == MapObjectType::PORTDOCK) { |
1838 | if (upcast(PortDock, dock, imm)) { |
1839 | + // here might be a problem so I (tiborb) put here |
1840 | + // this test, might be removed after some time |
1841 | + if (dock->get_fleet() == nullptr) { |
1842 | + log ("The dock on %3dx%3d withouth a fleet!\n", |
1843 | + dock->m_dockpoints.front().x, |
1844 | + dock->m_dockpoints.front().y); |
1845 | + } |
1846 | if (dock->get_fleet() != this && dock->get_owner() == get_owner()) { |
1847 | dock->get_fleet()->merge(egbase, this); |
1848 | return; |
1849 | |
1850 | === modified file 'src/economy/portdock.cc' |
1851 | --- src/economy/portdock.cc 2014-12-11 21:32:41 +0000 |
1852 | +++ src/economy/portdock.cc 2015-02-10 21:28:25 +0000 |
1853 | @@ -21,6 +21,8 @@ |
1854 | |
1855 | #include <memory> |
1856 | |
1857 | +#include <boost/format.hpp> |
1858 | + |
1859 | #include "base/log.h" |
1860 | #include "base/macros.h" |
1861 | #include "economy/fleet.h" |
1862 | @@ -165,7 +167,14 @@ |
1863 | } |
1864 | |
1865 | void PortDock::cleanup(EditorGameBase& egbase) { |
1866 | + |
1867 | + Warehouse* wh = nullptr; |
1868 | + |
1869 | if (egbase.objects().object_still_available(m_warehouse)) { |
1870 | + |
1871 | + //we need to remember this for possible recreation of portdock |
1872 | + wh = m_warehouse; |
1873 | + |
1874 | // Transfer all our wares into the warehouse. |
1875 | if (upcast(Game, game, &egbase)) { |
1876 | for (ShippingItem& shipping_item : m_waiting) { |
1877 | @@ -203,6 +212,17 @@ |
1878 | } |
1879 | |
1880 | PlayerImmovable::cleanup(egbase); |
1881 | + |
1882 | + //now let attempt to recreate the portdock |
1883 | + if (!wh->m_cleanup_in_progress){ |
1884 | + if (upcast(Game, game, &egbase)) { |
1885 | + if (game->is_loaded()) { //do not attempt when shutting down |
1886 | + Player& player = owner(); |
1887 | + wh->restore_portdock_or_destroy(egbase); |
1888 | + } |
1889 | + } |
1890 | + } |
1891 | + |
1892 | } |
1893 | |
1894 | /** |
1895 | |
1896 | === modified file 'src/logic/game.cc' |
1897 | --- src/logic/game.cc 2015-01-31 16:03:59 +0000 |
1898 | +++ src/logic/game.cc 2015-02-10 21:28:25 +0000 |
1899 | @@ -888,11 +888,11 @@ |
1900 | (get_gametime(), ship.get_owner()->player_number(), ship.serial(), coords)); |
1901 | } |
1902 | |
1903 | -void Game::send_player_ship_explore_island(Ship & ship, bool cw) |
1904 | +void Game::send_player_ship_explore_island(Ship & ship, ScoutingDirection direction) |
1905 | { |
1906 | send_player_command |
1907 | (*new CmdShipExploreIsland |
1908 | - (get_gametime(), ship.get_owner()->player_number(), ship.serial(), cw)); |
1909 | + (get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction)); |
1910 | } |
1911 | |
1912 | void Game::send_player_sink_ship(Ship & ship) { |
1913 | |
1914 | === modified file 'src/logic/game.h' |
1915 | --- src/logic/game.h 2015-01-31 16:03:59 +0000 |
1916 | +++ src/logic/game.h 2015-02-10 21:28:25 +0000 |
1917 | @@ -41,6 +41,7 @@ |
1918 | struct Flag; |
1919 | struct Path; |
1920 | struct PlayerImmovable; |
1921 | +enum class ScoutingDirection; |
1922 | struct Ship; |
1923 | struct PlayerEndStatus; |
1924 | class TrainingSite; |
1925 | @@ -182,7 +183,7 @@ |
1926 | |
1927 | void send_player_ship_scout_direction(Ship &, uint8_t); |
1928 | void send_player_ship_construct_port(Ship &, Coords); |
1929 | - void send_player_ship_explore_island(Ship &, bool); |
1930 | + void send_player_ship_explore_island(Ship &, ScoutingDirection); |
1931 | void send_player_sink_ship(Ship &); |
1932 | void send_player_cancel_expedition_ship(Ship &); |
1933 | |
1934 | |
1935 | === modified file 'src/logic/playercommand.cc' |
1936 | --- src/logic/playercommand.cc 2014-09-30 07:55:22 +0000 |
1937 | +++ src/logic/playercommand.cc 2015-02-10 21:28:25 +0000 |
1938 | @@ -784,6 +784,16 @@ |
1939 | { |
1940 | upcast(Ship, ship, game.objects().get_object(serial)); |
1941 | if (ship && ship->get_owner()->player_number() == sender()) { |
1942 | + if (!(ship->get_ship_state() == Widelands::Ship::EXP_WAITING || |
1943 | + ship->get_ship_state() == Widelands::Ship::EXP_FOUNDPORTSPACE)) { |
1944 | + log (" %1d:ship on %3dx%3d received scout command but not in " |
1945 | + "EXP_WAITING or PORTSPACE_FOUND status (expedition: %s), ignoring...\n", |
1946 | + ship->get_owner()->player_number(), |
1947 | + ship->get_position().x, |
1948 | + ship->get_position().y, |
1949 | + (ship->state_is_expedition())?"Y":"N"); |
1950 | + return; |
1951 | + } |
1952 | ship->exp_scout_direction(game, dir); |
1953 | } |
1954 | } |
1955 | @@ -841,6 +851,15 @@ |
1956 | { |
1957 | upcast(Ship, ship, game.objects().get_object(serial)); |
1958 | if (ship && ship->get_owner()->player_number() == sender()) { |
1959 | + if (ship->get_ship_state() != Widelands::Ship::EXP_FOUNDPORTSPACE) { |
1960 | + log (" %1d:ship on %3dx%3d received build port command but " |
1961 | + "not in PORTSPACE_FOUND status (expedition: %s), ignoring...\n", |
1962 | + ship->get_owner()->player_number(), |
1963 | + ship->get_position().x, |
1964 | + ship->get_position().y, |
1965 | + (ship->state_is_expedition())?"Y":"N"); |
1966 | + return; |
1967 | + } |
1968 | ship->exp_construct_port(game, coords); |
1969 | } |
1970 | } |
1971 | @@ -891,14 +910,24 @@ |
1972 | PlayerCommand (0, des.unsigned_8()) |
1973 | { |
1974 | serial = des.unsigned_32(); |
1975 | - clockwise = des.unsigned_8() == 1; |
1976 | + scouting_direction = ScoutingDirection::kClockwise; |
1977 | } |
1978 | |
1979 | void CmdShipExploreIsland::execute (Game & game) |
1980 | { |
1981 | upcast(Ship, ship, game.objects().get_object(serial)); |
1982 | if (ship && ship->get_owner()->player_number() == sender()) { |
1983 | - ship->exp_explore_island(game, clockwise); |
1984 | + if (!(ship->get_ship_state() == Widelands::Ship::EXP_WAITING || |
1985 | + ship->get_ship_state() == Widelands::Ship::EXP_FOUNDPORTSPACE)) { |
1986 | + log (" %1d:ship on %3dx%3d received explore island command " |
1987 | + "but not in EXP_WAITING or PORTSPACE_FOUND status (expedition: %s), ignoring...\n", |
1988 | + ship->get_owner()->player_number(), |
1989 | + ship->get_position().x, |
1990 | + ship->get_position().y, |
1991 | + (ship->state_is_expedition())?"Y":"N"); |
1992 | + return; |
1993 | + } |
1994 | + ship->exp_explore_island(game, scouting_direction); |
1995 | } |
1996 | } |
1997 | |
1998 | @@ -907,7 +936,7 @@ |
1999 | ser.unsigned_8 (PLCMD_SHIP_EXPLORE); |
2000 | ser.unsigned_8 (sender()); |
2001 | ser.unsigned_32(serial); |
2002 | - ser.unsigned_8 (clockwise ? 1 : 0); |
2003 | + ser.unsigned_8 (static_cast<uint8_t>(scouting_direction)); |
2004 | } |
2005 | |
2006 | #define PLAYER_CMD_SHIP_EXPLORE_ISLAND_VERSION 1 |
2007 | @@ -919,7 +948,7 @@ |
2008 | if (packet_version == PLAYER_CMD_SHIP_EXPLORE_ISLAND_VERSION) { |
2009 | PlayerCommand::read(fr, egbase, mol); |
2010 | serial = get_object_serial_or_zero<Ship>(fr.unsigned_32(), mol); |
2011 | - clockwise = fr.unsigned_8() == 1; |
2012 | + scouting_direction = static_cast<ScoutingDirection>(fr.unsigned_8()); |
2013 | } else |
2014 | throw GameDataError("unknown/unhandled version %u", packet_version); |
2015 | } catch (const WException & e) { |
2016 | @@ -938,7 +967,7 @@ |
2017 | fw.unsigned_32(mos.get_object_file_index_or_zero(egbase.objects().get_object(serial))); |
2018 | |
2019 | // Direction of exploration |
2020 | - fw.unsigned_8 (clockwise ? 1 : 0); |
2021 | + fw.unsigned_8(static_cast<uint8_t>(scouting_direction)); |
2022 | } |
2023 | |
2024 | |
2025 | |
2026 | === modified file 'src/logic/playercommand.h' |
2027 | --- src/logic/playercommand.h 2014-09-19 12:54:54 +0000 |
2028 | +++ src/logic/playercommand.h 2015-02-10 21:28:25 +0000 |
2029 | @@ -26,6 +26,7 @@ |
2030 | #include "economy/flag.h" |
2031 | #include "logic/message_id.h" |
2032 | #include "logic/path.h" |
2033 | +#include "logic/ship.h" |
2034 | #include "logic/trainingsite.h" |
2035 | #include "logic/warehouse.h" |
2036 | #include "logic/worker.h" |
2037 | @@ -356,8 +357,8 @@ |
2038 | struct CmdShipExploreIsland : public PlayerCommand { |
2039 | CmdShipExploreIsland() : PlayerCommand(), serial(0) {} // For savegame loading |
2040 | CmdShipExploreIsland |
2041 | - (int32_t const t, PlayerNumber const p, Serial s, bool cw) |
2042 | - : PlayerCommand(t, p), serial(s), clockwise(cw) |
2043 | + (int32_t const t, PlayerNumber const p, Serial s, ScoutingDirection direction) |
2044 | + : PlayerCommand(t, p), serial(s), scouting_direction(direction) |
2045 | {} |
2046 | |
2047 | void write(FileWrite &, EditorGameBase &, MapObjectSaver &) override; |
2048 | @@ -372,7 +373,7 @@ |
2049 | |
2050 | private: |
2051 | Serial serial; |
2052 | - bool clockwise; |
2053 | + ScoutingDirection scouting_direction; |
2054 | }; |
2055 | |
2056 | struct CmdShipSink : public PlayerCommand { |
2057 | |
2058 | === modified file 'src/logic/productionsite.cc' |
2059 | --- src/logic/productionsite.cc 2015-01-28 07:35:51 +0000 |
2060 | +++ src/logic/productionsite.cc 2015-02-10 21:28:25 +0000 |
2061 | @@ -212,7 +212,7 @@ |
2062 | delete[] m_working_positions; |
2063 | } |
2064 | |
2065 | -void ProductionSite::load_finish(EditorGameBase & egbase){ |
2066 | +void ProductionSite::load_finish(EditorGameBase & egbase) { |
2067 | Building::load_finish(egbase); |
2068 | calc_statistics(); |
2069 | } |
2070 | |
2071 | === modified file 'src/logic/ship.cc' |
2072 | --- src/logic/ship.cc 2014-12-28 16:45:37 +0000 |
2073 | +++ src/logic/ship.cc 2015-02-10 21:28:25 +0000 |
2074 | @@ -49,24 +49,24 @@ |
2075 | |
2076 | namespace Widelands { |
2077 | |
2078 | -ShipDescr::ShipDescr |
2079 | - (const char * given_name, const char * gdescname, |
2080 | - const std::string & directory, Profile & prof, Section & global_s, |
2081 | - const TribeDescr & gtribe) |
2082 | - : |
2083 | - BobDescr(MapObjectType::SHIP, given_name, gdescname, >ribe) |
2084 | -{ |
2085 | - { // global options |
2086 | - Section & idle_s = prof.get_safe_section("idle"); |
2087 | +ShipDescr::ShipDescr(const char* given_name, |
2088 | + const char* gdescname, |
2089 | + const std::string& directory, |
2090 | + Profile& prof, |
2091 | + Section& global_s, |
2092 | + const TribeDescr& gtribe) |
2093 | + : BobDescr(MapObjectType::SHIP, given_name, gdescname, >ribe) { |
2094 | + { // global options |
2095 | + Section& idle_s = prof.get_safe_section("idle"); |
2096 | add_animation("idle", g_gr->animations().load(directory, idle_s)); |
2097 | } |
2098 | m_sail_anims.parse(*this, directory, prof, "sail"); |
2099 | |
2100 | - Section * sinking_s = prof.get_section("sinking"); |
2101 | + Section* sinking_s = prof.get_section("sinking"); |
2102 | if (sinking_s) |
2103 | add_animation("sinking", g_gr->animations().load(directory, *sinking_s)); |
2104 | |
2105 | - m_capacity = global_s.get_natural("capacity", 20); |
2106 | + m_capacity = global_s.get_natural("capacity", 20); |
2107 | m_vision_range = global_s.get_natural("vision_range", 7); |
2108 | } |
2109 | |
2110 | @@ -74,18 +74,12 @@ |
2111 | return MOVECAPS_SWIM; |
2112 | } |
2113 | |
2114 | -Bob & ShipDescr::create_object() const { |
2115 | +Bob& ShipDescr::create_object() const { |
2116 | return *new Ship(*this); |
2117 | } |
2118 | |
2119 | - |
2120 | -Ship::Ship(const ShipDescr & gdescr) : |
2121 | - Bob(gdescr), |
2122 | - m_window(nullptr), |
2123 | - m_fleet(nullptr), |
2124 | - m_economy(nullptr), |
2125 | - m_ship_state(TRANSPORT) |
2126 | -{ |
2127 | +Ship::Ship(const ShipDescr& gdescr) |
2128 | + : Bob(gdescr), m_window(nullptr), m_fleet(nullptr), m_economy(nullptr), m_ship_state(TRANSPORT) { |
2129 | } |
2130 | |
2131 | Ship::~Ship() { |
2132 | @@ -104,13 +98,14 @@ |
2133 | return m_fleet; |
2134 | } |
2135 | |
2136 | -void Ship::init_auto_task(Game & game) { |
2137 | +void Ship::init_auto_task(Game& game) { |
2138 | start_task_ship(game); |
2139 | } |
2140 | |
2141 | -void Ship::init(EditorGameBase & egbase) { |
2142 | +void Ship::init(EditorGameBase& egbase) { |
2143 | Bob::init(egbase); |
2144 | init_fleet(egbase); |
2145 | + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::kGained)); |
2146 | } |
2147 | |
2148 | /** |
2149 | @@ -118,15 +113,15 @@ |
2150 | * The fleet code will automatically merge us into a larger |
2151 | * fleet, if one is reachable. |
2152 | */ |
2153 | -void Ship::init_fleet(EditorGameBase & egbase) { |
2154 | +void Ship::init_fleet(EditorGameBase& egbase) { |
2155 | assert(get_owner() != nullptr); |
2156 | - Fleet * fleet = new Fleet(*get_owner()); |
2157 | + Fleet* fleet = new Fleet(*get_owner()); |
2158 | fleet->add_ship(this); |
2159 | fleet->init(egbase); |
2160 | // fleet calls the set_fleet function appropriately |
2161 | } |
2162 | |
2163 | -void Ship::cleanup(EditorGameBase & egbase) { |
2164 | +void Ship::cleanup(EditorGameBase& egbase) { |
2165 | if (m_fleet) { |
2166 | m_fleet->remove_ship(egbase, this); |
2167 | } |
2168 | @@ -136,58 +131,56 @@ |
2169 | m_items.pop_back(); |
2170 | } |
2171 | |
2172 | + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::kLost)); |
2173 | + |
2174 | Bob::cleanup(egbase); |
2175 | } |
2176 | |
2177 | /** |
2178 | * This function is to be called only by @ref Fleet. |
2179 | */ |
2180 | -void Ship::set_fleet(Fleet * fleet) { |
2181 | +void Ship::set_fleet(Fleet* fleet) { |
2182 | m_fleet = fleet; |
2183 | } |
2184 | |
2185 | -void Ship::wakeup_neighbours(Game & game) { |
2186 | +void Ship::wakeup_neighbours(Game& game) { |
2187 | FCoords position = get_position(); |
2188 | Area<FCoords> area(position, 1); |
2189 | - std::vector<Bob *> ships; |
2190 | + std::vector<Bob*> ships; |
2191 | game.map().find_bobs(area, &ships, FindBobShip()); |
2192 | |
2193 | - for |
2194 | - (std::vector<Bob *>::const_iterator it = ships.begin(); |
2195 | - it != ships.end(); ++it) |
2196 | - { |
2197 | + for (std::vector<Bob*>::const_iterator it = ships.begin(); it != ships.end(); ++it) { |
2198 | if (*it == this) |
2199 | continue; |
2200 | |
2201 | - static_cast<Ship *>(*it)->ship_wakeup(game); |
2202 | + static_cast<Ship*>(*it)->ship_wakeup(game); |
2203 | } |
2204 | } |
2205 | |
2206 | - |
2207 | /** |
2208 | * Standard behaviour of ships. |
2209 | * |
2210 | * ivar1 = helper flag for coordination of mutual evasion of ships |
2211 | */ |
2212 | const Bob::Task Ship::taskShip = { |
2213 | - "ship", |
2214 | - static_cast<Bob::Ptr>(&Ship::ship_update), |
2215 | - nullptr, |
2216 | - nullptr, |
2217 | - true // unique task |
2218 | + "ship", |
2219 | + static_cast<Bob::Ptr>(&Ship::ship_update), |
2220 | + nullptr, |
2221 | + nullptr, |
2222 | + true // unique task |
2223 | }; |
2224 | |
2225 | -void Ship::start_task_ship(Game & game) { |
2226 | +void Ship::start_task_ship(Game& game) { |
2227 | push_task(game, taskShip); |
2228 | top_state().ivar1 = 0; |
2229 | } |
2230 | |
2231 | -void Ship::ship_wakeup(Game & game) { |
2232 | +void Ship::ship_wakeup(Game& game) { |
2233 | if (get_state(taskShip)) |
2234 | send_signal(game, "wakeup"); |
2235 | } |
2236 | |
2237 | -void Ship::ship_update(Game & game, Bob::State & state) { |
2238 | +void Ship::ship_update(Game& game, Bob::State& state) { |
2239 | // Handle signals |
2240 | std::string signal = get_signal(); |
2241 | if (!signal.empty()) { |
2242 | @@ -211,44 +204,43 @@ |
2243 | } |
2244 | |
2245 | switch (m_ship_state) { |
2246 | - case TRANSPORT: |
2247 | - if (ship_update_transport(game, state)) |
2248 | - return; |
2249 | - break; |
2250 | - case EXP_FOUNDPORTSPACE: |
2251 | - case EXP_SCOUTING: |
2252 | - case EXP_WAITING: |
2253 | - ship_update_expedition(game, state); |
2254 | - break; |
2255 | - case EXP_COLONIZING: |
2256 | - break; |
2257 | - case SINK_REQUEST: |
2258 | - if (descr().is_animation_known("sinking")) { |
2259 | - m_ship_state = SINK_ANIMATION; |
2260 | - start_task_idle(game, descr().get_animation("sinking"), 3000); |
2261 | - return; |
2262 | - } |
2263 | - log("Oh no... this ship has no sinking animation :(!\n"); |
2264 | - // fall trough |
2265 | - case SINK_ANIMATION: |
2266 | - // The sink animation has been played, so finally remove the ship from the map |
2267 | - pop_task(game); |
2268 | - remove(game); |
2269 | - return; |
2270 | - default: |
2271 | - assert(false); // never here |
2272 | + case TRANSPORT: |
2273 | + if (ship_update_transport(game, state)) |
2274 | + return; |
2275 | + break; |
2276 | + case EXP_FOUNDPORTSPACE: |
2277 | + case EXP_SCOUTING: |
2278 | + case EXP_WAITING: |
2279 | + ship_update_expedition(game, state); |
2280 | + break; |
2281 | + case EXP_COLONIZING: |
2282 | + break; |
2283 | + case SINK_REQUEST: |
2284 | + if (descr().is_animation_known("sinking")) { |
2285 | + m_ship_state = SINK_ANIMATION; |
2286 | + start_task_idle(game, descr().get_animation("sinking"), 3000); |
2287 | + return; |
2288 | + } |
2289 | + log("Oh no... this ship has no sinking animation :(!\n"); |
2290 | + // fall trough |
2291 | + case SINK_ANIMATION: |
2292 | + // The sink animation has been played, so finally remove the ship from the map |
2293 | + pop_task(game); |
2294 | + remove(game); |
2295 | + return; |
2296 | + default: |
2297 | + assert(false); // never here |
2298 | } |
2299 | |
2300 | // if the real update function failed (e.g. nothing to transport), the ship goes idle |
2301 | ship_update_idle(game, state); |
2302 | } |
2303 | |
2304 | - |
2305 | /// updates a ships tasks in transport mode \returns false if failed to update tasks |
2306 | -bool Ship::ship_update_transport(Game & game, Bob::State &) { |
2307 | - Map & map = game.map(); |
2308 | +bool Ship::ship_update_transport(Game& game, Bob::State&) { |
2309 | + Map& map = game.map(); |
2310 | |
2311 | - PortDock * dst = get_destination(game); |
2312 | + PortDock* dst = get_destination(game); |
2313 | if (!dst) { |
2314 | molog("ship_update: No destination anymore.\n"); |
2315 | if (m_items.empty()) |
2316 | @@ -277,7 +269,7 @@ |
2317 | |
2318 | molog("ship_update: Go to dock %u\n", dst->serial()); |
2319 | |
2320 | - PortDock * lastdock = m_lastdock.get(game); |
2321 | + PortDock* lastdock = m_lastdock.get(game); |
2322 | if (lastdock && lastdock != dst) { |
2323 | molog("ship_update: Have lastdock %u\n", lastdock->serial()); |
2324 | |
2325 | @@ -332,10 +324,9 @@ |
2326 | return true; |
2327 | } |
2328 | |
2329 | - |
2330 | /// updates a ships tasks in expedition mode |
2331 | -void Ship::ship_update_expedition(Game & game, Bob::State &) { |
2332 | - Map & map = game.map(); |
2333 | +void Ship::ship_update_expedition(Game& game, Bob::State&) { |
2334 | + Map& map = game.map(); |
2335 | |
2336 | assert(m_expedition); |
2337 | |
2338 | @@ -343,30 +334,30 @@ |
2339 | FCoords position = get_position(); |
2340 | for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { |
2341 | m_expedition->swimable[dir - 1] = |
2342 | - map.get_neighbour(position, dir).field->nodecaps() & MOVECAPS_SWIM; |
2343 | + map.get_neighbour(position, dir).field->nodecaps() & MOVECAPS_SWIM; |
2344 | } |
2345 | |
2346 | if (m_ship_state == EXP_SCOUTING) { |
2347 | // Check surrounding fields for port buildspaces |
2348 | - std::unique_ptr<std::list<Coords> > temp_port_buildspaces(new std::list<Coords>()); |
2349 | - MapRegion<Area<Coords> > mr(map, Area<Coords>(position, descr().vision_range())); |
2350 | + std::unique_ptr<std::list<Coords>> temp_port_buildspaces(new std::list<Coords>()); |
2351 | + MapRegion<Area<Coords>> mr(map, Area<Coords>(position, descr().vision_range())); |
2352 | bool new_port_space = false; |
2353 | do { |
2354 | if (map.is_port_space(mr.location())) { |
2355 | FCoords fc = map.get_fcoords(mr.location()); |
2356 | |
2357 | - // Check whether the maximum theoretical possible NodeCap of the field is of the size big |
2358 | + // Check whether the maximum theoretical possible NodeCap of the field is of the size |
2359 | + // big |
2360 | // and whether it can theoretically be a port space |
2361 | - if |
2362 | - ((map.get_max_nodecaps(game.world(), fc) & BUILDCAPS_SIZEMASK) != BUILDCAPS_BIG |
2363 | - || |
2364 | - map.find_portdock(fc).empty()) |
2365 | - { |
2366 | + if ((map.get_max_nodecaps(game.world(), fc) & BUILDCAPS_SIZEMASK) != BUILDCAPS_BIG || |
2367 | + map.find_portdock(fc).empty()) { |
2368 | continue; |
2369 | } |
2370 | |
2371 | - // NOTE This is the place to handle enemy territory and "clearing a port space from the enemy". |
2372 | - // NOTE There is a simple check for the current land owner to avoid placement of ports into enemy |
2373 | + // NOTE This is the place to handle enemy territory and "clearing a port space from the |
2374 | + // enemy". |
2375 | + // NOTE There is a simple check for the current land owner to avoid placement of ports |
2376 | + // into enemy |
2377 | // NOTE territory, as "clearing" is not yet implemented. |
2378 | // NOTE further it checks, whether there is a Player_immovable on one of the fields. |
2379 | // TODO(unknown): handle this more gracefully concering opposing players |
2380 | @@ -378,7 +369,7 @@ |
2381 | invalid = true; |
2382 | continue; |
2383 | } |
2384 | - BaseImmovable * baim = coord.field->get_immovable(); |
2385 | + BaseImmovable* baim = coord.field->get_immovable(); |
2386 | if (baim) |
2387 | if (is_a(PlayerImmovable, baim)) { |
2388 | invalid = true; |
2389 | @@ -387,60 +378,60 @@ |
2390 | |
2391 | // Check all neighboured fields that will be used by the port |
2392 | switch (step) { |
2393 | - case 0: |
2394 | - map.get_ln(fc, &coord); |
2395 | - break; |
2396 | - case 1: |
2397 | - map.get_tln(fc, &coord); |
2398 | - break; |
2399 | - case 2: |
2400 | - map.get_trn(fc, &coord); |
2401 | - break; |
2402 | - case 3: |
2403 | - // Flag coordinate |
2404 | - map.get_brn(fc, &coord); |
2405 | - break; |
2406 | - default: |
2407 | - break; |
2408 | + case 0: |
2409 | + map.get_ln(fc, &coord); |
2410 | + break; |
2411 | + case 1: |
2412 | + map.get_tln(fc, &coord); |
2413 | + break; |
2414 | + case 2: |
2415 | + map.get_trn(fc, &coord); |
2416 | + break; |
2417 | + case 3: |
2418 | + // Flag coordinate |
2419 | + map.get_brn(fc, &coord); |
2420 | + break; |
2421 | + default: |
2422 | + break; |
2423 | } |
2424 | } |
2425 | // Now check whether there is a flag in the surroundings of the flag position |
2426 | FCoords neighb; |
2427 | map.get_ln(coord, &neighb); |
2428 | for (uint8_t step = 0; !invalid && step < 5; ++step) { |
2429 | - BaseImmovable * baim = neighb.field->get_immovable(); |
2430 | + BaseImmovable* baim = neighb.field->get_immovable(); |
2431 | if (baim) |
2432 | if (is_a(Flag, baim)) { |
2433 | invalid = true; |
2434 | continue; |
2435 | } |
2436 | - // Check all neighboured fields but not the one already checked for a PlayerImmovable. |
2437 | + // Check all neighboured fields but not the one already checked for a |
2438 | + // PlayerImmovable. |
2439 | switch (step) { |
2440 | - case 0: |
2441 | - map.get_bln(coord, &neighb); |
2442 | - break; |
2443 | - case 1: |
2444 | - map.get_brn(coord, &neighb); |
2445 | - break; |
2446 | - case 2: |
2447 | - map.get_rn(coord, &neighb); |
2448 | - break; |
2449 | - case 3: |
2450 | - map.get_trn(coord, &neighb); |
2451 | - break; |
2452 | - default: |
2453 | - break; |
2454 | + case 0: |
2455 | + map.get_bln(coord, &neighb); |
2456 | + break; |
2457 | + case 1: |
2458 | + map.get_brn(coord, &neighb); |
2459 | + break; |
2460 | + case 2: |
2461 | + map.get_rn(coord, &neighb); |
2462 | + break; |
2463 | + case 3: |
2464 | + map.get_trn(coord, &neighb); |
2465 | + break; |
2466 | + default: |
2467 | + break; |
2468 | } |
2469 | } |
2470 | if (invalid) |
2471 | continue; |
2472 | |
2473 | bool pbs_saved = false; |
2474 | - for |
2475 | - (std::list<Coords>::const_iterator it = m_expedition->seen_port_buildspaces->begin(); |
2476 | - it != m_expedition->seen_port_buildspaces->end() && !pbs_saved; |
2477 | - ++it) |
2478 | - { |
2479 | + for (std::list<Coords>::const_iterator it = |
2480 | + m_expedition->seen_port_buildspaces->begin(); |
2481 | + it != m_expedition->seen_port_buildspaces->end() && !pbs_saved; |
2482 | + ++it) { |
2483 | // Check if the ship knows this port space already from its last check |
2484 | if (*it == mr.location()) { |
2485 | temp_port_buildspaces->push_back(mr.location()); |
2486 | @@ -462,10 +453,14 @@ |
2487 | send_message(game, msg_head, msg_body, "port.png"); |
2488 | } |
2489 | m_expedition->seen_port_buildspaces.swap(temp_port_buildspaces); |
2490 | + if (new_port_space) { |
2491 | + Notifications::publish( |
2492 | + NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand)); |
2493 | + } |
2494 | } |
2495 | } |
2496 | |
2497 | -void Ship::ship_update_idle(Game & game, Bob::State & state) { |
2498 | +void Ship::ship_update_idle(Game& game, Bob::State& state) { |
2499 | if (state.ivar1) { |
2500 | // We've just completed one step, so give neighbours |
2501 | // a chance to move away first |
2502 | @@ -475,172 +470,187 @@ |
2503 | return; |
2504 | } |
2505 | |
2506 | - // If we are waiting for the next transport job, check if we should move away from ships and shores |
2507 | + // If we are waiting for the next transport job, check if we should move away from ships and |
2508 | + // shores |
2509 | switch (m_ship_state) { |
2510 | - case TRANSPORT: { |
2511 | - FCoords position = get_position(); |
2512 | - Map & map = game.map(); |
2513 | - unsigned int dirs[LAST_DIRECTION + 1]; |
2514 | - unsigned int dirmax = 0; |
2515 | - |
2516 | + case TRANSPORT: { |
2517 | + FCoords position = get_position(); |
2518 | + Map& map = game.map(); |
2519 | + unsigned int dirs[LAST_DIRECTION + 1]; |
2520 | + unsigned int dirmax = 0; |
2521 | + |
2522 | + for (Direction dir = 0; dir <= LAST_DIRECTION; ++dir) { |
2523 | + FCoords node = dir ? map.get_neighbour(position, dir) : position; |
2524 | + dirs[dir] = node.field->nodecaps() & MOVECAPS_WALK ? 10 : 0; |
2525 | + |
2526 | + Area<FCoords> area(node, 0); |
2527 | + std::vector<Bob*> ships; |
2528 | + map.find_bobs(area, &ships, FindBobShip()); |
2529 | + |
2530 | + for (std::vector<Bob*>::const_iterator it = ships.begin(); it != ships.end(); ++it) { |
2531 | + if (*it == this) |
2532 | + continue; |
2533 | + |
2534 | + dirs[dir] += 3; |
2535 | + } |
2536 | + |
2537 | + dirmax = std::max(dirmax, dirs[dir]); |
2538 | + } |
2539 | + |
2540 | + if (dirmax) { |
2541 | + unsigned int prob[LAST_DIRECTION + 1]; |
2542 | + unsigned int totalprob = 0; |
2543 | + |
2544 | + // The probability for moving into a given direction is also |
2545 | + // affected by the "close" directions. |
2546 | for (Direction dir = 0; dir <= LAST_DIRECTION; ++dir) { |
2547 | - FCoords node = dir ? map.get_neighbour(position, dir) : position; |
2548 | - dirs[dir] = node.field->nodecaps() & MOVECAPS_WALK ? 10 : 0; |
2549 | - |
2550 | - Area<FCoords> area(node, 0); |
2551 | - std::vector<Bob *> ships; |
2552 | - map.find_bobs(area, &ships, FindBobShip()); |
2553 | - |
2554 | - for (std::vector<Bob *>::const_iterator it = ships.begin(); it != ships.end(); ++it) { |
2555 | - if (*it == this) |
2556 | - continue; |
2557 | - |
2558 | - dirs[dir] += 3; |
2559 | - } |
2560 | - |
2561 | - dirmax = std::max(dirmax, dirs[dir]); |
2562 | - } |
2563 | - |
2564 | - if (dirmax) { |
2565 | - unsigned int prob[LAST_DIRECTION + 1]; |
2566 | - unsigned int totalprob = 0; |
2567 | - |
2568 | - // The probability for moving into a given direction is also |
2569 | - // affected by the "close" directions. |
2570 | - for (Direction dir = 0; dir <= LAST_DIRECTION; ++dir) { |
2571 | - prob[dir] = 10 * dirmax - 10 * dirs[dir]; |
2572 | - |
2573 | - if (dir > 0) { |
2574 | - unsigned int delta = std::min(prob[dir], dirs[(dir % 6) + 1] + dirs[1 + ((dir - 1) % 6)]); |
2575 | - prob[dir] -= delta; |
2576 | - } |
2577 | - |
2578 | - totalprob += prob[dir]; |
2579 | - } |
2580 | - |
2581 | - if (totalprob == 0) { |
2582 | - start_task_idle(game, descr().main_animation(), 1500); |
2583 | - return; |
2584 | - } |
2585 | - |
2586 | - unsigned int rnd = game.logic_rand() % totalprob; |
2587 | - Direction dir = 0; |
2588 | - while (rnd >= prob[dir]) { |
2589 | - rnd -= prob[dir]; |
2590 | - ++dir; |
2591 | - } |
2592 | - |
2593 | - if (dir == 0 || dir > LAST_DIRECTION) { |
2594 | - start_task_idle(game, descr().main_animation(), 1500); |
2595 | - return; |
2596 | - } |
2597 | - |
2598 | - FCoords neighbour = map.get_neighbour(position, dir); |
2599 | - if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) { |
2600 | - start_task_idle(game, descr().main_animation(), 1500); |
2601 | - return; |
2602 | - } |
2603 | - |
2604 | - state.ivar1 = 1; |
2605 | - start_task_move(game, dir, descr().get_sail_anims(), false); |
2606 | - return; |
2607 | - } |
2608 | - // No desire to move around, so sleep |
2609 | - start_task_idle(game, descr().main_animation(), -1); |
2610 | + prob[dir] = 10 * dirmax - 10 * dirs[dir]; |
2611 | + |
2612 | + if (dir > 0) { |
2613 | + unsigned int delta = |
2614 | + std::min(prob[dir], dirs[(dir % 6) + 1] + dirs[1 + ((dir - 1) % 6)]); |
2615 | + prob[dir] -= delta; |
2616 | + } |
2617 | + |
2618 | + totalprob += prob[dir]; |
2619 | + } |
2620 | + |
2621 | + if (totalprob == 0) { |
2622 | + start_task_idle(game, descr().main_animation(), 1500); |
2623 | + return; |
2624 | + } |
2625 | + |
2626 | + unsigned int rnd = game.logic_rand() % totalprob; |
2627 | + Direction dir = 0; |
2628 | + while (rnd >= prob[dir]) { |
2629 | + rnd -= prob[dir]; |
2630 | + ++dir; |
2631 | + } |
2632 | + |
2633 | + if (dir == 0 || dir > LAST_DIRECTION) { |
2634 | + start_task_idle(game, descr().main_animation(), 1500); |
2635 | + return; |
2636 | + } |
2637 | + |
2638 | + FCoords neighbour = map.get_neighbour(position, dir); |
2639 | + if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) { |
2640 | + start_task_idle(game, descr().main_animation(), 1500); |
2641 | + return; |
2642 | + } |
2643 | + |
2644 | + state.ivar1 = 1; |
2645 | + start_task_move(game, dir, descr().get_sail_anims(), false); |
2646 | return; |
2647 | } |
2648 | - case EXP_SCOUTING: { |
2649 | - if (m_expedition->island_exploration) { // Exploration of the island |
2650 | - if (exp_close_to_coast()) { |
2651 | - if (m_expedition->direction == 0) { |
2652 | - // Make sure we know the location of the coast and use it as initial direction we come from |
2653 | - m_expedition->direction = WALK_SE; |
2654 | - for (uint8_t secure = 0; exp_dir_swimable(m_expedition->direction); ++secure) { |
2655 | - assert(secure < 6); |
2656 | - m_expedition->direction = get_cw_neighbour(m_expedition->direction); |
2657 | - } |
2658 | - m_expedition->direction = get_backward_dir(m_expedition->direction); |
2659 | - // Save the position - this is where we start |
2660 | - m_expedition->exploration_start = get_position(); |
2661 | - } else { |
2662 | - // Check whether the island was completely surrounded |
2663 | - if (get_position() == m_expedition->exploration_start) { |
2664 | - std::string msg_head = _("Island Circumnavigated"); |
2665 | - std::string msg_body = _("An expedition ship sailed around its" |
2666 | - " island without any events."); |
2667 | - send_message(game, msg_head, msg_body, |
2668 | - "ship_explore_island_cw.png"); |
2669 | - m_ship_state = EXP_WAITING; |
2670 | - return start_task_idle(game, descr().main_animation(), 1500); |
2671 | - } |
2672 | + // No desire to move around, so sleep |
2673 | + start_task_idle(game, descr().main_animation(), -1); |
2674 | + return; |
2675 | + } |
2676 | + |
2677 | + case EXP_SCOUTING: { |
2678 | + if (m_expedition->island_exploration) { // Exploration of the island |
2679 | + if (exp_close_to_coast()) { |
2680 | + if (m_expedition->direction == 0) { |
2681 | + // Make sure we know the location of the coast and use it as initial direction we |
2682 | + // come from |
2683 | + m_expedition->direction = WALK_SE; |
2684 | + for (uint8_t secure = 0; exp_dir_swimable(m_expedition->direction); ++secure) { |
2685 | + assert(secure < 6); |
2686 | + m_expedition->direction = get_cw_neighbour(m_expedition->direction); |
2687 | } |
2688 | - // The ship is supposed to follow the coast as close as possible, therefore the check for |
2689 | - // a swimable field begins at the neighbour field of the direction we came from. |
2690 | m_expedition->direction = get_backward_dir(m_expedition->direction); |
2691 | - if (m_expedition->clockwise) { |
2692 | - do { |
2693 | - m_expedition->direction = get_ccw_neighbour(m_expedition->direction); |
2694 | - } while (!exp_dir_swimable(m_expedition->direction)); |
2695 | - } else { |
2696 | - do { |
2697 | - m_expedition->direction = get_cw_neighbour(m_expedition->direction); |
2698 | - } while (!exp_dir_swimable(m_expedition->direction)); |
2699 | - } |
2700 | - state.ivar1 = 1; |
2701 | - return start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); |
2702 | - } else { |
2703 | - // The ship got the command to scout around an island, but is not close to any island |
2704 | - // Most likely the command was send as the ship was on an exploration and just leaving |
2705 | - // the island - therefore we try to find the island again. |
2706 | - FCoords position = get_position(); |
2707 | - Map & map = game.map(); |
2708 | - for (uint8_t dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { |
2709 | - FCoords neighbour = map.get_neighbour(position, dir); |
2710 | - for (uint8_t sur = FIRST_DIRECTION; sur <= LAST_DIRECTION; ++sur) |
2711 | - if (!(map.get_neighbour(neighbour, sur).field->nodecaps() & MOVECAPS_SWIM)) { |
2712 | - // Okay we found the next coast, so now the ship should go there. |
2713 | - // However, we do neither save the position as starting position, nor do we save |
2714 | - // the direction we currently go. So the ship can start exploring normally |
2715 | - state.ivar1 = 1; |
2716 | - return start_task_move(game, dir, descr().get_sail_anims(), false); |
2717 | - } |
2718 | - } |
2719 | - // if we are here, it seems something really strange happend. |
2720 | - log("WARNING: ship was not able to start exploration. Entering WAIT mode."); |
2721 | - m_ship_state = EXP_WAITING; |
2722 | - return start_task_idle(game, descr().main_animation(), 1500); |
2723 | - } |
2724 | - } else { // scouting towards a specific direction |
2725 | - if (exp_dir_swimable(m_expedition->direction)) { |
2726 | - // the scouting direction is still free to move |
2727 | - state.ivar1 = 1; |
2728 | - start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); |
2729 | - return; |
2730 | - } |
2731 | - // coast reached |
2732 | + // Save the position - this is where we start |
2733 | + m_expedition->exploration_start = get_position(); |
2734 | + } else { |
2735 | + // Check whether the island was completely surrounded |
2736 | + if (get_position() == m_expedition->exploration_start) { |
2737 | + std::string msg_head = _("Island Circumnavigated"); |
2738 | + std::string msg_body = _("An expedition ship sailed around its" |
2739 | + " island without any events."); |
2740 | + send_message(game, msg_head, msg_body, "ship_explore_island_cw.png"); |
2741 | + m_ship_state = EXP_WAITING; |
2742 | + |
2743 | + Notifications::publish( |
2744 | + NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand)); |
2745 | + |
2746 | + return start_task_idle(game, descr().main_animation(), 1500); |
2747 | + } |
2748 | + } |
2749 | + // The ship is supposed to follow the coast as close as possible, therefore the check |
2750 | + // for |
2751 | + // a swimable field begins at the neighbour field of the direction we came from. |
2752 | + m_expedition->direction = get_backward_dir(m_expedition->direction); |
2753 | + if (m_expedition->scouting_direction == ScoutingDirection::kClockwise) { |
2754 | + do { |
2755 | + m_expedition->direction = get_ccw_neighbour(m_expedition->direction); |
2756 | + } while (!exp_dir_swimable(m_expedition->direction)); |
2757 | + } else { |
2758 | + do { |
2759 | + m_expedition->direction = get_cw_neighbour(m_expedition->direction); |
2760 | + } while (!exp_dir_swimable(m_expedition->direction)); |
2761 | + } |
2762 | + state.ivar1 = 1; |
2763 | + return start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); |
2764 | + } else { |
2765 | + // The ship got the command to scout around an island, but is not close to any island |
2766 | + // Most likely the command was send as the ship was on an exploration and just leaving |
2767 | + // the island - therefore we try to find the island again. |
2768 | + FCoords position = get_position(); |
2769 | + Map& map = game.map(); |
2770 | + for (uint8_t dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { |
2771 | + FCoords neighbour = map.get_neighbour(position, dir); |
2772 | + for (uint8_t sur = FIRST_DIRECTION; sur <= LAST_DIRECTION; ++sur) |
2773 | + if (!(map.get_neighbour(neighbour, sur).field->nodecaps() & MOVECAPS_SWIM)) { |
2774 | + // Okay we found the next coast, so now the ship should go there. |
2775 | + // However, we do neither save the position as starting position, nor do we |
2776 | + // save |
2777 | + // the direction we currently go. So the ship can start exploring normally |
2778 | + state.ivar1 = 1; |
2779 | + return start_task_move(game, dir, descr().get_sail_anims(), false); |
2780 | + } |
2781 | + } |
2782 | + // if we are here, it seems something really strange happend. |
2783 | + log("WARNING: ship was not able to start exploration. Entering WAIT mode."); |
2784 | m_ship_state = EXP_WAITING; |
2785 | - start_task_idle(game, descr().main_animation(), 1500); |
2786 | - // Send a message to the player, that a new coast was reached |
2787 | - std::string msg_head = _("Coast Reached"); |
2788 | - std::string msg_body = |
2789 | - _("An expedition ship reached a coast and is waiting for further commands."); |
2790 | - send_message(game, msg_head, msg_body, "ship_explore_island_cw.png"); |
2791 | + return start_task_idle(game, descr().main_animation(), 1500); |
2792 | + } |
2793 | + } else { // scouting towards a specific direction |
2794 | + if (exp_dir_swimable(m_expedition->direction)) { |
2795 | + // the scouting direction is still free to move |
2796 | + state.ivar1 = 1; |
2797 | + start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); |
2798 | return; |
2799 | } |
2800 | + // coast reached |
2801 | + m_ship_state = EXP_WAITING; |
2802 | + start_task_idle(game, descr().main_animation(), 1500); |
2803 | + // Send a message to the player, that a new coast was reached |
2804 | + std::string msg_head = _("Coast Reached"); |
2805 | + std::string msg_body = |
2806 | + _("An expedition ship reached a coast and is waiting for further commands."); |
2807 | + send_message(game, msg_head, msg_body, "ship_explore_island_cw.png"); |
2808 | + |
2809 | + Notifications::publish( |
2810 | + NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand)); |
2811 | + |
2812 | + return; |
2813 | } |
2814 | - case EXP_COLONIZING: { |
2815 | - assert(m_expedition->seen_port_buildspaces && !m_expedition->seen_port_buildspaces->empty()); |
2816 | - BaseImmovable * baim = game.map()[m_expedition->seen_port_buildspaces->front()].get_immovable(); |
2817 | - assert(baim); |
2818 | + } |
2819 | + case EXP_COLONIZING: { |
2820 | + assert(m_expedition->seen_port_buildspaces && !m_expedition->seen_port_buildspaces->empty()); |
2821 | + BaseImmovable* baim = |
2822 | + game.map()[m_expedition->seen_port_buildspaces->front()].get_immovable(); |
2823 | + if (baim) { |
2824 | upcast(ConstructionSite, cs, baim); |
2825 | |
2826 | for (int i = m_items.size() - 1; i >= 0; --i) { |
2827 | - WareInstance * ware; |
2828 | - Worker * worker; |
2829 | + WareInstance* ware; |
2830 | + Worker* worker; |
2831 | m_items.at(i).get(game, &ware, &worker); |
2832 | if (ware) { |
2833 | - // no, we don't transfer the wares, we create new ones out of air and remove the old ones ;) |
2834 | - WaresQueue & wq = cs->waresqueue(ware->descr_index()); |
2835 | + // no, we don't transfer the wares, we create new ones out of air and remove the old |
2836 | + // ones ;) |
2837 | + WaresQueue& wq = cs->waresqueue(ware->descr_index()); |
2838 | const uint32_t max = wq.get_max_fill(); |
2839 | const uint32_t cur = wq.get_filled(); |
2840 | assert(max > cur); |
2841 | @@ -654,35 +664,56 @@ |
2842 | worker->set_location(cs); |
2843 | worker->set_position(game, cs->get_position()); |
2844 | worker->reset_tasks(game); |
2845 | - PartiallyFinishedBuilding::request_builder_callback |
2846 | - (game, *cs->get_builder_request(), worker->descr().worker_index(), worker, *cs); |
2847 | + PartiallyFinishedBuilding::request_builder_callback( |
2848 | + game, *cs->get_builder_request(), worker->descr().worker_index(), worker, *cs); |
2849 | m_items.resize(i); |
2850 | } |
2851 | } |
2852 | - if (m_items.empty()) { |
2853 | - m_ship_state = TRANSPORT; // That's it, expedition finished |
2854 | - |
2855 | - init_fleet(game); |
2856 | - m_expedition.reset(nullptr); |
2857 | - |
2858 | - if (upcast(InteractiveGameBase, igb, game.get_ibase())) |
2859 | - refresh_window(*igb); |
2860 | + } else { // it seems that port constructionsite has dissapeared |
2861 | + // Send a message to the player, that a port constructionsite is gone |
2862 | + std::string msg_head = _("New port constructionsite is gone"); |
2863 | + std::string msg_body = _("Unloading of wares failed, expedition is cancelled now."); |
2864 | + send_message(game, msg_head, msg_body, "port.png"); |
2865 | + send_signal(game, "cancel_expedition"); |
2866 | + } |
2867 | + |
2868 | + if (m_items.empty() || !baim) { // we are done, either way |
2869 | + m_ship_state = TRANSPORT; // That's it, expedition finished |
2870 | + |
2871 | + // Bring us back into a fleet and a economy. |
2872 | + init_fleet(game); |
2873 | + |
2874 | + // for case that there are any workers left on board |
2875 | + // (applicable when port construction space is kLost) |
2876 | + Worker* worker; |
2877 | + for (ShippingItem& item : m_items) { |
2878 | + item.get(game, nullptr, &worker); |
2879 | + if (worker) { |
2880 | + worker->reset_tasks(game); |
2881 | + worker->start_task_shipping(game, nullptr); |
2882 | + } |
2883 | } |
2884 | - return start_task_idle(game, descr().main_animation(), 1500); // unload the next item |
2885 | - } |
2886 | - |
2887 | - default: { |
2888 | - // wait for input |
2889 | - start_task_idle(game, descr().main_animation(), 1500); |
2890 | - return; |
2891 | - } |
2892 | + |
2893 | + m_expedition.reset(nullptr); |
2894 | + |
2895 | + if (upcast(InteractiveGameBase, igb, game.get_ibase())) |
2896 | + refresh_window(*igb); |
2897 | + return start_task_idle(game, descr().main_animation(), 1500); |
2898 | + } |
2899 | + } |
2900 | + |
2901 | + default: { |
2902 | + // wait for input |
2903 | + start_task_idle(game, descr().main_animation(), 1500); |
2904 | + return; |
2905 | + } |
2906 | } |
2907 | |
2908 | // never here |
2909 | - assert (false); |
2910 | + assert(false); |
2911 | } |
2912 | |
2913 | -void Ship::set_economy(Game & game, Economy * e) { |
2914 | +void Ship::set_economy(Game& game, Economy* e) { |
2915 | // Do not check here that the economy actually changed, because on loading |
2916 | // we rely that wares really get reassigned our economy. |
2917 | |
2918 | @@ -697,23 +728,23 @@ |
2919 | * |
2920 | * @note This is supposed to be called only from the scheduling code of @ref Fleet. |
2921 | */ |
2922 | -void Ship::set_destination(Game & game, PortDock & pd) { |
2923 | +void Ship::set_destination(Game& game, PortDock& pd) { |
2924 | molog("set_destination to %u (currently %" PRIuS " items)\n", pd.serial(), m_items.size()); |
2925 | m_destination = &pd; |
2926 | send_signal(game, "wakeup"); |
2927 | } |
2928 | |
2929 | -void Ship::add_item(Game & game, const ShippingItem & item) { |
2930 | +void Ship::add_item(Game& game, const ShippingItem& item) { |
2931 | assert(m_items.size() < descr().get_capacity()); |
2932 | |
2933 | m_items.push_back(item); |
2934 | m_items.back().set_location(game, this); |
2935 | } |
2936 | |
2937 | -void Ship::withdraw_items(Game & game, PortDock & pd, std::vector<ShippingItem> & items) { |
2938 | +void Ship::withdraw_items(Game& game, PortDock& pd, std::vector<ShippingItem>& items) { |
2939 | uint32_t dst = 0; |
2940 | for (uint32_t src = 0; src < m_items.size(); ++src) { |
2941 | - PortDock * destination = m_items[src].get_destination(game); |
2942 | + PortDock* destination = m_items[src].get_destination(game); |
2943 | if (!destination || destination == &pd) { |
2944 | items.push_back(m_items[src]); |
2945 | } else { |
2946 | @@ -726,8 +757,8 @@ |
2947 | /** |
2948 | * Find a path to the dock @p pd and follow it without using precomputed paths. |
2949 | */ |
2950 | -void Ship::start_task_movetodock(Game & game, PortDock & pd) { |
2951 | - Map & map = game.map(); |
2952 | +void Ship::start_task_movetodock(Game& game, PortDock& pd) { |
2953 | + Map& map = game.map(); |
2954 | StepEvalAStar se(pd.get_warehouse()->get_position()); |
2955 | se.m_swim = true; |
2956 | se.m_conservative = false; |
2957 | @@ -753,7 +784,7 @@ |
2958 | } |
2959 | |
2960 | /// Prepare everything for the coming exploration |
2961 | -void Ship::start_task_expedition(Game & game) { |
2962 | +void Ship::start_task_expedition(Game& game) { |
2963 | // Now we are waiting |
2964 | m_ship_state = EXP_WAITING; |
2965 | // Initialize a new, yet empty expedition |
2966 | @@ -762,7 +793,7 @@ |
2967 | m_expedition->island_exploration = false; |
2968 | m_expedition->direction = 0; |
2969 | m_expedition->exploration_start = Coords(0, 0); |
2970 | - m_expedition->clockwise = false; |
2971 | + m_expedition->scouting_direction = ScoutingDirection::kClockwise; |
2972 | m_expedition->economy.reset(new Economy(*get_owner())); |
2973 | |
2974 | // We are no longer in any other economy, but instead are an economy of our |
2975 | @@ -773,8 +804,8 @@ |
2976 | set_economy(game, m_expedition->economy.get()); |
2977 | |
2978 | for (int i = m_items.size() - 1; i >= 0; --i) { |
2979 | - WareInstance * ware; |
2980 | - Worker * worker; |
2981 | + WareInstance* ware; |
2982 | + Worker* worker; |
2983 | m_items.at(i).get(game, &ware, &worker); |
2984 | if (worker) { |
2985 | worker->reset_tasks(game); |
2986 | @@ -788,11 +819,12 @@ |
2987 | const std::string msg_head = _("Expedition Ready"); |
2988 | const std::string msg_body = _("An expedition ship is waiting for your commands."); |
2989 | send_message(game, msg_head, msg_body, "start_expedition.png"); |
2990 | + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::kWaitingForCommand)); |
2991 | } |
2992 | |
2993 | /// Initializes / changes the direction of scouting to @arg direction |
2994 | /// @note only called via player command |
2995 | -void Ship::exp_scout_direction(Game &, uint8_t direction) { |
2996 | +void Ship::exp_scout_direction(Game&, uint8_t direction) { |
2997 | assert(m_expedition); |
2998 | m_ship_state = EXP_SCOUTING; |
2999 | m_expedition->direction = direction; |
3000 | @@ -801,28 +833,29 @@ |
3001 | |
3002 | /// Initializes the construction of a port at @arg c |
3003 | /// @note only called via player command |
3004 | -void Ship::exp_construct_port (Game &, const Coords& c) { |
3005 | +void Ship::exp_construct_port(Game&, const Coords& c) { |
3006 | assert(m_expedition); |
3007 | BuildingIndex port_idx = get_owner()->tribe().safe_building_index("port"); |
3008 | get_owner()->force_csite(c, port_idx); |
3009 | m_ship_state = EXP_COLONIZING; |
3010 | } |
3011 | |
3012 | -/// Initializes / changes the direction the island exploration in @arg clockwise direction |
3013 | +/// Initializes / changes the direction the island exploration in @arg scouting_direction direction |
3014 | /// @note only called via player command |
3015 | -void Ship::exp_explore_island (Game &, bool clockwise) { |
3016 | +void Ship::exp_explore_island(Game&, ScoutingDirection scouting_direction) { |
3017 | assert(m_expedition); |
3018 | m_ship_state = EXP_SCOUTING; |
3019 | - m_expedition->clockwise = clockwise; |
3020 | + m_expedition->scouting_direction = scouting_direction; |
3021 | m_expedition->direction = 0; |
3022 | m_expedition->island_exploration = true; |
3023 | } |
3024 | |
3025 | /// Cancels a currently running expedition |
3026 | /// @note only called via player command |
3027 | -void Ship::exp_cancel (Game & game) { |
3028 | +void Ship::exp_cancel(Game& game) { |
3029 | // Running colonization has the highest priority before cancelation |
3030 | // + cancelation only works if an expedition is actually running |
3031 | + |
3032 | if ((m_ship_state == EXP_COLONIZING) || !state_is_expedition()) |
3033 | return; |
3034 | send_signal(game, "cancel_expedition"); |
3035 | @@ -834,7 +867,7 @@ |
3036 | // Theres nothing to be done for wares - they already changed |
3037 | // economy with us and the warehouse will make sure that they are |
3038 | // getting used. |
3039 | - Worker * worker; |
3040 | + Worker* worker; |
3041 | for (ShippingItem& item : m_items) { |
3042 | item.get(game, nullptr, &worker); |
3043 | if (worker) { |
3044 | @@ -859,7 +892,7 @@ |
3045 | |
3046 | /// Sinks the ship |
3047 | /// @note only called via player command |
3048 | -void Ship::sink_ship (Game & game) { |
3049 | +void Ship::sink_ship(Game& game) { |
3050 | // Running colonization has the highest priority + a sink request is only valid once |
3051 | if (!state_is_sinkable()) |
3052 | return; |
3053 | @@ -869,25 +902,22 @@ |
3054 | close_window(); |
3055 | } |
3056 | |
3057 | -void Ship::log_general_info(const EditorGameBase & egbase) |
3058 | -{ |
3059 | +void Ship::log_general_info(const EditorGameBase& egbase) { |
3060 | Bob::log_general_info(egbase); |
3061 | |
3062 | - molog |
3063 | - ("Fleet: %u, destination: %u, lastdock: %u, carrying: %" PRIuS "\n", |
3064 | - m_fleet? m_fleet->serial() : 0, |
3065 | - m_destination.serial(), m_lastdock.serial(), |
3066 | - m_items.size()); |
3067 | + molog("Fleet: %u, destination: %u, lastdock: %u, carrying: %" PRIuS "\n", |
3068 | + m_fleet ? m_fleet->serial() : 0, |
3069 | + m_destination.serial(), |
3070 | + m_lastdock.serial(), |
3071 | + m_items.size()); |
3072 | |
3073 | for (const ShippingItem& shipping_item : m_items) { |
3074 | - molog |
3075 | - (" IT %u, destination %u\n", |
3076 | - shipping_item.m_object.serial(), |
3077 | - shipping_item.m_destination_dock.serial()); |
3078 | + molog(" IT %u, destination %u\n", |
3079 | + shipping_item.m_object.serial(), |
3080 | + shipping_item.m_destination_dock.serial()); |
3081 | } |
3082 | } |
3083 | |
3084 | - |
3085 | /** |
3086 | * Send a message to the owning player. |
3087 | * |
3088 | @@ -895,29 +925,34 @@ |
3089 | * |
3090 | * \param msgsender a computer-readable description of why the message was sent |
3091 | * \param title user-visible title of the message |
3092 | - * \param description user-visible message body, will be placed in an appropriate rich-text paragraph |
3093 | + * \param description user-visible message body, will be placed in an appropriate rich-text |
3094 | + *paragraph |
3095 | * \param picture picture name relative to the pics directory |
3096 | */ |
3097 | -void Ship::send_message |
3098 | - (Game & game, const std::string & title, const std::string & description, const std::string & picture) |
3099 | -{ |
3100 | +void Ship::send_message(Game& game, |
3101 | + const std::string& title, |
3102 | + const std::string& description, |
3103 | + const std::string& picture) { |
3104 | std::string rt_description; |
3105 | if (picture.size() > 3) { |
3106 | - rt_description = "<rt image=pics/"; |
3107 | + rt_description = "<rt image=pics/"; |
3108 | rt_description += picture; |
3109 | rt_description += "><p font-size=14 font-face=DejaVuSerif>"; |
3110 | } else |
3111 | - rt_description = "<rt><p font-size=14 font-face=DejaVuSerif>"; |
3112 | + rt_description = "<rt><p font-size=14 font-face=DejaVuSerif>"; |
3113 | rt_description += description; |
3114 | rt_description += "</p></rt>"; |
3115 | |
3116 | - Message * msg = new Message |
3117 | - (Message::Type::kSeafaring, game.get_gametime(), title, rt_description, get_position(), m_serial); |
3118 | + Message* msg = new Message(Message::Type::kSeafaring, |
3119 | + game.get_gametime(), |
3120 | + title, |
3121 | + rt_description, |
3122 | + get_position(), |
3123 | + m_serial); |
3124 | |
3125 | get_owner()->add_message(game, *msg); |
3126 | } |
3127 | |
3128 | - |
3129 | /* |
3130 | ============================== |
3131 | |
3132 | @@ -928,20 +963,16 @@ |
3133 | |
3134 | #define SHIP_SAVEGAME_VERSION 4 |
3135 | |
3136 | -Ship::Loader::Loader() : |
3137 | - m_lastdock(0), |
3138 | - m_destination(0) |
3139 | -{ |
3140 | +Ship::Loader::Loader() : m_lastdock(0), m_destination(0) { |
3141 | } |
3142 | |
3143 | -const Bob::Task * Ship::Loader::get_task(const std::string & name) |
3144 | -{ |
3145 | - if (name == "shipidle" || name == "ship") return &taskShip; |
3146 | +const Bob::Task* Ship::Loader::get_task(const std::string& name) { |
3147 | + if (name == "shipidle" || name == "ship") |
3148 | + return &taskShip; |
3149 | return Bob::Loader::get_task(name); |
3150 | } |
3151 | |
3152 | -void Ship::Loader::load(FileRead & fr, uint8_t version) |
3153 | -{ |
3154 | +void Ship::Loader::load(FileRead& fr, uint8_t version) { |
3155 | Bob::Loader::load(fr); |
3156 | |
3157 | if (version >= 2) { |
3158 | @@ -950,15 +981,8 @@ |
3159 | m_ship_state = fr.unsigned_8(); |
3160 | |
3161 | // Expedition specific data |
3162 | - if |
3163 | - (m_ship_state == EXP_SCOUTING |
3164 | - || |
3165 | - m_ship_state == EXP_WAITING |
3166 | - || |
3167 | - m_ship_state == EXP_FOUNDPORTSPACE |
3168 | - || |
3169 | - m_ship_state == EXP_COLONIZING) |
3170 | - { |
3171 | + if (m_ship_state == EXP_SCOUTING || m_ship_state == EXP_WAITING || |
3172 | + m_ship_state == EXP_FOUNDPORTSPACE || m_ship_state == EXP_COLONIZING) { |
3173 | m_expedition.reset(new Expedition()); |
3174 | // Currently seen port build spaces |
3175 | m_expedition->seen_port_buildspaces.reset(new std::list<Coords>()); |
3176 | @@ -975,7 +999,7 @@ |
3177 | // Start coordinates of an island exploration |
3178 | m_expedition->exploration_start = read_coords_32(&fr); |
3179 | // Whether the exploration is done clockwise or counter clockwise |
3180 | - m_expedition->clockwise = fr.unsigned_8() == 1; |
3181 | + m_expedition->scouting_direction = static_cast<ScoutingDirection>(fr.unsigned_8()); |
3182 | } |
3183 | } else |
3184 | m_ship_state = TRANSPORT; |
3185 | @@ -990,11 +1014,10 @@ |
3186 | } |
3187 | } |
3188 | |
3189 | -void Ship::Loader::load_pointers() |
3190 | -{ |
3191 | +void Ship::Loader::load_pointers() { |
3192 | Bob::Loader::load_pointers(); |
3193 | |
3194 | - Ship & ship = get<Ship>(); |
3195 | + Ship& ship = get<Ship>(); |
3196 | |
3197 | if (m_lastdock) |
3198 | ship.m_lastdock = &mol().get<PortDock>(m_lastdock); |
3199 | @@ -1007,11 +1030,10 @@ |
3200 | } |
3201 | } |
3202 | |
3203 | -void Ship::Loader::load_finish() |
3204 | -{ |
3205 | +void Ship::Loader::load_finish() { |
3206 | Bob::Loader::load_finish(); |
3207 | |
3208 | - Ship & ship = get<Ship>(); |
3209 | + Ship& ship = get<Ship>(); |
3210 | |
3211 | // restore the state the ship is in |
3212 | ship.m_ship_state = m_ship_state; |
3213 | @@ -1021,7 +1043,8 @@ |
3214 | ship.m_expedition.swap(m_expedition); |
3215 | ship.m_expedition->economy.reset(new Economy(*ship.get_owner())); |
3216 | ship.m_economy = ship.m_expedition->economy.get(); |
3217 | - } else assert(m_ship_state == TRANSPORT); |
3218 | + } else |
3219 | + assert(m_ship_state == TRANSPORT); |
3220 | |
3221 | // Workers load code set their economy to the economy of their location |
3222 | // (which is a PlayerImmovable), that means that workers on ships do not get |
3223 | @@ -1032,10 +1055,7 @@ |
3224 | ship.set_economy(dynamic_cast<Game&>(egbase()), ship.m_economy); |
3225 | } |
3226 | |
3227 | - |
3228 | -MapObject::Loader * Ship::load |
3229 | - (EditorGameBase & egbase, MapObjectLoader & mol, FileRead & fr) |
3230 | -{ |
3231 | +MapObject::Loader* Ship::load(EditorGameBase& egbase, MapObjectLoader& mol, FileRead& fr) { |
3232 | std::unique_ptr<Loader> loader(new Loader); |
3233 | |
3234 | try { |
3235 | @@ -1045,32 +1065,28 @@ |
3236 | if (1 <= version && version <= SHIP_SAVEGAME_VERSION) { |
3237 | std::string owner = fr.c_string(); |
3238 | std::string name = fr.c_string(); |
3239 | - const ShipDescr * descr = nullptr; |
3240 | + const ShipDescr* descr = nullptr; |
3241 | |
3242 | egbase.manually_load_tribe(owner); |
3243 | |
3244 | - if (const TribeDescr * tribe = egbase.get_tribe(owner)) |
3245 | - descr = dynamic_cast<const ShipDescr *> |
3246 | - (tribe->get_bob_descr(name)); |
3247 | + if (const TribeDescr* tribe = egbase.get_tribe(owner)) |
3248 | + descr = dynamic_cast<const ShipDescr*>(tribe->get_bob_descr(name)); |
3249 | |
3250 | if (!descr) |
3251 | - throw GameDataError |
3252 | - ("undefined ship %s/%s", owner.c_str(), name.c_str()); |
3253 | + throw GameDataError("undefined ship %s/%s", owner.c_str(), name.c_str()); |
3254 | |
3255 | loader->init(egbase, mol, descr->create_object()); |
3256 | loader->load(fr, version); |
3257 | } else |
3258 | throw GameDataError("unknown/unhandled version %u", version); |
3259 | - } catch (const std::exception & e) { |
3260 | + } catch (const std::exception& e) { |
3261 | throw wexception("loading ship: %s", e.what()); |
3262 | } |
3263 | |
3264 | return loader.release(); |
3265 | } |
3266 | |
3267 | -void Ship::save |
3268 | - (EditorGameBase & egbase, MapObjectSaver & mos, FileWrite & fw) |
3269 | -{ |
3270 | +void Ship::save(EditorGameBase& egbase, MapObjectSaver& mos, FileWrite& fw) { |
3271 | fw.unsigned_8(HeaderShip); |
3272 | fw.unsigned_8(SHIP_SAVEGAME_VERSION); |
3273 | |
3274 | @@ -1087,11 +1103,9 @@ |
3275 | // currently seen port buildspaces |
3276 | assert(m_expedition->seen_port_buildspaces); |
3277 | fw.unsigned_8(m_expedition->seen_port_buildspaces->size()); |
3278 | - for |
3279 | - (std::list<Coords>::const_iterator it = m_expedition->seen_port_buildspaces->begin(); |
3280 | - it != m_expedition->seen_port_buildspaces->end(); |
3281 | - ++it) |
3282 | - { |
3283 | + for (std::list<Coords>::const_iterator it = m_expedition->seen_port_buildspaces->begin(); |
3284 | + it != m_expedition->seen_port_buildspaces->end(); |
3285 | + ++it) { |
3286 | write_coords_32(&fw, *it); |
3287 | } |
3288 | // swimability of the directions |
3289 | @@ -1104,7 +1118,7 @@ |
3290 | // Start coordinates of an island exploration |
3291 | write_coords_32(&fw, m_expedition->exploration_start); |
3292 | // Whether the exploration is done clockwise or counter clockwise |
3293 | - fw.unsigned_8(m_expedition->clockwise ? 1 : 0); |
3294 | + fw.unsigned_8(static_cast<uint8_t>(m_expedition->scouting_direction)); |
3295 | } |
3296 | |
3297 | fw.unsigned_32(mos.get_object_file_index_or_zero(m_lastdock.get(egbase))); |
3298 | @@ -1116,4 +1130,4 @@ |
3299 | } |
3300 | } |
3301 | |
3302 | -} // namespace Widelands |
3303 | +} // namespace Widelands |
3304 | |
3305 | === modified file 'src/logic/ship.h' |
3306 | --- src/logic/ship.h 2014-09-29 12:37:07 +0000 |
3307 | +++ src/logic/ship.h 2015-02-10 21:28:25 +0000 |
3308 | @@ -37,6 +37,25 @@ |
3309 | struct Fleet; |
3310 | class PortDock; |
3311 | |
3312 | +// This can't be part of the Ship class because of forward declaration in game.h |
3313 | +enum class ScoutingDirection { |
3314 | + kCounterClockwise = 0, // This comes first for savegame compatibility (used to be = 0) |
3315 | + kClockwise = 1 |
3316 | +}; |
3317 | + |
3318 | +struct NoteShipMessage { |
3319 | + CAN_BE_SEND_AS_NOTE(NoteId::ShipMessage) |
3320 | + |
3321 | + Ship* ship; |
3322 | + |
3323 | + enum class Message {kLost, kGained, kWaitingForCommand}; |
3324 | + Message message; |
3325 | + |
3326 | + NoteShipMessage(Ship* const init_ship, Message const init_message) |
3327 | + : ship(init_ship), message(init_message) { |
3328 | + } |
3329 | +}; |
3330 | + |
3331 | struct ShipDescr : BobDescr { |
3332 | ShipDescr |
3333 | (char const * name, char const * descname, |
3334 | @@ -185,7 +204,7 @@ |
3335 | |
3336 | void exp_scout_direction(Game &, uint8_t); |
3337 | void exp_construct_port (Game &, const Coords&); |
3338 | - void exp_explore_island (Game &, bool); |
3339 | + void exp_explore_island (Game &, ScoutingDirection); |
3340 | |
3341 | void exp_cancel (Game &); |
3342 | void sink_ship (Game &); |
3343 | @@ -225,7 +244,7 @@ |
3344 | bool island_exploration; |
3345 | uint8_t direction; |
3346 | Coords exploration_start; |
3347 | - bool clockwise; |
3348 | + ScoutingDirection scouting_direction; |
3349 | std::unique_ptr<Economy> economy; |
3350 | }; |
3351 | std::unique_ptr<Expedition> m_expedition; |
3352 | |
3353 | === modified file 'src/logic/warehouse.cc' |
3354 | --- src/logic/warehouse.cc 2014-12-28 16:45:37 +0000 |
3355 | +++ src/logic/warehouse.cc 2015-02-10 21:28:25 +0000 |
3356 | @@ -478,8 +478,20 @@ |
3357 | Area<FCoords> |
3358 | (egbase.map().get_fcoords(get_position()), conquer_radius))); |
3359 | |
3360 | - if (descr().get_isport()) |
3361 | + if (descr().get_isport()) { |
3362 | init_portdock(egbase); |
3363 | + PortDock* pd = m_portdock; |
3364 | + // should help diagnose problems with marine |
3365 | + if (!pd->get_fleet()) { |
3366 | + log(" Warning: portdock without a fleet created (%3dx%3d)\n", |
3367 | + get_position().x, |
3368 | + get_position().y); |
3369 | + } |
3370 | + } |
3371 | + |
3372 | + //this is default |
3373 | + m_cleanup_in_progress = false; |
3374 | + |
3375 | } |
3376 | |
3377 | /** |
3378 | @@ -493,7 +505,9 @@ |
3379 | Map & map = egbase.map(); |
3380 | std::vector<Coords> dock = map.find_portdock(get_position()); |
3381 | if (dock.empty()) { |
3382 | - log("Attempting to setup port without neighboring water.\n"); |
3383 | + log("Attempting to setup port without neighboring water (coords: %3dx%3d).\n", |
3384 | + get_position().x, |
3385 | + get_position().y); |
3386 | return; |
3387 | } |
3388 | |
3389 | @@ -509,6 +523,16 @@ |
3390 | |
3391 | if (get_economy() != nullptr) |
3392 | m_portdock->set_economy(get_economy()); |
3393 | + |
3394 | + // this is just to indicate something wrong is going on |
3395 | + //(tiborb) |
3396 | + PortDock* pd_tmp = m_portdock; |
3397 | + if (!pd_tmp->get_fleet()) { |
3398 | + log (" portdock for port at %3dx%3d created but without a fleet!\n", |
3399 | + get_position().x, |
3400 | + get_position().y); |
3401 | + } |
3402 | + |
3403 | } |
3404 | |
3405 | void Warehouse::destroy(EditorGameBase & egbase) |
3406 | @@ -516,11 +540,36 @@ |
3407 | Building::destroy(egbase); |
3408 | } |
3409 | |
3410 | +// if the port still exists and we are in game we first try to restore the portdock |
3411 | +void Warehouse::restore_portdock_or_destroy(EditorGameBase& egbase) { |
3412 | + Warehouse::init_portdock(egbase); |
3413 | + if (!m_portdock) { |
3414 | + log(" Portdock could not be restored, removing the port now (coords: %3dx%3d)\n", |
3415 | + get_position().x, |
3416 | + get_position().y); |
3417 | + Building::destroy(egbase); |
3418 | + } else { |
3419 | + molog ("Message: portdock restored\n"); |
3420 | + PortDock* pd_tmp = m_portdock; |
3421 | + if (!pd_tmp->get_fleet()) { |
3422 | + log (" Portdock restored but without a fleet!\n"); |
3423 | + } |
3424 | + } |
3425 | +} |
3426 | + |
3427 | + |
3428 | /// Destroy the warehouse. |
3429 | -void Warehouse::cleanup(EditorGameBase & egbase) |
3430 | -{ |
3431 | +void Warehouse::cleanup(EditorGameBase& egbase) { |
3432 | + |
3433 | + // if this is a port, it will remove also portdock. |
3434 | + // But portdock must know that it should not try to recreate itself |
3435 | + m_cleanup_in_progress = true; |
3436 | + |
3437 | if (egbase.objects().object_still_available(m_portdock)) { |
3438 | m_portdock->remove(egbase); |
3439 | + } |
3440 | + |
3441 | + if (!egbase.objects().object_still_available(m_portdock)) { |
3442 | m_portdock = nullptr; |
3443 | } |
3444 | |
3445 | |
3446 | === modified file 'src/logic/warehouse.h' |
3447 | --- src/logic/warehouse.h 2015-02-08 18:16:41 +0000 |
3448 | +++ src/logic/warehouse.h 2015-02-10 21:28:25 +0000 |
3449 | @@ -136,6 +136,8 @@ |
3450 | |
3451 | void destroy(EditorGameBase &) override; |
3452 | |
3453 | + void restore_portdock_or_destroy(EditorGameBase &); |
3454 | + |
3455 | void act(Game & game, uint32_t data) override; |
3456 | |
3457 | void set_economy(Economy *) override; |
3458 | @@ -272,6 +274,11 @@ |
3459 | std::vector<PlannedWorkers> m_planned_workers; |
3460 | |
3461 | PortDock * m_portdock; |
3462 | + |
3463 | + //this is information for portdock,to know whether it should |
3464 | + //try to recreate itself |
3465 | + bool m_cleanup_in_progress; |
3466 | + |
3467 | }; |
3468 | |
3469 | } |
3470 | |
3471 | === modified file 'src/notifications/note_ids.h' |
3472 | --- src/notifications/note_ids.h 2014-11-23 14:34:38 +0000 |
3473 | +++ src/notifications/note_ids.h 2015-02-10 21:28:25 +0000 |
3474 | @@ -32,8 +32,9 @@ |
3475 | FieldPossession, |
3476 | FieldTransformed, |
3477 | ProductionSiteOutOfResources, |
3478 | - |
3479 | + ShipMessage, |
3480 | GraphicResolutionChanged, |
3481 | + |
3482 | }; |
3483 | |
3484 | #endif // end of include guard: WL_NOTIFICATIONS_NOTE_IDS_H |
3485 | |
3486 | === modified file 'src/wui/shipwindow.cc' |
3487 | --- src/wui/shipwindow.cc 2014-11-30 18:49:38 +0000 |
3488 | +++ src/wui/shipwindow.cc 2015-02-10 21:28:25 +0000 |
3489 | @@ -70,7 +70,7 @@ |
3490 | void act_cancel_expedition(); |
3491 | void act_scout_towards(uint8_t); |
3492 | void act_construct_port(); |
3493 | - void act_explore_island(bool); |
3494 | + void act_explore_island(ScoutingDirection); |
3495 | |
3496 | private: |
3497 | InteractiveGameBase & m_igbase; |
3498 | @@ -120,7 +120,7 @@ |
3499 | m_btn_explore_island_cw = |
3500 | make_button |
3501 | (exp_top, "expcw", _("Explore the island’s coast clockwise"), pic_explore_cw, |
3502 | - boost::bind(&ShipWindow::act_explore_island, this, true)); |
3503 | + boost::bind(&ShipWindow::act_explore_island, this, ScoutingDirection::kClockwise)); |
3504 | exp_top->add(m_btn_explore_island_cw, 0, false); |
3505 | |
3506 | m_btn_scout[WALK_NE - 1] = |
3507 | @@ -156,7 +156,7 @@ |
3508 | m_btn_explore_island_ccw = |
3509 | make_button |
3510 | (exp_bot, "expccw", _("Explore the island’s coast counter clockwise"), pic_explore_ccw, |
3511 | - boost::bind(&ShipWindow::act_explore_island, this, false)); |
3512 | + boost::bind(&ShipWindow::act_explore_island, this, ScoutingDirection::kCounterClockwise)); |
3513 | exp_bot->add(m_btn_explore_island_ccw, 0, false); |
3514 | |
3515 | m_btn_scout[WALK_SE - 1] = |
3516 | @@ -327,7 +327,7 @@ |
3517 | } |
3518 | |
3519 | /// Explores the island cw or ccw |
3520 | -void ShipWindow::act_explore_island(bool cw) { |
3521 | +void ShipWindow::act_explore_island(ScoutingDirection direction) { |
3522 | bool coast_nearby = false; |
3523 | bool moveable = false; |
3524 | for (Direction dir = 1; (dir <= LAST_DIRECTION) && (!coast_nearby || !moveable); ++dir) { |
3525 | @@ -338,7 +338,7 @@ |
3526 | } |
3527 | if (!coast_nearby || !moveable) |
3528 | return; |
3529 | - m_igbase.game().send_player_ship_explore_island(m_ship, cw); |
3530 | + m_igbase.game().send_player_ship_explore_island(m_ship, direction); |
3531 | } |
3532 | |
3533 | |
3534 | |
3535 | === modified file 'test/maps/ship_transportation.wmf/scripting/init.lua' |
3536 | --- test/maps/ship_transportation.wmf/scripting/init.lua 2014-08-01 15:30:12 +0000 |
3537 | +++ test/maps/ship_transportation.wmf/scripting/init.lua 2015-02-10 21:28:25 +0000 |
3538 | @@ -37,6 +37,23 @@ |
3539 | return nil |
3540 | end |
3541 | |
3542 | +function portdock2() |
3543 | + local portdock = map:get_field(15, 4).immovable |
3544 | + if portdock then |
3545 | + return portdock |
3546 | + end |
3547 | + local portdock = map:get_field(14, 5).immovable |
3548 | + if portdock then |
3549 | + return portdock |
3550 | + end |
3551 | + local portdock = map:get_field(14, 4).immovable |
3552 | + if portdock then |
3553 | + return portdock |
3554 | + end |
3555 | + print ("portdock not found") |
3556 | + return nill |
3557 | +end |
3558 | + |
3559 | function start_building_farm() |
3560 | p1:place_building("farm", map:get_field(18, 4), true, true) |
3561 | connected_road(p1, map:get_field(18,5).immovable, "l,l|tl,tr|", true) |
3562 | |
3563 | === added file 'test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua' |
3564 | --- test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua 1970-01-01 00:00:00 +0000 |
3565 | +++ test/maps/ship_transportation.wmf/scripting/test_rip_portdock_with_worker_and_ware_in_transit.lua 2015-02-10 21:28:25 +0000 |
3566 | @@ -0,0 +1,54 @@ |
3567 | +run(function() |
3568 | + sleep(100) |
3569 | + game.desired_speed = 10 * 1000 |
3570 | + |
3571 | + create_first_port() |
3572 | + create_second_port() |
3573 | + |
3574 | + --removing builder from port2 |
3575 | + port2():set_workers{ |
3576 | + builder = 0, |
3577 | + } |
3578 | + |
3579 | + assert_equal(port2():get_workers("builder"), 0) |
3580 | + |
3581 | + start_building_farm() |
3582 | + port1():set_workers{ |
3583 | + builder = 1, |
3584 | + } |
3585 | + port1():set_wares{ |
3586 | + blackwood = 1, |
3587 | + } |
3588 | + |
3589 | + assert_equal(port1():get_workers("builder"), 1) |
3590 | + assert_equal(port1():get_wares("blackwood"), 1) |
3591 | + |
3592 | + while ship:get_workers() == 0 or ship:get_wares() == 0 do |
3593 | + sleep(500) |
3594 | + end |
3595 | + |
3596 | + local flag_oversea = port2().flag |
3597 | + |
3598 | + stable_save("restored_port") |
3599 | + |
3600 | + -- remove the portdock while the blackwood is in transit. |
3601 | + portdock2():remove() |
3602 | + |
3603 | + sleep(5000) |
3604 | + |
3605 | + assert_equal(p1:get_workers("builder"), 1) |
3606 | + assert_equal(p1:get_wares("blackwood"), 1) |
3607 | + assert_equal(ship.debug_economy, port1().debug_economy) |
3608 | + assert_equal(ship.debug_economy, flag_oversea.debug_economy) |
3609 | + |
3610 | + sleep(5000) |
3611 | + |
3612 | + --just wait till everything is gone to port2 |
3613 | + while ship:get_workers() > 0 or ship:get_wares() > 0 or |
3614 | + port1():get_workers("builder") > 0 or port1():get_wares("blackwood") > 0 do |
3615 | + sleep(50) |
3616 | + end |
3617 | + |
3618 | + print("# All Tests passed.") |
3619 | + wl.ui.MapView():close() |
3620 | +end) |
3621 | |
3622 | === modified file 'tribes/atlanteans/bread/conf' |
3623 | --- tribes/atlanteans/bread/conf 2014-07-28 14:04:36 +0000 |
3624 | +++ tribes/atlanteans/bread/conf 2015-02-10 21:28:25 +0000 |
3625 | @@ -1,7 +1,7 @@ |
3626 | help=_This tasty bread is made in bakeries out of cornflour, blackroot flour and water. It is appreciated as basic food for miners, scouts and soldiers in training sites (labyrinth and dungeon). |
3627 | |
3628 | default_target_quantity=20 |
3629 | -preciousness=2 |
3630 | +preciousness=5 |
3631 | |
3632 | # No description yet |
3633 | [idle] |
3634 | |
3635 | === modified file 'tribes/empire/bread/conf' |
3636 | --- tribes/empire/bread/conf 2014-07-28 14:04:36 +0000 |
3637 | +++ tribes/empire/bread/conf 2015-02-10 21:28:25 +0000 |
3638 | @@ -1,7 +1,7 @@ |
3639 | help=_The bakers of the Empire make really tasty bread out of flour and water. It is used in taverns and inns to prepare rations and meals. Bread is also consumed at the training sites (arena, colosseum, training camp). |
3640 | |
3641 | default_target_quantity=20 |
3642 | -preciousness=4 |
3643 | +preciousness=5 |
3644 | |
3645 | [idle] |
3646 | pics=bread_idle.png |
3647 | |
3648 | === modified file 'tribes/empire/meal/conf' |
3649 | --- tribes/empire/meal/conf 2013-07-23 19:04:12 +0000 |
3650 | +++ tribes/empire/meal/conf 2015-02-10 21:28:25 +0000 |
3651 | @@ -1,7 +1,7 @@ |
3652 | help=_A real meal is made in inns out of bread and fish/meat. It satisfies the needs of miners in deep mines. |
3653 | |
3654 | default_target_quantity=20 |
3655 | -preciousness=5 |
3656 | +preciousness=4 |
3657 | |
3658 | [idle] |
3659 | pics=idle.png |
3660 | |
3661 | === modified file 'tribes/empire/ration/conf' |
3662 | --- tribes/empire/ration/conf 2014-07-28 14:04:36 +0000 |
3663 | +++ tribes/empire/ration/conf 2015-02-10 21:28:25 +0000 |
3664 | @@ -1,7 +1,7 @@ |
3665 | help=_A small bite to keep miners strong and working. Rations are also consumed by the scout on his scouting trips. They are produced in a tavern out of fish or meat or bread. |
3666 | |
3667 | default_target_quantity=20 |
3668 | -preciousness=5 |
3669 | +preciousness=4 |
3670 | |
3671 | [idle] |
3672 | pics=idle.png |
Also I run codecheck over couple of files that I had edited, therefore the diff shows quite a lot of cosmetic changes.