2015-11-24 18:52:09 +01:00
|
|
|
#include "platform.h"
|
|
|
|
#include "config.h"
|
2012-06-30 20:07:28 +02:00
|
|
|
#include "types.h"
|
|
|
|
#include "ally.h"
|
|
|
|
|
2015-11-24 18:52:09 +01:00
|
|
|
#include "unit.h"
|
|
|
|
#include "region.h"
|
|
|
|
#include "group.h"
|
|
|
|
#include "faction.h"
|
2017-09-22 17:58:10 +02:00
|
|
|
#include "objtypes.h"
|
2015-11-24 18:52:09 +01:00
|
|
|
|
2018-09-29 11:37:17 +02:00
|
|
|
#include <kernel/attrib.h>
|
2017-12-28 18:29:40 +01:00
|
|
|
#include <util/strings.h>
|
2018-09-29 13:21:46 +02:00
|
|
|
#include <kernel/gamedata.h>
|
2017-09-22 09:15:37 +02:00
|
|
|
|
|
|
|
#include <storage.h>
|
|
|
|
|
2015-11-24 18:52:09 +01:00
|
|
|
#include <assert.h>
|
2015-11-24 19:15:53 +01:00
|
|
|
#include <string.h>
|
2012-06-30 20:07:28 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2018-10-28 13:45:36 +01:00
|
|
|
#define BLOCKSIZE 15
|
|
|
|
typedef struct allies {
|
|
|
|
struct allies *next;
|
|
|
|
int num;
|
|
|
|
const struct faction *factions[BLOCKSIZE];
|
|
|
|
int status[BLOCKSIZE];
|
|
|
|
} allies;
|
|
|
|
|
|
|
|
static void block_insert(allies *al, const struct faction *f, int status) {
|
|
|
|
int i = al->num++;
|
|
|
|
al->status[i] = status;
|
|
|
|
al->factions[i] = f;
|
|
|
|
/* TODO: heapify */
|
|
|
|
}
|
|
|
|
|
|
|
|
static int block_search(allies *al, const struct faction *f) {
|
|
|
|
int i;
|
|
|
|
/* TODO: binary search */
|
|
|
|
for (i = 0; i != al->num; ++i) {
|
|
|
|
if (al->factions[i] == f) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return BLOCKSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int allies_get(allies *al, const struct faction *f)
|
|
|
|
{
|
|
|
|
for (; al; al = al->next) {
|
|
|
|
int i = block_search(al, f);
|
|
|
|
if (i != BLOCKSIZE) {
|
|
|
|
return al->status[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void allies_set(allies **p_al, const struct faction *f, int status)
|
|
|
|
{
|
|
|
|
while (*p_al) {
|
|
|
|
allies *al = *p_al;
|
|
|
|
int i = block_search(al, f);
|
|
|
|
if (i != BLOCKSIZE) {
|
|
|
|
if (status == 0) {
|
|
|
|
if (--al->num != i) {
|
|
|
|
al->factions[i] = al->factions[al->num];
|
|
|
|
al->status[i] = al->status[al->num];
|
|
|
|
/* TODO: repair heap up or down */
|
|
|
|
}
|
|
|
|
else if (al->num == 0) {
|
|
|
|
*p_al = al->next;
|
|
|
|
free(al);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
al->status[i] = status;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (al->num < BLOCKSIZE) {
|
|
|
|
block_insert(al, f, status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p_al = &al->next;
|
|
|
|
}
|
|
|
|
*p_al = calloc(1, sizeof(allies));
|
|
|
|
block_insert(*p_al, f, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
void allies_write(gamedata * data, const allies *alist)
|
|
|
|
{
|
|
|
|
const allies *al;
|
|
|
|
for (al = alist; al; al = al->next) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i != al->num; ++i) {
|
|
|
|
const faction * f = al->factions[i];
|
|
|
|
if (f && f->_alive) {
|
|
|
|
write_faction_reference(f, data->store);
|
|
|
|
WRITE_INT(data->store, al->status[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write_faction_reference(NULL, data->store);
|
|
|
|
}
|
|
|
|
|
|
|
|
void allies_read(gamedata * data, allies **sfp)
|
|
|
|
{
|
|
|
|
for (;;) {
|
|
|
|
int aid, state;
|
|
|
|
READ_INT(data->store, &aid);
|
|
|
|
/* TODO: deal with unresolved factions, somehow */
|
|
|
|
if (aid >=0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
READ_INT(data->store, &state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-26 10:54:09 +02:00
|
|
|
typedef struct ally {
|
|
|
|
struct ally *next;
|
|
|
|
struct faction *faction;
|
|
|
|
int status;
|
|
|
|
} ally;
|
|
|
|
|
2018-10-26 22:16:34 +02:00
|
|
|
static ally * ally_add(ally **al_p, struct faction *f) {
|
|
|
|
ally * al;
|
|
|
|
while (*al_p) {
|
|
|
|
al = *al_p;
|
|
|
|
if (f && al->faction == f) return al;
|
|
|
|
al_p = &al->next;
|
|
|
|
}
|
|
|
|
al = (ally *)calloc(1, sizeof(ally));
|
|
|
|
al->faction = f;
|
|
|
|
*al_p = al;
|
|
|
|
return al;
|
|
|
|
}
|
|
|
|
|
2018-10-26 21:49:58 +02:00
|
|
|
void allies_free(ally *al)
|
|
|
|
{
|
|
|
|
while (al) {
|
|
|
|
ally * an = al->next;
|
|
|
|
free(al);
|
|
|
|
al = an;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-28 13:45:36 +01:00
|
|
|
ally *ally_clone(const ally *al) {
|
2018-10-26 21:49:58 +02:00
|
|
|
ally *al_clone = NULL, **al_end = &al_clone;
|
|
|
|
|
|
|
|
for (; al; al = al->next) {
|
|
|
|
if (al->faction) {
|
|
|
|
ally * al_new = ally_add(al_end, al->faction);
|
|
|
|
al_new->status = al->status;
|
|
|
|
al_end = &al_new->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return al_clone;
|
|
|
|
}
|
|
|
|
|
2018-10-28 13:45:36 +01:00
|
|
|
int ally_walk(ally *allies, cb_ally_walk callback, void *udata)
|
2018-10-26 10:54:09 +02:00
|
|
|
{
|
|
|
|
ally *al;
|
|
|
|
for (al = allies; al; al = al->next) {
|
2018-10-26 20:57:59 +02:00
|
|
|
int err = callback(allies, al->faction, al->status, udata);
|
|
|
|
if (err != 0) {
|
|
|
|
return err;
|
|
|
|
}
|
2018-10-26 10:54:09 +02:00
|
|
|
}
|
2018-10-26 20:57:59 +02:00
|
|
|
return 0;
|
2018-10-26 10:54:09 +02:00
|
|
|
}
|
|
|
|
|
2018-10-26 21:49:58 +02:00
|
|
|
void write_allies(gamedata * data, const ally *alist)
|
|
|
|
{
|
|
|
|
const ally *a;
|
|
|
|
for (a = alist; a; a = a->next) {
|
|
|
|
if (a->faction && a->faction->_alive) {
|
|
|
|
write_faction_reference(a->faction, data->store);
|
|
|
|
WRITE_INT(data->store, a->status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write_faction_reference(NULL, data->store);
|
|
|
|
}
|
|
|
|
|
2018-10-26 19:47:50 +02:00
|
|
|
void read_allies(gamedata * data, ally **sfp)
|
2017-09-22 09:15:37 +02:00
|
|
|
{
|
|
|
|
for (;;) {
|
|
|
|
int aid;
|
2017-09-22 17:58:10 +02:00
|
|
|
READ_INT(data->store, &aid);
|
2017-09-22 09:15:37 +02:00
|
|
|
if (aid > 0) {
|
2017-09-22 17:58:10 +02:00
|
|
|
ally * al = ally_add(sfp, NULL);
|
2017-09-22 09:15:37 +02:00
|
|
|
int state;
|
2017-09-22 17:58:10 +02:00
|
|
|
if ((al->faction = findfaction(aid)) == NULL) {
|
2018-10-30 06:30:32 +01:00
|
|
|
al->faction = faction_create(aid);
|
2017-09-22 17:58:10 +02:00
|
|
|
}
|
2017-09-22 09:15:37 +02:00
|
|
|
READ_INT(data->store, &state);
|
|
|
|
al->status = state & HELP_ALL;
|
|
|
|
sfp = &al->next;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-30 20:07:28 +02:00
|
|
|
ally * ally_find(ally *al, const struct faction *f) {
|
2014-10-31 15:38:37 +01:00
|
|
|
for (; al; al = al->next) {
|
|
|
|
if (al->faction == f) return al;
|
|
|
|
}
|
|
|
|
return 0;
|
2012-06-30 20:07:28 +02:00
|
|
|
}
|
|
|
|
|
2015-11-24 18:52:09 +01:00
|
|
|
static int ally_flag(const char *s, int help_mask)
|
|
|
|
{
|
|
|
|
if ((help_mask & HELP_MONEY) && strcmp(s, "money") == 0)
|
|
|
|
return HELP_MONEY;
|
|
|
|
if ((help_mask & HELP_FIGHT) && strcmp(s, "fight") == 0)
|
|
|
|
return HELP_FIGHT;
|
|
|
|
if ((help_mask & HELP_GIVE) && strcmp(s, "give") == 0)
|
|
|
|
return HELP_GIVE;
|
|
|
|
if ((help_mask & HELP_GUARD) && strcmp(s, "guard") == 0)
|
|
|
|
return HELP_GUARD;
|
|
|
|
if ((help_mask & HELP_FSTEALTH) && strcmp(s, "stealth") == 0)
|
|
|
|
return HELP_FSTEALTH;
|
|
|
|
if ((help_mask & HELP_TRAVEL) && strcmp(s, "travel") == 0)
|
|
|
|
return HELP_TRAVEL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Specifies automatic alliance modes.
|
|
|
|
* If this returns a value then the bits set are immutable between alliance
|
|
|
|
* partners (faction::alliance) and cannot be changed with the HELP command.
|
|
|
|
*/
|
|
|
|
int AllianceAuto(void)
|
|
|
|
{
|
|
|
|
int value;
|
|
|
|
const char *str = config_get("alliance.auto");
|
|
|
|
value = 0;
|
|
|
|
if (str != NULL) {
|
2017-12-28 18:29:40 +01:00
|
|
|
char *sstr = str_strdup(str);
|
2015-11-24 18:52:09 +01:00
|
|
|
char *tok = strtok(sstr, " ");
|
|
|
|
while (tok) {
|
|
|
|
value |= ally_flag(tok, -1);
|
|
|
|
tok = strtok(NULL, " ");
|
|
|
|
}
|
|
|
|
free(sstr);
|
|
|
|
}
|
|
|
|
return value & HelpMask();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2018-10-26 19:47:50 +02:00
|
|
|
autoalliance(const faction * sf, const faction * f2)
|
2015-11-24 18:52:09 +01:00
|
|
|
{
|
2018-10-26 20:57:59 +02:00
|
|
|
if (sf->alliance == f2->alliance) {
|
|
|
|
return AllianceAuto();
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-09 21:20:43 +01:00
|
|
|
static void init_npcfaction(variant *var)
|
2015-11-24 18:52:09 +01:00
|
|
|
{
|
2018-02-09 21:20:43 +01:00
|
|
|
var->i = 1;
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
attrib_type at_npcfaction = {
|
|
|
|
"npcfaction",
|
|
|
|
init_npcfaction,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
a_writeint,
|
|
|
|
a_readint,
|
2016-02-09 06:43:19 +01:00
|
|
|
NULL,
|
2015-11-24 18:52:09 +01:00
|
|
|
ATF_UNIQUE
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Limits the available help modes
|
|
|
|
* The bitfield returned by this function specifies the available help modes
|
|
|
|
* in this game (so you can, for example, disable HELP GIVE globally).
|
|
|
|
* Disabling a status will disable the command sequence entirely (order parsing
|
|
|
|
* uses this function).
|
|
|
|
*/
|
|
|
|
int HelpMask(void)
|
|
|
|
{
|
2018-10-26 20:57:59 +02:00
|
|
|
static int config, rule;
|
2016-09-23 20:36:57 +02:00
|
|
|
if (config_changed(&config)) {
|
|
|
|
const char *str = config_get("rules.help.mask");
|
|
|
|
if (str != NULL) {
|
2017-12-28 18:29:40 +01:00
|
|
|
char *sstr = str_strdup(str);
|
2016-09-23 20:36:57 +02:00
|
|
|
char *tok = strtok(sstr, " ");
|
2018-10-26 20:57:59 +02:00
|
|
|
rule = 0;
|
2016-09-23 20:36:57 +02:00
|
|
|
while (tok) {
|
|
|
|
rule |= ally_flag(tok, -1);
|
|
|
|
tok = strtok(NULL, " ");
|
|
|
|
}
|
|
|
|
free(sstr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rule = HELP_ALL;
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return rule;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int AllianceRestricted(void)
|
|
|
|
{
|
2018-10-26 20:57:59 +02:00
|
|
|
static int config, rule;
|
|
|
|
if (config_changed(&config)) {
|
|
|
|
const char *str = config_get("alliance.restricted");
|
|
|
|
rule = 0;
|
|
|
|
if (str != NULL) {
|
|
|
|
char *sstr = str_strdup(str);
|
|
|
|
char *tok = strtok(sstr, " ");
|
|
|
|
while (tok) {
|
|
|
|
rule |= ally_flag(tok, -1);
|
|
|
|
tok = strtok(NULL, " ");
|
|
|
|
}
|
|
|
|
free(sstr);
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
2018-10-26 20:57:59 +02:00
|
|
|
rule &= HelpMask();
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
|
|
|
return rule;
|
|
|
|
}
|
|
|
|
|
2018-10-26 21:49:58 +02:00
|
|
|
int alliance_status(const faction *f, const faction *f2, int status) {
|
2018-10-26 20:57:59 +02:00
|
|
|
status |= autoalliance(f, f2);
|
|
|
|
if (status > 0) {
|
|
|
|
int mask = AllianceRestricted();
|
|
|
|
if (mask) {
|
|
|
|
if (a_find(f->attribs, &at_npcfaction)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
if (a_find(f2->attribs, &at_npcfaction)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
if (f->alliance != f2->alliance) {
|
|
|
|
status &= ~mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2015-11-24 18:52:09 +01:00
|
|
|
int
|
2018-10-26 19:47:50 +02:00
|
|
|
alliedgroup(const struct faction *f,
|
2018-10-26 20:57:59 +02:00
|
|
|
const struct faction *f2, const struct group *g, int mask)
|
2015-11-24 18:52:09 +01:00
|
|
|
{
|
2018-10-26 20:57:59 +02:00
|
|
|
ally *all = g ? g->allies : f->allies;
|
|
|
|
int status;
|
2018-10-26 19:47:50 +02:00
|
|
|
|
2016-01-11 14:42:36 +01:00
|
|
|
if (!(faction_alive(f) && faction_alive(f2))) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-10-26 21:49:58 +02:00
|
|
|
status = ally_get(all, f2) & mask;
|
2018-10-26 20:57:59 +02:00
|
|
|
return alliance_status(f, f2, status);
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2018-10-26 20:57:59 +02:00
|
|
|
alliedfaction(const struct faction *f, const struct faction *f2, int mask)
|
2015-11-24 18:52:09 +01:00
|
|
|
{
|
2018-10-26 20:57:59 +02:00
|
|
|
return alliedgroup(f, f2, NULL, mask);
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Die Gruppe von Einheit u hat helfe zu f2 gesetzt. */
|
2018-10-26 21:49:58 +02:00
|
|
|
int alliedunit(const unit * u, const faction * f2, int mask)
|
2015-11-24 18:52:09 +01:00
|
|
|
{
|
|
|
|
assert(u);
|
|
|
|
assert(f2);
|
|
|
|
assert(u->region); /* the unit should be in a region, but it's possible that u->number==0 (TEMP units) */
|
2018-10-26 19:47:50 +02:00
|
|
|
if (u->faction == f2) {
|
2018-10-26 21:49:58 +02:00
|
|
|
return mask;
|
2018-10-26 19:47:50 +02:00
|
|
|
}
|
|
|
|
if (!faction_alive(f2)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2015-11-24 18:52:09 +01:00
|
|
|
if (u->faction != NULL && f2 != NULL) {
|
2018-10-26 22:12:43 +02:00
|
|
|
group *g;
|
|
|
|
|
2018-10-26 21:49:58 +02:00
|
|
|
if (mask & HELP_FIGHT) {
|
2015-11-24 18:52:09 +01:00
|
|
|
if ((u->flags & UFL_DEFENDER) || (u->faction->flags & FFL_DEFENDER)) {
|
|
|
|
faction *owner = region_get_owner(u->region);
|
|
|
|
/* helps the owner of the region */
|
|
|
|
if (owner == f2) {
|
|
|
|
return HELP_FIGHT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-26 22:12:43 +02:00
|
|
|
g = get_group(u);
|
|
|
|
if (g) {
|
|
|
|
return alliedgroup(u->faction, f2, g, mask);
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
2018-10-26 21:49:58 +02:00
|
|
|
return alliedfaction(u->faction, f2, mask);
|
2015-11-24 18:52:09 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-26 19:47:50 +02:00
|
|
|
void ally_set(ally **allies, struct faction *f, int status) {
|
|
|
|
ally *al;
|
|
|
|
while (*allies) {
|
|
|
|
al = *allies;
|
|
|
|
if (al->faction == f) {
|
|
|
|
if (status != 0) {
|
|
|
|
al->status = status;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*allies = al->next;
|
|
|
|
free(al);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
allies = &al->next;
|
|
|
|
}
|
|
|
|
al = ally_add(allies, f);
|
|
|
|
al->status = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ally_get(ally *allies, const struct faction *f) {
|
|
|
|
ally *al;
|
|
|
|
for (al = allies; al; al = al->next) {
|
|
|
|
if (al->faction == f) {
|
|
|
|
return al->status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|