Merge lp:~widelands-dev/widelands/seafaring-ai into lp:widelands

Proposed by TiborB
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
Reviewer Review Type Date Requested Status
SirVer Approve
GunChleoc Approve
TiborB Needs Resubmitting
Review via email: mp+242271@code.launchpad.net

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.

To post a comment you must log in.
Revision history for this message
TiborB (tiborb95) wrote :

Also I run codecheck over couple of files that I had edited, therefore the diff shows quite a lot of cosmetic changes.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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

Revision history for this message
GunChleoc (gunchleoc) wrote :

Running clang-format is good :) Doesn't work on my system though :(

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
TiborB (tiborb95) wrote :

correction: allships is a list, not a vector, as I believed, sorry

Revision history for this message
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 :)

Revision history for this message
TiborB (tiborb95) wrote :

This is mystery to me, see two lines:

//ref_cast<ConstructionSite const, Building const>(b).building().name().c_str());
dynamic_cast<const ConstructionSite&>((b).building().name().c_str()));

commented out is before, the second one is what I want to use. Before it used to compile, now it complains:

defaultai.cc:3766:48: error: 'const class Widelands::Building' has no member named 'building'

Can you see any problem here?

Revision history for this message
GunChleoc (gunchleoc) wrote :

> //ref_cast<ConstructionSite const, Building const>(b).building().name().c_str());
> dynamic_cast<const ConstructionSite&>((b).building().name().c_str()));
>
> commented out is before, the second one is what I want to use. Before it used to compile, now it complains:
>
> defaultai.cc:3766:48: error: 'const class Widelands::Building' has no member named 'building'
>
> Can you see any problem here?

You are casting everything instead of just the construction site. Try this:

dynamic_cast<const ConstructionSite&>(b).building().name().c_str());

Revision history for this message
TiborB (tiborb95) wrote :

So I implemented comments I saw there, merged trunk, resolved conflicts :(

Revision history for this message
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.

Revision history for this message
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. :)

Revision history for this message
SirVer (sirver) wrote :

I think you can maximize the fun by making smaller branches. They are faster to merge, clean and review.

Revision history for this message
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 :)

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
TiborB (tiborb95) wrote :

@GunChleoc

Thanks for testing:)

1. In fact the ship algorithm (I mean not part of AI) sometimes incorectly reports islandcircumnavigated (signal), even though nothing were circumnavigated, only banks are too close. And AI is not able to find out the the signal is errorneous. When I tested seafaring AI I edited all such spots on my maps where ships got stucked - making passages wider.

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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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.

review: Needs Fixing
Revision history for this message
TiborB (tiborb95) wrote :

Added some answers to the comments.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I added a comment to one of your comments :)

Revision history for this message
TiborB (tiborb95) wrote :

I cannot see it. What line number?

Revision history for this message
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://bugs.launchpad.net/widelands/+bug/1367725

Revision history for this message
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...

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks, this makes the diff a lot smaller :)

Give us a shout when you're ready again.

Revision history for this message
TiborB (tiborb95) wrote :

Alright, I fixed what remained. Yet there is one NOCOM with question :)

Revision history for this message
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::kDoNothing;

etc...

Note that I also renamed fleetStatus to FleetStatus, because it's an Object class.

Revision history for this message
TiborB (tiborb95) wrote :

Oh, I believed uint8_t is the type. Then 'uint8_t' is not needed at all, is it?

Revision history for this message
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 :)

Revision history for this message
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://bazaar.launchpad.net/~widelands-dev/widelands/seafaring-ai/view/head:/src/logic/playercommand.cc#L787 (+ 2 similar test below)

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.

Revision history for this message
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.

Revision history for this message
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...

Revision history for this message
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.

Revision history for this message
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.

review: Needs Resubmitting
Revision history for this message
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...

Revision history for this message
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.

Revision history for this message
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 :)

Revision history for this message
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 :)

Revision history for this message
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

Revision history for this message
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.

Revision history for this message
TiborB (tiborb95) wrote :

of course

I tested only AI-only games, because I dont have enough time for a real game...

Revision history for this message
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.

review: Needs Fixing
Revision history for this message
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?

Revision history for this message
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_initialization() to create the invariants that the AI relies on. You can check if the buildings vector is also filled if you skip most work in late_initialization.

Revision history for this message
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...

Revision history for this message
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_initialization, see here:

http://bazaar.launchpad.net/~widelands-dev/widelands/seafaring-ai/view/head:/src/ai/defaultai.cc#L574

So I am working for similar code to scan for ships and will push it to the branch once it is tested and polished ...

Revision history for this message
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...

review: Needs Resubmitting
Revision history for this message
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.

Revision history for this message
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...

Revision history for this message
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...

Revision history for this message
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.

review: Approve
Revision history for this message
SirVer (sirver) wrote :

ship it! (see what I did there :))

review: Approve
Revision history for this message
GunChleoc (gunchleoc) wrote :

Houston, we have e problem: test_rip_first_port_with_worker_in_portdock.lua hangs.

review: Needs Fixing
Revision history for this message
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....

Revision history for this message
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.

Revision history for this message
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...

review: Needs Resubmitting
Revision history for this message
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.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I see the same as SirVer - tests work in trunk.

Revision history for this message
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_test.py -b ./widelands -r test_rip_second_port_with_worker_in_portdock.lua -k
Using './widelands' binary.
test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua ...
  Running Widelands ... FAIL

======================================================================
FAIL: test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./regression_test.py", line 99, in runTest
    self.verify_success(stdout, stdout_filename)
  File "./regression_test.py", line 122, in verify_success
    "Widelands exited abnormally. %s" % common_msg
AssertionError: Widelands exited abnormally. Analyze the files in /tmp/widelands_regression_testbVVt4p to see why this test case failed. Stdout is
  /tmp/widelands_regression_testbVVt4p/stdout_00.txt

----------------------------------------------------------------------
Ran 1 test in 21.740s

FAILED (failures=1)

Content of log file:

$cat /tmp/widelands_regression_testKNpEbT/stdout_00.txt
[.....]
MO(5,portdock): set_need_ship(false)
MO(5,portdock): ... trigger fleet update
# All Tests passed. <= NOTE HERE
WareList: 1 items of 8 left.
widelands: /usr/include/boost/signals2/detail/lwm_pthreads.hpp:60: void boost::signals2::mutex::lock(): Assertion `pthread_mutex_lock(&m_) == 0' failed.

I will double check with actual trunk tonight

Revision history for this message
TiborB (tiborb95) wrote :

so I checked on actual trunk - the same problem....

?

Revision history for this message
SirVer (sirver) wrote :

Maybe try updating boost? Seems like a boost bug in what you see there.

Revision history for this message
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_first_port_with_worker_in_portdock.lua without running the whole testsuite?
./widelands --scenario=test/maps/ship_transportation.wmf doesn't work.

Revision history for this message
SirVer (sirver) wrote :

./regression_test.py -r test_rip_first_port_with_worker

-r searches for the given regular expression in the test name (combination of map + lua file).

Revision history for this message
TiborB (tiborb95) wrote :

the file is test_rip_second_port_with_worker_in_portdock.lua

After some investigation I found that folowing command will run just particular script:

./widelands --verbose=true --datadir=. --disable_fx=true --disable_music=true --language=en_US --scenario=test/maps/ship_transportation.wmf --script=test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua

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/lib/libc.so.6
#2 0xb7807589 in abort () from /usr/lib/lib/libc.so.6
#3 0xb77ff027 in __assert_fail_base () from /usr/lib/lib/libc.so.6
#4 0xb77ff0ab in __assert_fail () from /usr/lib/lib/libc.so.6
#5 0x0877261f in boost::signals2::mutex::lock (this=0xa9b3d14) at /usr/include/boost/signals2/detail/lwm_pthreads.hpp:60
#6 0x08775874 in boost::signals2::detail::unique_lock<boost::signals2::mutex>::unique_lock (this=0xbfffefdc, m=...)
    at /usr/include/boost/signals2/detail/unique_lock.hpp:29
#7 0x08888105 in boost::signals2::detail::signal_impl<void (), boost::signals2::optional_last_value<void>, int, std::less<int>, boost::function<void ()>, boost::function<void (boost::signals2::connection const&)>, boost::signals2::mutex>::operator()() (
    this=0xa9b3d08) at /usr/include/boost/signals2/detail/signal_template.hpp:229
#8 0x08886f63 in boost::signals2::signal<void (), boost::signals2::optional_last_value<void>, int, std::less<int>, boost::function<void ()>, boost::function<void (boost::signals2::connection const&)>, boost::signals2::mutex>::operator()() (this=0xa9b3c30)
    at /usr/include/boost/signals2/detail/signal_template.hpp:718
#9 0x088c9690 in Widelands::WareList::remove (this=0xa9b3c30, i=8 '\b', count=1) at ../src/logic/warelist.cc:80
#10 0x08adc7df in Widelands::Economy::remove_workers (this=0xa9b3c08, id=8 '\b', count=1) at ../src/economy/economy.cc:401
#11 0x088cdcdd in Widelands::Worker::set_economy (this=0xa9976c0, economy=0x0) at ../src/logic/worker.cc:1168
#12 0x088cf2cc in Widelands::Worker::shipping_pop (this=0xa9976c0, game=...) at ../src/logic/worker.cc:1644
#13 0x0880ba6e in Widelands::Bob::do_pop_task (this=0xa9976c0, game=...) at ../src/logic/bob.cc:283
#14 0x0880be0c in Widelands::Bob::reset_tasks (this=0xa9976c0, game=...) at ../src/logic/bob.cc:388
#15 0x088cdef2 in Widelands::Worker::cleanup (this=0xa9976c0, egbase=...) at ../src/logic/worker.cc:1220
#16 0x08841ee2 in Widelands::MapObject::remove (this=0xa9976c0, egbase=...) at ../src/logic/instances.cc:369
#17 0x08841514 in Widelands::ObjectManager::cleanup (this=0xbffff490, egbase=...) at ../src/logic/instances.cc:161
#18 0x08772be8 in Widelands::EditorGameBase::cleanup_objects (this=0xbffff47c) at ../src/logic/editor_game_base.h:180
#19 0x0882a7a7 in Widelands::Game::run (this=0xbffff47c, loader_ui=0xbffff2b4, start_game_type=Widelands::Game::NewSPScenario,
script_to_run="test/maps/ship_transportation.wmf/scripting/test_rip_second_port_with_worker_in_portdock.lua", replay=false)

This was trunk...

Revision history for this message
TiborB (tiborb95) wrote :

boost version: 1.57.0 (installed, it is arch linux, and also widelands reports this version when compiling)

Revision history for this message
Tino (tino79) wrote :

I am not sure if this is related:
https://github.com/boostorg/signals2/pull/8/files

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.

Revision history for this message
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.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I'm still on Boost 1.54

Revision history for this message
GunChleoc (gunchleoc) wrote :

Test won't run at all with ./regression_test.py -r test_rip_first_port_with_worker_in_portdock - I get a file not found. Tibor's method works though, here is the relevant output:

Trying to run: map:scripting/init.lua: done
Trying to run: test/maps/ship_transportation.wmf/scripting/test_rip_first_port_with_worker_in_portdock.lua: done
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::save_game() took 53ms
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.

Revision history for this message
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://bazaar.launchpad.net/~widelands-dev/widelands/seafaring-ai/view/head:/src/logic/worker.cc#L1644 is used - it crashes.

It need further investigation and also I am not very glad to have to tinker with these parts of code.

Revision history for this message
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...

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
SirVer (sirver) wrote :

Have you tried (temporarily)reverting the change that recreates port docks? It seems the most likely culprit for me.

> 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://code.launchpad.net/~widelands-dev/widelands/seafaring-ai/+merge/242271
> You are reviewing the proposed merge of lp:~widelands-dev/widelands/seafaring-ai into lp:widelands.

Revision history for this message
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...

Revision history for this message
TiborB (tiborb95) wrote :

OK, I moved forward. I reworked the restoration of portdock, returned a bit to initial design and now test_rip_first_port_with_*_in_portdock.lua passed, but test_rip_second_port_with_ware_in_portdock.lua still crashes with that boost error. (As I said it crashes when game is shutting)

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.

Revision history for this message
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.

Revision history for this message
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.

review: Approve
Revision history for this message
TiborB (tiborb95) wrote :

I just completed tests on my debian PC - all passed - I just dont know....

Revision history for this message
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?

review: Approve
Revision history for this message
TiborB (tiborb95) wrote :

SirVer - I added such test: test_rip_portdock_with_worker_and_ware_in_transit.lua

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.

Revision history for this message
GunChleoc (gunchleoc) wrote :
Revision history for this message
SirVer (sirver) wrote :

> SirVer - I added such test: test_rip_portdock_with_worker_and_ware_in_transit.lua

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.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks, Tibor. I will take care of the merging :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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, &gtribe)
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, &gtribe) {
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

Subscribers

People subscribed via source and target branches

to status/vote changes: