Merge branch 'develop' of github.com:eressea/server into develop

This commit is contained in:
Enno Rehling 2015-11-26 16:38:23 +01:00
commit 7dd4475939
13 changed files with 92 additions and 73 deletions

View File

@ -883,7 +883,7 @@
</race> </race>
<race name="seaserpent" magres="0.500000" maxaura="1.0" regaura="1.0" weight="20000" capacity="5000" speed="1.0" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes"> <race name="seaserpent" magres="0.500000" maxaura="1.0" regaura="1.0" weight="20000" capacity="5000" speed="1.0" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes">
<ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes"/> <ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes" moveattack="yes"/>
<function name="name" value="namegeneric"/> <function name="name" value="namegeneric"/>
<function name="move" value="moveswimming"/> <function name="move" value="moveswimming"/>
<skill name="tactics" modifier="4"/> <skill name="tactics" modifier="4"/>

View File

@ -1172,7 +1172,7 @@
<attack type="1" damage="1d1"/> <attack type="1" damage="1d1"/>
</race> </race>
<race name="seaserpent" magres="0.500000" maxaura="1.000000" regaura="1.000000" recruitcost="5000" weight="20000" capacity="5000" speed="1.000000" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes" unarmedguard="yes"> <race name="seaserpent" magres="0.500000" maxaura="1.000000" regaura="1.000000" recruitcost="5000" weight="20000" capacity="5000" speed="1.000000" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes" unarmedguard="yes">
<ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes"/> <ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes" moveattack="yes"/>
<function name="name" value="namegeneric"/> <function name="name" value="namegeneric"/>
<function name="move" value="moveswimming"/> <function name="move" value="moveswimming"/>
<skill name="tactics" modifier="4"/> <skill name="tactics" modifier="4"/>

View File

@ -200,6 +200,7 @@ set(TESTS_SRC
laws.test.c laws.test.c
magic.test.c magic.test.c
market.test.c market.test.c
monsters.test.c
move.test.c move.test.c
piracy.test.c piracy.test.c
prefix.test.c prefix.test.c

View File

@ -214,6 +214,7 @@ extern "C" {
#define RCF_SHIPSPEED (1<<26) /* race gets +1 on shipspeed */ #define RCF_SHIPSPEED (1<<26) /* race gets +1 on shipspeed */
#define RCF_STONEGOLEM (1<<27) /* race gets stonegolem properties */ #define RCF_STONEGOLEM (1<<27) /* race gets stonegolem properties */
#define RCF_IRONGOLEM (1<<28) /* race gets irongolem properties */ #define RCF_IRONGOLEM (1<<28) /* race gets irongolem properties */
#define RCF_ATTACK_MOVED (1<<29) /* may attack if it has moved */
/* Economic flags */ /* Economic flags */
#define ECF_KEEP_ITEM (1<<1) /* gibt Gegenstände weg */ #define ECF_KEEP_ITEM (1<<1) /* gibt Gegenstände weg */

View File

@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "curse.h" #include "curse.h"
#include "item.h" #include "item.h"
#include "move.h" #include "move.h"
#include "monster.h"
#include "order.h" #include "order.h"
#include "plane.h" #include "plane.h"
#include "race.h" #include "race.h"
@ -56,6 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <util/lists.h> #include <util/lists.h>
#include <util/log.h> #include <util/log.h>
#include <util/parser.h> #include <util/parser.h>
#include <util/rand.h>
#include <util/resolve.h> #include <util/resolve.h>
#include <util/rng.h> #include <util/rng.h>
#include <util/variant.h> #include <util/variant.h>
@ -1125,10 +1127,11 @@ void set_number(unit * u, int count)
u->number = (unsigned short)count; u->number = (unsigned short)count;
} }
bool learn_skill(unit * u, skill_t sk, double chance) bool learn_skill(unit * u, skill_t sk, double learn_chance)
{ {
skill *sv = u->skills; skill *sv = u->skills;
if (chance < 1.0 && rng_int() % 10000 >= chance * 10000) if (learn_chance < 1.0 && rng_int() % 10000 >= learn_chance * 10000)
if (!chance(learn_chance))
return false; return false;
while (sv != u->skills + u->skill_size) { while (sv != u->skills + u->skill_size) {
assert(sv->weeks > 0); assert(sv->weeks > 0);
@ -1891,7 +1894,7 @@ static double produceexp_chance(void) {
void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double)) void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double))
{ {
if (n != 0 && playerrace(u_race(u))) { if (n != 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) {
double chance = produceexp_chance(); double chance = produceexp_chance();
if (chance > 0.0F) { if (chance > 0.0F) {
learn(u, sk, (n * chance) / u->number); learn(u, sk, (n * chance) / u->number);

View File

@ -1604,6 +1604,8 @@ static void parse_ai(race * rc, xmlNodePtr node)
rc->flags |= RCF_MOVERANDOM; rc->flags |= RCF_MOVERANDOM;
if (xml_bvalue(node, "learn", false)) if (xml_bvalue(node, "learn", false))
rc->flags |= RCF_LEARN; rc->flags |= RCF_LEARN;
if (xml_bvalue(node, "moveattack", false))
rc->flags |= RCF_ATTACK_MOVED;
} }
static int parse_races(xmlDocPtr doc) static int parse_races(xmlDocPtr doc)

View File

@ -657,7 +657,7 @@ static void test_unarmed_races_can_guard(CuTest *tc) {
setup_guard(&fix, false); setup_guard(&fix, false);
rc = rc_get_or_create(fix.u->_race->_name); rc = rc_get_or_create(fix.u->_race->_name);
rc->flags |= RCF_UNARMEDGUARD; fset(rc, RCF_UNARMEDGUARD);
CuAssertIntEquals(tc, E_GUARD_OK, can_start_guarding(fix.u)); CuAssertIntEquals(tc, E_GUARD_OK, can_start_guarding(fix.u));
update_guards(); update_guards();
CuAssertTrue(tc, fval(fix.u, UFL_GUARD)); CuAssertTrue(tc, fval(fix.u, UFL_GUARD));

View File

@ -69,7 +69,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
bool monster_is_waiting(const unit * u) bool monster_is_waiting(const unit * u)
{ {
if (fval(u, UFL_ISNEW | UFL_MOVED)) int test = fval(u_race(u), RCF_ATTACK_MOVED) ? UFL_ISNEW : UFL_ISNEW | UFL_MOVED;
if (fval(u, test))
return true; return true;
return false; return false;
} }

View File

@ -71,7 +71,7 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#define MOVECHANCE 25 /* chance fuer bewegung */ #define MOVECHANCE .25 /* chance fuer bewegung */
#define DRAGON_RANGE 20 /* Max. Distanz zum nächsten Drachenziel */ #define DRAGON_RANGE 20 /* Max. Distanz zum nächsten Drachenziel */
#define MAXILLUSION_TEXTS 3 #define MAXILLUSION_TEXTS 3
@ -83,6 +83,10 @@ static void give_peasants(unit *u, const item_type *itype, int reduce) {
unit_addorder(u, parse_order(buf, u->faction->locale)); unit_addorder(u, parse_order(buf, u->faction->locale));
} }
static double random_move_chance(void) {
return config_get_flt("rules.monsters.random_move_chance", MOVECHANCE);
}
static void reduce_weight(unit * u) static void reduce_weight(unit * u)
{ {
int capacity, weight = 0; int capacity, weight = 0;
@ -155,9 +159,30 @@ static order *monster_attack(unit * u, const unit * target)
return create_order(K_ATTACK, u->faction->locale, "%i", target->no); return create_order(K_ATTACK, u->faction->locale, "%i", target->no);
} }
int monster_attacks(unit * monster, bool respect_buildings, bool rich_only)
{
region *r = monster->region;
unit *u2;
int money = 0;
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->faction != monster->faction && cansee(monster->faction, r, u2, 0) && !in_safe_building(u2, monster)) {
int m = get_money(u2);
if (!rich_only || m > 0) {
order *ord = monster_attack(monster, u2);
if (ord) {
addlist(&monster->orders, ord);
money += m;
}
}
}
}
return money;
}
static order *get_money_for_dragon(region * r, unit * udragon, int wanted) static order *get_money_for_dragon(region * r, unit * udragon, int wanted)
{ {
int n; int money;
bool attacks = attack_chance > 0.0; bool attacks = attack_chance > 0.0;
/* falls genug geld in der region ist, treiben wir steuern ein. */ /* falls genug geld in der region ist, treiben wir steuern ein. */
@ -171,26 +196,14 @@ static order *get_money_for_dragon(region * r, unit * udragon, int wanted)
/* falls der drache launisch ist, oder das regionssilber knapp, greift er alle an /* falls der drache launisch ist, oder das regionssilber knapp, greift er alle an
* und holt sich Silber von Einheiten, vorausgesetzt er bewacht bereits */ * und holt sich Silber von Einheiten, vorausgesetzt er bewacht bereits */
n = 0; money = 0;
if (attacks && is_guard(udragon, GUARD_TAX)) { if (attacks && is_guard(udragon, GUARD_TAX)) {
unit *u; money += monster_attacks(udragon, true, true);
for (u = r->units; u; u = u->next) {
if (u->faction != udragon->faction && cansee(udragon->faction, r, u, 0) && !in_safe_building(u, udragon)) {
int m = get_money(u);
if (m != 0) {
order *ord = monster_attack(udragon, u);
if (ord) {
addlist(&udragon->orders, ord);
n += m;
}
}
}
}
} }
/* falls die einnahmen erreicht werden, bleibt das monster noch eine */ /* falls die einnahmen erreicht werden, bleibt das monster noch eine */
/* runde hier. */ /* runde hier. */
if (n + rmoney(r) >= wanted) { if (money + rmoney(r) >= wanted) {
return create_order(K_LOOT, default_locale, NULL); return create_order(K_LOOT, default_locale, NULL);
} }
@ -397,10 +410,10 @@ static int dragon_affinity_value(region * r, unit * u)
int m = all_money(r, u->faction); int m = all_money(r, u->faction);
if (u_race(u) == get_race(RC_FIREDRAGON)) { if (u_race(u) == get_race(RC_FIREDRAGON)) {
return (int)(normalvariate(m, m / 2)); return dice(4, m / 2);
} }
else { else {
return (int)(normalvariate(m, m / 4)); return dice(6, m / 3);
} }
} }
@ -536,21 +549,6 @@ static order *monster_seeks_target(region * r, unit * u)
} }
#endif #endif
static void monster_attacks(unit * monster)
{
region *r = monster->region;
unit *u;
for (u = r->units; u; u = u->next) {
if (u->faction != monster->faction && cansee(monster->faction, r, u, 0) && !in_safe_building(u, monster)) {
order *ord = monster_attack(monster, u);
if (ord) {
addlist(&monster->orders, ord);
}
}
}
}
static const char *random_growl(void) static const char *random_growl(void)
{ {
switch (rng_int() % 5) { switch (rng_int() % 5) {
@ -742,9 +740,10 @@ static order *plan_dragon(unit * u)
} }
} }
if (long_order == NULL) { if (long_order == NULL) {
int attempts = 0;
skill_t sk = SK_PERCEPTION; skill_t sk = SK_PERCEPTION;
/* study perception (or a random useful skill) */ /* study perception (or a random useful skill) */
while (!skill_enabled(sk) || u_race(u)->bonus[sk] < -5) { while ((!skill_enabled(sk) || (attempts < MAXSKILLS && u_race(u)->bonus[sk] < (++attempts < 10?1:-5 )))) {
sk = (skill_t)(rng_int() % MAXSKILLS); sk = (skill_t)(rng_int() % MAXSKILLS);
} }
long_order = create_order(K_STUDY, u->faction->locale, "'%s'", long_order = create_order(K_STUDY, u->faction->locale, "'%s'",
@ -763,7 +762,7 @@ void plan_monsters(faction * f)
for (r = regions; r; r = r->next) { for (r = regions; r; r = r->next) {
unit *u; unit *u;
bool attacking = false; bool attacking = chance(attack_chance);
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
attrib *ta; attrib *ta;
@ -777,20 +776,17 @@ void plan_monsters(faction * f)
free_orders(&u->orders); free_orders(&u->orders);
if (skill_enabled(SK_PERCEPTION)) { if (skill_enabled(SK_PERCEPTION)) {
/* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */ /* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */
/* TODO: this only works for playerrace */
produceexp(u, SK_PERCEPTION, u->number); produceexp(u, SK_PERCEPTION, u->number);
} }
if (!attacking) {
if (chance(attack_chance)) attacking = true;
}
if (u->status > ST_BEHIND) { if (u->status > ST_BEHIND) {
setstatus(u, ST_FIGHT); setstatus(u, ST_FIGHT);
/* all monsters fight */ /* all monsters fight */
} }
if (attacking && (!r->land || is_guard(u, GUARD_TAX))) { if (attacking && (!r->land || is_guard(u, GUARD_TAX))) {
monster_attacks(u); monster_attacks(u, true, false);
} }
/* units with a plan to kill get ATTACK orders: */ /* units with a plan to kill get ATTACK orders: */
ta = a_find(u->attribs, &at_hate); ta = a_find(u->attribs, &at_hate);
if (ta && !monster_is_waiting(u)) { if (ta && !monster_is_waiting(u)) {
@ -825,24 +821,12 @@ void plan_monsters(faction * f)
} }
} }
else if (u_race(u)->flags & RCF_MOVERANDOM) { else if (u_race(u)->flags & RCF_MOVERANDOM) {
if (rng_int() % 100 < MOVECHANCE || check_overpopulated(u)) { if (chance(random_move_chance()) || check_overpopulated(u)) {
long_order = monster_move(r, u); long_order = monster_move(r, u);
} }
} }
} }
if (long_order == NULL && unit_can_study(u)) {
/* Einheiten, die Waffenlosen Kampf lernen könnten, lernen es um
* zu bewachen: */
if (u_race(u)->bonus[SK_WEAPONLESS] != -99) {
if (effskill(u, SK_WEAPONLESS, 0) < 1) {
long_order =
create_order(K_STUDY, f->locale, "'%s'",
skillname(SK_WEAPONLESS, f->locale));
}
}
}
if (long_order == NULL) { if (long_order == NULL) {
/* Ab hier noch nicht generalisierte Spezialbehandlungen. */ /* Ab hier noch nicht generalisierte Spezialbehandlungen. */
@ -871,6 +855,18 @@ void plan_monsters(faction * f)
break; break;
} }
} }
if (long_order == NULL && unit_can_study(u)) {
/* Einheiten, die Waffenlosen Kampf lernen könnten, lernen es um
* zu bewachen: */
if (u_race(u)->bonus[SK_WEAPONLESS] != -99) {
if (effskill(u, SK_WEAPONLESS, 0) < 1) {
long_order =
create_order(K_STUDY, f->locale, "'%s'",
skillname(SK_WEAPONLESS, f->locale));
}
}
}
if (long_order) { if (long_order) {
addlist(&u->orders, long_order); addlist(&u->orders, long_order);
} }

View File

@ -23,6 +23,7 @@
#include <string.h> #include <string.h>
extern void plan_monsters(struct faction *f); extern void plan_monsters(struct faction *f);
extern int monster_attacks(unit * monster, bool respect_buildings, bool rich_only);
static void init_language(void) static void init_language(void)
{ {
@ -130,8 +131,6 @@ static void test_monsters_attack_ocean(CuTest * tc)
test_cleanup(); test_cleanup();
} }
extern int monster_attacks(unit * monster, bool respect_buildings, bool rich_only);
static void test_monsters_waiting(CuTest * tc) static void test_monsters_waiting(CuTest * tc)
{ {
faction *f, *f2; faction *f, *f2;
@ -141,7 +140,7 @@ static void test_monsters_waiting(CuTest * tc)
create_monsters(&f, &f2, &r, &u, &m); create_monsters(&f, &f2, &r, &u, &m);
guard(m, GUARD_TAX); guard(m, GUARD_TAX);
fset(m, UFL_ISNEW); fset(m, UFL_ISNEW);
// FIXME: monster_attacks(m, false, false); monster_attacks(m, false, false);
CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m)); CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m));
test_cleanup(); test_cleanup();
} }
@ -161,12 +160,11 @@ static void test_seaserpent_piracy(CuTest * tc)
u_setrace(m, rc = test_create_race("seaserpent")); u_setrace(m, rc = test_create_race("seaserpent"));
assert(!m->region->land); assert(!m->region->land);
fset(m, UFL_MOVED); fset(m, UFL_MOVED);
// fset(rc, RCF_ATTACK_MOVED); fset(rc, RCF_ATTACK_MOVED);
config_set("rules.monsters.attack_chance", "1"); config_set("rules.monsters.attack_chance", "1");
plan_monsters(f2); plan_monsters(f2);
CuAssertPtrNotNull(tc, find_order("PIRATERIE", m)); CuAssertPtrNotNull(tc, find_order("PIRATERIE", m));
CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m)); CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m));
test_cleanup(); test_cleanup();
@ -265,11 +263,11 @@ CuSuite *get_monsters_suite(void)
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_monsters_attack); SUITE_ADD_TEST(suite, test_monsters_attack);
SUITE_ADD_TEST(suite, test_monsters_attack_ocean); SUITE_ADD_TEST(suite, test_monsters_attack_ocean);
DISABLE_TEST(suite, test_seaserpent_piracy); SUITE_ADD_TEST(suite, test_seaserpent_piracy);
DISABLE_TEST(suite, test_monsters_waiting); SUITE_ADD_TEST(suite, test_monsters_waiting);
SUITE_ADD_TEST(suite, test_monsters_attack_not); SUITE_ADD_TEST(suite, test_monsters_attack_not);
SUITE_ADD_TEST(suite, test_dragon_attacks_the_rich); SUITE_ADD_TEST(suite, test_dragon_attacks_the_rich);
DISABLE_TEST(suite, test_dragon_moves); SUITE_ADD_TEST(suite, test_dragon_moves);
DISABLE_TEST(suite, test_monsters_learn_exp); SUITE_ADD_TEST(suite, test_monsters_learn_exp);
return suite; return suite;
} }

View File

@ -10,6 +10,7 @@
#include <kernel/faction.h> #include <kernel/faction.h>
#include <kernel/messages.h> #include <kernel/messages.h>
#include <kernel/order.h> #include <kernel/order.h>
#include <kernel/race.h>
#include <kernel/region.h> #include <kernel/region.h>
#include <kernel/ship.h> #include <kernel/ship.h>
#include <kernel/terrain.h> #include <kernel/terrain.h>
@ -65,6 +66,8 @@ static attrib *mk_piracy(const faction * pirate, const faction * target,
} }
static bool validate_pirate(unit *u, order *ord) { static bool validate_pirate(unit *u, order *ord) {
if (fval(u_race(u), RCF_SWIM | RCF_FLY))
return true;
if (!u->ship) { if (!u->ship) {
cmistake(u, ord, 144, MSG_MOVE); cmistake(u, ord, 144, MSG_MOVE);
return false; return false;

View File

@ -8,6 +8,7 @@
#include <kernel/terrain.h> #include <kernel/terrain.h>
#include <kernel/faction.h> #include <kernel/faction.h>
#include <kernel/order.h> #include <kernel/order.h>
#include <kernel/race.h>
#include <util/base36.h> #include <util/base36.h>
#include <util/language.h> #include <util/language.h>
@ -66,6 +67,7 @@ static void test_piracy_cmd(CuTest * tc) {
} }
static void test_piracy_cmd_errors(CuTest * tc) { static void test_piracy_cmd_errors(CuTest * tc) {
race *r;
faction *f; faction *f;
unit *u, *u2; unit *u, *u2;
order *ord; order *ord;
@ -73,7 +75,8 @@ static void test_piracy_cmd_errors(CuTest * tc) {
setup_piracy(); setup_piracy();
st_boat = st_get_or_create("boat"); st_boat = st_get_or_create("boat");
u = test_create_unit(f = test_create_faction(0), test_create_region(0, 0, get_or_create_terrain("ocean"))); r = test_create_race("pirates");
u = test_create_unit(f = test_create_faction(r), test_create_region(0, 0, get_or_create_terrain("ocean")));
f->locale = get_or_create_locale("de"); f->locale = get_or_create_locale("de");
ord = create_order(K_PIRACY, f->locale, ""); ord = create_order(K_PIRACY, f->locale, "");
assert(u && ord); assert(u && ord);
@ -81,6 +84,17 @@ static void test_piracy_cmd_errors(CuTest * tc) {
piracy_cmd(u, ord); piracy_cmd(u, ord);
CuAssertPtrNotNullMsg(tc, "must be on a ship for PIRACY", test_find_messagetype(f->msgs, "error144")); CuAssertPtrNotNullMsg(tc, "must be on a ship for PIRACY", test_find_messagetype(f->msgs, "error144"));
test_clear_messages(f);
fset(r, RCF_SWIM);
piracy_cmd(u, ord);
CuAssertPtrEquals_Msg(tc, "swimmers are pirates", 0, test_find_messagetype(f->msgs, "error144"));
CuAssertPtrEquals_Msg(tc, "swimmers are pirates", 0, test_find_messagetype(f->msgs, "error146"));
freset(r, RCF_SWIM);
fset(r, RCF_FLY);
CuAssertPtrEquals_Msg(tc, "flyers are pirates", 0, test_find_messagetype(f->msgs, "error144"));
freset(r, RCF_FLY);
test_clear_messages(f);
u_set_ship(u, test_create_ship(u->region, st_boat)); u_set_ship(u, test_create_ship(u->region, st_boat));
u2 = test_create_unit(u->faction, u->region); u2 = test_create_unit(u->faction, u->region);

View File

@ -236,10 +236,10 @@ void test_create_world(void)
test_create_itemtype("iron"); test_create_itemtype("iron");
test_create_itemtype("stone"); test_create_itemtype("stone");
t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION); t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO);
t_plain->size = 1000; t_plain->size = 1000;
t_plain->max_road = 100; t_plain->max_road = 100;
t_ocean = test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO); t_ocean = test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO | FLY_INTO);
t_ocean->size = 0; t_ocean->size = 0;
island[0] = test_create_region(0, 0, t_plain); island[0] = test_create_region(0, 0, t_plain);