forked from github/server
Merge pull request #655 from ennorehling/develop
BUG 2281: guardhouses are unlimited
This commit is contained in:
commit
635edf314d
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<xi:include href="config://default/buildings/castle-2.xml" />
|
<xi:include href="config://default/buildings/castle-2.xml" />
|
||||||
|
|
||||||
<building name="watch" capacity="1" fort="yes">
|
<building name="watch" maxsize="10" capacity="1" fort="yes">
|
||||||
<function name="name" value="fort_name"/>
|
<function name="name" value="fort_name"/>
|
||||||
<function name="protection" value="building_protection"/>
|
<function name="protection" value="building_protection"/>
|
||||||
<function name="taxes" value="lua_building_taxes"/>
|
<function name="taxes" value="lua_building_taxes"/>
|
||||||
|
|
|
@ -5,6 +5,20 @@ if not config.embassy then return nil end
|
||||||
local embassy = {}
|
local embassy = {}
|
||||||
local home = nil
|
local home = nil
|
||||||
|
|
||||||
|
-- global exports (use item)
|
||||||
|
function use_seashell(u, amount)
|
||||||
|
-- Muschelplateau...
|
||||||
|
local visit = u.faction:get_key('mupL')
|
||||||
|
if visit and u.region~= home then
|
||||||
|
local turns = get_turn() - visit
|
||||||
|
local msg = message.create('msg_event')
|
||||||
|
msg:set_string("string", u.name .. "(" .. itoa36(u.id) .. ") erzählt den Bewohnern von " .. u.region.name .. " von Muschelplateau, das die Partei " .. u.faction.name .. " vor " .. turns .. " Wochen besucht hat." )
|
||||||
|
msg:send_region(u.region)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
return -4
|
||||||
|
end
|
||||||
|
|
||||||
function embassy.init()
|
function embassy.init()
|
||||||
home = get_region(165,30)
|
home = get_region(165,30)
|
||||||
if home==nil then
|
if home==nil then
|
||||||
|
@ -20,7 +34,7 @@ function embassy.update()
|
||||||
eressea.log.debug("updating embassies in " .. tostring(home))
|
eressea.log.debug("updating embassies in " .. tostring(home))
|
||||||
local u
|
local u
|
||||||
for u in home.units do
|
for u in home.units do
|
||||||
if not u.faction:get_key('mupL') then
|
if u.faction:get_key('mupL')==0 then
|
||||||
if (u.faction:add_item('seashell', 1)>0) then
|
if (u.faction:add_item('seashell', 1)>0) then
|
||||||
eressea.log.debug("new seashell for " .. tostring(u.faction))
|
eressea.log.debug("new seashell for " .. tostring(u.faction))
|
||||||
u.faction:set_key('mupL', get_turn())
|
u.faction:set_key('mupL', get_turn())
|
||||||
|
|
|
@ -11,13 +11,45 @@ function teardown()
|
||||||
eressea.settings.set("rules.food.flags", "0")
|
eressea.settings.set("rules.food.flags", "0")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function test_build_watch()
|
||||||
|
local r = region.create(0, 0, "plain")
|
||||||
|
local f = faction.create("e3build@eressea.de", "human", "de")
|
||||||
|
local u = unit.create(f, r, 1)
|
||||||
|
|
||||||
|
u.number = 20
|
||||||
|
u:add_item("log", 20)
|
||||||
|
u.id = 42
|
||||||
|
|
||||||
|
u:set_skill("building", 1)
|
||||||
|
u:add_order("MACHE Wache")
|
||||||
|
process_orders()
|
||||||
|
assert_not_nil(u.building)
|
||||||
|
assert_equal(5, u.building.size)
|
||||||
|
|
||||||
|
u:set_skill("building", 2)
|
||||||
|
u:add_order("MACHE Wache " .. itoa36(u.building.id))
|
||||||
|
process_orders()
|
||||||
|
assert_not_nil(u.building)
|
||||||
|
assert_equal(10, u.building.size)
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_watch()
|
||||||
|
local r = region.create(0, 0, "plain")
|
||||||
|
local b = building.create(r, "watch")
|
||||||
|
|
||||||
|
assert_equal("scaffolding", b:get_typename(1))
|
||||||
|
assert_equal("scaffolding", b:get_typename(4))
|
||||||
|
assert_equal("guardhouse", b:get_typename(5))
|
||||||
|
assert_equal("guardhouse", b:get_typename(9))
|
||||||
|
assert_equal("guardtower", b:get_typename(10))
|
||||||
|
end
|
||||||
|
|
||||||
function test_small_castles()
|
function test_small_castles()
|
||||||
local r = region.create(0, 0, "plain")
|
local r = region.create(0, 0, "plain")
|
||||||
local f1 = faction.create("noreply@eressea.de", "human", "de")
|
local f1 = faction.create("noreply@eressea.de", "human", "de")
|
||||||
local u1 = unit.create(f1, r, 1)
|
local u1 = unit.create(f1, r, 1)
|
||||||
local f2 = faction.create("noreply@eressea.de", "halfling", "de")
|
local f2 = faction.create("noreply@eressea.de", "halfling", "de")
|
||||||
local u2 = unit.create(f2, r, 1)
|
local u2 = unit.create(f2, r, 1)
|
||||||
u1:add_item("money", 10000)
|
|
||||||
|
|
||||||
local b = building.create(r, "castle")
|
local b = building.create(r, "castle")
|
||||||
u2.building = b
|
u2.building = b
|
||||||
|
|
|
@ -42,6 +42,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TNONE = 0, TINTEGER = 1
|
||||||
|
} dict_type;
|
||||||
|
|
||||||
typedef struct dict_data {
|
typedef struct dict_data {
|
||||||
dict_type type;
|
dict_type type;
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -62,94 +66,38 @@ static int dict_read(attrib * a, void *owner, gamedata *data)
|
||||||
storage *store = data->store;
|
storage *store = data->store;
|
||||||
char name[NAMESIZE];
|
char name[NAMESIZE];
|
||||||
dict_data *dd = (dict_data *)a->data.v;
|
dict_data *dd = (dict_data *)a->data.v;
|
||||||
int result, n;
|
int n;
|
||||||
float flt;
|
|
||||||
|
|
||||||
READ_STR(store, name, sizeof(name));
|
READ_STR(store, name, sizeof(name));
|
||||||
dd->name = strdup(name);
|
dd->name = strdup(name);
|
||||||
READ_INT(store, &n);
|
READ_INT(store, &n);
|
||||||
dd->type = (dict_type)n;
|
dd->type = (dict_type)n;
|
||||||
switch (dd->type) {
|
if (dd->type != TINTEGER) {
|
||||||
case TINTEGER:
|
log_error("read dict, invalid type %d", n);
|
||||||
|
return AT_READ_FAIL;
|
||||||
|
}
|
||||||
READ_INT(store, &dd->data.i);
|
READ_INT(store, &dd->data.i);
|
||||||
break;
|
|
||||||
case TREAL:
|
|
||||||
READ_FLT(store, &flt);
|
|
||||||
if ((int)flt == flt) {
|
|
||||||
dd->type = TINTEGER;
|
|
||||||
dd->data.i = (int)flt;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dd->data.real = flt;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TSTRING:
|
|
||||||
READ_STR(store, name, sizeof(name));
|
|
||||||
dd->data.str = strdup(name);
|
|
||||||
break;
|
|
||||||
case TBUILDING:
|
|
||||||
result =
|
|
||||||
read_reference(&dd->data.b, data, read_building_reference,
|
|
||||||
resolve_building);
|
|
||||||
if (result == 0 && !dd->data.b) {
|
|
||||||
return AT_READ_FAIL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TUNIT:
|
|
||||||
result =
|
|
||||||
read_reference(&dd->data.u, data, read_unit_reference, resolve_unit);
|
|
||||||
if (result == 0 && !dd->data.u) {
|
|
||||||
return AT_READ_FAIL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TFACTION:
|
|
||||||
result =
|
|
||||||
read_reference(&dd->data.f, data, read_faction_reference,
|
|
||||||
resolve_faction);
|
|
||||||
if (result == 0 && !dd->data.f) {
|
|
||||||
return AT_READ_FAIL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TREGION:
|
|
||||||
result =
|
|
||||||
read_reference(&dd->data.r, data, read_region_reference,
|
|
||||||
RESOLVE_REGION(data->version));
|
|
||||||
if (result == 0 && !dd->data.r) {
|
|
||||||
return AT_READ_FAIL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TSHIP:
|
|
||||||
/* return read_ship_reference(&data->data.sh, store); */
|
|
||||||
assert(!"not implemented");
|
|
||||||
break;
|
|
||||||
case TNONE:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return AT_READ_FAIL;
|
|
||||||
}
|
|
||||||
return AT_READ_DEPR;
|
return AT_READ_DEPR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dict_init(attrib * a)
|
static void dict_init(attrib * a)
|
||||||
{
|
{
|
||||||
dict_data *data;
|
dict_data *dd;
|
||||||
a->data.v = malloc(sizeof(dict_data));
|
a->data.v = malloc(sizeof(dict_data));
|
||||||
data = (dict_data *)a->data.v;
|
dd = (dict_data *)a->data.v;
|
||||||
data->type = TNONE;
|
dd->type = TNONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dict_done(attrib * a)
|
static void dict_done(attrib * a)
|
||||||
{
|
{
|
||||||
dict_data *data = (dict_data *)a->data.v;
|
dict_data *dd = (dict_data *)a->data.v;
|
||||||
if (data->type == TSTRING)
|
free(dd->name);
|
||||||
free(data->data.str);
|
|
||||||
free(data->name);
|
|
||||||
free(a->data.v);
|
free(a->data.v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dict_upgrade(attrib **alist, attrib *abegin) {
|
static void dict_upgrade(attrib **alist, attrib *abegin) {
|
||||||
int n = 0, *keys = 0;
|
int n = 0, *keys = 0;
|
||||||
int i = 0, val[4];
|
int i = 0, val[8];
|
||||||
attrib *a, *ak = a_find(*alist, &at_keys);
|
attrib *a, *ak = a_find(*alist, &at_keys);
|
||||||
if (ak) {
|
if (ak) {
|
||||||
keys = (int *)ak->data.v;
|
keys = (int *)ak->data.v;
|
||||||
|
@ -162,7 +110,9 @@ static void dict_upgrade(attrib **alist, attrib *abegin) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (strcmp(dd->name, "embassy_muschel")==0) {
|
if (strcmp(dd->name, "embassy_muschel")==0) {
|
||||||
val[i++] = atoi36("mupL");
|
val[i * 2] = atoi36("mupL");
|
||||||
|
val[i * 2 + 1] = dd->data.i;
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log_error("dict conversion, bad entry %s", dd->name);
|
log_error("dict conversion, bad entry %s", dd->name);
|
||||||
|
@ -170,23 +120,35 @@ static void dict_upgrade(attrib **alist, attrib *abegin) {
|
||||||
}
|
}
|
||||||
if (i == 4) {
|
if (i == 4) {
|
||||||
keys = realloc(keys, sizeof(int) * (n + i + 1));
|
keys = realloc(keys, sizeof(int) * (n + i + 1));
|
||||||
memcpy(keys + n + 1, val, sizeof(int)*i);
|
memcpy(keys + n + 1, val, sizeof(val));
|
||||||
n += i;
|
n += i;
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
keys = realloc(keys, sizeof(int) * (n + i + 1));
|
keys = realloc(keys, sizeof(int) * (2 * (n + i) + 1));
|
||||||
memcpy(keys + n + 1, val, sizeof(int)*i);
|
memcpy(keys + n*2 + 1, val, sizeof(int)*i*2);
|
||||||
if (!ak) {
|
if (!ak) {
|
||||||
ak = a_add(alist, a_new(&at_keys));
|
ak = a_add(alist, a_new(&at_keys));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ak) {
|
||||||
ak->data.v = keys;
|
ak->data.v = keys;
|
||||||
|
if (keys) {
|
||||||
keys[0] = n + i;
|
keys[0] = n + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attrib_type at_dict = {
|
attrib_type at_dict = {
|
||||||
"object", dict_init, dict_done, NULL,
|
"object", dict_init, dict_done, NULL,
|
||||||
NULL, dict_read, dict_upgrade
|
NULL, dict_read, dict_upgrade
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void dict_set(attrib * a, const char * name, int value)
|
||||||
|
{
|
||||||
|
dict_data *dd = (dict_data *)a->data.v;
|
||||||
|
dd->name = strdup(name);
|
||||||
|
dd->type = TINTEGER;
|
||||||
|
dd->data.i = value;
|
||||||
|
}
|
||||||
|
|
|
@ -13,18 +13,16 @@
|
||||||
#ifndef H_ATTRIBUTE_OBJECT
|
#ifndef H_ATTRIBUTE_OBJECT
|
||||||
#define H_ATTRIBUTE_OBJECT
|
#define H_ATTRIBUTE_OBJECT
|
||||||
|
|
||||||
#include <util/variant.h>
|
struct attrib_type;
|
||||||
|
struct attrib;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
extern struct attrib_type at_dict; // DEPRECATED: at_dict has been replaced with at_keys
|
||||||
TNONE = 0, TINTEGER = 1, TREAL = 2, TSTRING = 3,
|
|
||||||
TUNIT = 10, TFACTION = 11, TREGION = 12, TBUILDING = 13, TSHIP = 14
|
|
||||||
} dict_type;
|
|
||||||
|
|
||||||
extern struct attrib_type at_dict;
|
void dict_set(struct attrib * a, const char * name, int value);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
static void a_writekeys(const attrib *a, const void *o, storage *store) {
|
static void a_writekeys(const attrib *a, const void *o, storage *store) {
|
||||||
int i, *keys = (int *)a->data.v;
|
int i, *keys = (int *)a->data.v;
|
||||||
for (i = 0; i <= keys[0]; ++i) {
|
WRITE_INT(store, keys[0]);
|
||||||
WRITE_INT(store, keys[i]);
|
for (i = 0; i < keys[0]; ++i) {
|
||||||
|
WRITE_INT(store, keys[i * 2 + 1]);
|
||||||
|
WRITE_INT(store, keys[i * 2 + 2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +44,16 @@ static int a_readkeys(attrib * a, void *owner, gamedata *data) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return AT_READ_FAIL;
|
return AT_READ_FAIL;
|
||||||
}
|
}
|
||||||
a->data.v = p = malloc(sizeof(int)*(i + 1));
|
a->data.v = p = malloc(sizeof(int)*(i*2 + 1));
|
||||||
*p++ = i;
|
*p++ = i;
|
||||||
while (i--) {
|
while (i--) {
|
||||||
READ_INT(data->store, p++);
|
READ_INT(data->store, p++);
|
||||||
|
if (data->version >= KEYVAL_VERSION) {
|
||||||
|
READ_INT(data->store, p++);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*p++ = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return AT_READ_OK;
|
return AT_READ_OK;
|
||||||
}
|
}
|
||||||
|
@ -71,30 +79,35 @@ attrib_type at_keys = {
|
||||||
|
|
||||||
static void a_upgradekeys(attrib **alist, attrib *abegin) {
|
static void a_upgradekeys(attrib **alist, attrib *abegin) {
|
||||||
int n = 0, *keys = 0;
|
int n = 0, *keys = 0;
|
||||||
int i = 0, val[4];
|
int i = 0, val[8];
|
||||||
attrib *a, *ak = a_find(*alist, &at_keys);
|
attrib *a, *ak = a_find(*alist, &at_keys);
|
||||||
if (ak) {
|
if (ak) {
|
||||||
keys = (int *)ak->data.v;
|
keys = (int *)ak->data.v;
|
||||||
if (keys) n = keys[0];
|
if (keys) n = keys[0];
|
||||||
}
|
}
|
||||||
for (a = abegin; a && a->type == abegin->type; a = a->next) {
|
for (a = abegin; a && a->type == abegin->type; a = a->next) {
|
||||||
val[i++] = a->data.i;
|
val[i * 2] = a->data.i;
|
||||||
if (i == 4) {
|
val[i * 2 + 1] = 1;
|
||||||
keys = realloc(keys, sizeof(int) * (n + i + 1));
|
if (++i == 4) {
|
||||||
memcpy(keys + n + 1, val, sizeof(int)*i);
|
keys = realloc(keys, sizeof(int) * (2 * (n + i) + 1));
|
||||||
|
memcpy(keys + 2 * n + 1, val, sizeof(val));
|
||||||
n += i;
|
n += i;
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
keys = realloc(keys, sizeof(int) * (n + i + 1));
|
keys = realloc(keys, sizeof(int) * (2 * (n + i) + 1));
|
||||||
memcpy(keys + n + 1, val, sizeof(int)*i);
|
memcpy(keys + 2 * n + 1, val, sizeof(int)*i*2);
|
||||||
if (!ak) {
|
if (!ak) {
|
||||||
ak = a_add(alist, a_new(&at_keys));
|
ak = a_add(alist, a_new(&at_keys));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ak) {
|
||||||
ak->data.v = keys;
|
ak->data.v = keys;
|
||||||
|
if (keys) {
|
||||||
keys[0] = n + i;
|
keys[0] = n + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attrib_type at_key = {
|
attrib_type at_key = {
|
||||||
|
@ -107,9 +120,9 @@ attrib_type at_key = {
|
||||||
a_upgradekeys
|
a_upgradekeys
|
||||||
};
|
};
|
||||||
|
|
||||||
void key_set(attrib ** alist, int key)
|
void key_set(attrib ** alist, int key, int val)
|
||||||
{
|
{
|
||||||
int *keys, n = 1;
|
int *keys, n = 0;
|
||||||
attrib *a;
|
attrib *a;
|
||||||
assert(key != 0);
|
assert(key != 0);
|
||||||
a = a_find(*alist, &at_keys);
|
a = a_find(*alist, &at_keys);
|
||||||
|
@ -118,12 +131,13 @@ void key_set(attrib ** alist, int key)
|
||||||
}
|
}
|
||||||
keys = (int *)a->data.v;
|
keys = (int *)a->data.v;
|
||||||
if (keys) {
|
if (keys) {
|
||||||
n = keys[0] + 1;
|
n = keys[0];
|
||||||
}
|
}
|
||||||
keys = realloc(keys, sizeof(int) *(n + 1));
|
keys = realloc(keys, sizeof(int) *(2 * n + 3));
|
||||||
// TODO: does insertion sort pay off here?
|
// TODO: does insertion sort pay off here? prob. not.
|
||||||
keys[0] = n;
|
keys[0] = n + 1;
|
||||||
keys[n] = key;
|
keys[2 * n + 1] = key;
|
||||||
|
keys[2 * n + 2] = val;
|
||||||
a->data.v = keys;
|
a->data.v = keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,29 +149,31 @@ void key_unset(attrib ** alist, int key)
|
||||||
if (a) {
|
if (a) {
|
||||||
int i, *keys = (int *)a->data.v;
|
int i, *keys = (int *)a->data.v;
|
||||||
if (keys) {
|
if (keys) {
|
||||||
for (i = 1; i <= keys[0]; ++i) {
|
int n = keys[0];
|
||||||
if (keys[i] == key) {
|
for (i = 0; i != n; ++i) {
|
||||||
keys[i] = keys[keys[0]];
|
if (keys[2 * i + 1] == key) {
|
||||||
|
memmove(keys + 2 * i + 1, keys + 2 * n - 1, 2 * sizeof(int));
|
||||||
keys[0]--;
|
keys[0]--;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool key_get(attrib *alist, int key) {
|
int key_get(attrib *alist, int key) {
|
||||||
attrib *a;
|
attrib *a;
|
||||||
assert(key != 0);
|
assert(key != 0);
|
||||||
a = a_find(alist, &at_keys);
|
a = a_find(alist, &at_keys);
|
||||||
if (a) {
|
if (a) {
|
||||||
int i, *keys = (int *)a->data.v;
|
int i, *keys = (int *)a->data.v;
|
||||||
if (keys) {
|
if (keys) {
|
||||||
for (i = 1; i <= keys[0]; ++i) {
|
for (i = 0; i != keys[0]; ++i) {
|
||||||
if (keys[i] == key) {
|
if (keys[i*2+1] == key) {
|
||||||
return true;
|
return keys[i * 2 + 2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,12 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
struct attrib;
|
struct attrib;
|
||||||
struct attrib_type;
|
struct attrib_type;
|
||||||
extern struct attrib_type at_key;
|
extern struct attrib_type at_key; // DEPRECATED: at_key has been replaced with at_keys
|
||||||
extern struct attrib_type at_keys;
|
extern struct attrib_type at_keys;
|
||||||
|
|
||||||
void key_set(struct attrib **alist, int key);
|
void key_set(struct attrib **alist, int key, int value);
|
||||||
void key_unset(struct attrib **alist, int key);
|
void key_unset(struct attrib **alist, int key);
|
||||||
bool key_get(struct attrib *alist, int key);
|
int key_get(struct attrib *alist, int key);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
|
#include "dict.h"
|
||||||
|
|
||||||
#include <util/attrib.h>
|
#include <util/attrib.h>
|
||||||
|
#include <util/base36.h>
|
||||||
#include <CuTest.h>
|
#include <CuTest.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
static void test_get_set_keys(CuTest *tc) {
|
static void test_get_set_keys(CuTest *tc) {
|
||||||
attrib *a = 0;
|
attrib *a = 0;
|
||||||
key_set(&a, 42);
|
key_set(&a, 42, 1);
|
||||||
key_set(&a, 43);
|
key_set(&a, 43, 2);
|
||||||
key_set(&a, 44);
|
key_set(&a, 44, 3);
|
||||||
CuAssertTrue(tc, key_get(a, 42));
|
CuAssertIntEquals(tc, 1, key_get(a, 42));
|
||||||
CuAssertTrue(tc, key_get(a, 43));
|
CuAssertIntEquals(tc, 2, key_get(a, 43));
|
||||||
CuAssertTrue(tc, key_get(a, 44));
|
CuAssertIntEquals(tc, 3, key_get(a, 44));
|
||||||
key_unset(&a, 42);
|
key_unset(&a, 42);
|
||||||
CuAssertTrue(tc, !key_get(a, 42));
|
CuAssertIntEquals(tc, 0, key_get(a, 42));
|
||||||
CuAssertTrue(tc, key_get(a, 43));
|
CuAssertIntEquals(tc, 2, key_get(a, 43));
|
||||||
CuAssertTrue(tc, key_get(a, 44));
|
CuAssertIntEquals(tc, 3, key_get(a, 44));
|
||||||
a_removeall(&a, NULL);
|
a_removeall(&a, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ static attrib *key_set_orig(attrib **alist, int key) {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_upgrade(CuTest *tc) {
|
static void test_upgrade_key(CuTest *tc) {
|
||||||
attrib *alist = 0;
|
attrib *alist = 0;
|
||||||
key_set_orig(&alist, 40);
|
key_set_orig(&alist, 40);
|
||||||
key_set_orig(&alist, 41);
|
key_set_orig(&alist, 41);
|
||||||
|
@ -35,18 +37,31 @@ static void test_upgrade(CuTest *tc) {
|
||||||
key_set_orig(&alist, 44);
|
key_set_orig(&alist, 44);
|
||||||
CuAssertPtrNotNull(tc, alist->type->upgrade);
|
CuAssertPtrNotNull(tc, alist->type->upgrade);
|
||||||
alist->type->upgrade(&alist, alist);
|
alist->type->upgrade(&alist, alist);
|
||||||
CuAssertTrue(tc, key_get(alist, 40));
|
CuAssertIntEquals(tc, 1, key_get(alist, 40));
|
||||||
CuAssertTrue(tc, key_get(alist, 41));
|
CuAssertIntEquals(tc, 1, key_get(alist, 41));
|
||||||
CuAssertTrue(tc, key_get(alist, 42));
|
CuAssertIntEquals(tc, 1, key_get(alist, 42));
|
||||||
CuAssertTrue(tc, key_get(alist, 43));
|
CuAssertIntEquals(tc, 1, key_get(alist, 43));
|
||||||
CuAssertTrue(tc, key_get(alist, 44));
|
CuAssertIntEquals(tc, 1, key_get(alist, 44));
|
||||||
a_removeall(&alist, NULL);
|
a_removeall(&alist, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_upgrade_dict(CuTest *tc) {
|
||||||
|
attrib *a;
|
||||||
|
|
||||||
|
a = a_new(&at_dict);
|
||||||
|
|
||||||
|
dict_set(a, "embassy_muschel", 42);
|
||||||
|
CuAssertPtrNotNull(tc, a->type->upgrade);
|
||||||
|
a->type->upgrade(&a, a);
|
||||||
|
CuAssertIntEquals(tc, 42, key_get(a, atoi36("mupL")));
|
||||||
|
a_removeall(&a, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
CuSuite *get_key_suite(void)
|
CuSuite *get_key_suite(void)
|
||||||
{
|
{
|
||||||
CuSuite *suite = CuSuiteNew();
|
CuSuite *suite = CuSuiteNew();
|
||||||
SUITE_ADD_TEST(suite, test_get_set_keys);
|
SUITE_ADD_TEST(suite, test_get_set_keys);
|
||||||
SUITE_ADD_TEST(suite, test_upgrade);
|
SUITE_ADD_TEST(suite, test_upgrade_key);
|
||||||
|
SUITE_ADD_TEST(suite, test_upgrade_dict);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,7 +248,7 @@ static int tolua_faction_getkey(lua_State * L)
|
||||||
const char *name = tolua_tostring(L, 2, 0);
|
const char *name = tolua_tostring(L, 2, 0);
|
||||||
int flag = atoi36(name);
|
int flag = atoi36(name);
|
||||||
|
|
||||||
lua_pushboolean(L, key_get(self->attribs, flag));
|
lua_pushinteger(L, key_get(self->attribs, flag));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,11 +256,11 @@ static int tolua_faction_setkey(lua_State * L)
|
||||||
{
|
{
|
||||||
faction *self = (faction *)tolua_tousertype(L, 1, 0);
|
faction *self = (faction *)tolua_tousertype(L, 1, 0);
|
||||||
const char *name = tolua_tostring(L, 2, 0);
|
const char *name = tolua_tostring(L, 2, 0);
|
||||||
int value = tolua_toboolean(L, 3, 0);
|
int value = (int)tolua_tonumber(L, 3, 0);
|
||||||
int flag = atoi36(name);
|
int flag = atoi36(name);
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
key_set(&self->attribs, flag);
|
key_set(&self->attribs, flag, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
key_unset(&self->attribs, flag);
|
key_unset(&self->attribs, flag);
|
||||||
|
|
|
@ -544,11 +544,11 @@ static int tolua_region_setkey(lua_State * L)
|
||||||
{
|
{
|
||||||
region *self = (region *)tolua_tousertype(L, 1, 0);
|
region *self = (region *)tolua_tousertype(L, 1, 0);
|
||||||
const char *name = tolua_tostring(L, 2, 0);
|
const char *name = tolua_tostring(L, 2, 0);
|
||||||
int value = tolua_toboolean(L, 3, 0);
|
int value = (int)tolua_tonumber(L, 3, 0);
|
||||||
int flag = atoi36(name);
|
int flag = atoi36(name);
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
key_set(&self->attribs, flag);
|
key_set(&self->attribs, flag, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
key_unset(&self->attribs, flag);
|
key_unset(&self->attribs, flag);
|
||||||
|
|
|
@ -786,7 +786,7 @@ static int tolua_unit_set_flag(lua_State * L)
|
||||||
int value = (int)tolua_tonumber(L, 3, 0);
|
int value = (int)tolua_tonumber(L, 3, 0);
|
||||||
int flag = atoi36(name);
|
int flag = atoi36(name);
|
||||||
if (value) {
|
if (value) {
|
||||||
key_set(&self->attribs, flag);
|
key_set(&self->attribs, flag, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
key_unset(&self->attribs, flag);
|
key_unset(&self->attribs, flag);
|
||||||
|
|
|
@ -206,10 +206,10 @@ static int tolua_translate(lua_State * L)
|
||||||
static int tolua_setkey(lua_State * L)
|
static int tolua_setkey(lua_State * L)
|
||||||
{
|
{
|
||||||
const char *name = tolua_tostring(L, 1, 0);
|
const char *name = tolua_tostring(L, 1, 0);
|
||||||
int value = tolua_toboolean(L, 2, 0);
|
int value = (int)tolua_tonumber(L, 3, 0);
|
||||||
int flag = atoi36(name);
|
int flag = atoi36(name);
|
||||||
if (value) {
|
if (value) {
|
||||||
key_set(&global.attribs, flag);
|
key_set(&global.attribs, flag, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
key_unset(&global.attribs, flag);
|
key_unset(&global.attribs, flag);
|
||||||
|
|
|
@ -2707,7 +2707,7 @@ static void expandloot(region * r, request * lootorders)
|
||||||
{
|
{
|
||||||
unit *u;
|
unit *u;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int looted = 0;
|
int m, looted = 0;
|
||||||
int startmoney = rmoney(r);
|
int startmoney = rmoney(r);
|
||||||
|
|
||||||
expandorders(r, lootorders);
|
expandorders(r, lootorders);
|
||||||
|
@ -2724,9 +2724,9 @@ static void expandloot(region * r, request * lootorders)
|
||||||
free(g_requests);
|
free(g_requests);
|
||||||
|
|
||||||
/* Lowering morale by 1 depending on the looted money (+20%) */
|
/* Lowering morale by 1 depending on the looted money (+20%) */
|
||||||
|
m = region_get_morale(r);
|
||||||
|
if (m && startmoney>0) {
|
||||||
if (rng_int() % 100 < 20 + (looted * 80) / startmoney) {
|
if (rng_int() % 100 < 20 + (looted * 80) / startmoney) {
|
||||||
int m = region_get_morale(r);
|
|
||||||
if (m) {
|
|
||||||
/*Nur Moral -1, turns is not changed, so the first time nothing happens if the morale is good*/
|
/*Nur Moral -1, turns is not changed, so the first time nothing happens if the morale is good*/
|
||||||
region_set_morale(r, m - 1, -1);
|
region_set_morale(r, m - 1, -1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -428,6 +428,76 @@ int roqf_factor(void)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int use_materials(unit *u, const construction *type, int n, int completed) {
|
||||||
|
if (type->materials) {
|
||||||
|
int c;
|
||||||
|
for (c = 0; type->materials[c].number; c++) {
|
||||||
|
const struct resource_type *rtype = type->materials[c].rtype;
|
||||||
|
int prebuilt =
|
||||||
|
required(completed, type->reqsize, type->materials[c].number);
|
||||||
|
int need =
|
||||||
|
required(completed + n, type->reqsize, type->materials[c].number);
|
||||||
|
int multi = 1;
|
||||||
|
int canuse = 100; /* normalization */
|
||||||
|
if (building_is_active(u->building) && inside_building(u)) {
|
||||||
|
canuse = matmod(u->building->type->attribs, u, rtype, canuse);
|
||||||
|
}
|
||||||
|
if (canuse < 0) {
|
||||||
|
return canuse; /* pass errors to caller */
|
||||||
|
}
|
||||||
|
canuse = matmod(type->attribs, u, rtype, canuse);
|
||||||
|
|
||||||
|
assert(canuse % 100 == 0
|
||||||
|
|| !"only constant multipliers are implemented in build()");
|
||||||
|
multi = canuse / 100;
|
||||||
|
if (canuse < 0) {
|
||||||
|
return canuse; /* pass errors to caller */
|
||||||
|
}
|
||||||
|
use_pooled(u, rtype, GET_DEFAULT,
|
||||||
|
(need - prebuilt + multi - 1) / multi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int count_materials(unit *u, const construction *type, int n, int completed)
|
||||||
|
{
|
||||||
|
if (type->materials) {
|
||||||
|
int c;
|
||||||
|
for (c = 0; n > 0 && type->materials[c].number; c++) {
|
||||||
|
const struct resource_type *rtype = type->materials[c].rtype;
|
||||||
|
int need, prebuilt;
|
||||||
|
int canuse = get_pooled(u, rtype, GET_DEFAULT, INT_MAX);
|
||||||
|
|
||||||
|
if (building_is_active(u->building) && inside_building(u)) {
|
||||||
|
canuse = matmod(u->building->type->attribs, u, rtype, canuse);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canuse < 0)
|
||||||
|
return canuse; /* pass errors to caller */
|
||||||
|
canuse = matmod(type->attribs, u, rtype, canuse);
|
||||||
|
if (type->reqsize > 1) {
|
||||||
|
prebuilt =
|
||||||
|
required(completed, type->reqsize, type->materials[c].number);
|
||||||
|
for (; n;) {
|
||||||
|
need =
|
||||||
|
required(completed + n, type->reqsize, type->materials[c].number);
|
||||||
|
if (need - prebuilt <= canuse)
|
||||||
|
break;
|
||||||
|
--n; /* TODO: optimieren? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int maxn = canuse / type->materials[c].number;
|
||||||
|
if (maxn < n) {
|
||||||
|
n = maxn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
/** Use up resources for building an object.
|
/** Use up resources for building an object.
|
||||||
* Build up to 'size' points of 'type', where 'completed'
|
* Build up to 'size' points of 'type', where 'completed'
|
||||||
* of the first object have already been finished. return the
|
* of the first object have already been finished. return the
|
||||||
|
@ -492,7 +562,7 @@ int build(unit * u, const construction * ctype, int completed, int want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; want > 0 && skills > 0;) {
|
for (; want > 0 && skills > 0;) {
|
||||||
int c, n;
|
int err, n;
|
||||||
|
|
||||||
/* skip over everything that's already been done:
|
/* skip over everything that's already been done:
|
||||||
* type->improvement==NULL means no more improvements, but no size limits
|
* type->improvement==NULL means no more improvements, but no size limits
|
||||||
|
@ -559,66 +629,16 @@ int build(unit * u, const construction * ctype, int completed, int want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type->materials)
|
n = count_materials(u, type, n, completed);
|
||||||
for (c = 0; n > 0 && type->materials[c].number; c++) {
|
|
||||||
const struct resource_type *rtype = type->materials[c].rtype;
|
|
||||||
int need, prebuilt;
|
|
||||||
int canuse = get_pooled(u, rtype, GET_DEFAULT, INT_MAX);
|
|
||||||
|
|
||||||
if (building_is_active(u->building) && inside_building(u)) {
|
|
||||||
canuse = matmod(u->building->type->attribs, u, rtype, canuse);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canuse < 0)
|
|
||||||
return canuse; /* pass errors to caller */
|
|
||||||
canuse = matmod(type->attribs, u, rtype, canuse);
|
|
||||||
if (type->reqsize > 1) {
|
|
||||||
prebuilt =
|
|
||||||
required(completed, type->reqsize, type->materials[c].number);
|
|
||||||
for (; n;) {
|
|
||||||
need =
|
|
||||||
required(completed + n, type->reqsize, type->materials[c].number);
|
|
||||||
if (need - prebuilt <= canuse)
|
|
||||||
break;
|
|
||||||
--n; /* TODO: optimieren? */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int maxn = canuse / type->materials[c].number;
|
|
||||||
if (maxn < n)
|
|
||||||
n = maxn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (n <= 0) {
|
if (n <= 0) {
|
||||||
if (made == 0)
|
if (made == 0)
|
||||||
return ENOMATERIALS;
|
return ENOMATERIALS;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (type->materials)
|
err = use_materials(u, type, n, completed);
|
||||||
for (c = 0; type->materials[c].number; c++) {
|
if (err < 0) {
|
||||||
const struct resource_type *rtype = type->materials[c].rtype;
|
return err;
|
||||||
int prebuilt =
|
|
||||||
required(completed, type->reqsize, type->materials[c].number);
|
|
||||||
int need =
|
|
||||||
required(completed + n, type->reqsize, type->materials[c].number);
|
|
||||||
int multi = 1;
|
|
||||||
int canuse = 100; /* normalization */
|
|
||||||
if (building_is_active(u->building) && inside_building(u)) {
|
|
||||||
canuse = matmod(u->building->type->attribs, u, rtype, canuse);
|
|
||||||
}
|
|
||||||
if (canuse < 0)
|
|
||||||
return canuse; /* pass errors to caller */
|
|
||||||
canuse = matmod(type->attribs, u, rtype, canuse);
|
|
||||||
|
|
||||||
assert(canuse % 100 == 0
|
|
||||||
|| !"only constant multipliers are implemented in build()");
|
|
||||||
multi = canuse / 100;
|
|
||||||
if (canuse < 0)
|
|
||||||
return canuse; /* pass errors to caller */
|
|
||||||
|
|
||||||
use_pooled(u, rtype, GET_DEFAULT,
|
|
||||||
(need - prebuilt + multi - 1) / multi);
|
|
||||||
}
|
}
|
||||||
made += n;
|
made += n;
|
||||||
skills -= n * type->minskill;
|
skills -= n * type->minskill;
|
||||||
|
|
|
@ -84,7 +84,7 @@ static void test_group_readwrite(CuTest * tc)
|
||||||
f = test_create_faction(0);
|
f = test_create_faction(0);
|
||||||
new_group(f, "NW", 42);
|
new_group(f, "NW", 42);
|
||||||
g = new_group(f, "Egoisten", 43);
|
g = new_group(f, "Egoisten", 43);
|
||||||
key_set(&g->attribs, 44);
|
key_set(&g->attribs, 44, 44);
|
||||||
al = ally_add(&g->allies, f);
|
al = ally_add(&g->allies, f);
|
||||||
al->status = HELP_GIVE;
|
al->status = HELP_GIVE;
|
||||||
write_groups(&store, f);
|
write_groups(&store, f);
|
||||||
|
@ -108,7 +108,7 @@ static void test_group_readwrite(CuTest * tc)
|
||||||
CuAssertStrEquals(tc, "Egoisten", f->groups->next->name);
|
CuAssertStrEquals(tc, "Egoisten", f->groups->next->name);
|
||||||
CuAssertPtrEquals(tc, 0, f->groups->allies);
|
CuAssertPtrEquals(tc, 0, f->groups->allies);
|
||||||
g = f->groups->next;
|
g = f->groups->next;
|
||||||
CuAssertTrue(tc, key_get(g->attribs, 44));
|
CuAssertIntEquals(tc, 44, key_get(g->attribs, 44));
|
||||||
CuAssertPtrNotNull(tc, g->allies);
|
CuAssertPtrNotNull(tc, g->allies);
|
||||||
CuAssertPtrEquals(tc, 0, g->allies->next);
|
CuAssertPtrEquals(tc, 0, g->allies->next);
|
||||||
CuAssertPtrEquals(tc, f, g->allies->faction);
|
CuAssertPtrEquals(tc, f, g->allies->faction);
|
||||||
|
|
|
@ -939,7 +939,7 @@ struct order *ord)
|
||||||
use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
|
use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
|
||||||
user->number);
|
user->number);
|
||||||
|
|
||||||
key_set(&f->attribs, atoi36("mbst"));
|
key_set(&f->attribs, atoi36("mbst"), turn);
|
||||||
set_level(user, SK_MAGIC, 3);
|
set_level(user, SK_MAGIC, 3);
|
||||||
|
|
||||||
ADDMSG(&user->faction->msgs, msg_message("use_item",
|
ADDMSG(&user->faction->msgs, msg_message("use_item",
|
||||||
|
|
|
@ -211,8 +211,8 @@ static void test_readwrite_attrib(CuTest *tc) {
|
||||||
attrib *a = NULL;
|
attrib *a = NULL;
|
||||||
|
|
||||||
test_setup();
|
test_setup();
|
||||||
key_set(&a, 41);
|
key_set(&a, 41, 42);
|
||||||
key_set(&a, 42);
|
key_set(&a, 42, 43);
|
||||||
mstream_init(&data.strm);
|
mstream_init(&data.strm);
|
||||||
gamedata_init(&data, &store, RELEASE_VERSION);
|
gamedata_init(&data, &store, RELEASE_VERSION);
|
||||||
write_attribs(data.store, a, NULL);
|
write_attribs(data.store, a, NULL);
|
||||||
|
@ -223,8 +223,8 @@ static void test_readwrite_attrib(CuTest *tc) {
|
||||||
read_attribs(&data, &a, NULL);
|
read_attribs(&data, &a, NULL);
|
||||||
mstream_done(&data.strm);
|
mstream_done(&data.strm);
|
||||||
gamedata_done(&data);
|
gamedata_done(&data);
|
||||||
CuAssertTrue(tc, key_get(a, 41));
|
CuAssertIntEquals(tc, 42, key_get(a, 41));
|
||||||
CuAssertTrue(tc, key_get(a, 42));
|
CuAssertIntEquals(tc, 43, key_get(a, 42));
|
||||||
a_removeall(&a, NULL);
|
a_removeall(&a, NULL);
|
||||||
|
|
||||||
test_cleanup();
|
test_cleanup();
|
||||||
|
|
|
@ -11,8 +11,9 @@
|
||||||
const char *eressea_version(void) {
|
const char *eressea_version(void) {
|
||||||
#ifdef ERESSEA_BUILDNO
|
#ifdef ERESSEA_BUILDNO
|
||||||
return ERESSEA_VERSION "-" ERESSEA_BUILDNO;
|
return ERESSEA_VERSION "-" ERESSEA_BUILDNO;
|
||||||
#endif
|
#else
|
||||||
return ERESSEA_VERSION;
|
return ERESSEA_VERSION;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int version_no(const char *str) {
|
int version_no(const char *str) {
|
||||||
|
|
|
@ -32,10 +32,11 @@
|
||||||
#define NOCRYPT_VERSION 352 /* passwords are plaintext again */
|
#define NOCRYPT_VERSION 352 /* passwords are plaintext again */
|
||||||
#define ATHASH_VERSION 353 /* attribute-type hash, not name */
|
#define ATHASH_VERSION 353 /* attribute-type hash, not name */
|
||||||
#define NOWATCH_VERSION 354 /* plane->watchers is gone */
|
#define NOWATCH_VERSION 354 /* plane->watchers is gone */
|
||||||
|
#define KEYVAL_VERSION 355 /* at_keys has values */
|
||||||
/* unfinished: */
|
/* unfinished: */
|
||||||
#define CRYPT_VERSION 400 /* passwords are encrypted */
|
#define CRYPT_VERSION 400 /* passwords are encrypted */
|
||||||
|
|
||||||
#define RELEASE_VERSION NOWATCH_VERSION /* current datafile */
|
#define RELEASE_VERSION KEYVAL_VERSION /* current datafile */
|
||||||
#define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */
|
#define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */
|
||||||
#define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */
|
#define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue