2010-08-08 10:06:34 +02:00
|
|
|
/*
|
2015-01-30 22:10:29 +01:00
|
|
|
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
2014-08-16 13:46:34 +02:00
|
|
|
Katja Zedel <katze@felidae.kn-bremen.de
|
|
|
|
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
**/
|
|
|
|
|
|
|
|
#include <platform.h>
|
|
|
|
#include <kernel/config.h>
|
|
|
|
#include "faction.h"
|
|
|
|
#include "alliance.h"
|
2012-06-30 20:07:28 +02:00
|
|
|
#include "ally.h"
|
2015-11-22 15:45:31 +01:00
|
|
|
#include "curse.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
#include "equipment.h"
|
|
|
|
#include "group.h"
|
|
|
|
#include "item.h"
|
2014-06-09 18:54:48 +02:00
|
|
|
#include "messages.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
#include "plane.h"
|
|
|
|
#include "race.h"
|
|
|
|
#include "region.h"
|
2015-11-24 19:32:52 +01:00
|
|
|
#include "save.h"
|
2012-05-24 09:56:54 +02:00
|
|
|
#include "spellbook.h"
|
2010-08-08 10:06:34 +02:00
|
|
|
#include "terrain.h"
|
|
|
|
#include "unit.h"
|
|
|
|
#include "version.h"
|
|
|
|
|
|
|
|
/* util includes */
|
|
|
|
#include <util/attrib.h>
|
|
|
|
#include <util/base36.h>
|
2012-06-04 08:36:40 +02:00
|
|
|
#include <util/bsdstring.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
#include <util/event.h>
|
|
|
|
#include <util/goodies.h>
|
|
|
|
#include <util/lists.h>
|
|
|
|
#include <util/language.h>
|
|
|
|
#include <util/log.h>
|
2015-11-24 19:53:27 +01:00
|
|
|
#include <util/parser.h>
|
2016-01-12 23:52:30 +01:00
|
|
|
#include <util/password.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
#include <util/resolve.h>
|
|
|
|
#include <util/rng.h>
|
|
|
|
#include <util/variant.h>
|
|
|
|
#include <util/unicode.h>
|
2015-11-24 19:53:27 +01:00
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
#include <attributes/otherfaction.h>
|
|
|
|
|
2015-11-24 19:53:27 +01:00
|
|
|
#include <quicklist.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>
|
2015-11-22 15:45:31 +01:00
|
|
|
#include <limits.h>
|
|
|
|
#include <math.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2015-11-22 15:45:31 +01:00
|
|
|
#include <string.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
faction *factions;
|
|
|
|
|
|
|
|
/** remove the faction from memory.
|
|
|
|
* this frees all memory that's only accessible through the faction,
|
|
|
|
* but you should still call funhash and remove the faction from the
|
|
|
|
* global list.
|
|
|
|
*/
|
2016-01-11 18:17:24 +01:00
|
|
|
static void free_faction(faction * f)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-11-07 22:58:29 +01:00
|
|
|
funhash(f);
|
2015-10-14 14:08:50 +02:00
|
|
|
if (f->msgs) {
|
|
|
|
free_messagelist(f->msgs->begin);
|
|
|
|
free(f->msgs);
|
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
while (f->battles) {
|
|
|
|
struct bmsg *bm = f->battles;
|
|
|
|
f->battles = bm->next;
|
2015-10-14 14:08:50 +02:00
|
|
|
if (bm->msgs) {
|
|
|
|
free_messagelist(bm->msgs->begin);
|
|
|
|
free(bm->msgs);
|
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
free(bm);
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2015-10-13 21:50:23 +02:00
|
|
|
if (f->spellbook) {
|
|
|
|
free_spellbook(f->spellbook);
|
|
|
|
}
|
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
while (f->groups) {
|
|
|
|
group *g = f->groups;
|
|
|
|
f->groups = g->next;
|
|
|
|
free_group(g);
|
|
|
|
}
|
|
|
|
freelist(f->allies);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
free(f->email);
|
|
|
|
free(f->banner);
|
2016-01-12 06:46:51 +01:00
|
|
|
free(f->_password);
|
2014-08-16 13:46:34 +02:00
|
|
|
free(f->name);
|
2015-10-13 23:33:11 +02:00
|
|
|
if (f->seen_factions) {
|
|
|
|
ql_free(f->seen_factions);
|
|
|
|
f->seen_factions = 0;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
while (f->attribs) {
|
|
|
|
a_remove(&f->attribs, f->attribs);
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
i_freeall(&f->items);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
freelist(f->ursprung);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-11-01 18:34:53 +01:00
|
|
|
#define FMAXHASH 2039
|
|
|
|
faction *factionhash[FMAXHASH];
|
|
|
|
|
|
|
|
void fhash(faction * f)
|
|
|
|
{
|
|
|
|
int index = f->no % FMAXHASH;
|
|
|
|
f->nexthash = factionhash[index];
|
|
|
|
factionhash[index] = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void funhash(faction * f)
|
|
|
|
{
|
|
|
|
int index = f->no % FMAXHASH;
|
|
|
|
faction **fp = factionhash + index;
|
2014-11-07 22:58:29 +01:00
|
|
|
while (*fp && (*fp) != f) {
|
2014-11-01 18:34:53 +01:00
|
|
|
fp = &(*fp)->nexthash;
|
2014-11-07 22:58:29 +01:00
|
|
|
}
|
|
|
|
if (*fp == f) {
|
|
|
|
*fp = f->nexthash;
|
|
|
|
}
|
2014-11-01 18:34:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static faction *ffindhash(int no)
|
|
|
|
{
|
|
|
|
int index = no % FMAXHASH;
|
|
|
|
faction *f = factionhash[index];
|
|
|
|
while (f && f->no != no)
|
|
|
|
f = f->nexthash;
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
faction *findfaction(int n)
|
|
|
|
{
|
|
|
|
faction *f = ffindhash(n);
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:53:27 +01:00
|
|
|
faction *getfaction(void)
|
|
|
|
{
|
|
|
|
return findfaction(getid());
|
|
|
|
}
|
|
|
|
|
2014-06-23 16:28:10 +02:00
|
|
|
void set_show_item(faction * f, const struct item_type *itype)
|
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
attrib *a = a_add(&f->attribs, a_new(&at_showitem));
|
|
|
|
a->data.v = (void *)itype;
|
2014-06-23 16:28:10 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
const unit *random_unit_in_faction(const faction * f)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
unit *u;
|
|
|
|
int c = 0, u_nr;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2015-11-03 23:14:06 +01:00
|
|
|
if (!f->units) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
for (u = f->units; u; u = u->next)
|
|
|
|
c++;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
u_nr = rng_int() % c;
|
|
|
|
c = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
for (u = f->units; u; u = u->next)
|
|
|
|
if (u_nr == c)
|
|
|
|
return u;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
/* Hier sollte er nie ankommen */
|
|
|
|
return NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
const char *factionname(const faction * f)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
typedef char name[OBJECTIDSIZE + 1];
|
|
|
|
static name idbuf[8];
|
|
|
|
static int nextbuf = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
char *ibuf = idbuf[(++nextbuf) % 8];
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
if (f && f->name) {
|
|
|
|
slprintf(ibuf, sizeof(name), "%s (%s)", f->name, itoa36(f->no));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
strcpy(ibuf, "Unbekannte Partei (?)");
|
|
|
|
}
|
|
|
|
return ibuf;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
int resolve_faction(variant id, void *address)
|
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
int result = 0;
|
|
|
|
faction *f = NULL;
|
|
|
|
if (id.i != 0) {
|
|
|
|
f = findfaction(id.i);
|
|
|
|
if (f == NULL) {
|
|
|
|
result = -1;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2016-02-19 18:25:21 +01:00
|
|
|
assert(address);
|
2014-08-16 13:46:34 +02:00
|
|
|
*(faction **)address = f;
|
|
|
|
return result;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2015-11-22 12:47:57 +01:00
|
|
|
bool faction_id_is_unused(int id)
|
|
|
|
{
|
|
|
|
return findfaction(id) == NULL;
|
|
|
|
}
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
#define MAX_FACTION_ID (36*36*36*36)
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
static int unused_faction_id(void)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
int id = rng_int() % MAX_FACTION_ID;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
while (!faction_id_is_unused(id)) {
|
2014-12-17 17:22:26 +01:00
|
|
|
if (++id == MAX_FACTION_ID) {
|
2014-08-16 13:46:34 +02:00
|
|
|
id = 0;
|
2014-12-17 17:22:26 +01:00
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
return id;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
faction *addfaction(const char *email, const char *password,
|
2014-08-16 13:46:34 +02:00
|
|
|
const struct race * frace, const struct locale * loc, int subscription)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
faction *f = calloc(sizeof(faction), 1);
|
|
|
|
char buf[128];
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
if (set_email(&f->email, email) != 0) {
|
2014-11-01 22:12:11 +01:00
|
|
|
log_warning("Invalid email address for faction %s: %s\n", itoa36(f->no), email);
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2016-01-12 06:46:51 +01:00
|
|
|
if (!password) password = itoa36(rng_int());
|
2016-02-16 07:30:26 +01:00
|
|
|
faction_setpassword(f, password_encode(password, PASSWORD_DEFAULT));
|
2016-01-12 06:46:51 +01:00
|
|
|
ADDMSG(&f->msgs, msg_message("changepasswd", "value", password));
|
2014-08-16 13:46:34 +02:00
|
|
|
|
|
|
|
f->alliance_joindate = turn;
|
|
|
|
f->lastorders = turn;
|
2016-01-11 09:55:47 +01:00
|
|
|
f->_alive = true;
|
2014-08-16 13:46:34 +02:00
|
|
|
f->age = 0;
|
|
|
|
f->race = frace;
|
|
|
|
f->magiegebiet = 0;
|
|
|
|
f->locale = loc;
|
|
|
|
f->subscription = subscription;
|
|
|
|
|
|
|
|
f->options =
|
|
|
|
want(O_REPORT) | want(O_ZUGVORLAGE) | want(O_COMPUTER) | want(O_COMPRESS) |
|
|
|
|
want(O_ADRESSEN) | want(O_STATISTICS);
|
|
|
|
|
|
|
|
f->no = unused_faction_id();
|
|
|
|
if (rule_region_owners()) {
|
|
|
|
alliance *al = makealliance(f->no, NULL);
|
|
|
|
setalliance(f, al);
|
|
|
|
}
|
|
|
|
addlist(&factions, f);
|
|
|
|
fhash(f);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
slprintf(buf, sizeof(buf), "%s %s", LOC(loc, "factiondefault"), factionid(f));
|
|
|
|
f->name = _strdup(buf);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
if (!f->race) {
|
|
|
|
log_warning("creating a faction that has no race", factionid(f));
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
return f;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
unit *addplayer(region * r, faction * f)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
unit *u;
|
|
|
|
char buffer[32];
|
|
|
|
|
|
|
|
assert(f->units == NULL);
|
2015-05-19 08:02:32 +02:00
|
|
|
faction_setorigin(f, 0, r->x, r->y);
|
2014-12-25 18:40:46 +01:00
|
|
|
u = create_unit(r, f, 1, f->race, 0, NULL, NULL);
|
2014-08-16 13:46:34 +02:00
|
|
|
equip_items(&u->faction->items, get_equipment("new_faction"));
|
|
|
|
equip_unit(u, get_equipment("first_unit"));
|
2014-08-24 21:49:55 +02:00
|
|
|
sprintf(buffer, "first_%s", u_race(u)->_name);
|
2014-08-16 13:46:34 +02:00
|
|
|
equip_unit(u, get_equipment(buffer));
|
|
|
|
u->hp = unit_max_hp(u) * u->number;
|
|
|
|
fset(u, UFL_ISNEW);
|
|
|
|
if (f->race == get_race(RC_DAEMON)) {
|
|
|
|
race_t urc;
|
|
|
|
race *rc;
|
|
|
|
do {
|
|
|
|
urc = (race_t)(rng_int() % MAXRACES);
|
|
|
|
rc = get_race(urc);
|
|
|
|
} while (rc == NULL || urc == RC_DAEMON || !playerrace(rc));
|
|
|
|
u->irace = rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return u;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2014-12-22 14:30:04 +01:00
|
|
|
bool checkpasswd(const faction * f, const char *passwd)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2016-01-12 00:52:42 +01:00
|
|
|
if (!passwd) return false;
|
2016-01-12 23:52:30 +01:00
|
|
|
|
2016-02-29 14:57:01 +01:00
|
|
|
if (f->_password && password_verify(f->_password, passwd) == VERIFY_FAIL) {
|
2016-01-12 23:52:30 +01:00
|
|
|
log_warning("password check failed: %s", factionname(f));
|
|
|
|
return false;
|
2016-01-12 00:52:42 +01:00
|
|
|
}
|
2016-01-12 23:52:30 +01:00
|
|
|
return true;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
variant read_faction_reference(struct storage * store)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
variant id;
|
|
|
|
READ_INT(store, &id.i);
|
|
|
|
return id;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
void write_faction_reference(const faction * f, struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2016-02-16 07:30:26 +01:00
|
|
|
assert(!f || f->_alive);
|
|
|
|
WRITE_INT(store, f ? f->no : 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2016-01-11 18:17:24 +01:00
|
|
|
static faction *dead_factions;
|
|
|
|
|
|
|
|
void free_flist(faction **fp) {
|
|
|
|
faction * flist = *fp;
|
|
|
|
for (flist = factions; flist;) {
|
|
|
|
faction *f = flist;
|
|
|
|
flist = f->next;
|
|
|
|
free_faction(f);
|
|
|
|
free(f);
|
|
|
|
}
|
|
|
|
*fp = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_factions(void) {
|
|
|
|
free_flist(&factions);
|
|
|
|
free_flist(&dead_factions);
|
|
|
|
}
|
|
|
|
|
2016-01-11 09:55:47 +01:00
|
|
|
void destroyfaction(faction ** fp)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2016-01-11 09:55:47 +01:00
|
|
|
faction * f = *fp;
|
2014-08-16 13:46:34 +02:00
|
|
|
unit *u = f->units;
|
|
|
|
|
2016-01-11 09:55:47 +01:00
|
|
|
*fp = f->next;
|
2016-01-11 18:17:24 +01:00
|
|
|
f->next = dead_factions;
|
|
|
|
dead_factions = f;
|
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
fset(f, FFL_QUIT);
|
2016-01-11 11:54:45 +01:00
|
|
|
f->_alive = false;
|
2014-08-16 13:46:34 +02:00
|
|
|
|
|
|
|
if (f->spellbook) {
|
|
|
|
spellbook_clear(f->spellbook);
|
|
|
|
free(f->spellbook);
|
|
|
|
f->spellbook = 0;
|
|
|
|
}
|
|
|
|
|
2015-09-09 11:08:33 +02:00
|
|
|
if (f->seen_factions) {
|
|
|
|
ql_free(f->seen_factions);
|
|
|
|
f->seen_factions = 0;
|
|
|
|
}
|
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
while (u) {
|
|
|
|
/* give away your stuff, make zombies if you cannot (quest items) */
|
|
|
|
int result = gift_items(u, GIFT_FRIENDS | GIFT_PEASANTS);
|
|
|
|
if (result != 0) {
|
|
|
|
unit *zombie = u;
|
|
|
|
u = u->nextF;
|
|
|
|
make_zombie(zombie);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
region *r = u->region;
|
|
|
|
|
|
|
|
if (!fval(r->terrain, SEA_REGION) && !!playerrace(u_race(u))) {
|
|
|
|
const race *rc = u_race(u);
|
|
|
|
int m = rmoney(r);
|
|
|
|
|
|
|
|
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
|
|
|
int p = rpeasants(u->region);
|
|
|
|
int h = rhorses(u->region);
|
|
|
|
item *itm;
|
|
|
|
|
|
|
|
/* Personen gehen nur an die Bauern, wenn sie auch von dort
|
|
|
|
* stammen */
|
|
|
|
if (rc->ec_flags & ECF_REC_HORSES) { /* Zentauren an die Pferde */
|
|
|
|
h += u->number;
|
|
|
|
}
|
2014-09-25 08:59:29 +02:00
|
|
|
else { /* Orks zählen nur zur Hälfte */
|
2014-08-16 13:46:34 +02:00
|
|
|
p += (int)(u->number * rc->recruit_multi);
|
|
|
|
}
|
|
|
|
for (itm = u->items; itm; itm = itm->next) {
|
|
|
|
if (itm->type->flags & ITF_ANIMAL) {
|
|
|
|
h += itm->number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rsetpeasants(r, p);
|
|
|
|
rsethorses(r, h);
|
|
|
|
}
|
|
|
|
m += get_money(u);
|
|
|
|
rsetmoney(r, m);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
set_number(u, 0);
|
|
|
|
u = u->nextF;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
2016-01-11 14:42:36 +01:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
handle_event(f->attribs, "destroy", f);
|
2016-01-11 14:59:25 +01:00
|
|
|
#if 0
|
|
|
|
faction *ff;
|
2014-08-16 13:46:34 +02:00
|
|
|
for (ff = factions; ff; ff = ff->next) {
|
|
|
|
group *g;
|
2016-01-11 09:55:47 +01:00
|
|
|
ally *sf, **sfp;
|
2014-08-16 13:46:34 +02:00
|
|
|
|
2016-01-11 09:55:47 +01:00
|
|
|
for (sfp = &ff->allies; *sfp;) {
|
|
|
|
sf = *sfp;
|
|
|
|
if (sf->faction == f || sf->faction == NULL) {
|
|
|
|
*sfp = sf->next;
|
|
|
|
free(sf);
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
2016-01-11 09:55:47 +01:00
|
|
|
else
|
|
|
|
sfp = &(*sfp)->next;
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
|
|
|
for (g = ff->groups; g; g = g->next) {
|
2016-01-11 09:55:47 +01:00
|
|
|
for (sfp = &g->allies; *sfp; ) {
|
|
|
|
sf = *sfp;
|
|
|
|
if (sf->faction == f || sf->faction == NULL) {
|
|
|
|
*sfp = sf->next;
|
|
|
|
free(sf);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sfp = &(*sfp)->next;
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
2016-01-11 14:59:25 +01:00
|
|
|
#endif
|
|
|
|
|
2016-04-03 12:23:51 +02:00
|
|
|
if (f->alliance) {
|
2016-01-11 09:55:47 +01:00
|
|
|
setalliance(f, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
funhash(f);
|
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
/* units of other factions that were disguised as this faction
|
|
|
|
* have their disguise replaced by ordinary faction hiding. */
|
2015-11-09 13:36:52 +01:00
|
|
|
if (rule_stealth_other()) {
|
2016-01-11 14:59:25 +01:00
|
|
|
// TODO: f.alive should be tested for in get_otherfaction
|
2014-08-16 13:46:34 +02:00
|
|
|
region *rc;
|
|
|
|
for (rc = regions; rc; rc = rc->next) {
|
|
|
|
for (u = rc->units; u; u = u->next) {
|
|
|
|
attrib *a = a_find(u->attribs, &at_otherfaction);
|
2015-11-09 13:36:52 +01:00
|
|
|
if (a && get_otherfaction(a) == f) {
|
2014-08-16 13:46:34 +02:00
|
|
|
a_removeall(&u->attribs, &at_otherfaction);
|
2015-11-09 13:36:52 +01:00
|
|
|
if (rule_stealth_anon()) {
|
|
|
|
fset(u, UFL_ANON_FACTION);
|
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
int get_alliance(const faction * a, const faction * b)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
const ally *sf = a->allies;
|
|
|
|
for (; sf != NULL; sf = sf->next) {
|
|
|
|
if (sf->faction == b) {
|
|
|
|
return sf->status;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
void set_alliance(faction * a, faction * b, int status)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
ally **sfp;
|
|
|
|
sfp = &a->allies;
|
|
|
|
while (*sfp) {
|
|
|
|
ally *sf = *sfp;
|
|
|
|
if (sf->faction == b)
|
|
|
|
break;
|
|
|
|
sfp = &sf->next;
|
|
|
|
}
|
|
|
|
if (*sfp == NULL) {
|
2014-10-31 15:13:05 +01:00
|
|
|
ally *sf = ally_add(sfp, b);
|
2014-08-16 13:46:34 +02:00
|
|
|
sf->status = status;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
(*sfp)->status |= status;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
void renumber_faction(faction * f, int no)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
funhash(f);
|
|
|
|
f->no = no;
|
|
|
|
fhash(f);
|
|
|
|
fset(f, FFL_NEWID);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SMART_INTERVALS
|
2011-03-07 08:02:35 +01:00
|
|
|
void update_interval(struct faction *f, struct region *r)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
if (r == NULL || f == NULL)
|
|
|
|
return;
|
|
|
|
if (f->first == NULL || f->first->index > r->index) {
|
|
|
|
f->first = r;
|
|
|
|
}
|
|
|
|
if (f->last == NULL || f->last->index <= r->index) {
|
|
|
|
f->last = r;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
const char *faction_getname(const faction * self)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
return self->name ? self->name : "";
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
void faction_setname(faction * self, const char *name)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
free(self->name);
|
|
|
|
if (name)
|
|
|
|
self->name = _strdup(name);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
const char *faction_getemail(const faction * self)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
return self->email ? self->email : "";
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
void faction_setemail(faction * self, const char *email)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
free(self->email);
|
|
|
|
if (email)
|
|
|
|
self->email = _strdup(email);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
const char *faction_getbanner(const faction * self)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
return self->banner ? self->banner : "";
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
void faction_setbanner(faction * self, const char *banner)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
free(self->banner);
|
|
|
|
if (banner)
|
|
|
|
self->banner = _strdup(banner);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2016-01-12 23:52:30 +01:00
|
|
|
void faction_setpassword(faction * f, const char *pwhash)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2016-02-16 07:30:26 +01:00
|
|
|
assert(pwhash);
|
|
|
|
// && pwhash[0] == '$');
|
2016-01-12 06:46:51 +01:00
|
|
|
free(f->_password);
|
2016-01-12 23:52:30 +01:00
|
|
|
f->_password = _strdup(pwhash);
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2012-06-24 07:41:07 +02:00
|
|
|
bool valid_race(const struct faction *f, const struct race *rc)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
if (f->race == rc)
|
|
|
|
return true;
|
|
|
|
else {
|
|
|
|
const char *str = get_param(f->race->parameters, "other_race");
|
|
|
|
if (str)
|
|
|
|
return (bool)(rc_find(str) == rc);
|
|
|
|
return false;
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
struct alliance *f_get_alliance(const struct faction *f)
|
2010-08-08 10:06:34 +02:00
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
if (f->alliance && !(f->alliance->flags & ALF_NON_ALLIED)) {
|
|
|
|
return f->alliance;
|
|
|
|
}
|
|
|
|
return NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
}
|
2012-05-26 09:33:55 +02:00
|
|
|
|
|
|
|
struct spellbook * faction_get_spellbook(struct faction *f)
|
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
if (f->spellbook) {
|
|
|
|
return f->spellbook;
|
|
|
|
}
|
|
|
|
if (f->magiegebiet != M_GRAY) {
|
|
|
|
return get_spellbook(magic_school[f->magiegebiet]);
|
|
|
|
}
|
|
|
|
return 0;
|
2012-05-26 09:33:55 +02:00
|
|
|
}
|
2014-06-21 08:59:04 +02:00
|
|
|
|
|
|
|
static int allied_skillcount(const faction * f, skill_t sk)
|
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
int num = 0;
|
|
|
|
alliance *a = f_get_alliance(f);
|
|
|
|
quicklist *members = a->members;
|
|
|
|
int qi;
|
2014-06-21 08:59:04 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
for (qi = 0; members; ql_advance(&members, &qi, 1)) {
|
|
|
|
faction *m = (faction *)ql_get(members, qi);
|
|
|
|
num += count_skill(m, sk);
|
|
|
|
}
|
|
|
|
return num;
|
2014-06-21 08:59:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int allied_skilllimit(const faction * f, skill_t sk)
|
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
static int value = -1;
|
|
|
|
if (value < 0) {
|
2015-11-22 10:44:46 +01:00
|
|
|
value = config_get_int("alliance.skilllimit", 0);
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
|
|
|
return value;
|
2014-06-21 08:59:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int count_skill(faction * f, skill_t sk)
|
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
int n = 0;
|
|
|
|
unit *u;
|
2014-06-21 08:59:04 +02:00
|
|
|
|
2014-08-16 13:46:34 +02:00
|
|
|
for (u = f->units; u; u = u->nextF) {
|
|
|
|
if (has_skill(u, sk)) {
|
|
|
|
if (!is_familiar(u)) {
|
|
|
|
n += u->number;
|
|
|
|
}
|
|
|
|
}
|
2014-06-21 08:59:04 +02:00
|
|
|
}
|
2014-08-16 13:46:34 +02:00
|
|
|
return n;
|
2014-06-21 08:59:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int skill_limit(faction * f, skill_t sk)
|
|
|
|
{
|
2014-08-16 13:46:34 +02:00
|
|
|
int m = INT_MAX;
|
|
|
|
int al = allied_skilllimit(f, sk);
|
|
|
|
if (al > 0) {
|
|
|
|
if (sk != SK_ALCHEMY && sk != SK_MAGIC)
|
|
|
|
return INT_MAX;
|
|
|
|
if (f_get_alliance(f)) {
|
2015-11-03 23:13:04 +01:00
|
|
|
int sc, fl, ac = listlen(f->alliance->members); /* number of factions */
|
|
|
|
|
|
|
|
assert(ac > 0);
|
|
|
|
fl = (al + ac - 1) / ac; /* faction limit, rounded up */
|
2014-08-16 13:46:34 +02:00
|
|
|
/* the faction limit may not be achievable because it would break the alliance-limit */
|
2015-11-03 23:13:04 +01:00
|
|
|
sc = al - allied_skillcount(f, sk);
|
2014-08-16 13:46:34 +02:00
|
|
|
if (sc <= 0)
|
|
|
|
return 0;
|
|
|
|
return fl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sk == SK_MAGIC) {
|
|
|
|
m = max_magicians(f);
|
|
|
|
}
|
|
|
|
else if (sk == SK_ALCHEMY) {
|
2015-11-22 10:44:46 +01:00
|
|
|
m = config_get_int("rules.maxskills.alchemy", MAXALCHEMISTS);
|
2014-08-16 13:46:34 +02:00
|
|
|
}
|
|
|
|
return m;
|
2014-06-21 08:59:04 +02:00
|
|
|
}
|
|
|
|
|
2014-10-16 07:34:09 +02:00
|
|
|
void remove_empty_factions(void)
|
|
|
|
{
|
2016-01-11 09:55:47 +01:00
|
|
|
faction **fp;
|
2014-10-16 07:34:09 +02:00
|
|
|
|
|
|
|
for (fp = &factions; *fp;) {
|
|
|
|
faction *f = *fp;
|
|
|
|
|
2016-01-11 11:54:45 +01:00
|
|
|
if (!(f->_alive && f->units!=NULL) && !fval(f, FFL_NOIDLEOUT)) {
|
2016-01-11 09:55:47 +01:00
|
|
|
log_debug("dead: %s", factionname(f));
|
|
|
|
destroyfaction(fp);
|
2014-10-16 07:34:09 +02:00
|
|
|
}
|
2016-01-11 09:55:47 +01:00
|
|
|
else {
|
2014-10-16 07:34:09 +02:00
|
|
|
fp = &(*fp)->next;
|
2016-01-11 09:55:47 +01:00
|
|
|
}
|
2014-10-16 07:34:09 +02:00
|
|
|
}
|
|
|
|
}
|
2015-05-19 08:02:32 +02:00
|
|
|
|
2016-01-11 14:42:36 +01:00
|
|
|
bool faction_alive(const faction *f) {
|
2016-01-11 12:25:23 +01:00
|
|
|
assert(f);
|
|
|
|
return f->_alive || (f->flags&FFL_NPC);
|
|
|
|
}
|
|
|
|
|
2015-05-19 08:02:32 +02:00
|
|
|
void faction_getorigin(const faction * f, int id, int *x, int *y)
|
|
|
|
{
|
|
|
|
ursprung *ur;
|
|
|
|
|
|
|
|
assert(f && x && y);
|
|
|
|
for (ur = f->ursprung; ur; ur = ur->next) {
|
|
|
|
if (ur->id == id) {
|
|
|
|
*x = ur->x;
|
|
|
|
*y = ur->y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void faction_setorigin(faction * f, int id, int x, int y)
|
|
|
|
{
|
|
|
|
ursprung *ur;
|
|
|
|
assert(f != NULL);
|
|
|
|
for (ur = f->ursprung; ur; ur = ur->next) {
|
|
|
|
if (ur->id == id) {
|
|
|
|
ur->x = ur->x + x;
|
|
|
|
ur->y = ur->y + y;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ur = calloc(1, sizeof(ursprung));
|
|
|
|
ur->id = id;
|
|
|
|
ur->x = x;
|
|
|
|
ur->y = y;
|
|
|
|
|
|
|
|
addlist(&f->ursprung, ur);
|
|
|
|
}
|
|
|
|
|
2015-11-22 15:45:31 +01:00
|
|
|
|
|
|
|
int count_faction(const faction * f, int flags)
|
|
|
|
{
|
|
|
|
unit *u;
|
|
|
|
int n = 0;
|
|
|
|
for (u = f->units; u; u = u->nextF) {
|
|
|
|
const race *rc = u_race(u);
|
|
|
|
int x = (flags&COUNT_UNITS) ? 1 : u->number;
|
|
|
|
if (f->race != rc) {
|
|
|
|
if (!playerrace(rc)) {
|
|
|
|
if (flags&COUNT_MONSTERS) {
|
|
|
|
n += x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (flags&COUNT_MIGRANTS) {
|
|
|
|
if (!is_cursed(u->attribs, C_SLAVE, 0)) {
|
|
|
|
n += x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (flags&COUNT_DEFAULT) {
|
|
|
|
n += x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
int count_units(const faction * f)
|
|
|
|
{
|
|
|
|
return count_faction(f, COUNT_ALL | COUNT_UNITS);
|
|
|
|
}
|
|
|
|
|
|
|
|
int count_all(const faction * f)
|
|
|
|
{
|
|
|
|
return count_faction(f, COUNT_ALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int count_migrants(const faction * f)
|
|
|
|
{
|
|
|
|
return count_faction(f, COUNT_MIGRANTS);
|
|
|
|
}
|
|
|
|
|
2015-11-22 15:53:50 +01:00
|
|
|
#define MIGRANTS_NONE 0
|
|
|
|
#define MIGRANTS_LOG10 1
|
|
|
|
|
2015-11-22 15:45:31 +01:00
|
|
|
int count_maxmigrants(const faction * f)
|
|
|
|
{
|
2015-11-22 15:53:50 +01:00
|
|
|
int formula = get_param_int(f->race->parameters, "migrants.formula", 0);
|
2015-11-22 15:45:31 +01:00
|
|
|
|
2015-11-22 15:53:50 +01:00
|
|
|
if (formula == MIGRANTS_LOG10) {
|
|
|
|
int nsize = count_all(f);
|
|
|
|
if (nsize > 0) {
|
|
|
|
int x = (int)(log10(nsize / 50.0) * 20);
|
|
|
|
if (x < 0) x = 0;
|
|
|
|
return x;
|
2015-11-22 15:45:31 +01:00
|
|
|
}
|
|
|
|
}
|
2015-11-22 15:53:50 +01:00
|
|
|
return 0;
|
2015-11-22 15:45:31 +01:00
|
|
|
}
|
2015-11-24 19:32:52 +01:00
|
|
|
|
|
|
|
static void init_maxmagicians(struct attrib *a)
|
|
|
|
{
|
|
|
|
a->data.i = MAXMAGICIANS;
|
|
|
|
}
|
|
|
|
|
|
|
|
attrib_type at_maxmagicians = {
|
|
|
|
"maxmagicians",
|
|
|
|
init_maxmagicians,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
a_writeint,
|
|
|
|
a_readint,
|
|
|
|
ATF_UNIQUE
|
|
|
|
};
|
|
|
|
|
|
|
|
int max_magicians(const faction * f)
|
|
|
|
{
|
|
|
|
int m = config_get_int("rules.maxskills.magic", MAXMAGICIANS);
|
|
|
|
attrib *a;
|
|
|
|
|
|
|
|
if ((a = a_find(f->attribs, &at_maxmagicians)) != NULL) {
|
|
|
|
m = a->data.i;
|
|
|
|
}
|
|
|
|
if (f->race == get_race(RC_ELF))
|
|
|
|
++m;
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:53:27 +01:00
|
|
|
#define DMAXHASH 7919
|
|
|
|
typedef struct dead {
|
|
|
|
struct dead *nexthash;
|
|
|
|
faction *f;
|
|
|
|
int no;
|
|
|
|
} dead;
|
|
|
|
|
|
|
|
static dead *deadhash[DMAXHASH];
|
|
|
|
|
|
|
|
void dhash(int no, faction * f)
|
|
|
|
{
|
|
|
|
dead *hash = (dead *)calloc(1, sizeof(dead));
|
|
|
|
dead *old = deadhash[no % DMAXHASH];
|
|
|
|
hash->no = no;
|
|
|
|
hash->f = f;
|
|
|
|
deadhash[no % DMAXHASH] = hash;
|
|
|
|
hash->nexthash = old;
|
|
|
|
}
|
|
|
|
|
|
|
|
faction *dfindhash(int no)
|
|
|
|
{
|
|
|
|
dead *old;
|
|
|
|
|
|
|
|
if (no < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (old = deadhash[no % DMAXHASH]; old; old = old->nexthash) {
|
|
|
|
if (old->no == no) {
|
|
|
|
return old->f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2016-01-12 06:46:51 +01:00
|
|
|
|
|
|
|
int writepasswd(void)
|
|
|
|
{
|
|
|
|
FILE *F;
|
|
|
|
char zText[128];
|
|
|
|
|
|
|
|
sprintf(zText, "%s/passwd", basepath());
|
|
|
|
F = fopen(zText, "w");
|
|
|
|
if (!F) {
|
|
|
|
perror(zText);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
faction *f;
|
|
|
|
log_info("writing passwords...");
|
|
|
|
|
|
|
|
for (f = factions; f; f = f->next) {
|
|
|
|
fprintf(F, "%s:%s:%s:%u\n",
|
|
|
|
factionid(f), f->email, f->_password, f->subscription);
|
|
|
|
}
|
|
|
|
fclose(F);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|