forked from github/server
Merge branch 'master' of github.com:eressea/server
This commit is contained in:
commit
e5b54ba953
|
@ -27,3 +27,4 @@ game-e3/reports/
|
||||||
*.log
|
*.log
|
||||||
*.log.*
|
*.log.*
|
||||||
tags
|
tags
|
||||||
|
Thumbs.db
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Eressea Rules
|
||||||
|
|
||||||
|
© Copyright Enno Rehling 2014
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
1. Introduction
|
||||||
|
2. First Turn by Example
|
||||||
|
3. The Economy: Recruiting, Upkeep and Silver
|
||||||
|
4. Studying Skills
|
||||||
|
5. Movement and Seafaring
|
||||||
|
6. Combat
|
||||||
|
7. Alliances and Groups
|
||||||
|
7. Resources and Production
|
||||||
|
8. Magic and Alchemy
|
||||||
|
9. Castles and other buildings
|
||||||
|
10. Races
|
||||||
|
11. List of Skills
|
||||||
|
12. List of Buildings
|
||||||
|
12. List of Items
|
||||||
|
13. Appendices and Tables
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
This game would not be possible without the work of Russell Wallace, who created the Atlantis PBEM in 1993. I would also like to thank Alex Schröder, who wrote the first German translation of Atlantis. Christian Schlittchen, Katja Zedel and Henning Peters started Eressea, and invited me to be a part of their team and become their friends in the process. Ingo Wilken, Benjamin Bärmann, Stefan Reich, and Martin Hershoff have contributed code to the server, and valuable design input. Many of the features are a direct result of entertaining debates on the eressea-kom discussion list, and I would like to thank them and the entire community for their support and encouragement over the years.
|
||||||
|
|
||||||
|
Enno Rehling, April 2014
|
|
@ -0,0 +1,79 @@
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Eressea is a play-by-email strategy role-playing game that simulates a fantasy world. Unlike most video games, there are are no fancy graphics, but everything that happens is communicated by text.
|
||||||
|
|
||||||
|
As a player, you are in control of a number of characters, which are referred to as units, which combine to make a faction. Your faction competes with other factions over the resources of the world that you explore together. Sometimes this leads to war, and sometimes to friendship between factions, and eventually it all adds up to a rich history of the world, with its own heroes and legends.
|
||||||
|
|
||||||
|
Eressea is different from most games in many respects, but most of all in that there is no declared winner. There is not even a goal, other than to enjoy yourself, and set your own goals. Do you want to monopolize a natural resource? Become the strongest military force? Create a democratic government for the factions in your part of the world? Collect trophies from slain dragons? Map the entire globe? Those are all valid goals to set for yourself, but you might find your own.
|
||||||
|
|
||||||
|
### How to play
|
||||||
|
|
||||||
|
Eressea is played in weekly turns. Every weekend, you receive a textual report containing your units, the part of the world that they can observe, and any events that happened since the last report.
|
||||||
|
|
||||||
|
Based on that report, players send an email to the game's server that contains orders for all of their units. A week later, the server evaluates the orders from all players, updates the state of the world, and sends out new reports.
|
||||||
|
|
||||||
|
A (slightly simplified) report may look like this:
|
||||||
|
|
||||||
|
Report for Eressea, Sunday, 13. April 2014, 23:25
|
||||||
|
|
||||||
|
It is the first week of the month of harvest moon in
|
||||||
|
the 1. year of the fourth age. It is summer.
|
||||||
|
|
||||||
|
Guardians (xin8), elves/draig (guardian@gmx.de)
|
||||||
|
|
||||||
|
Your faction has 4 people in 1 unit.
|
||||||
|
|
||||||
|
Vetkan (10,1), highland, 1012 peasants, 13156 silver,
|
||||||
|
17 horses. To the northwest lies the forests of
|
||||||
|
Buviken (9,2), to the northeast the mountains of Rebus
|
||||||
|
(10,2), to the east the plain of Setutvul (-9,1), to
|
||||||
|
the southeast the mountains of Vithil (-9,0), to the
|
||||||
|
southwest the forests of Posotmetid (10,0) and to the
|
||||||
|
west the forests of Cesartapun (9,1)
|
||||||
|
|
||||||
|
The local market offers silk and mandrakes.
|
||||||
|
|
||||||
|
Statistics for Vetkan (10,1):
|
||||||
|
|
||||||
|
Peasant wages: 11 silver
|
||||||
|
Recruits: 25 peasants
|
||||||
|
|
||||||
|
Pelenth (opw4), size 10, fortification.
|
||||||
|
|
||||||
|
* Fighters of Pelenth (r6b0), 4 elves,
|
||||||
|
aggressive, skills: melee 5, has: platemail,
|
||||||
|
sword, 500 silver.
|
||||||
|
|
||||||
|
+ Osswid the Destroyer (wtmj), Vikings (atnf), 1
|
||||||
|
dwarf, has: 1 iron.
|
||||||
|
|
||||||
|
What you see here are two units, Osswid (wtmj) and the Fighters of Pelenth (r6b0). The fighters are our own unit, but Osswid belongs to the faction Vikings (atnf). Every unit, faction or other object in the game is identified by a four-letter code, and it is common to append it to the name in parentheses. The fort of Pelenth (opw4) is a building, and our fighters are inside, which causes them to be indented a little more, and gives them some benefits including additional protection in combat.
|
||||||
|
|
||||||
|
While Osswid is a single dwarf, our unit of fighters consists of several elves. Units can any number of men, which are always of the same race. Each unit can learn any number of skills, and our Fighters are skilled in melee combat at a respectable level 5. They own a sword and one platemail, which means that one of them will be well equipped when going into battle, while the other three will fight with their bare hands. The unit also has 500 pieces of silver, the currency of the game.
|
||||||
|
|
||||||
|
Since he isn't of our own faction, we cannot tell what skills Osswid has, but we can tell that he's unarmed.
|
||||||
|
|
||||||
|
We see all this because one of our units is in the region, the highland of Vetkan (10,1). Regions do not have a four-letter identifier, but are instead identified by their coordinates on the map, which makes it easier to visualize the world.
|
||||||
|
|
||||||
|
The region of Vetkan is surrounded by a number of other regions on six sides, since Eressea's Map uses a hexagonal grid. Every region has a terrain, and we can see highland, mountain, plain and forest regions in our vicinity. Each terrain has different qualities: For example, mountains are good for mining iron ore and quarries, while plains are fertile and forests produce lumber. Each region is inhabited by a peasant population, and you can see that Vetkan has a population of 1012. These peasants are the basis of the region's economy, and they own a combined 13,156 silver that we could try to acquire through taxation or other means.
|
||||||
|
|
||||||
|
Example orders for this faction may look like this:
|
||||||
|
|
||||||
|
ERESSEA xin8 "password"
|
||||||
|
UNIT r6b0
|
||||||
|
STUDY perception
|
||||||
|
GIVE wtmj 100 silver
|
||||||
|
GIVE TEMP 1 150 silver
|
||||||
|
GIVE TEMP 2 150 silver
|
||||||
|
MAKE TEMP 1
|
||||||
|
RECRUIT 1
|
||||||
|
MOVE W
|
||||||
|
END
|
||||||
|
MAKE TEMP 2
|
||||||
|
RECRUIT 1
|
||||||
|
MOVE E
|
||||||
|
END
|
||||||
|
|
||||||
|
Every set of orders begins with the faction's id and password, and is followed by orders for one or more units. A unit's orders begin with the word UNIT followed by the unit's id, and one or more orders. In this case, we are giving the unit an order to STUDY the perception skill, and to GIVE silver to three other units. STUDY is a standard action, and each unit can perform exactly one per turn. If you do not specify a standard action, then the unit will usually repeat the action from the previous week. GIVE is a free action, and all units can perform as many free actions in a week as they like.
|
||||||
|
|
||||||
|
The MAKE TEMP order is special: It creates a new unit with a temporary id (in this case, we're calling them TEMP 1 and TEMP 2). The server will replace this with a unique id by the time it sends the next report, but until then, we can address the new unit by this temporary identifier, for example to give it the silver that it requires for recruiting new members. RECRUIT is another example of a free action, while MOVE is a standard action. The MAKE TEMP order is technically given by the unit whose orders preceded it, and the new unit's orders are finished with the END keyword.
|
|
@ -375,7 +375,14 @@ static int tolua_faction_set_locale(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);
|
||||||
self->locale = find_locale(name);
|
const struct locale *loc = find_locale(name);
|
||||||
|
if (loc) {
|
||||||
|
self->locale = loc;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tolua_pushstring(L, "invalid locale");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4521,10 +4521,6 @@ void init_processor(void)
|
||||||
add_proc_region(p, &enter_1, "Betreten (1. Versuch)");
|
add_proc_region(p, &enter_1, "Betreten (1. Versuch)");
|
||||||
add_proc_order(p, K_USE, &use_cmd, 0, "Benutzen");
|
add_proc_order(p, K_USE, &use_cmd, 0, "Benutzen");
|
||||||
|
|
||||||
if (!global.disabled[K_GM]) {
|
|
||||||
add_proc_global(p, &gmcommands, "GM Kommandos");
|
|
||||||
}
|
|
||||||
|
|
||||||
p += 10; /* in case it has any effects on alliance victories */
|
p += 10; /* in case it has any effects on alliance victories */
|
||||||
add_proc_order(p, K_GIVE, &give_control_cmd, 0, "GIB KOMMANDO");
|
add_proc_order(p, K_GIVE, &give_control_cmd, 0, "GIB KOMMANDO");
|
||||||
|
|
||||||
|
|
|
@ -54,65 +54,25 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
/**
|
|
||||||
** at_permissions
|
|
||||||
**/
|
|
||||||
|
|
||||||
static void mistake(const unit * u, struct order *ord, const char *comment)
|
|
||||||
{
|
|
||||||
if (!is_monsters(u->faction)) {
|
|
||||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "mistake",
|
|
||||||
"error", comment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_permissions(const attrib * a, const void *owner, struct storage *store)
|
|
||||||
{
|
|
||||||
a_write(store, (attrib *) a->data.v, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_permissions(attrib * a, void *owner, struct storage *store)
|
static int read_permissions(attrib * a, void *owner, struct storage *store)
|
||||||
{
|
{
|
||||||
attrib *attr = NULL;
|
attrib *attr = NULL;
|
||||||
a_read(store, &attr, NULL);
|
a_read(store, &attr, NULL);
|
||||||
a->data.v = attr;
|
a_free(attr);
|
||||||
return AT_READ_OK;
|
return AT_READ_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct attrib_type at_permissions = {
|
struct attrib_type at_permissions = {
|
||||||
"GM:permissions",
|
"GM:permissions",
|
||||||
NULL, NULL, NULL,
|
NULL, NULL, NULL,
|
||||||
write_permissions, read_permissions,
|
NULL, read_permissions,
|
||||||
ATF_UNIQUE
|
ATF_UNIQUE
|
||||||
};
|
};
|
||||||
|
|
||||||
attrib *make_atpermissions(void)
|
|
||||||
{
|
|
||||||
return a_new(&at_permissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: CREATE <number> <itemtype>
|
|
||||||
**/
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_gmcreate(const attrib * a, const void *owner, struct storage *store)
|
|
||||||
{
|
|
||||||
const item_type *itype = (const item_type *)a->data.v;
|
|
||||||
assert(itype);
|
|
||||||
WRITE_TOK(store, resourcename(itype->rtype, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_gmcreate(attrib * a, void *owner, struct storage *store)
|
static int read_gmcreate(attrib * a, void *owner, struct storage *store)
|
||||||
{
|
{
|
||||||
char zText[32];
|
char zText[32];
|
||||||
READ_TOK(store, zText, sizeof(zText));
|
READ_TOK(store, zText, sizeof(zText));
|
||||||
a->data.v = it_find(zText);
|
|
||||||
if (a->data.v == NULL) {
|
|
||||||
log_error("unknown itemtype %s in gmcreate attribute\n", zText);
|
|
||||||
return AT_READ_FAIL;
|
|
||||||
}
|
|
||||||
return AT_READ_OK;
|
return AT_READ_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,640 +80,11 @@ static int read_gmcreate(attrib * a, void *owner, struct storage *store)
|
||||||
attrib_type at_gmcreate = {
|
attrib_type at_gmcreate = {
|
||||||
"GM:create",
|
"GM:create",
|
||||||
NULL, NULL, NULL,
|
NULL, NULL, NULL,
|
||||||
write_gmcreate, read_gmcreate
|
NULL, read_gmcreate
|
||||||
};
|
};
|
||||||
|
|
||||||
attrib *make_atgmcreate(const struct item_type * itype)
|
|
||||||
{
|
|
||||||
attrib *a = a_new(&at_gmcreate);
|
|
||||||
a->data.v = (void *)itype;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gm_create(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (permissions)
|
|
||||||
permissions = (attrib *) permissions->data.v;
|
|
||||||
if (!permissions)
|
|
||||||
return;
|
|
||||||
i = getint();
|
|
||||||
|
|
||||||
if (i > 0) {
|
|
||||||
const char *iname = getstrtoken();
|
|
||||||
const item_type *itype = finditemtype(iname, u->faction->locale);
|
|
||||||
if (itype == NULL) {
|
|
||||||
mistake(u, ord, "unknown item.");
|
|
||||||
} else {
|
|
||||||
attrib *a = a_find(permissions, &at_gmcreate);
|
|
||||||
|
|
||||||
while (a && a->type == &at_gmcreate && a->data.v != (void *)itype)
|
|
||||||
a = a->next;
|
|
||||||
if (a)
|
|
||||||
i_change(&u->items, itype, i);
|
|
||||||
else
|
|
||||||
mistake(u, ord, "your faction cannot create this item.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool has_permission(const attrib * permissions, unsigned int key)
|
|
||||||
{
|
|
||||||
return (find_key((attrib *) permissions->data.v, key) ||
|
|
||||||
find_key((attrib *) permissions->data.v, atoi36("master")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: GATE <id> <x> <y>
|
|
||||||
** requires: permission-key "gmgate"
|
|
||||||
**/
|
|
||||||
static void gm_gate(const void *tnext, struct unit * u, struct order *ord)
|
|
||||||
{
|
|
||||||
const struct plane *pl = rplane(u->region);
|
|
||||||
int id = getid();
|
|
||||||
int x = rel_to_abs(pl, u->faction, getint(), 0);
|
|
||||||
int y = rel_to_abs(pl, u->faction, getint(), 1);
|
|
||||||
building *b = findbuilding(id);
|
|
||||||
region *r;
|
|
||||||
|
|
||||||
pnormalize(&x, &y, pl);
|
|
||||||
r = findregion(x, y);
|
|
||||||
if (b == NULL || r == NULL || pl != rplane(b->region) || pl != rplane(r)) {
|
|
||||||
mistake(u, ord, "the unit cannot transform this building.");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (permissions && has_permission(permissions, atoi36("gmgate"))) {
|
|
||||||
remove_triggers(&b->attribs, "timer", &tt_gate);
|
|
||||||
remove_triggers(&b->attribs, "create", &tt_unguard);
|
|
||||||
if (r != b->region) {
|
|
||||||
add_trigger(&b->attribs, "timer", trigger_gate(b, r));
|
|
||||||
add_trigger(&b->attribs, "create", trigger_unguard(b));
|
|
||||||
fset(b, BLD_UNGUARDED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: TERRAFORM <x> <y> <terrain>
|
|
||||||
** requires: permission-key "gmterf"
|
|
||||||
**/
|
|
||||||
static void gm_terraform(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
const struct plane *p = rplane(u->region);
|
|
||||||
int x = rel_to_abs(p, u->faction, getint(), 0);
|
|
||||||
int y = rel_to_abs(p, u->faction, getint(), 1);
|
|
||||||
const char *c = getstrtoken();
|
|
||||||
variant token;
|
|
||||||
void **tokens = get_translations(u->faction->locale, UT_TERRAINS);
|
|
||||||
region *r;
|
|
||||||
pnormalize(&x, &y, p);
|
|
||||||
r = findregion(x, y);
|
|
||||||
|
|
||||||
if (r == NULL || p != rplane(r)) {
|
|
||||||
mistake(u, ord, "region is in another plane.");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmterf")))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (findtoken(*tokens, c, &token) != E_TOK_NOMATCH) {
|
|
||||||
const terrain_type *terrain = (const terrain_type *)token.v;
|
|
||||||
terraform_region(r, terrain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: TELEPORT <unit> <x> <y>
|
|
||||||
** requires: permission-key "gmtele"
|
|
||||||
**/
|
|
||||||
static void gm_teleport(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
const struct plane *p = rplane(u->region);
|
|
||||||
unit *to = findunit(getid());
|
|
||||||
int x = rel_to_abs(p, u->faction, getint(), 0);
|
|
||||||
int y = rel_to_abs(p, u->faction, getint(), 1);
|
|
||||||
region *r = findregion(x, y);
|
|
||||||
|
|
||||||
if (r == NULL || p != rplane(r)) {
|
|
||||||
mistake(u, ord, "region is in another plane.");
|
|
||||||
} else if (to == NULL) {
|
|
||||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
|
|
||||||
""));
|
|
||||||
} else if (rplane(to->region) != rplane(r) && !ucontact(to, u)) {
|
|
||||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_no_contact",
|
|
||||||
"target", to));
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmtele"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
} else
|
|
||||||
move_unit(to, r, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: TELL PLANE <string>
|
|
||||||
** requires: permission-key "gmmsgr"
|
|
||||||
**/
|
|
||||||
static void gm_messageplane(const void *tnext, struct unit *gm, struct order *ord)
|
|
||||||
{
|
|
||||||
const struct plane *p = rplane(gm->region);
|
|
||||||
const char *zmsg = getstrtoken();
|
|
||||||
if (p == NULL) {
|
|
||||||
mistake(gm, ord, "In diese Ebene kann keine Nachricht gesandt werden.");
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(gm->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) {
|
|
||||||
mistake(gm, ord, "permission denied.");
|
|
||||||
} else {
|
|
||||||
message *msg = msg_message("msg_event", "string", zmsg);
|
|
||||||
faction *f;
|
|
||||||
region *r;
|
|
||||||
for (f = factions; f; f = f->next) {
|
|
||||||
freset(f, FFL_SELECT);
|
|
||||||
}
|
|
||||||
for (r = regions; r; r = r->next) {
|
|
||||||
unit *u;
|
|
||||||
if (rplane(r) != p)
|
|
||||||
continue;
|
|
||||||
for (u = r->units; u; u = u->next)
|
|
||||||
if (!fval(u->faction, FFL_SELECT)) {
|
|
||||||
f = u->faction;
|
|
||||||
fset(f, FFL_SELECT);
|
|
||||||
add_message(&f->msgs, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg_release(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gm_messagefaction(const void *tnext, struct unit *gm, struct order *ord)
|
|
||||||
{
|
|
||||||
int n = getid();
|
|
||||||
faction *f = findfaction(n);
|
|
||||||
const char *msg = getstrtoken();
|
|
||||||
plane *p = rplane(gm->region);
|
|
||||||
attrib *permissions = a_find(gm->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) {
|
|
||||||
mistake(gm, ord, "permission denied.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (f != NULL) {
|
|
||||||
region *r;
|
|
||||||
for (r = regions; r; r = r->next)
|
|
||||||
if (rplane(r) == p) {
|
|
||||||
unit *u;
|
|
||||||
for (u = r->units; u; u = u->next)
|
|
||||||
if (u->faction == f) {
|
|
||||||
add_message(&f->msgs, msg_message("msg_event", "string", msg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mistake(gm, ord, "cannot send messages to this faction.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: TELL REGION <x> <y> <string>
|
|
||||||
** requires: permission-key "gmmsgr"
|
|
||||||
**/
|
|
||||||
static void gm_messageregion(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
const struct plane *p = rplane(u->region);
|
|
||||||
int x = rel_to_abs(p, u->faction, getint(), 0);
|
|
||||||
int y = rel_to_abs(p, u->faction, getint(), 1);
|
|
||||||
const char *msg = getstrtoken();
|
|
||||||
region *r = findregion(x, y);
|
|
||||||
|
|
||||||
if (r == NULL || p != rplane(r)) {
|
|
||||||
mistake(u, ord, "region is in another plane.");
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
} else {
|
|
||||||
add_message(&r->msgs, msg_message("msg_event", "string", msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: KILL UNIT <id> <string>
|
|
||||||
** requires: permission-key "gmkill"
|
|
||||||
**/
|
|
||||||
static void gm_killunit(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
const struct plane *p = rplane(u->region);
|
|
||||||
unit *target = findunit(getid());
|
|
||||||
const char *msg = getstrtoken();
|
|
||||||
region *r = target->region;
|
|
||||||
|
|
||||||
if (r == NULL || p != rplane(r)) {
|
|
||||||
mistake(u, ord, "region is in another plane.");
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmkill"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
} else {
|
|
||||||
scale_number(target, 0);
|
|
||||||
ADDMSG(&target->faction->msgs, msg_message("killedbygm",
|
|
||||||
"region unit string", r, target, msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: KILL FACTION <id> <string>
|
|
||||||
** requires: permission-key "gmmsgr"
|
|
||||||
**/
|
|
||||||
static void gm_killfaction(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
int n = getid();
|
|
||||||
faction *f = findfaction(n);
|
|
||||||
const char *msg = getstrtoken();
|
|
||||||
plane *p = rplane(u->region);
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmkill"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (f != NULL) {
|
|
||||||
region *r;
|
|
||||||
for (r = regions; r; r = r->next)
|
|
||||||
if (rplane(r) == p) {
|
|
||||||
unit *target;
|
|
||||||
for (target = r->units; target; target = target->next) {
|
|
||||||
if (target->faction == f) {
|
|
||||||
scale_number(target, 0);
|
|
||||||
ADDMSG(&target->faction->msgs, msg_message("killedbygm",
|
|
||||||
"region unit string", r, target, msg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mistake(u, ord, "cannot remove a unit from this faction.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: TELL <unit> <string>
|
|
||||||
** requires: permission-key "gmmsgr"
|
|
||||||
**/
|
|
||||||
static void gm_messageunit(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
const struct plane *p = rplane(u->region);
|
|
||||||
unit *target = findunit(getid());
|
|
||||||
const char *msg = getstrtoken();
|
|
||||||
region *r;
|
|
||||||
|
|
||||||
if (target == NULL) {
|
|
||||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
|
|
||||||
""));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = target->region;
|
|
||||||
|
|
||||||
if (r == NULL || p != rplane(r)) {
|
|
||||||
mistake(u, ord, "region is in another plane.");
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmmsgu"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
} else {
|
|
||||||
add_message(&target->faction->msgs,
|
|
||||||
msg_message("regionmessage", "region sender string", r, u, msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: GIVE <unit> <int> <itemtype>
|
|
||||||
** requires: permission-key "gmgive"
|
|
||||||
**/
|
|
||||||
static void gm_give(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
unit *to = findunit(getid());
|
|
||||||
int num = getint();
|
|
||||||
const item_type *itype = finditemtype(getstrtoken(), u->faction->locale);
|
|
||||||
|
|
||||||
if (to == NULL || rplane(to->region) != rplane(u->region)) {
|
|
||||||
/* unknown or in another plane */
|
|
||||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
|
|
||||||
""));
|
|
||||||
} else if (itype == NULL || i_get(u->items, itype) == 0) {
|
|
||||||
/* unknown or not enough */
|
|
||||||
mistake(u, ord, "invalid item or item not found.");
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmgive"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
} else {
|
|
||||||
int i = i_get(u->items, itype);
|
|
||||||
if (i < num)
|
|
||||||
num = i;
|
|
||||||
if (num) {
|
|
||||||
i_change(&u->items, itype, -num);
|
|
||||||
i_change(&to->items, itype, num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: TAKE <unit> <int> <itemtype>
|
|
||||||
** requires: permission-key "gmtake"
|
|
||||||
**/
|
|
||||||
static void gm_take(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
unit *to = findunit(getid());
|
|
||||||
int num = getint();
|
|
||||||
const item_type *itype = finditemtype(getstrtoken(), u->faction->locale);
|
|
||||||
|
|
||||||
if (to == NULL || rplane(to->region) != rplane(u->region)) {
|
|
||||||
/* unknown or in another plane */
|
|
||||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
|
|
||||||
""));
|
|
||||||
} else if (itype == NULL || i_get(to->items, itype) == 0) {
|
|
||||||
/* unknown or not enough */
|
|
||||||
mistake(u, ord, "invalid item or item not found.");
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmtake"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
} else {
|
|
||||||
int i = i_get(to->items, itype);
|
|
||||||
if (i < num)
|
|
||||||
num = i;
|
|
||||||
if (num) {
|
|
||||||
i_change(&to->items, itype, -num);
|
|
||||||
i_change(&u->items, itype, num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
** GM: SKILL <unit> <skill> <tage>
|
|
||||||
** requires: permission-key "gmskil"
|
|
||||||
**/
|
|
||||||
static void gm_skill(const void *tnext, struct unit *u, struct order *ord)
|
|
||||||
{
|
|
||||||
unit *to = findunit(getid());
|
|
||||||
skill_t skill = findskill(getstrtoken(), u->faction->locale);
|
|
||||||
int num = getint();
|
|
||||||
|
|
||||||
if (to == NULL || rplane(to->region) != rplane(u->region)) {
|
|
||||||
/* unknown or in another plane */
|
|
||||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
|
|
||||||
""));
|
|
||||||
} else if (skill == NOSKILL || skill == SK_MAGIC || skill == SK_ALCHEMY) {
|
|
||||||
/* unknown or not enough */
|
|
||||||
mistake(u, ord, "unknown skill, or skill cannot be raised.");
|
|
||||||
} else if (num < 0 || num > 30) {
|
|
||||||
/* sanity check failed */
|
|
||||||
mistake(u, ord, "invalid value.");
|
|
||||||
} else {
|
|
||||||
/* checking permissions */
|
|
||||||
attrib *permissions = a_find(u->faction->attribs, &at_permissions);
|
|
||||||
if (!permissions || !has_permission(permissions, atoi36("gmskil"))) {
|
|
||||||
mistake(u, ord, "permission denied.");
|
|
||||||
} else {
|
|
||||||
set_level(to, skill, num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void * g_keys;
|
|
||||||
static void * g_root;
|
|
||||||
static void * g_tell;
|
|
||||||
static void * g_kill;
|
|
||||||
|
|
||||||
void register_gmcmd(void)
|
void register_gmcmd(void)
|
||||||
{
|
{
|
||||||
at_register(&at_gmcreate);
|
at_register(&at_gmcreate);
|
||||||
at_register(&at_permissions);
|
at_register(&at_permissions);
|
||||||
add_command(&g_root, &g_keys, "gm", NULL);
|
|
||||||
add_command(&g_keys, NULL, "terraform", &gm_terraform);
|
|
||||||
add_command(&g_keys, NULL, "create", &gm_create);
|
|
||||||
add_command(&g_keys, NULL, "gate", &gm_gate);
|
|
||||||
add_command(&g_keys, NULL, "give", &gm_give);
|
|
||||||
add_command(&g_keys, NULL, "take", &gm_take);
|
|
||||||
add_command(&g_keys, NULL, "teleport", &gm_teleport);
|
|
||||||
add_command(&g_keys, NULL, "skill", &gm_skill);
|
|
||||||
add_command(&g_keys, &g_tell, "tell", NULL);
|
|
||||||
add_command(&g_tell, NULL, "region", &gm_messageregion);
|
|
||||||
add_command(&g_tell, NULL, "unit", &gm_messageunit);
|
|
||||||
add_command(&g_tell, NULL, "plane", &gm_messageplane);
|
|
||||||
add_command(&g_tell, NULL, "faction", &gm_messagefaction);
|
|
||||||
add_command(&g_keys, &g_kill, "kill", NULL);
|
|
||||||
add_command(&g_kill, NULL, "unit", &gm_killunit);
|
|
||||||
add_command(&g_kill, NULL, "faction", &gm_killfaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* execute gm-commands for all units in the game
|
|
||||||
*/
|
|
||||||
|
|
||||||
void gmcommands(void)
|
|
||||||
{
|
|
||||||
region **rp = ®ions;
|
|
||||||
while (*rp) {
|
|
||||||
region *r = *rp;
|
|
||||||
unit **up = &r->units;
|
|
||||||
while (*up) {
|
|
||||||
unit *u = *up;
|
|
||||||
struct order *ord;
|
|
||||||
for (ord = u->orders; ord; ord = ord->next) {
|
|
||||||
if (get_keyword(ord) == K_GM) {
|
|
||||||
do_command(&g_root, u, ord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (u == *up)
|
|
||||||
up = &u->next;
|
|
||||||
}
|
|
||||||
if (*rp == r)
|
|
||||||
rp = &r->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXTENSION 10000
|
|
||||||
|
|
||||||
faction *gm_addquest(const char *email, const char *name, int radius,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
plane *pl;
|
|
||||||
watcher *w = calloc(sizeof(watcher), 1);
|
|
||||||
region *center;
|
|
||||||
bool invalid = false;
|
|
||||||
int minx, miny, maxx, maxy, cx, cy;
|
|
||||||
int x;
|
|
||||||
faction *f;
|
|
||||||
|
|
||||||
/* GM playfield */
|
|
||||||
do {
|
|
||||||
minx = ((rng_int() % (2 * EXTENSION)) - EXTENSION);
|
|
||||||
miny = ((rng_int() % (2 * EXTENSION)) - EXTENSION);
|
|
||||||
for (x = 0; !invalid && x <= radius * 2; ++x) {
|
|
||||||
int y;
|
|
||||||
for (y = 0; !invalid && y <= radius * 2; ++y) {
|
|
||||||
region *r = findregion(minx + x, miny + y);
|
|
||||||
if (r)
|
|
||||||
invalid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (invalid);
|
|
||||||
maxx = minx + 2 * radius;
|
|
||||||
cx = minx + radius;
|
|
||||||
maxy = miny + 2 * radius;
|
|
||||||
cy = miny + radius;
|
|
||||||
pl = create_new_plane(rng_int(), name, minx, maxx, miny, maxy, flags);
|
|
||||||
center = new_region(cx, cy, pl, 0);
|
|
||||||
for (x = 0; x <= 2 * radius; ++x) {
|
|
||||||
int y;
|
|
||||||
for (y = 0; y <= 2 * radius; ++y) {
|
|
||||||
region *r = findregion(minx + x, miny + y);
|
|
||||||
if (!r) {
|
|
||||||
r = new_region(minx + x, miny + y, pl, 0);
|
|
||||||
}
|
|
||||||
freset(r, RF_ENCOUNTER);
|
|
||||||
if (distance(r, center) == radius) {
|
|
||||||
terraform_region(r, newterrain(T_FIREWALL));
|
|
||||||
} else if (r == center) {
|
|
||||||
terraform_region(r, newterrain(T_PLAIN));
|
|
||||||
} else {
|
|
||||||
terraform_region(r, newterrain(T_OCEAN));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* watcher: */
|
|
||||||
f = gm_addfaction(email, pl, center);
|
|
||||||
w->faction = f;
|
|
||||||
w->mode = see_unit;
|
|
||||||
w->next = pl->watchers;
|
|
||||||
pl->watchers = w;
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
faction *gm_addfaction(const char *email, plane * p, region * r)
|
|
||||||
{
|
|
||||||
attrib *a;
|
|
||||||
unit *u;
|
|
||||||
faction *f = calloc(1, sizeof(faction));
|
|
||||||
|
|
||||||
assert(p != NULL);
|
|
||||||
|
|
||||||
/* GM faction */
|
|
||||||
a_add(&f->attribs, make_key(atoi36("quest")));
|
|
||||||
f->banner = _strdup("quest faction");
|
|
||||||
f->name = _strdup("quest faction");
|
|
||||||
f->passw = _strdup(itoa36(rng_int()));
|
|
||||||
if (set_email(&f->email, email) != 0) {
|
|
||||||
log_error("Invalid email address for faction %s: %s\n", itoa36(f->no), email);
|
|
||||||
}
|
|
||||||
f->race = new_race[RC_TEMPLATE];
|
|
||||||
f->age = 0;
|
|
||||||
f->lastorders = turn;
|
|
||||||
f->alive = true;
|
|
||||||
f->locale = default_locale;
|
|
||||||
f->options =
|
|
||||||
want(O_COMPRESS) | want(O_REPORT) | want(O_COMPUTER) | want(O_ADRESSEN);
|
|
||||||
{
|
|
||||||
faction *xist;
|
|
||||||
int id = atoi36("gm00") - 1;
|
|
||||||
do {
|
|
||||||
xist = findfaction(++id);
|
|
||||||
} while (xist);
|
|
||||||
|
|
||||||
f->no = id;
|
|
||||||
addlist(&factions, f);
|
|
||||||
fhash(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* generic permissions */
|
|
||||||
a = a_add(&f->attribs, a_new(&at_permissions));
|
|
||||||
if (a) {
|
|
||||||
attrib *ap = (attrib *) a->data.v;
|
|
||||||
const char *keys[] =
|
|
||||||
{ "gmterf", "gmtele", "gmgive", "gmskil", "gmtake", "gmmsgr", "gmmsgu",
|
|
||||||
"gmgate", 0 };
|
|
||||||
const char **key_p = keys;
|
|
||||||
while (*key_p) {
|
|
||||||
add_key(&ap, atoi36(*key_p));
|
|
||||||
++key_p;
|
|
||||||
}
|
|
||||||
a_add(&ap, make_atgmcreate(resource2item(r_silver)));
|
|
||||||
|
|
||||||
a->data.v = ap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* one initial unit */
|
|
||||||
u = create_unit(r, f, 1, new_race[RC_TEMPLATE], 1, "quest master", NULL);
|
|
||||||
u->irace = new_race[RC_GNOME];
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
plane *gm_addplane(int radius, unsigned int flags, const char *name)
|
|
||||||
{
|
|
||||||
region *center;
|
|
||||||
plane *pl;
|
|
||||||
bool invalid = false;
|
|
||||||
int minx, miny, maxx, maxy, cx, cy;
|
|
||||||
int x;
|
|
||||||
|
|
||||||
/* GM playfield */
|
|
||||||
do {
|
|
||||||
minx = (rng_int() % (2 * EXTENSION)) - EXTENSION;
|
|
||||||
miny = (rng_int() % (2 * EXTENSION)) - EXTENSION;
|
|
||||||
for (x = 0; !invalid && x <= radius * 2; ++x) {
|
|
||||||
int y;
|
|
||||||
for (y = 0; !invalid && y <= radius * 2; ++y) {
|
|
||||||
region *r = findregion(minx + x, miny + y);
|
|
||||||
if (r)
|
|
||||||
invalid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (invalid);
|
|
||||||
maxx = minx + 2 * radius;
|
|
||||||
cx = minx + radius;
|
|
||||||
maxy = miny + 2 * radius;
|
|
||||||
cy = miny + radius;
|
|
||||||
pl = create_new_plane(rng_int(), name, minx, maxx, miny, maxy, flags);
|
|
||||||
center = new_region(cx, cy, pl, 0);
|
|
||||||
for (x = 0; x <= 2 * radius; ++x) {
|
|
||||||
int y;
|
|
||||||
for (y = 0; y <= 2 * radius; ++y) {
|
|
||||||
region *r = findregion(minx + x, miny + y);
|
|
||||||
if (!r)
|
|
||||||
r = new_region(minx + x, miny + y, pl, 0);
|
|
||||||
freset(r, RF_ENCOUNTER);
|
|
||||||
if (distance(r, center) == radius) {
|
|
||||||
terraform_region(r, newterrain(T_FIREWALL));
|
|
||||||
} else if (r == center) {
|
|
||||||
terraform_region(r, newterrain(T_PLAIN));
|
|
||||||
} else {
|
|
||||||
terraform_region(r, newterrain(T_OCEAN));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pl;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,7 +321,7 @@ int a_read(struct storage *store, attrib ** attribs, void *owner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(!"fehler: keine laderoutine für attribut");
|
assert(!"error: no registered callback can read attribute");
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_TOK(store, zText, sizeof(zText));
|
READ_TOK(store, zText, sizeof(zText));
|
||||||
|
|
Loading…
Reference in New Issue