Allow alternative names for some spells.

This commit is contained in:
Enno Rehling 2020-07-17 21:20:55 +02:00
parent a5bca33bf7
commit e80701ef35
18 changed files with 272 additions and 50 deletions

View File

@ -1,11 +1,34 @@
{ {
"include": [ "include": [
"config://res/translations/strings.de.po", "config://res/translations/strings.de.po",
"config://res/translations/strings-e2.de.po", "config://res/translations/strings-e2.de.po",
"config://res/translations/strings.en.po", "config://res/translations/strings.en.po",
"config://res/translations/strings-e2.en.po", "config://res/translations/strings-e2.en.po",
"config://res/translations/messages.de.po", "config://res/translations/messages.de.po",
"config://res/translations/messages.en.po", "config://res/translations/messages.en.po",
"config://res/core/messages.xml" "config://res/core/messages.xml"
] ],
"aliases": {
"de": {
"spell::earthquake": [
"Erdelementar",
"Beschwörung eines Erdelemntares"
],
"spell::goodwinds": [
"Wasserelementar",
"Beschwöre einen Wasserelementar"
],
"spell::stormwinds": [
"Sturmelementar",
"Beschwörung eines Sturmelementares"
],
"spell::summonfireelemental": [
"Hitzeelementar",
"Beschwörung eines Hitzeelementares"
]
},
"en": {
"spell::migration": "Rite of Acceptance"
}
}
} }

View File

@ -1,11 +1,11 @@
{ {
"include": [ "include": [
"config://res/translations/strings.de.po", "config://res/translations/strings.de.po",
"config://res/translations/strings-e3.de.po", "config://res/translations/strings-e3.de.po",
"config://res/translations/messages.de.po", "config://res/translations/messages.de.po",
"config://res/translations/strings.en.po", "config://res/translations/strings.en.po",
"config://res/translations/strings-e3.en.po", "config://res/translations/strings-e3.en.po",
"config://res/translations/messages.en.po", "config://res/translations/messages.en.po",
"config://res/core/messages.xml" "config://res/core/messages.xml"
] ]
} }

View File

@ -582,3 +582,40 @@ function test_buy_sell()
assert_equal(4, u:get_item(item)) assert_equal(4, u:get_item(item))
assert_not_equal(0, u:get_item('money')) assert_not_equal(0, u:get_item('money'))
end end
function test_seacast()
local r = region.create(0,0, "plain")
for i = 1,10 do
-- this prevents storms (only high seas have storms)
region.create(i, 1, "plain")
end
for i = 1,10 do
region.create(i, 0, "ocean")
end
local f = faction.create("human", "noreply@eressea.de", "de")
local s1 = ship.create(r, "boat")
local u1 = unit.create(f, r, 2)
u1:set_skill("sailing", 3)
u1:add_item("money", 1000)
u1.ship = s1
local u2 = unit.create(f, r, 1)
u2.race = "elf"
u2:set_skill("magic", 6)
u2.magic = "gwyrrd"
u2.aura = 60
u2.ship = s1
u2:add_spell("stormwinds")
u2:clear_orders()
u2:add_order("Zaubere stufe 2 'Sturmelementar' " .. itoa36(s1.id))
u1:clear_orders()
u1:add_order("NACH O O O O")
process_orders()
assert_equal(4, u2.region.x)
u2:clear_orders()
u2:add_order("Zaubere stufe 2 'Beschwörung eines Sturmelementares' " .. itoa36(s1.id))
u1:clear_orders()
u1:add_order("NACH O O O O")
process_orders()
assert_equal(8, u2.region.x)
end

View File

@ -168,7 +168,7 @@ function test_no_teach()
-- TODO: assert something (reflecting skills sucks!) -- TODO: assert something (reflecting skills sucks!)
end end
function test_seecast() function test_seacast()
local r = region.create(0,0, "plain") local r = region.create(0,0, "plain")
for i = 1,10 do for i = 1,10 do
-- this prevents storms (only high seas have storms) -- this prevents storms (only high seas have storms)

View File

@ -1738,7 +1738,7 @@ void do_combatmagic(battle * b, combatmagic_t was)
if (sp == NULL) if (sp == NULL)
continue; continue;
ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang)); ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang));
if (!cancast(mage, sp, 1, 1, ord)) { if (!cancast(mage, sp, 1, 1, ord)) {
free_order(ord); free_order(ord);
continue; continue;
@ -1820,7 +1820,7 @@ static void do_combatspell(troop at)
fi->magic = 0; /* Hat keinen Kampfzauber, kaempft nichtmagisch weiter */ fi->magic = 0; /* Hat keinen Kampfzauber, kaempft nichtmagisch weiter */
return; return;
} }
ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang)); ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang));
if (!cancast(mage, sp, 1, 1, ord)) { if (!cancast(mage, sp, 1, 1, ord)) {
fi->magic = 0; /* Kann nicht mehr Zaubern, kaempft nichtmagisch weiter */ fi->magic = 0; /* Kann nicht mehr Zaubern, kaempft nichtmagisch weiter */
return; return;

View File

@ -476,10 +476,12 @@ static int cr_spell(variant var, char *buffer, const void *userdata)
{ {
const faction *report = (const faction *)userdata; const faction *report = (const faction *)userdata;
spell *sp = (spell *)var.v; spell *sp = (spell *)var.v;
if (sp != NULL) if (sp != NULL) {
sprintf(buffer, "\"%s\"", spell_name(sp, report->locale)); sprintf(buffer, "\"%s\"", spell_name(mkname_spell(sp), report->locale));
else }
else {
strcpy(buffer, "\"\""); strcpy(buffer, "\"\"");
}
return 0; return 0;
} }
@ -708,7 +710,8 @@ static void cr_output_spells(stream *out, const unit * u, int maxlevel)
spellbook_entry * sbe = (spellbook_entry *)selist_get(ql, qi); spellbook_entry * sbe = (spellbook_entry *)selist_get(ql, qi);
if (sbe->level <= maxlevel) { if (sbe->level <= maxlevel) {
const spell *sp = spellref_get(&sbe->spref); const spell *sp = spellref_get(&sbe->spref);
const char *name = translate(mkname("spell", sp->sname), spell_name(sp, f->locale)); const char * spname = mkname("spell", sp->sname);
const char *name = translate(spname, spell_name(spname, f->locale));
if (!header) { if (!header) {
stream_printf(out, "SPRUECHE\n"); stream_printf(out, "SPRUECHE\n");
header = 1; header = 1;
@ -923,12 +926,12 @@ void cr_output_unit(stream *out, const faction * f,
int level; int level;
const spell *sp = mage_get_combatspell(mage, i, &level); const spell *sp = mage_get_combatspell(mage, i, &level);
if (sp) { if (sp) {
const char *name; const char *name = mkname_spell(sp);
if (level > maxlevel) { if (level > maxlevel) {
level = maxlevel; level = maxlevel;
} }
stream_printf(out, "KAMPFZAUBER %d\n", i); stream_printf(out, "KAMPFZAUBER %d\n", i);
name = translate(mkname("spell", sp->sname), spell_name(sp, lang)); name = translate(name, spell_name(name, lang));
stream_printf(out, "\"%s\";name\n", name); stream_printf(out, "\"%s\";name\n", name);
stream_printf(out, "%d;level\n", level); stream_printf(out, "%d;level\n", level);
} }
@ -1057,8 +1060,8 @@ static void cr_find_address(FILE * F, const faction * uf, selist * addresses)
static void cr_reportspell(FILE * F, const spell * sp, int level, const struct locale *lang) static void cr_reportspell(FILE * F, const spell * sp, int level, const struct locale *lang)
{ {
int k; int k;
const char *name = const char *spname = mkname_spell(sp);
translate(mkname("spell", sp->sname), spell_name(sp, lang)); const char *name = translate(spname, spell_name(spname, lang));
fprintf(F, "ZAUBER %d\n", str_hash(sp->sname)); fprintf(F, "ZAUBER %d\n", str_hash(sp->sname));
fprintf(F, "\"%s\";name\n", name); fprintf(F, "\"%s\";name\n", name);

View File

@ -10,6 +10,7 @@
#include "kernel/faction.h" #include "kernel/faction.h"
#include "kernel/item.h" #include "kernel/item.h"
#include "util/aliases.h"
#include "util/functions.h" #include "util/functions.h"
#include "util/language.h" #include "util/language.h"
#include "util/log.h" #include "util/log.h"
@ -53,6 +54,7 @@ void game_done(void)
free_config(); free_config();
free_locales(); free_locales();
#endif #endif
free_aliases();
free_prefixes(); free_prefixes();
free_special_directions(); free_special_directions();
kernel_done(); kernel_done();

View File

@ -18,6 +18,7 @@
/* util includes */ /* util includes */
#include "kernel/attrib.h" #include "kernel/attrib.h"
#include "util/aliases.h"
#include "util/crmessage.h" #include "util/crmessage.h"
#include "util/functions.h" #include "util/functions.h"
#include "util/keyword.h" #include "util/keyword.h"
@ -696,6 +697,41 @@ static void json_strings(cJSON *json) {
} }
} }
static void json_add_aliases(cJSON *json, const struct locale *lang) {
cJSON *child;
str_aliases *aliases = get_aliases(lang);
for (child = json->child; child; child = child->next) {
if (child->type == cJSON_String) {
alias_add(aliases, child->string, child->valuestring);
}
else if (child->type == cJSON_Array) {
cJSON *entry;
for (entry = child->child; entry; entry = entry->next) {
if (entry->type == cJSON_String) {
alias_add(aliases, child->string, entry->valuestring);
}
}
}
}
}
static void json_aliases(cJSON *json) {
cJSON *child;
if (json->type != cJSON_Object) {
log_error("aliases is not a json array: %d", json->type);
return;
}
for (child = json->child; child; child = child->next) {
if (child->type == cJSON_Object) {
struct locale *lang = get_locale(child->string);
json_add_aliases(child, lang);
}
else {
log_error("strings for locale `%s` are not a json object: %d", child->string, child->type);
}
}
}
static void json_direction(cJSON *json, struct locale *lang) { static void json_direction(cJSON *json, struct locale *lang) {
cJSON *child; cJSON *child;
if (json->type != cJSON_Object) { if (json->type != cJSON_Object) {
@ -1108,6 +1144,9 @@ void json_config(cJSON *json) {
else if (strcmp(child->string, "strings") == 0) { else if (strcmp(child->string, "strings") == 0) {
json_strings(child); json_strings(child);
} }
else if (strcmp(child->string, "aliases") == 0) {
json_aliases(child);
}
else if (strcmp(child->string, "calendar") == 0) { else if (strcmp(child->string, "calendar") == 0) {
json_calendar(child); json_calendar(child);
} }

View File

@ -1396,7 +1396,6 @@ void region_set_owner(struct region *r, struct faction *owner, int turn)
faction *update_owners(region * r) faction *update_owners(region * r)
{ {
faction *f = NULL; faction *f = NULL;
assert(rule_region_owners());
if (r->land) { if (r->land) {
building *bowner = largestbuilding(r, cmp_current_owner, false); building *bowner = largestbuilding(r, cmp_current_owner, false);
building *blargest = largestbuilding(r, cmp_taxes, false); building *blargest = largestbuilding(r, cmp_taxes, false);

View File

@ -22,12 +22,15 @@
#include <spells/unitcurse.h> #include <spells/unitcurse.h>
#include <kernel/ally.h> #include <kernel/ally.h>
#include <kernel/attrib.h>
#include <kernel/building.h> #include <kernel/building.h>
#include <kernel/callbacks.h> #include <kernel/callbacks.h>
#include <kernel/config.h> #include <kernel/config.h>
#include <kernel/curse.h> #include <kernel/curse.h>
#include <kernel/equipment.h> #include <kernel/equipment.h>
#include <kernel/event.h>
#include <kernel/faction.h> #include <kernel/faction.h>
#include <kernel/gamedata.h>
#include <kernel/item.h> #include <kernel/item.h>
#include <kernel/messages.h> #include <kernel/messages.h>
#include <kernel/objtypes.h> #include <kernel/objtypes.h>
@ -44,10 +47,8 @@
#include <kernel/unit.h> #include <kernel/unit.h>
/* util includes */ /* util includes */
#include <kernel/attrib.h> #include <util/aliases.h>
#include <util/base36.h> #include <util/base36.h>
#include <kernel/event.h>
#include <kernel/gamedata.h>
#include <util/language.h> #include <util/language.h>
#include <util/lists.h> #include <util/lists.h>
#include <util/log.h> #include <util/log.h>
@ -2927,10 +2928,14 @@ const char *spell_info(const spell * sp, const struct locale *lang)
return LOC(lang, mkname("spellinfo", sp->sname)); return LOC(lang, mkname("spellinfo", sp->sname));
} }
/* TODO: should take the name, not the spell (spellref optimizations) */ const char *mkname_spell(const spell *sp)
const char *spell_name(const spell * sp, const struct locale *lang)
{ {
return LOC(lang, mkname("spell", sp->sname)); return mkname("spell", sp->sname);
}
const char *spell_name(const char *spname, const struct locale *lang)
{
return LOC(lang, spname);
} }
const char *curse_name(const curse_type * ctype, const struct locale *lang) const char *curse_name(const curse_type * ctype, const struct locale *lang)
@ -2948,14 +2953,22 @@ static void select_spellbook(void **tokens, spellbook *sb, const struct locale *
for (qi = 0, ql = sb->spells; ql; selist_advance(&ql, &qi, 1)) { for (qi = 0, ql = sb->spells; ql; selist_advance(&ql, &qi, 1)) {
spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi); spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi);
const spell *sp = spellref_get(&sbe->spref); const spell *sp = spellref_get(&sbe->spref);
const char *n = spell_name(sp, lang); const char *spname = mkname("spell", sp->sname);
const char *n = spell_name(spname, lang);
if (!n) { if (!n) {
log_error("no translation in locale %s for spell %s\n", locale_name(lang), sp->sname); log_error("no translation in locale %s for spell %s\n", locale_name(lang), sp->sname);
} }
else { else {
variant token; variant token;
const str_alias *aliases = alias_get(lang, spname);
token.v = (void *)sp; token.v = (void *)sp;
addtoken((struct tnode **)tokens, n, token); addtoken((struct tnode **)tokens, n, token);
if (aliases) {
int i;
for (i = 0; i != MAXSTRINGS && aliases->strings[i]; ++i) {
addtoken((struct tnode **)tokens, aliases->strings[i], token);
}
}
} }
} }
} }

View File

@ -315,10 +315,10 @@ extern "C" {
void fix_fam_spells(struct unit *u); void fix_fam_spells(struct unit *u);
void fix_fam_migrant(struct unit *u); void fix_fam_migrant(struct unit *u);
const char *mkname_spell(const struct spell *sp);
const char *spell_name(const char *spname, const struct locale *lang);
const char *spell_info(const struct spell *sp, const char *spell_info(const struct spell *sp,
const struct locale *lang); const struct locale *lang);
const char *spell_name(const struct spell *sp,
const struct locale *lang);
const char *curse_name(const struct curse_type *ctype, const char *curse_name(const struct curse_type *ctype,
const struct locale *lang); const struct locale *lang);

View File

@ -189,7 +189,7 @@ void test_getspell_unit(CuTest * tc)
lang = test_create_locale(); lang = test_create_locale();
sp = create_spell("testspell"); sp = create_spell("testspell");
locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp"); locale_setstring(lang, mkname_spell(sp), "Herp-a-derp");
CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang)); CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang));
@ -248,7 +248,7 @@ void test_getspell_school(CuTest * tc)
lang = test_create_locale(); lang = test_create_locale();
sp = create_spell("testspell"); sp = create_spell("testspell");
locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp"); locale_setstring(lang, mkname_spell(sp), "Herp-a-derp");
CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang)); CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang));
@ -402,8 +402,8 @@ void test_multi_cast(CuTest *tc) {
CuAssertPtrEquals(tc, sp, find_spell("fireball")); CuAssertPtrEquals(tc, sp, find_spell("fireball"));
lang = test_create_locale(); lang = test_create_locale();
locale_setstring(lang, mkname("spell", sp->sname), "Feuerball"); locale_setstring(lang, mkname_spell(sp), "Feuerball");
CuAssertStrEquals(tc, "Feuerball", spell_name(sp, lang)); CuAssertStrEquals(tc, "Feuerball", spell_name(mkname_spell(sp), lang));
u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0));
set_level(u, SK_MAGIC, 10); set_level(u, SK_MAGIC, 10);

View File

@ -256,7 +256,7 @@ void nr_spell_syntax(char *buf, size_t size, spellbook_entry * sbe, const struct
sbs_strcat(&sbs, " n]"); sbs_strcat(&sbs, " n]");
} }
spname = spell_name(sp, lang); spname = spell_name(mkname_spell(sp), lang);
if (strchr(spname, ' ') != NULL) { if (strchr(spname, ' ') != NULL) {
/* contains spaces, needs quotes */ /* contains spaces, needs quotes */
sbs_strcat(&sbs, " '"); sbs_strcat(&sbs, " '");
@ -422,7 +422,7 @@ void nr_spell(struct stream *out, spellbook_entry * sbe, const struct locale *la
sbstring sbs; sbstring sbs;
newline(out); newline(out);
centre(out, spell_name(sp, lang), true); centre(out, spell_name(mkname_spell(sp), lang), true);
newline(out); newline(out);
paragraph(out, LOC(lang, "nr_spell_description"), 0, 0, 0); paragraph(out, LOC(lang, "nr_spell_description"), 0, 0, 0);
paragraph(out, spell_info(sp, lang), 2, 0, 0); paragraph(out, spell_info(sp, lang), 2, 0, 0);

View File

@ -808,7 +808,7 @@ void bufunit(const faction * f, const unit * u, const faction *fv,
sbs_strcat(sbp, ", "); sbs_strcat(sbp, ", ");
} }
/* TODO: no need to deref the spellref here (spref->name is good) */ /* TODO: no need to deref the spellref here (spref->name is good) */
sbs_strcat(sbp, spell_name(sp, lang)); sbs_strcat(sbp, spell_name(mkname_spell(sp), lang));
} }
} }
@ -830,7 +830,7 @@ void bufunit(const faction * f, const unit * u, const faction *fv,
sp = get_combatspell(u, i); sp = get_combatspell(u, i);
if (sp) { if (sp) {
int sl = get_combatspelllevel(u, i); int sl = get_combatspelllevel(u, i);
sbs_strcat(sbp, spell_name(sp, lang)); sbs_strcat(sbp, spell_name(mkname_spell(sp), lang));
if (sl > 0) { if (sl > 0) {
sbs_printf(sbp, "(%d)", sl); sbs_printf(sbp, "(%d)", sl);
} }
@ -1877,7 +1877,7 @@ static void eval_spell(struct opstack **stack, const void *userdata)
const struct faction *f = (const struct faction *)userdata; const struct faction *f = (const struct faction *)userdata;
const struct spell *sp = (const struct spell *)opop(stack).v; const struct spell *sp = (const struct spell *)opop(stack).v;
const char *c = const char *c =
sp ? spell_name(sp, f->locale) : LOC(f->locale, "an_unknown_spell"); sp ? spell_name(mkname_spell(sp), f->locale) : LOC(f->locale, "an_unknown_spell");
variant var; variant var;
assert(c || !"spell without description!"); assert(c || !"spell without description!");

View File

@ -27,13 +27,14 @@
#include <kernel/spellbook.h> #include <kernel/spellbook.h>
#include <kernel/terrain.h> #include <kernel/terrain.h>
#include <util/aliases.h>
#include <util/functions.h> #include <util/functions.h>
#include "util/keyword.h" #include <util/keyword.h>
#include <util/language.h> #include <util/language.h>
#include <util/lists.h> #include <util/lists.h>
#include <util/message.h> #include <util/message.h>
#include <util/log.h> #include <util/log.h>
#include "util/param.h" #include <util/param.h>
#include <util/rand.h> #include <util/rand.h>
#include <util/assert.h> #include <util/assert.h>
@ -249,6 +250,7 @@ static void test_reset(void) {
free_shiptypes(); free_shiptypes();
free_races(); free_races();
free_spellbooks(); free_spellbooks();
free_aliases();
free_prefixes(); free_prefixes();
mt_clear(); mt_clear();

View File

@ -3,6 +3,7 @@ project(util C)
add_subdirectory (crypto) add_subdirectory (crypto)
SET(_TEST_FILES SET(_TEST_FILES
# aliases.test.c
base36.test.c base36.test.c
# crmessage.test.c # crmessage.test.c
# dice.test.c # dice.test.c
@ -31,6 +32,7 @@ variant.test.c
) )
SET(_FILES SET(_FILES
aliases.c
base36.c base36.c
crmessage.c crmessage.c
dice.c dice.c

79
src/util/aliases.c Normal file
View File

@ -0,0 +1,79 @@
#include "aliases.h"
#include <strings.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
static struct str_aliases *g_aliases;
void free_aliases(void)
{
while (g_aliases) {
int i;
struct str_aliases * anext = g_aliases->next;
for (i = 0; i != MAXALIASES && g_aliases->alternatives[i].key; ++i) {
int j;
struct str_alias *alias = g_aliases->alternatives + i;
free(alias->key);
for (j = 0; j != MAXSTRINGS && alias->strings[j]; ++j) {
free(alias->strings[j]);
}
}
free(g_aliases);
g_aliases = anext;
}
}
str_aliases *get_aliases(const struct locale *lang) {
str_aliases *aliases = g_aliases;
while (aliases && aliases->lang != lang) {
aliases = aliases->next;
}
if (aliases == NULL) {
aliases = calloc(1, sizeof(struct str_aliases));
aliases->next = g_aliases;
aliases->lang = lang;
g_aliases = aliases;
}
return aliases;
}
void alias_add(struct str_aliases *aliases, const char *key, const char *text) {
int i, j;
struct str_alias *alias;
for (i = 0; i != MAXALIASES && aliases->alternatives[i].key; ++i) {
if (0 == strcmp(key, aliases->alternatives[i].key)) {
break;
}
}
assert(i != MAXALIASES);
alias = aliases->alternatives + i;
alias->key = str_strdup(key);
for (j = 0; j != MAXSTRINGS; ++j) {
if (alias->strings[j] == NULL) {
alias->strings[j] = str_strdup(text);
break;
}
}
assert(j != MAXSTRINGS);
}
const struct str_alias *alias_get(const struct locale *lang, const char *key)
{
str_aliases *aliases = g_aliases;
while (aliases && aliases->lang != lang) {
aliases = aliases->next;
}
if (aliases) {
int i;
for (i = 0; i != MAXALIASES && aliases->alternatives[i].key; ++i) {
if (0 == strcmp(key, aliases->alternatives[i].key)) {
return aliases->alternatives + i;
}
}
}
return NULL;
}

23
src/util/aliases.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
struct locale;
#define MAXSTRINGS 2
#define MAXALIASES 16
typedef struct str_alias {
char *key;
char *strings[MAXSTRINGS];
} str_alias;
typedef struct str_aliases {
struct str_aliases *next;
const struct locale *lang;
str_alias alternatives[MAXALIASES];
} str_aliases;
void free_aliases(void);
str_aliases *get_aliases(const struct locale *lang);
void alias_add(str_aliases *aliases, const char *key, const char *text);
const str_alias *alias_get(const struct locale *lang, const char *key);