2015-01-30 20:37:14 +01:00
|
|
|
/*
|
2010-08-08 10:06:34 +02:00
|
|
|
+-------------------+
|
|
|
|
| | Christian Schlittchen <corwin@amber.kn-bremen.de>
|
|
|
|
| Eressea PBEM host | Enno Rehling <enno@eressea.de>
|
|
|
|
| (c) 1998 - 2004 | Katja Zedel <katze@felidae.kn-bremen.de>
|
|
|
|
| |
|
|
|
|
+-------------------+
|
|
|
|
|
|
|
|
This program may not be used, modified or distributed
|
|
|
|
without prior permission by the authors of Eressea.
|
2014-09-29 20:38:01 +02:00
|
|
|
*/
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
#include <platform.h>
|
|
|
|
#include <kernel/config.h>
|
|
|
|
#include "settings.h"
|
|
|
|
|
|
|
|
#include "wormhole.h"
|
|
|
|
|
|
|
|
/* kernel includes */
|
|
|
|
#include <kernel/building.h>
|
|
|
|
#include <kernel/faction.h>
|
2014-06-09 18:54:48 +02:00
|
|
|
#include <kernel/messages.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
#include <kernel/plane.h>
|
|
|
|
#include <kernel/region.h>
|
|
|
|
#include <kernel/unit.h>
|
|
|
|
|
|
|
|
/* util includes */
|
|
|
|
#include <util/attrib.h>
|
2016-02-13 13:42:02 +01:00
|
|
|
#include <util/gamedata.h>
|
2010-08-15 04:41:18 +02:00
|
|
|
#include <util/language.h>
|
2011-04-27 06:30:06 +02:00
|
|
|
#include <util/resolve.h>
|
|
|
|
#include <util/rng.h>
|
2017-01-26 17:41:21 +01:00
|
|
|
#include <selist.h>
|
2013-12-31 10:06:28 +01:00
|
|
|
|
|
|
|
#include <storage.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
/* libc includes */
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2012-06-24 07:41:07 +02:00
|
|
|
static bool good_region(const region * r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-09-29 20:38:01 +02:00
|
|
|
return (!fval(r, RF_CHAOTIC) && r->age > 30 && rplane(r) == NULL
|
|
|
|
&& r->units != NULL && r->land != NULL);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
static int cmp_age(const void *v1, const void *v2)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-09-29 20:38:01 +02:00
|
|
|
const region **r1 = (const region **)v1;
|
|
|
|
const region **r2 = (const region **)v2;
|
|
|
|
if ((*r1)->age < (*r2)->age)
|
|
|
|
return -1;
|
|
|
|
if ((*r1)->age > (*r2)->age)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2015-12-16 22:18:44 +01:00
|
|
|
static int wormhole_age(struct attrib *a, void *owner)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2015-12-17 13:10:09 +01:00
|
|
|
building *entry = (building *)owner;
|
|
|
|
region *exit = (region *)a->data.v;
|
|
|
|
int maxtransport = entry->size;
|
|
|
|
region *r = entry->region;
|
2014-09-29 20:38:01 +02:00
|
|
|
unit *u = r->units;
|
|
|
|
|
2017-01-10 16:31:05 +01:00
|
|
|
UNUSED_ARG(owner);
|
2014-09-29 20:38:01 +02:00
|
|
|
for (; u != NULL && maxtransport != 0; u = u->next) {
|
2015-12-17 13:10:09 +01:00
|
|
|
if (u->building == entry) {
|
2014-09-29 20:38:01 +02:00
|
|
|
message *m = NULL;
|
|
|
|
if (u->number > maxtransport || has_limited_skills(u)) {
|
|
|
|
m = msg_message("wormhole_requirements", "unit region", u, u->region);
|
|
|
|
}
|
2015-12-17 13:10:09 +01:00
|
|
|
else if (exit != NULL) {
|
|
|
|
move_unit(u, exit, NULL);
|
2014-09-29 20:38:01 +02:00
|
|
|
maxtransport -= u->number;
|
2015-12-17 13:10:09 +01:00
|
|
|
m = msg_message("wormhole_exit", "unit region", u, exit);
|
|
|
|
add_message(&exit->msgs, m);
|
2014-09-29 20:38:01 +02:00
|
|
|
}
|
|
|
|
if (m != NULL) {
|
|
|
|
add_message(&u->faction->msgs, m);
|
|
|
|
msg_release(m);
|
|
|
|
}
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2015-12-17 13:10:09 +01:00
|
|
|
remove_building(&r->buildings, entry);
|
2014-09-29 20:38:01 +02:00
|
|
|
ADDMSG(&r->msgs, msg_message("wormhole_dissolve", "region", r));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-09-29 20:38:01 +02:00
|
|
|
/* age returns 0 if the attribute needs to be removed, !=0 otherwise */
|
|
|
|
return AT_AGE_KEEP;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-12-13 16:43:35 +01:00
|
|
|
static void wormhole_write(const struct attrib *a, const void *owner, struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2015-12-17 13:10:09 +01:00
|
|
|
region *exit = (region *)a->data.v;
|
|
|
|
write_region_reference(exit, store);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** conversion code, turn 573, 2008-05-23 */
|
2011-03-07 08:02:35 +01:00
|
|
|
static int resolve_exit(variant id, void *address)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-09-29 20:38:01 +02:00
|
|
|
building *b = findbuilding(id.i);
|
|
|
|
region **rp = address;
|
|
|
|
if (b) {
|
|
|
|
*rp = b->region;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*rp = NULL;
|
|
|
|
return -1;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2016-02-13 13:42:02 +01:00
|
|
|
static int wormhole_read(struct attrib *a, void *owner, struct gamedata *data)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2016-02-13 13:42:02 +01:00
|
|
|
storage *store = data->store;
|
2016-02-13 14:09:36 +01:00
|
|
|
resolve_fun resolver = (data->version < UIDHASH_VERSION)
|
2014-09-29 20:38:01 +02:00
|
|
|
? resolve_exit : resolve_region_id;
|
2016-02-13 14:09:36 +01:00
|
|
|
read_fun reader = (data->version < UIDHASH_VERSION)
|
2014-09-29 20:38:01 +02:00
|
|
|
? read_building_reference : read_region_reference;
|
|
|
|
|
2016-02-13 14:09:36 +01:00
|
|
|
if (data->version < ATTRIBOWNER_VERSION) {
|
2015-12-17 13:04:51 +01:00
|
|
|
READ_INT(store, NULL);
|
|
|
|
}
|
2016-02-13 20:38:32 +01:00
|
|
|
if (read_reference(&a->data.v, data, reader, resolver) == 0) {
|
2015-12-17 13:10:09 +01:00
|
|
|
if (!a->data.v) {
|
2014-09-29 20:38:01 +02:00
|
|
|
return AT_READ_FAIL;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-09-29 20:38:01 +02:00
|
|
|
return AT_READ_OK;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static attrib_type at_wormhole = {
|
2014-09-29 20:38:01 +02:00
|
|
|
"wormhole",
|
2015-12-17 13:10:09 +01:00
|
|
|
NULL,
|
|
|
|
NULL,
|
2014-09-29 20:38:01 +02:00
|
|
|
wormhole_age,
|
|
|
|
wormhole_write,
|
|
|
|
wormhole_read,
|
2016-02-09 06:43:19 +01:00
|
|
|
NULL,
|
2014-09-29 20:38:01 +02:00
|
|
|
ATF_UNIQUE
|
2010-08-08 10:06:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
make_wormhole(const building_type * bt_wormhole, region * r1, region * r2)
|
|
|
|
{
|
2014-09-29 20:38:01 +02:00
|
|
|
building *b1 = new_building(bt_wormhole, r1, default_locale);
|
|
|
|
building *b2 = new_building(bt_wormhole, r2, default_locale);
|
|
|
|
attrib *a1 = a_add(&b1->attribs, a_new(&at_wormhole));
|
|
|
|
attrib *a2 = a_add(&b2->attribs, a_new(&at_wormhole));
|
2015-12-17 13:10:09 +01:00
|
|
|
a1->data.v = b2->region;
|
|
|
|
a2->data.v = b1->region;
|
2016-08-06 13:52:29 +02:00
|
|
|
b1->size = b2->size = bt_wormhole->maxcapacity * bt_wormhole->capacity;
|
2014-09-29 20:38:01 +02:00
|
|
|
ADDMSG(&r1->msgs, msg_message("wormhole_appear", "region", r1));
|
|
|
|
ADDMSG(&r2->msgs, msg_message("wormhole_appear", "region", r2));
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#define WORMHOLE_CHANCE 10000
|
2014-09-29 23:19:59 +02:00
|
|
|
|
2017-01-26 17:41:21 +01:00
|
|
|
static void select_wormhole_regions(selist **rlistp, int *countp) {
|
|
|
|
selist *rlist = 0;
|
2014-09-29 20:38:01 +02:00
|
|
|
region *r = regions;
|
2014-09-29 23:19:59 +02:00
|
|
|
int count = 0;
|
2014-09-29 20:38:01 +02:00
|
|
|
|
|
|
|
while (r != NULL) {
|
|
|
|
int next = rng_int() % (2 * WORMHOLE_CHANCE);
|
|
|
|
while (r != NULL && (next != 0 || !good_region(r))) {
|
|
|
|
if (good_region(r)) {
|
|
|
|
--next;
|
|
|
|
}
|
|
|
|
r = r->next;
|
|
|
|
}
|
|
|
|
if (r == NULL)
|
|
|
|
break;
|
2017-01-26 17:41:21 +01:00
|
|
|
selist_push(&rlist, r);
|
2014-09-29 20:38:01 +02:00
|
|
|
++count;
|
|
|
|
r = r->next;
|
|
|
|
}
|
|
|
|
|
2014-09-29 23:19:59 +02:00
|
|
|
*countp = count;
|
|
|
|
*rlistp = rlist;
|
|
|
|
}
|
2014-09-29 20:38:01 +02:00
|
|
|
|
2017-01-26 17:41:21 +01:00
|
|
|
void sort_wormhole_regions(selist *rlist, region **match, int count) {
|
|
|
|
selist *ql;
|
2014-09-29 23:19:59 +02:00
|
|
|
int qi, i = 0;
|
2014-09-29 20:38:01 +02:00
|
|
|
|
2017-01-26 17:41:21 +01:00
|
|
|
for (ql = rlist, qi = 0; i != count; selist_advance(&ql, &qi, 1)) {
|
|
|
|
match[i++] = (region *)selist_get(ql, qi);
|
2014-09-29 20:38:01 +02:00
|
|
|
}
|
|
|
|
qsort(match, count, sizeof(region *), cmp_age);
|
2014-09-29 23:19:59 +02:00
|
|
|
}
|
2014-09-29 20:38:01 +02:00
|
|
|
|
2014-09-29 23:19:59 +02:00
|
|
|
void make_wormholes(region **match, int count, const building_type *bt_wormhole) {
|
|
|
|
int i;
|
2014-09-29 20:38:01 +02:00
|
|
|
count /= 2;
|
|
|
|
for (i = 0; i != count; ++i) {
|
|
|
|
make_wormhole(bt_wormhole, match[i], match[i + count]);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-09-29 23:19:59 +02:00
|
|
|
}
|
|
|
|
|
2014-12-13 16:43:35 +01:00
|
|
|
void wormholes_update(void)
|
2014-09-29 23:19:59 +02:00
|
|
|
{
|
|
|
|
const building_type *bt_wormhole = bt_find("wormhole");
|
2017-01-26 17:41:21 +01:00
|
|
|
selist *rlist = 0;
|
2014-09-29 23:19:59 +02:00
|
|
|
int count = 0;
|
|
|
|
region **match;
|
|
|
|
|
|
|
|
if (bt_wormhole == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
select_wormhole_regions(&rlist, &count);
|
|
|
|
if (count < 2) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
match = (region **)malloc(sizeof(region *) * count);
|
|
|
|
sort_wormhole_regions(rlist, match, count);
|
2017-01-26 17:41:21 +01:00
|
|
|
selist_free(rlist);
|
2014-09-29 23:19:59 +02:00
|
|
|
make_wormholes(match, count, bt_wormhole);
|
2014-09-29 20:38:01 +02:00
|
|
|
free(match);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-12-13 16:43:35 +01:00
|
|
|
void wormholes_register(void)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-09-29 20:38:01 +02:00
|
|
|
at_register(&at_wormhole);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|