server/src/modules/wormhole.c

226 lines
5.5 KiB
C
Raw Normal View History

2010-08-08 10:06:34 +02:00
/* vi: set ts=2:
+-------------------+
| | 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.
*/
#include <platform.h>
#include <kernel/config.h>
#include "settings.h"
#include "wormhole.h"
/* kernel includes */
#include <kernel/building.h>
#include <kernel/faction.h>
#include <kernel/messages.h>
2010-08-08 10:06:34 +02:00
#include <kernel/plane.h>
#include <kernel/region.h>
#include <kernel/unit.h>
#include <kernel/version.h>
/* util includes */
#include <util/attrib.h>
2010-08-15 04:41:18 +02:00
#include <util/language.h>
#include <util/resolve.h>
#include <util/rng.h>
#include <quicklist.h>
#include <storage.h>
2010-08-08 10:06:34 +02:00
/* libc includes */
#include <assert.h>
#include <stdlib.h>
static bool good_region(const region * r)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01: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
{
2011-03-07 08:02:35 +01: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;
2010-08-08 10:06:34 +02:00
return 0;
}
typedef struct wormhole_data {
2011-03-07 08:02:35 +01:00
building *entry;
region *exit;
2010-08-08 10:06:34 +02:00
} wormhole_data;
2011-03-07 08:02:35 +01:00
static void wormhole_init(struct attrib *a)
2010-08-08 10:06:34 +02:00
{
a->data.v = calloc(1, sizeof(wormhole_data));
}
2011-03-07 08:02:35 +01:00
static void wormhole_done(struct attrib *a)
2010-08-08 10:06:34 +02:00
{
free(a->data.v);
}
2011-03-07 08:02:35 +01:00
static int wormhole_age(struct attrib *a)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
wormhole_data *data = (wormhole_data *) a->data.v;
2010-08-08 10:06:34 +02:00
int maxtransport = data->entry->size;
2011-03-07 08:02:35 +01:00
region *r = data->entry->region;
unit *u = r->units;
for (; u != NULL && maxtransport != 0; u = u->next) {
if (u->building == data->entry) {
message *m = NULL;
if (u->number > maxtransport || has_limited_skills(u)) {
2010-08-08 10:06:34 +02:00
m = msg_message("wormhole_requirements", "unit region", u, u->region);
2011-03-07 08:02:35 +01:00
} else if (data->exit != NULL) {
2010-08-08 10:06:34 +02:00
move_unit(u, data->exit, NULL);
maxtransport -= u->number;
m = msg_message("wormhole_exit", "unit region", u, data->exit);
add_message(&data->exit->msgs, m);
}
2011-03-07 08:02:35 +01:00
if (m != NULL) {
2010-08-08 10:06:34 +02:00
add_message(&u->faction->msgs, m);
msg_release(m);
}
}
}
remove_building(&r->buildings, data->entry);
ADDMSG(&r->msgs, msg_message("wormhole_dissolve", "region", r));
/* age returns 0 if the attribute needs to be removed, !=0 otherwise */
return AT_AGE_KEEP;
}
static void
2011-03-07 08:02:35 +01:00
wormhole_write(const struct attrib *a, const void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
wormhole_data *data = (wormhole_data *) a->data.v;
2010-08-08 10:06:34 +02:00
write_building_reference(data->entry, store);
write_region_reference(data->exit, store);
}
/** 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
{
2011-03-07 08:02:35 +01:00
building *b = findbuilding(id.i);
region **rp = address;
2010-08-08 10:06:34 +02:00
if (b) {
*rp = b->region;
return 0;
}
*rp = NULL;
return -1;
}
2011-03-07 08:02:35 +01:00
static int wormhole_read(struct attrib *a, void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
wormhole_data *data = (wormhole_data *) a->data.v;
resolve_fun resolver = (global.data_version < UIDHASH_VERSION)
? resolve_exit : resolve_region_id;
read_fun reader = (global.data_version < UIDHASH_VERSION)
? read_building_reference : read_region_reference;
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
int rb =
read_reference(&data->entry, store, read_building_reference,
resolve_building);
2010-08-08 10:06:34 +02:00
int rr = read_reference(&data->exit, store, reader, resolver);
2011-03-07 08:02:35 +01:00
if (rb == 0 && rr == 0) {
2010-08-08 10:06:34 +02:00
if (!data->exit || !data->entry) {
return AT_READ_FAIL;
}
}
return AT_READ_OK;
}
static attrib_type at_wormhole = {
"wormhole",
wormhole_init,
wormhole_done,
wormhole_age,
wormhole_write,
wormhole_read,
ATF_UNIQUE
};
static void
make_wormhole(const building_type * bt_wormhole, region * r1, region * r2)
{
2011-03-07 08:02:35 +01: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));
wormhole_data *d1 = (wormhole_data *) a1->data.v;
wormhole_data *d2 = (wormhole_data *) a2->data.v;
2010-08-08 10:06:34 +02:00
d1->entry = b1;
d2->entry = b2;
d1->exit = b2->region;
d2->exit = b1->region;
b1->size = bt_wormhole->maxsize;
b2->size = bt_wormhole->maxsize;
ADDMSG(&r1->msgs, msg_message("wormhole_appear", "region", r1));
ADDMSG(&r2->msgs, msg_message("wormhole_appear", "region", r2));
}
2011-03-07 08:02:35 +01:00
void create_wormholes(void)
2010-08-08 10:06:34 +02:00
{
#define WORMHOLE_CHANCE 10000
2011-03-07 08:02:35 +01:00
const building_type *bt_wormhole = bt_find("wormhole");
quicklist *ql, *rlist = 0;
2011-03-07 08:02:35 +01:00
region *r = regions;
int qi, i = 0, count = 0;
2011-03-07 08:02:35 +01:00
region **match;
if (bt_wormhole == NULL)
return;
2010-08-08 10:06:34 +02:00
/*
* select a list of regions. we'll sort them by age later.
*/
2011-03-07 08:02:35 +01:00
while (r != NULL) {
int next = rng_int() % (2 * WORMHOLE_CHANCE);
while (r != NULL && (next != 0 || !good_region(r))) {
2010-08-08 10:06:34 +02:00
if (good_region(r)) {
--next;
}
2011-03-07 08:02:35 +01:00
r = r->next;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if (r == NULL)
break;
ql_push(&rlist, r);
2010-08-08 10:06:34 +02:00
++count;
2011-03-07 08:02:35 +01:00
r = r->next;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if (count < 2)
return;
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
match = (region **) malloc(sizeof(region *) * count);
for (ql = rlist,qi = 0; i != count; ql_advance(&ql, &qi, 1)) {
match[i++] = (region *)ql_get(ql, qi);
2010-08-08 10:06:34 +02:00
}
qsort(match, count, sizeof(region *), cmp_age);
ql_free(rlist);
2010-08-08 10:06:34 +02:00
count /= 2;
2011-03-07 08:02:35 +01:00
for (i = 0; i != count; ++i) {
make_wormhole(bt_wormhole, match[i], match[i + count]);
2010-08-08 10:06:34 +02:00
}
free(match);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void register_wormholes(void)
2010-08-08 10:06:34 +02:00
{
at_register(&at_wormhole);
}