server/src/common/kernel/karma.c

613 lines
18 KiB
C

/* vi: set ts=2:
*
* Eressea PB(E)M host Copyright (C) 1998-2003
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
* Enno Rehling (enno@eressea-pbem.de)
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
*/
/* TODO: enum auf fst_ umstellen. Pointer auf Display-Routine */
#include <config.h>
#include "eressea.h"
#include "karma.h"
/* kernel includes */
#include "unit.h"
#include "race.h"
#include "region.h"
#include "item.h"
#include "group.h"
#include "pool.h"
#include "skill.h"
#include "faction.h"
#include "magic.h"
#include "message.h"
/* util includes */
#include <attrib.h>
#include <base36.h>
/* libc includes */
#include <math.h>
#include <stdlib.h>
attrib_type at_faction_special = {
"faction_special", NULL, NULL, NULL, a_writedefault, a_readdefault
};
int
age_prayer_timeout(attrib *a) {
return --a->data.sa[0];
}
attrib_type at_prayer_timeout = {
"prayer_timeout", NULL, NULL, age_prayer_timeout, a_writedefault, a_readdefault
};
attrib_type at_prayer_effect = {
"prayer_effect", NULL, NULL, NULL, NULL, NULL
};
attrib_type at_wyrm = {
"wyrm", NULL, NULL, NULL, a_writedefault, a_readdefault
};
attrib_type at_fshidden = {
"fshidden", NULL, NULL, NULL, a_writedefault, a_readdefault
};
attrib_type at_jihad = {
"jihad", NULL, NULL, NULL, a_writedefault, a_readdefault
};
struct fspecialdata fspecials[MAXFACTIONSPECIALS] = {
{
"Regeneration",
"Personen in einer Partei mit dieser Eigenschaft heilen jeden "
"Schaden innerhalb einer Woche und zusätzlich in jeder Kampfrunde "
"HP entsprechend ihres Ausdauer-Talents. Sie benötigen jedoch 11 "
"Silber Unterhalt pro Woche.",
1
},
{ /* TODO: Für alte Parteien zu stark */
"Städter",
"Personen einer Partei mit dieser Eigenschaft lieben die Städte und "
"verabscheuen das Leben in der freien Natur. Ihr Arbeitlohn verändert "
"sich in Abhängigkeit vom größten Gebäude in der Region. Ist es eine "
"Zitadelle so erhalten sie 2 Silber mehr pro Runde, bei einer Festung "
"1 Silber mehr. Bei einer Burg bekommen sie den normalen Arbeitslohn, "
"bei einem Turm 1 Silber, bei einer Befestigung 3 Silber weniger. Gibt "
"es kein entsprechendes Gebäude in der Region, verringert sich ihr "
"Arbeitslohn um 5 Silber.",
1
},
{
"Barbar",
"Einheiten dieser Partei erhalten durch Lernen von Waffentalenten "
"(Taktik und Reiten zählen nicht dazu!) 40 statt 30 Lerntage. Weitere "
"Stufen erhöhen den Bonus um 5 Lerntage. Die Fokussierung auf das "
"Kriegerdasein führt jedoch zu Problemen, andere Talente zu erlernen. "
"In allen nichtkriegerischen Talenten einschließlich Magie und Taktik "
"erhalten sie die entsprechende Anzahl von Lerntagen weniger pro "
"Lernwoche.",
100
},
/* TODO: Noch nicht so implementiert. */
{
"Wyrm",
"Eine Partei mit dieser Eigenschaft kann einen ihrer Magier mit Hilfe "
"eines speziellen Zaubers permanent in einen Drachen verwandeln. Der "
"Drache ist zunächst jung, kann sich jedoch durch Lernen des speziellen "
"Talents 'Drachenwesen' in einen größeren Drachen verwandeln. Um zu "
"einem ausgewachsenen Drachen zu werden benötigt der verwandelte Magier "
"die Talentstufe 6, um zu einem Wyrm zu werden die Talentstufe 12. "
"Ein junger Drache benötigt 1000 Silber Unterhalt pro Woche, ein "
"ausgewachsener Drache 5000 und ein Wyrm 10000 Silber. Bekommt er "
"dieses Silber nicht, besteht eine Wahrscheinlichkeit, das er desertiert!",
100
},
/* TODO: Relativ sinnlose Eigenschaft. */
{
"Miliz",
"Alle Personen dieser Partei beginnen mit 30 Talenttagen in allen "
"Waffentalenten, in denen ihre Rasse keinen Malus hat. Zusätzliche "
"Stufen bringen jeweils einen zusätzlichen Talentpunkt.",
100
},
{ /* Ohne Schiffsunterhaltskosten schwache Eigenschaft. */
/* Aufpassen: Tragkraft? */
"Feenreich",
"Alle Personen dieser Partei wiegen nur noch die Hälfte ihres normalen "
"Gewichtes. Die Nähe zum Feenreich macht sie jedoch besonders "
"anfällig gegen Waffen aus Eisen, bei einem Treffer durch eine "
"solche Waffe nehmen sie einen zusätzlichen Schadenspunkt. Zusätzliche "
"Stufen dieser Eigenschaft verringern das Gewicht auf 1/3, 1/4, ... und "
"erhöhen den Schaden durch Eisenwaffen um einen weiteren Punkt.",
100
},
{
"Administrator",
"Das Einheitenlimit einer Partei mit dieser Eigenschaft erhöht sich um "
"400 Einheiten. Leider verschlingt der Verwaltungsaufwand viel Silber, "
"so dass sich der Unterhalt pro Person um 1 Silberstück erhöht. Weitere "
"Stufen der Eigenschaft erhöhen das Limit um weitere 400 Einheiten und "
"den Unterhalt um ein weiteres Silberstück.",
100
},
/* TODO: Noch nicht so implementiert */
{
"Telepath",
"Der Geist fremder Personen bleibt den Magiern einer Partei mit dieser "
"Eigenschaft nicht verschlossen. Fremde Einheiten erscheinen im Report "
"wie eigene Einheiten, mit allen Talentwerten und Gegenständen. Leider "
"führt eine so intensive Beschäftigung mit dem Geistigen zur körperlichen "
"Verkümmerung, und die Partei erhält -1 auf alle Talente außer "
"Alchemie, Kräuterkunde, Magie, Spionage, Tarnung und Wahrnehmung. Wird "
"diese Eigenschaft ein zweites Mal erworben, so bleibt die Wirkung nicht "
"auf Magier beschränkt, sondern alle Einheiten einer Partei können "
"die Talente und Gegenstände aller fremden Einheiten sehen. Allerdings "
"gibt es in beiden Fällen eine Einschränkung: Die Einheit muß sich einen "
"Monat lang auf die psychischen Ströme einer Region einstellen, bevor "
"sie in der Lage ist, die Gedanken der anderen zu lesen.",
2
},
{
"Amphibium",
"Einheiten dieser Partei können durch Ozeanfelder laufen. Allerdings "
"nehmen sie 10 Schadenspunkte, wenn sie am Ende einer Woche in einem "
"Ozeanfeld stehen. Pferde weigern sich, in ein Ozeanfeld zu laufen. "
"Zusätzliche Stufen dieser Eigenschaft reduzieren den Schaden um jeweils "
"5 Punkte. Achtung: Auf dem Ozean wird kein Schaden regeneriert.",
3
},
/* TODO: negative Eigenschaft */
{
"Magokrat",
"Eine Partei mit dieser Eigenschaft hat eine so hohe magische "
"Affinität, dass sie pro Stufe der Eigenschaft zwei zusätzlich "
"Magier ausbilden kann.",
100
},
/* TODO: negative Eigenschaft, vergleichsweise schwach */
{
"Sappeur",
"Befestigungen wirken gegen Einheiten einer Partei mit dieser "
"Eigenschaft nur mit ihrer halben Schutzwirkung (aufgerundet).",
1
},
/* TODO: Noch nicht implementiert */
{
"Springer",
"Einheiten einer Partei mit dieser Eigenschaft können sich mit "
"Hilfe eines speziellen Befehls über eine Entfernung von zwei "
"Regionen teleportieren. Einheiten, die sich so teleportieren, "
"bleiben jedoch für einige Wochen instabil und können sich in "
"seltenen Fällen selbständig in eine zufällige Nachbarregion "
"versetzen. Zusätzliche Stufen erhöhen die Reichweite um jeweils "
"eine Region, erhöhen jedoch die Wahrscheinlichkeit eines zufälligen "
"Versetzens geringfügig.",
100
},
{
/* Evt. zwei Stufen draus machen */
"Versteckt",
"Eine Partei mit dieser Eigenschaft hat die Fähigkeit zu Tarnung "
"zur Perfektion getrieben. Jede Einheit mit mindestens Tarnung 3 "
"versteckt alle ihre Gegenstände so, daß sie von anderen Parteien "
"nicht mehr gesehen werden können. Jede Einheit mit mindestens Tarnung 6 "
"schafft es auch, die Zahl der in ihr befindlichen Personen zu verbergen. "
"Um diese Eigenschaft steuern zu können, stehen diesen Parteien die "
"Befehle TARNE ANZAHL [NICHT] und TARNE GEGENSTÄNDE [NICHT] zur "
"Verfügung.",
1
},
/* TODO: Noch nicht implementiert */
{
"Erdelementarist",
"Alle Gebäude dieser Partei sind von Erdelementaren beseelt und können "
"sich mit Hilfe eines speziellen Befehls jede Woche um eine Region "
"bewegen. Dies macht es den Bewohnern - welche alle einer Partei mit "
"dieser Eigenschaft angehören müssen - jedoch unmöglich, in dieser "
"Woche ihren normalen Tätigkeiten nachzugehen.",
1
},
{
"Magische Immunität",
"Eine Partei mit dieser Eigenschaft ist völlig immun gegen alle Arten "
"von Magie. Allerdings verlieren die Magier einer solchen Partei ihre "
"Fähigkeit, Aura zu regenerieren, völlig.",
1
},
/* TODO: Noch nicht implementiert */
{
"Vielseitige Magie",
"Eine Partei mit dieser Eigenschaft kann einen ihrer Magier in einem "
"anderen als dem Parteimagiegebiet ausbilden. Weitere Stufen ermöglichen "
"jeweils einem weiteren Magier das Lernen eines anderen Gebiets.",
100
},
{
"Jihad",
"Eine Partei mit dieser Eigenschaft kann eine (Spieler)-Rasse "
"mit dem speziellen kurzen Befehl JIHAD <RASSE> zum Feind erklären. "
"Bei einem Kampf gegen einen Angehörigen dieser Rasse bekommen ihre "
"Kämpfer grundsätzlich einen Bonus von +1 auf Angriff und Schaden. "
"Allerdings kann es zu spontanen Pogromen gegen Angehörige der mit einem "
"Jihad belegten Rasse kommen. Wird die Eigenschaft mehrmals erworben "
"können entweder mehrere Rassen mit einem Jihad belegt werden, "
"oder eine Rasse mehrfach, in diesem Fall addiert sich die Wirkung. "
"Ein einmal erklärter Jihad kann nicht wieder rückgängig gemacht "
"werden.",
100
},
/* TODO: is_undead() und Sonderbehandlungen von Untoten */
/* in dieser Form ultimative Eigenschaft für Orks */
{
"Untot",
"Personen einer Partei mit dieser Eigenschaft bekommen automatisch doppelt "
"soviele Trefferpunkte wie normale Angehörige der entsprechenden Rasse, "
"verlieren jedoch ihre Fähigkeit zur Regeneration erlittenen Schadens "
"komplett.",
1
},
{
"Windvolk",
"Ein solches Volk ist sehr athletisch und die Kinder üben besonders "
"den Langstreckenlauf von kleinauf. Nach jahrelangem Training sind "
"sie dann in der Lage, sich zu Fuß so schnell zu bewegen als würden "
"sie reiten. Allerdings hat das jahrelange Konditionstraining ihre "
"Kräfte schwinden lassen, und ihre Tragkraft ist um 2 Gewichtseinheiten "
"verringert.",
1
},
{
"Glück",
"Diese Eigenschaft bewirkt, das der Partei gelegentlich positive "
"Ereignisse zustoßen. Dies können spontane Verbesserungen des "
"Wissensstandes, ein Fund wertvoller Gegenstände oder ähnliches "
"sein. Je häufiger diese Eigenschaft erworben wird, desto größer die "
"Wahrscheinlichkeit für solche Ereignisse, und desto postiver ihre "
"Auswirkungen.",
100
},
{
"Lykanthrop",
"Angehörige einer Partei mit dieser Eigenschaft sind Werwesen. Einheiten "
"einer solchen Partei können sich mit Hilfe der langen Befehle 'WERWESEN' und "
"'WERWESEN NICHT' in eine andere Form verwandeln. Beide Befehle haben "
"nur eine gewisse Erfolgswahrscheinlichkeit und funktionieren nicht immer: "
"Je höher die Stufe der Eigenschaft, desto größer die Wahrscheinlichkeit, "
"dass die Verwandlung gelingt, und je geringer die Chance, sich "
"zurückzuverwandeln. Zudem besteht eine zunehmende kleine Chance der "
"spontanen Verwandlung. In Werform erhalten die Einheiten einen Bonus auf "
"Angriff, Schaden und natürliche Rüstung in Höhe der Eigenschaft. Sie "
"benutzen jedoch keine Pferde im Kampf, und verwenden als Rüstungen nur "
"Schilde. Sie sind eingeschränkt und können in Werform kein Geld verdienen, "
"nicht Zaubern und nicht Lernen oder Lehren.",
4
},
/* TODO: Noch nicht implementiert */
{
"Elite",
"Für eine Partei mit dieser Eigenschaft verdoppeln sich alle Boni- und "
"Mali ihrer Rasse. Ihre Unterhaltskosten erhöhen sich auf 12 Silber pro "
"Runde, ihr Einheitenlimit reduziert sich auf 25%%. Diese Eigenschaft "
"kann nicht erworben werden, wenn die Partei nach dem Erwerb zuviele "
"Einheiten hätte.",
1
}
};
void
buy_special(unit *u, strlist *S, fspecial_t special)
{
int count = 0;
int cost;
attrib *a, *a2 = NULL;
faction *f = u->faction;
/* Kosten berechnen */
for(a=a_find(f->attribs, &at_faction_special); a; a=a->nexttype) {
count += a->data.sa[1];
if(a->data.sa[0] == special) a2 = a;
}
cost = (int)(100 * pow(3, count));
/* Prüfen, ob genug Karma vorhanden. */
if(f->karma < cost) {
cmistake(u, S->s, 250, MSG_EVENT);
return;
}
/* Alles ok, attribut geben */
if(a2) {
if(a2->data.sa[1] < fspecials[special].maxlevel) {
a2->data.sa[1]++;
add_message(&f->msgs, new_message(f,
"new_fspecial_level%S:special%d:level", special, a2->data.sa[1]));
} else {
cmistake(u, S->s, 251, MSG_EVENT);
return;
}
} else {
a2 = a_add(&f->attribs, a_new(&at_faction_special));
a2->data.sa[0] = (short)special;
a2->data.sa[1] = 1;
add_message(&f->msgs, new_message(f, "new_fspecial%S:special", special));
}
}
int
fspecial(const faction *f, fspecial_t special)
{
attrib *a;
for(a=a_find(f->attribs, &at_faction_special); a; a=a->nexttype) {
if(a->data.sa[0] == special) return a->data.sa[1];
}
return 0;
}
void
sacrificings(void)
{
region *r;
unit *u;
for(r=regions; r; r=r->next) {
for(u=r->units; u; u=u->next) {
if(igetkeyword(u->thisorder, u->faction->locale) == K_SACRIFICE) {
int n = 1, karma;
const char *s = getstrtoken();
if(s && *s) n = atoi(s);
if(n <= 0) {
cmistake(u, u->thisorder, 252, MSG_EVENT);
continue;
}
s = getstrtoken();
switch(findparam(s, u->faction->locale)) {
case P_SILVER:
n = use_pooled(u, r, R_SILVER, n);
if(n < 10000) {
cmistake(u, u->thisorder, 51, MSG_EVENT);
continue;
}
change_resource(u, R_SILVER, n);
karma = n/10000;
u->faction->karma += karma;
break;
case P_AURA:
if(!is_mage(u)) {
cmistake(u, u->thisorder, 214, MSG_EVENT);
continue;
}
if (get_level(u, SK_MAGIC) < 10) {
cmistake(u, u->thisorder, 253, MSG_EVENT);
continue;
}
n = min(get_spellpoints(u), min(max_spellpoints(u->region, u), n));
if(n <= 0) {
cmistake(u, u->thisorder, 254, MSG_EVENT);
continue;
}
karma = n;
u->faction->karma += n;
change_maxspellpoints(u, -n);
break;
default:
cmistake(u, u->thisorder, 255, MSG_EVENT);
}
}
}
}
}
void
prayers(void)
{
region *r;
unit *u;
strlist *S;
for(r=regions; r; r=r->next) {
for(u=r->units; u; u=u->next) {
for(S = u->orders; S; S = S->next) if(igetkeyword(S->s, u->faction->locale) == K_PRAY) {
attrib *a, *a2;
unit *u2;
int karma_cost;
short mult = 1;
param_t p;
const char *s = getstrtoken();
if(findparam(s, u->faction->locale) == P_FOR) s = getstrtoken();
p = findparam(s, u->faction->locale);
switch(p) {
case P_AURA:
if(!is_mage(u)) {
cmistake(u, S->s, 214, MSG_EVENT);
continue;
}
case P_AID:
case P_MERCY:
break;
default:
cmistake(u, S->s, 256, MSG_EVENT);
continue;
}
a = a_find(u->faction->attribs, &at_prayer_timeout);
if (a) mult = (short)(2 * a->data.sa[1]);
karma_cost = 10 * mult;
if(u->faction->karma < karma_cost) {
cmistake(u, S->s, 250, MSG_EVENT);
continue;
}
u->faction->karma -= karma_cost;
add_message(&u->faction->msgs, new_message(u->faction,
"pray_success%u:unit",u));
switch(p) {
case P_AURA:
set_spellpoints(u, max_spellpoints(u->region, u));
break;
case P_AID:
for(u2 = r->units; u2; u2=u2->next) if(u2->faction == u->faction) {
a2 = a_add(&u->attribs, a_new(&at_prayer_effect));
a2->data.sa[0] = PR_AID;
a2->data.sa[1] = 1;
}
break;
case P_MERCY:
for(u2 = r->units; u2; u2=u2->next) if(u2->faction == u->faction) {
a2 = a_add(&u->attribs, a_new(&at_prayer_effect));
a2->data.sa[0] = PR_MERCY;
a2->data.sa[1] = 80;
}
break;
}
if(!a) a = a_add(&u->faction->attribs, a_new(&at_prayer_timeout));
a->data.sa[0] = (short)(mult * 14);
a->data.sa[1] = (short)mult;
}
}
}
}
void
set_jihad(void)
{
region *r;
unit *u;
strlist *S;
for(r=regions; r; r=r->next) {
for(u=r->units; u; u=u->next) {
for(S = u->orders; S; S=S->next) if(igetkeyword(S->s, u->faction->locale) == K_SETJIHAD) {
faction *f = u->faction;
int can = fspecial(f, FS_JIHAD);
int has = 0;
const race * jrace;
race_t jrt;
attrib *a;
const char *s;
for(a = a_find(f->attribs, &at_jihad); a; a = a->nexttype) {
has += a->data.sa[1];
}
if(has >= can) {
cmistake(u, S->s, 280, MSG_EVENT);
continue;
}
s = getstrtoken();
if(!s || !*s) {
cmistake(u, S->s, 281, MSG_EVENT);
continue;
}
jrace = rc_find(s);
jrt = old_race(jrace);
if (!playerrace(jrace)) {
cmistake(u, S->s, 282, MSG_EVENT);
continue;
}
for(a = a_find(f->attribs, &at_jihad); a; a = a->nexttype) {
if (a->data.sa[0] == jrt) break;
}
if(a) {
a->data.sa[1]++;
} else {
a = a_add(&f->attribs, a_new(&at_jihad));
a->data.sa[0] = (short)jrt;
a->data.sa[1] = 1;
}
add_message(&f->msgs, msg_message("setjihad",
"race", jrace));
}
}
}
}
int
jihad(faction *f, const race * rc)
{
attrib *a;
race_t jrt = old_race(rc);
for(a = a_find(f->attribs, &at_jihad); a; a = a->nexttype) {
if(a->data.sa[0] == jrt) return a->data.sa[1];
}
return 0;
}
void
jihad_attacks(void)
{
faction *f;
region *r;
unit *u, *u2;
strlist *S;
attrib *a;
ally *sf, **sfp;
for(f=factions; f; f=f->next) if(fspecial(f, FS_JIHAD)) {
for(r=f->first; r != f->last; r = r->next) if(rand()%1000 <= 1) {
boolean doit = false;
for(u=r->units; u; u=u->next) if(jihad(f, u->race)) {
doit = true;
break;
}
if(doit == false) continue;
printf("\tPogrom durch %s in %s\n", factionid(f), regionid(r));
for(u2 = r->units; u; u=u->next) if(u2->faction == f) {
for(u=r->units; u; u=u->next) if(jihad(f, u->race)) {
/* Allianz auflösen */
sfp = &u2->faction->allies;
a = a_find(u2->attribs, &at_group);
if (a) sfp = &((group*)a->data.v)->allies;
for (sf=*sfp; sf; sf = sf->next) {
if(sf->faction == u->faction) break;
}
if (sf) sf->status = sf->status & (HELP_ALL - HELP_FIGHT);
sprintf(buf, "%s %s", locale_string(u->faction->locale, keywords[K_ATTACK]), unitid(u));
S = makestrlist(buf);
addlist(&u2->orders, S);
}
}
}
}
}