diff --git a/CMakeLists.txt b/CMakeLists.txt
index 361cf6f5e..9730625b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,16 +54,6 @@ CONFIGURE_FILE (
INCLUDE_DIRECTORIES (${CMAKE_BINARY_DIR}/include)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_AUTOCONF")
-IF(CMAKE_COMPILER_IS_GNUCC)
- SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -DHAVE__BOOL")
-elseif(MSVC)
- set(CMAKE_EXE_LINKER_FLAGS_DEBUG
- "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrt.lib")
- set(CMAKE_EXE_LINKER_FLAGS_RELEASE
- "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib")
-ELSE(CMAKE_COMPILER_IS_GNUCC)
- MESSAGE(STATUS "Unknown compiler ${CMAKE_C_COMPILER_ID}")
-ENDIF(CMAKE_COMPILER_IS_GNUCC)
find_package (LibXml2)
find_package (SQLite3)
diff --git a/conf/e2/config.xml b/conf/e2/config.xml
index 2ca33346c..7265cbb89 100644
--- a/conf/e2/config.xml
+++ b/conf/e2/config.xml
@@ -22,7 +22,6 @@
-
diff --git a/conf/e3/config.xml b/conf/e3/config.xml
index b30ad8bfc..32f941cb8 100644
--- a/conf/e3/config.xml
+++ b/conf/e3/config.xml
@@ -10,7 +10,6 @@
-
diff --git a/conf/e4/config.xml b/conf/e4/config.xml
index 44cc35471..fff10fbc3 100644
--- a/conf/e4/config.xml
+++ b/conf/e4/config.xml
@@ -10,7 +10,6 @@
-
diff --git a/game-e2/config.xml b/game-e2/config.xml
index 871f893d8..ec8a8855e 100644
--- a/game-e2/config.xml
+++ b/game-e2/config.xml
@@ -22,7 +22,6 @@
-
diff --git a/game-e3/config.xml b/game-e3/config.xml
index 5841fe90b..a66b20393 100644
--- a/game-e3/config.xml
+++ b/game-e3/config.xml
@@ -1,7 +1,5 @@
-
-
diff --git a/res/directions.xml b/res/directions.xml
deleted file mode 100644
index 0e2e0b5fe..000000000
--- a/res/directions.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f90c82258..71c397f5b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,14 +1,6 @@
cmake_minimum_required(VERSION 2.6)
project (server C)
-IF(CMAKE_COMPILER_IS_GNUCC)
- SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Werror -Wno-unknown-pragmas -Wstrict-prototypes -Wpointer-arith -Wno-char-subscripts -Wno-long-long")
- SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -DHAVE__BOOL")
-ELSEIF(MSVC)
-ELSE(CMAKE_COMPILER_IS_GNUCC)
- MESSAGE(STATUS "Unknown compiler ${CMAKE_C_COMPILER_ID}")
-ENDIF(CMAKE_COMPILER_IS_GNUCC)
-
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
include_directories (${CRITBIT_INCLUDE_DIR})
include_directories (${CJSON_INCLUDE_DIR})
@@ -20,6 +12,19 @@ include_directories (${LUA_INCLUDE_DIR})
include_directories (${BSON_INCLUDE_DIR})
include_directories (${INIPARSER_INCLUDE_DIR})
+IF(CMAKE_COMPILER_IS_GNUCC)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Werror -Wno-unknown-pragmas -Wstrict-prototypes -Wpointer-arith -Wno-char-subscripts -Wno-long-long")
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -DHAVE__BOOL")
+elseif(MSVC)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4 /WX /MP")
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG
+ "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrt.lib")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE
+ "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib")
+ELSE(CMAKE_COMPILER_IS_GNUCC)
+ MESSAGE(STATUS "Unknown compiler ${CMAKE_C_COMPILER_ID}")
+ENDIF(CMAKE_COMPILER_IS_GNUCC)
+
add_subdirectory(util)
add_subdirectory(kernel)
add_subdirectory(items)
@@ -58,6 +63,14 @@ TOLUA_BINDING(settings.pkg bind_settings.h)
ENDIF()
set (ERESSEA_SRC
+ move.c
+ spells.c
+ battle.c
+ alchemy.c
+ stealth.c
+ vortex.c
+ names.c
+ reports.c
eressea.c
callback.c
direction.c
@@ -141,7 +154,12 @@ target_link_libraries(eressea
set(TESTS_SRC
test_eressea.c
tests.c
+ battle.test.c
+ vortex.test.c
tests.test.c
+ reports.test.c
+ stealth.test.c
+ move.test.c
callback.test.c
direction.test.c
keyword.test.c
diff --git a/src/kernel/alchemy.c b/src/alchemy.c
similarity index 97%
rename from src/kernel/alchemy.c
rename to src/alchemy.c
index bce078899..c9516740a 100644
--- a/src/kernel/alchemy.c
+++ b/src/alchemy.c
@@ -19,18 +19,18 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include "alchemy.h"
-
-#include "item.h"
-#include "faction.h"
-#include "messages.h"
-#include "build.h"
-#include "magic.h"
-#include "region.h"
-#include "pool.h"
-#include "race.h"
-#include "unit.h"
-#include "skill.h"
#include "move.h"
+#include "skill.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
/* util includes */
#include
diff --git a/src/kernel/alchemy.h b/src/alchemy.h
similarity index 100%
rename from src/kernel/alchemy.h
rename to src/alchemy.h
diff --git a/src/battle.c b/src/battle.c
new file mode 100644
index 000000000..5a4cd2779
--- /dev/null
+++ b/src/battle.c
@@ -0,0 +1,4342 @@
+/*
+Copyright (c) 1998-2010, Enno Rehling
+Katja Zedel
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+**/
+
+#include
+#include
+#include "battle.h"
+#include "alchemy.h"
+#include "move.h"
+#include "skill.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+/* attributes includes */
+#include
+#include
+#include
+#include
+#include
+
+/* util includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* libc includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static FILE *bdebug;
+
+#define TACTICS_BONUS 1 /* when undefined, we have a tactics round. else this is the bonus tactics give */
+#define TACTICS_MODIFIER 1 /* modifier for generals in the front/rear */
+
+#define CATAPULT_INITIAL_RELOAD 4 /* erster schuss in runde 1 + rng_int() % INITIAL */
+#define CATAPULT_STRUCTURAL_DAMAGE
+
+#define BASE_CHANCE 70 /* 70% Basis-Überlebenschance */
+#ifdef NEW_COMBATSKILLS_RULE
+#define TDIFF_CHANGE 5 /* 5% höher pro Stufe */
+#define DAMAGE_QUOTIENT 2 /* damage += skilldiff/DAMAGE_QUOTIENT */
+#else
+#define TDIFF_CHANGE 10
+# define DAMAGE_QUOTIENT 1 /* damage += skilldiff/DAMAGE_QUOTIENT */
+#endif
+
+#undef DEBUG_FAST /* should be disabled when b->fast and b->rowcache works */
+#define DEBUG_SELECT /* should be disabled if select_enemy works */
+
+typedef enum combatmagic {
+ DO_PRECOMBATSPELL,
+ DO_POSTCOMBATSPELL
+} combatmagic_t;
+
+/* globals */
+static int obs_count = 0;
+
+#define MINSPELLRANGE 1
+#define MAXSPELLRANGE 7
+
+#ifndef ROW_FACTOR
+# define ROW_FACTOR 10
+#endif
+#define EFFECT_PANIC_SPELL 0.25
+#define TROLL_REGENERATION 0.10
+
+/* Nach dem alten System: */
+static int missile_range[2] = { FIGHT_ROW, BEHIND_ROW };
+static int melee_range[2] = { FIGHT_ROW, FIGHT_ROW };
+
+static message *msg_separator;
+
+const troop no_troop = { 0, 0 };
+
+static int max_turns = 0;
+static int damage_rules = 0;
+static int loot_rules = 0;
+static int skill_formula = 0;
+
+#define FORMULA_ORIG 0
+#define FORMULA_NEW 1
+
+#define LOOT_MONSTERS (1<<0)
+#define LOOT_SELF (1<<1) /* code is mutually exclusive with LOOT_OTHERS */
+#define LOOT_OTHERS (1<<2)
+#define LOOT_KEEPLOOT (1<<4)
+
+#define DAMAGE_CRITICAL (1<<0)
+#define DAMAGE_MELEE_BONUS (1<<1)
+#define DAMAGE_MISSILE_BONUS (1<<2)
+#define DAMAGE_UNARMED_BONUS (1<<3)
+#define DAMAGE_SKILL_BONUS (1<<4)
+/** initialize rules from configuration.
+ */
+static void static_rules(void)
+{
+ loot_rules =
+ get_param_int(global.parameters, "rules.combat.loot",
+ LOOT_MONSTERS | LOOT_OTHERS | LOOT_KEEPLOOT);
+ /* new formula to calculate to-hit-chance */
+ skill_formula =
+ get_param_int(global.parameters, "rules.combat.skill_formula",
+ FORMULA_ORIG);
+ /* maximum number of combat turns */
+ max_turns =
+ get_param_int(global.parameters, "rules.combat.turns", COMBAT_TURNS);
+ /* damage calculation */
+ if (get_param_int(global.parameters, "rules.combat.critical", 1)) {
+ damage_rules |= DAMAGE_CRITICAL;
+ }
+ if (get_param_int(global.parameters, "rules.combat.melee_bonus", 1)) {
+ damage_rules |= DAMAGE_MELEE_BONUS;
+ }
+ if (get_param_int(global.parameters, "rules.combat.missile_bonus", 1)) {
+ damage_rules |= DAMAGE_MISSILE_BONUS;
+ }
+ if (get_param_int(global.parameters, "rules.combat.unarmed_bonus", 1)) {
+ damage_rules |= DAMAGE_UNARMED_BONUS;
+ }
+ if (get_param_int(global.parameters, "rules.combat.skill_bonus", 1)) {
+ damage_rules |= DAMAGE_SKILL_BONUS;
+ }
+}
+
+static int army_index(side * s)
+{
+ return s->index;
+}
+
+static char *sidename(side * s)
+{
+#define SIDENAMEBUFLEN 256
+ static int bufno; /* STATIC_XCALL: used across calls */
+ static char sidename_buf[4][SIDENAMEBUFLEN]; /* STATIC_RESULT: used for return, not across calls */
+
+ bufno = bufno % 4;
+ strlcpy(sidename_buf[bufno], factionname(s->stealthfaction ? s->stealthfaction : s->faction), SIDENAMEBUFLEN);
+ return sidename_buf[bufno++];
+}
+
+static const char *sideabkz(side * s, bool truename)
+{
+ static char sideabkz_buf[8]; /* STATIC_RESULT: used for return, not across calls */
+ const faction *f = (s->stealthfaction
+ && !truename) ? s->stealthfaction : s->faction;
+
+#undef SIDE_ABKZ
+#ifdef SIDE_ABKZ
+ abkz(f->name, sideabkz_buf, sizeof(sideabkz_buf), 3);
+#else
+ strcpy(sideabkz_buf, itoa36(f->no));
+#endif
+ return sideabkz_buf;
+}
+
+static void message_faction(battle * b, faction * f, struct message *m)
+{
+ region *r = b->region;
+
+ if (f->battles == NULL || f->battles->r != r) {
+ struct bmsg *bm = (struct bmsg *)calloc(1, sizeof(struct bmsg));
+ bm->next = f->battles;
+ f->battles = bm;
+ bm->r = r;
+ }
+ add_message(&f->battles->msgs, m);
+}
+
+int armedmen(const unit * u, bool siege_weapons)
+{
+ item *itm;
+ int n = 0;
+ if (!(urace(u)->flags & RCF_NOWEAPONS)) {
+ if (effskill(u, SK_WEAPONLESS) >= 1) {
+ /* kann ohne waffen bewachen: fuer drachen */
+ n = u->number;
+ }
+ else {
+ /* alle Waffen werden gezaehlt, und dann wird auf die Anzahl
+ * Personen minimiert */
+ for (itm = u->items; itm; itm = itm->next) {
+ const weapon_type *wtype = resource2weapon(itm->type->rtype);
+ if (wtype == NULL || (!siege_weapons && (wtype->flags & WTF_SIEGE)))
+ continue;
+ if (effskill(u, wtype->skill) >= 1)
+ n += itm->number;
+ /* if (effskill(u, wtype->skill) >= wtype->minskill) n += itm->number; */
+ if (n > u->number)
+ break;
+ }
+ n = _min(n, u->number);
+ }
+ }
+ return n;
+}
+
+void message_all(battle * b, message * m)
+{
+ bfaction *bf;
+ plane *p = rplane(b->region);
+ watcher *w;
+
+ for (bf = b->factions; bf; bf = bf->next) {
+ message_faction(b, bf->faction, m);
+ }
+ if (p)
+ for (w = p->watchers; w; w = w->next) {
+ for (bf = b->factions; bf; bf = bf->next)
+ if (bf->faction == w->faction)
+ break;
+ if (bf == NULL)
+ message_faction(b, w->faction, m);
+ }
+}
+
+static void fbattlerecord(battle * b, faction * f, const char *s)
+{
+ message *m = msg_message("battle_msg", "string", s);
+ message_faction(b, f, m);
+ msg_release(m);
+}
+
+/* being an enemy or a friend is (and must always be!) symmetrical */
+#define enemy_i(as, di) (as->relations[di]&E_ENEMY)
+#define friendly_i(as, di) (as->relations[di]&E_FRIEND)
+#define enemy(as, ds) (as->relations[ds->index]&E_ENEMY)
+#define friendly(as, ds) (as->relations[ds->index]&E_FRIEND)
+
+static bool set_enemy(side * as, side * ds, bool attacking)
+{
+ int i;
+ for (i = 0; i != MAXSIDES; ++i) {
+ if (ds->enemies[i] == NULL)
+ ds->enemies[i] = as;
+ if (ds->enemies[i] == as)
+ break;
+ }
+ for (i = 0; i != MAXSIDES; ++i) {
+ if (as->enemies[i] == NULL)
+ as->enemies[i] = ds;
+ if (as->enemies[i] == ds)
+ break;
+ }
+ assert(i != MAXSIDES);
+ if (attacking)
+ as->relations[ds->index] |= E_ATTACKING;
+ if ((ds->relations[as->index] & E_ENEMY) == 0) {
+ /* enemy-relation are always symmetrical */
+ assert((as->relations[ds->index] & (E_ENEMY | E_FRIEND)) == 0);
+ ds->relations[as->index] |= E_ENEMY;
+ as->relations[ds->index] |= E_ENEMY;
+ return true;
+ }
+ return false;
+}
+
+static void set_friendly(side * as, side * ds)
+{
+ assert((as->relations[ds->index] & E_ENEMY) == 0);
+ ds->relations[as->index] |= E_FRIEND;
+ as->relations[ds->index] |= E_FRIEND;
+}
+
+static int allysfm(const side * s, const faction * f, int mode)
+{
+ if (s->faction == f)
+ return mode;
+ if (s->group) {
+ return alliedgroup(s->battle->plane, s->faction, f, s->group->allies, mode);
+ }
+ return alliedfaction(s->battle->plane, s->faction, f, mode);
+}
+
+static int allysf(const side * s, const faction * f)
+{
+ return allysfm(s, f, HELP_FIGHT);
+}
+
+static int dead_fighters(const fighter * df)
+{
+ return df->unit->number - df->alive - df->run.number;
+}
+
+fighter *select_corpse(battle * b, fighter * af)
+/* Wählt eine Leiche aus, der af hilft. casualties ist die Anzahl der
+ * Toten auf allen Seiten (im Array). Wenn af == NULL, wird die
+ * Parteizugehörigkeit ignoriert, und irgendeine Leiche genommen.
+ *
+ * Untote werden nicht ausgewählt (casualties, not dead) */
+{
+ int si, di, maxcasualties = 0;
+ fighter *df;
+ side *s;
+
+ for (si = 0; si != b->nsides; ++si) {
+ side *s = b->sides + si;
+ if (af == NULL || (!enemy_i(af->side, si) && allysf(af->side, s->faction))) {
+ maxcasualties += s->casualties;
+ }
+ }
+ di = rng_int() % maxcasualties;
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ for (df = s->fighters; df; df = df->next) {
+ /* Geflohene haben auch 0 hp, dürfen hier aber nicht ausgewählt
+ * werden! */
+ int dead = dead_fighters(df);
+ if (!playerrace(u_race(df->unit)))
+ continue;
+
+ if (af && !helping(af->side, df->side))
+ continue;
+ if (di < dead) {
+ return df;
+ }
+ di -= dead;
+ }
+ }
+
+ return NULL;
+}
+
+bool helping(const side * as, const side * ds)
+{
+ if (as->faction == ds->faction)
+ return true;
+ return (bool)(!enemy(as, ds) && allysf(as, ds->faction));
+}
+
+int statusrow(int status)
+{
+ switch (status) {
+ case ST_AGGRO:
+ case ST_FIGHT:
+ return FIGHT_ROW;
+ case ST_BEHIND:
+ case ST_CHICKEN:
+ return BEHIND_ROW;
+ case ST_AVOID:
+ return AVOID_ROW;
+ case ST_FLEE:
+ return FLEE_ROW;
+ default:
+ assert(!"unknown combatrow");
+ }
+ return FIGHT_ROW;
+}
+
+static double hpflee(int status)
+/* if hp drop below this percentage, run away */
+{
+ switch (status) {
+ case ST_AGGRO:
+ return 0.0;
+ case ST_FIGHT:
+ case ST_BEHIND:
+ return 0.2;
+ case ST_CHICKEN:
+ case ST_AVOID:
+ return 0.9;
+ case ST_FLEE:
+ return 1.0;
+ default:
+ assert(!"unknown combatrow");
+ }
+ return 0.0;
+}
+
+static int get_row(const side * s, int row, const side * vs)
+{
+ bool counted[MAXSIDES];
+ int enemyfront = 0;
+ int line, result;
+ int retreat = 0;
+ int size[NUMROWS];
+ int front = 0;
+ battle *b = s->battle;
+
+ memset(counted, 0, sizeof(counted));
+ memset(size, 0, sizeof(size));
+ for (line = FIRST_ROW; line != NUMROWS; ++line) {
+ int si, sa_i;
+ /* how many enemies are there in the first row? */
+ for (si = 0; s->enemies[si]; ++si) {
+ side *se = s->enemies[si];
+ if (se->size[line] > 0) {
+ enemyfront += se->size[line];
+ /* - s->nonblockers[line] (nicht, weil angreifer) */
+ }
+ }
+ for (sa_i = 0; sa_i != b->nsides; ++sa_i) {
+ side *sa = b->sides + sa_i;
+ /* count people that like me, but don't like my enemy */
+ if (friendly_i(s, sa_i) && enemy_i(vs, sa_i)) {
+ if (!counted[sa_i]) {
+ int i;
+
+ for (i = 0; i != NUMROWS; ++i) {
+ size[i] += sa->size[i] - sa->nonblockers[i];
+ }
+ counted[sa_i] = true;
+ }
+ }
+ }
+ if (enemyfront)
+ break;
+ }
+ if (enemyfront) {
+ for (line = FIRST_ROW; line != NUMROWS; ++line) {
+ front += size[line];
+ if (!front || front < enemyfront / ROW_FACTOR)
+ ++retreat;
+ else if (front)
+ break;
+ }
+ }
+
+ /* every entry in the size[] array means someone trying to defend us.
+ * 'retreat' is the number of rows falling.
+ */
+ result = _max(FIRST_ROW, row - retreat);
+
+ return result;
+}
+
+int get_unitrow(const fighter * af, const side * vs)
+{
+ int row = statusrow(af->status);
+ if (vs == NULL) {
+ int i;
+ for (i = FIGHT_ROW; i != row; ++i)
+ if (af->side->size[i])
+ break;
+ return FIGHT_ROW + (row - i);
+ }
+ else {
+ battle *b = vs->battle;
+ if (row != b->rowcache.row || b->alive != b->rowcache.alive
+ || af->side != b->rowcache.as || vs != b->rowcache.vs) {
+ b->rowcache.alive = b->alive;
+ b->rowcache.as = af->side;
+ b->rowcache.vs = vs;
+ b->rowcache.row = row;
+ b->rowcache.result = get_row(af->side, row, vs);
+ return b->rowcache.result;
+ }
+#ifdef DEBUG_FAST /* validation code */
+ {
+ int i = get_row(af->side, row, vs);
+ assert(i == b->rowcache.result);
+ }
+#endif
+ return b->rowcache.result;
+ }
+}
+
+static void reportcasualties(battle * b, fighter * fig, int dead)
+{
+ struct message *m;
+ region *r = NULL;
+ if (fig->alive == fig->unit->number)
+ return;
+ m = msg_message("casualties", "unit runto run alive fallen",
+ fig->unit, r, fig->run.number, fig->alive, dead);
+ message_all(b, m);
+ msg_release(m);
+}
+
+static int
+contest_classic(int skilldiff, const armor_type * ar, const armor_type * sh)
+{
+ int p, vw = BASE_CHANCE - TDIFF_CHANGE * skilldiff;
+ double mod = 1.0;
+
+ if (ar != NULL)
+ mod *= (1 + ar->penalty);
+ if (sh != NULL)
+ mod *= (1 + sh->penalty);
+ vw = (int)(100 - ((100 - vw) * mod));
+
+ do {
+ p = rng_int() % 100;
+ vw -= p;
+ } while (vw >= 0 && p >= 90);
+ return (vw <= 0);
+}
+
+/** new rule for Eressea 1.5
+ * \param skilldiff - the attack skill with every modifier applied
+ */
+static int
+contest_new(int skilldiff, const troop dt, const armor_type * ar,
+const armor_type * sh)
+{
+ double tohit = 0.5 + skilldiff * 0.1;
+ if (tohit < 0.5)
+ tohit = 0.5;
+ if (chance(tohit)) {
+ int defense = effskill(dt.fighter->unit, SK_STAMINA);
+ double tosave = defense * 0.05;
+ return !chance(tosave);
+ }
+ return 0;
+}
+
+static int
+contest(int skdiff, const troop dt, const armor_type * ar,
+const armor_type * sh)
+{
+ if (skill_formula == FORMULA_ORIG) {
+ return contest_classic(skdiff, ar, sh);
+ }
+ else {
+ return contest_new(skdiff, dt, ar, sh);
+ }
+}
+
+static bool is_riding(const troop t)
+{
+ if (t.fighter->building != NULL)
+ return false;
+ if (t.fighter->horses + t.fighter->elvenhorses > t.index)
+ return true;
+ return false;
+}
+
+static weapon *preferred_weapon(const troop t, bool attacking)
+{
+ weapon *missile = t.fighter->person[t.index].missile;
+ weapon *melee = t.fighter->person[t.index].melee;
+ if (attacking) {
+ if (melee == NULL || (missile && missile->attackskill > melee->attackskill)) {
+ return missile;
+ }
+ }
+ else {
+ if (melee == NULL || (missile
+ && missile->defenseskill > melee->defenseskill)) {
+ return missile;
+ }
+ }
+ return melee;
+}
+
+static weapon *select_weapon(const troop t, bool attacking,
+ bool ismissile)
+ /* select the primary weapon for this trooper */
+{
+ if (attacking) {
+ if (ismissile) {
+ /* from the back rows, have to use your missile weapon */
+ return t.fighter->person[t.index].missile;
+ }
+ }
+ else {
+ if (!ismissile) {
+ /* have to use your melee weapon if it's melee */
+ return t.fighter->person[t.index].melee;
+ }
+ }
+ return preferred_weapon(t, attacking);
+}
+
+static bool i_canuse(const unit * u, const item_type * itype)
+{
+ if (itype->canuse) {
+ return itype->canuse(u, itype);
+ }
+ return true;
+}
+
+static int
+weapon_skill(const weapon_type * wtype, const unit * u, bool attacking)
+/* the 'pure' skill when using this weapon to attack or defend.
+ * only undiscriminate modifiers (not affected by troops or enemies)
+ * are taken into account, e.g. no horses, magic, etc. */
+{
+ int skill;
+
+ if (wtype == NULL) {
+ skill = effskill(u, SK_WEAPONLESS);
+ if (skill <= 0) {
+ /* wenn kein waffenloser kampf, dann den rassen-defaultwert */
+ if (u_race(u) == get_race(RC_ORC)) {
+ int sword = effskill(u, SK_MELEE);
+ int spear = effskill(u, SK_SPEAR);
+ skill = _max(sword, spear) - 3;
+ if (attacking) {
+ skill = _max(skill, u_race(u)->at_default);
+ }
+ else {
+ skill = _max(skill, u_race(u)->df_default);
+ }
+ }
+ else {
+ if (attacking) {
+ skill = u_race(u)->at_default;
+ }
+ else {
+ skill = u_race(u)->df_default;
+ }
+ }
+ }
+ else {
+ /* der rassen-defaultwert kann höher sein als der Talentwert von
+ * waffenloser kampf */
+ if (attacking) {
+ if (skill < u_race(u)->at_default)
+ skill = u_race(u)->at_default;
+ }
+ else {
+ if (skill < u_race(u)->df_default)
+ skill = u_race(u)->df_default;
+ }
+ }
+ if (attacking) {
+ skill += u_race(u)->at_bonus;
+ if (fval(u->region->terrain, SEA_REGION) && u->ship)
+ skill += u->ship->type->at_bonus;
+ }
+ else {
+ skill += u_race(u)->df_bonus;
+ if (fval(u->region->terrain, SEA_REGION) && u->ship)
+ skill += u->ship->type->df_bonus;
+ }
+ }
+ else {
+ /* changed: if we own a weapon, we have at least a skill of 0 */
+ if (!i_canuse(u, wtype->itype))
+ return -1;
+ skill = effskill(u, wtype->skill);
+ if (skill < wtype->minskill)
+ skill = 0;
+ if (skill > 0) {
+ if (attacking) {
+ skill += u_race(u)->at_bonus;
+ }
+ else {
+ skill += u_race(u)->df_bonus;
+ }
+ }
+ if (attacking) {
+ skill += wtype->offmod;
+ }
+ else {
+ skill += wtype->defmod;
+ }
+ }
+
+ return skill;
+}
+
+static int CavalrySkill(void)
+{
+ static int skill = -1;
+
+ if (skill < 0) {
+ skill = get_param_int(global.parameters, "rules.cavalry.skill", 2);
+ }
+ return skill;
+}
+
+#define BONUS_SKILL 1
+#define BONUS_DAMAGE 2
+static int CavalryBonus(const unit * u, troop enemy, int type)
+{
+ static int mode = -1;
+
+ if (mode < 0) {
+ mode = get_param_int(global.parameters, "rules.cavalry.mode", 1);
+ }
+ if (mode == 0) {
+ /* old rule, Eressea 1.0 compat */
+ return (type == BONUS_SKILL) ? 2 : 0;
+ }
+ else {
+ /* new rule, chargers in Eressea 1.1 */
+ int skl = effskill(u, SK_RIDING);
+ /* only half against trolls */
+ if (skl > 0) {
+ if (type == BONUS_DAMAGE) {
+ int dmg = _min(skl, 8);
+ if (u_race(enemy.fighter->unit) == get_race(RC_TROLL)) {
+ dmg = dmg / 4;
+ }
+ else {
+ dmg = dmg / 2;
+ }
+ return dmg;
+ }
+ else {
+ skl = skl / 2;
+ return _min(skl, 4);
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+weapon_effskill(troop t, troop enemy, const weapon * w, bool attacking,
+bool missile)
+/* effektiver Waffenskill während des Kampfes */
+{
+ /* In dieser Runde alle die Modifier berechnen, die fig durch die
+ * Waffen bekommt. */
+ fighter *tf = t.fighter;
+ unit *tu = t.fighter->unit;
+ int skill;
+ const weapon_type *wtype = w ? w->type : NULL;
+
+ if (wtype == NULL) {
+ /* Ohne Waffe: Waffenlose Angriffe */
+ skill = weapon_skill(NULL, tu, attacking);
+ }
+ else {
+ if (attacking) {
+ skill = w->attackskill;
+ }
+ else {
+ skill = w->defenseskill;
+ }
+ if (wtype->modifiers != NULL) {
+ /* Pferdebonus, Lanzenbonus, usw. */
+ int m;
+ unsigned int flags =
+ WMF_SKILL | (attacking ? WMF_OFFENSIVE : WMF_DEFENSIVE);
+
+ if (is_riding(t))
+ flags |= WMF_RIDING;
+ else
+ flags |= WMF_WALKING;
+ if (is_riding(enemy))
+ flags |= WMF_AGAINST_RIDING;
+ else
+ flags |= WMF_AGAINST_WALKING;
+
+ for (m = 0; wtype->modifiers[m].value; ++m) {
+ if ((wtype->modifiers[m].flags & flags) == flags) {
+ race_list *rlist = wtype->modifiers[m].races;
+ if (rlist != NULL) {
+ while (rlist) {
+ if (rlist->data == u_race(tu))
+ break;
+ rlist = rlist->next;
+ }
+ if (rlist == NULL)
+ continue;
+ }
+ skill += wtype->modifiers[m].value;
+ }
+ }
+ }
+ }
+
+ /* Burgenbonus, Pferdebonus */
+ if (is_riding(t) && (wtype == NULL || (fval(wtype, WTF_HORSEBONUS)
+ && !fval(wtype, WTF_MISSILE)))) {
+ skill += CavalryBonus(tu, enemy, BONUS_SKILL);
+ if (wtype)
+ skill =
+ skillmod(urace(tu)->attribs, tu, tu->region, wtype->skill, skill,
+ SMF_RIDING);
+ }
+
+ if (t.index < tf->elvenhorses) {
+ /* Elfenpferde: Helfen dem Reiter, egal ob und welche Waffe. Das ist
+ * eleganter, und vor allem einfacher, sonst muß man noch ein
+ * WMF_ELVENHORSE einbauen. */
+ skill += 2;
+ }
+
+ if (skill > 0 && !attacking && missile) {
+ /*
+ * Wenn ich verteidige, und nicht direkt meinem Feind gegenüberstehe,
+ * halbiert sich mein Skill: (z.B. gegen Fernkämpfer. Nahkämpfer
+ * können mich eh nicht treffen)
+ */
+ skill /= 2;
+ }
+ return skill;
+}
+
+static const armor_type *select_armor(troop t, bool shield)
+{
+ unsigned int type = shield ? ATF_SHIELD : 0;
+ unit *u = t.fighter->unit;
+ const armor *a = t.fighter->armors;
+ int geschuetzt = 0;
+
+ /* some monsters should not use armor (dragons in chainmail? ha!) */
+ if (!(u_race(u)->battle_flags & BF_EQUIPMENT))
+ return NULL;
+
+ /* ... neither do werewolves */
+ if (fval(u, UFL_WERE)) {
+ return NULL;
+ }
+
+ for (; a; a = a->next) {
+ if ((a->atype->flags & ATF_SHIELD) == type) {
+ geschuetzt += a->count;
+ if (geschuetzt > t.index) {
+ /* unser Kandidat wird geschuetzt */
+ return a->atype;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Hier ist zu beachten, ob und wie sich Zauber und Artefakte, die
+ * Rüstungschutz geben, addieren.
+ * - Artefakt "trollbelt" gibt Rüstung +1
+ * - Zauber Rindenhaut gibt Rüstung +3
+ */
+static int trollbelts(const unit *u) {
+ const struct resource_type *belt = rt_find("trollbelt");
+ return belt ? i_get(u->items, belt->itype) : 0;
+}
+
+int select_magicarmor(troop t)
+{
+ unit *u = t.fighter->unit;
+ int ma = 0;
+
+ if (trollbelts(u) > t.index) /* unser Kandidat wird geschuetzt */
+ ma += 1;
+
+ return ma;
+}
+
+/* Sind side ds und Magier des meffect verbündet, dann return 1*/
+bool meffect_protection(battle * b, meffect * s, side * ds)
+{
+ if (!s->magician->alive)
+ return false;
+ if (s->duration <= 0)
+ return false;
+ if (enemy(s->magician->side, ds))
+ return false;
+ if (allysf(s->magician->side, ds->faction))
+ return true;
+ return false;
+}
+
+/* Sind side as und Magier des meffect verfeindet, dann return 1*/
+bool meffect_blocked(battle * b, meffect * s, side * as)
+{
+ if (!s->magician->alive)
+ return false;
+ if (s->duration <= 0)
+ return false;
+ if (enemy(s->magician->side, as))
+ return true;
+ return false;
+}
+
+/* rmfighter wird schon im PRAECOMBAT gebraucht, da gibt es noch keine
+ * troops */
+void rmfighter(fighter * df, int i)
+{
+ side *ds = df->side;
+
+ /* nicht mehr personen abziehen, als in der Einheit am Leben sind */
+ assert(df->alive >= i);
+ assert(df->alive <= df->unit->number);
+
+ /* erst ziehen wir die Anzahl der Personen von den Kämpfern in der
+ * Schlacht, dann von denen auf dieser Seite ab*/
+ df->side->alive -= i;
+ df->side->battle->alive -= i;
+
+ /* Dann die Kampfreihen aktualisieren */
+ ds->size[SUM_ROW] -= i;
+ ds->size[statusrow(df->status)] -= i;
+
+ /* Spezialwirkungen, z.B. Schattenritter */
+ if (u_race(df->unit)->battle_flags & BF_NOBLOCK) {
+ ds->nonblockers[SUM_ROW] -= i;
+ ds->nonblockers[statusrow(df->status)] -= i;
+ }
+
+ /* und die Einheit selbst aktualisieren */
+ df->alive -= i;
+}
+
+static void rmtroop(troop dt)
+{
+ fighter *df = dt.fighter;
+
+ /* troop ist immer eine einzele Person */
+ rmfighter(df, 1);
+
+ assert(dt.index >= 0 && dt.index < df->unit->number);
+ df->person[dt.index] = df->person[df->alive - df->removed];
+ if (df->removed) {
+ df->person[df->alive - df->removed] = df->person[df->alive];
+ }
+ df->person[df->alive].hp = 0;
+}
+
+void remove_troop(troop dt)
+{
+ fighter *df = dt.fighter;
+ struct person p = df->person[dt.index];
+ battle *b = df->side->battle;
+ b->fast.alive = -1; /* invalidate cached value */
+ b->rowcache.alive = -1; /* invalidate cached value */
+ ++df->removed;
+ ++df->side->removed;
+ df->person[dt.index] = df->person[df->alive - df->removed];
+ df->person[df->alive - df->removed] = p;
+}
+
+void kill_troop(troop dt)
+{
+ fighter *df = dt.fighter;
+ unit *du = df->unit;
+
+ rmtroop(dt);
+ if (!df->alive) {
+ char eqname[64];
+ const struct equipment *eq;
+ if (u_race(du)->itemdrop) {
+ item *drops = u_race(du)->itemdrop(u_race(du), du->number - df->run.number);
+
+ if (drops != NULL) {
+ i_merge(&du->items, &drops);
+ }
+ }
+ sprintf(eqname, "%s_spoils", u_race(du)->_name);
+ eq = get_equipment(eqname);
+ if (eq != NULL) {
+ equip_items(&du->items, eq);
+ }
+ }
+}
+
+/** reduces the target's exp by an equivalent of n points learning
+ * 30 points = 1 week
+ */
+void drain_exp(struct unit *u, int n)
+{
+ skill_t sk = (skill_t)(rng_int() % MAXSKILLS);
+ skill_t ssk;
+
+ ssk = sk;
+
+ while (get_level(u, sk) == 0) {
+ sk++;
+ if (sk == MAXSKILLS)
+ sk = 0;
+ if (sk == ssk) {
+ sk = NOSKILL;
+ break;
+ }
+ }
+ if (sk != NOSKILL) {
+ skill *sv = unit_skill(u, sk);
+ while (n > 0) {
+ if (n >= 30 * u->number) {
+ reduce_skill(u, sv, 1);
+ n -= 30;
+ }
+ else {
+ if (rng_int() % (30 * u->number) < n)
+ reduce_skill(u, sv, 1);
+ n = 0;
+ }
+ }
+ }
+}
+
+const char *rel_dam(int dam, int hp)
+{
+ double q = (double)dam / (double)hp;
+
+ if (q > 0.75) {
+ return "eine klaffende Wunde";
+ }
+ else if (q > 0.5) {
+ return "eine schwere Wunde";
+ }
+ else if (q > 0.25) {
+ return "eine Wunde";
+ }
+ return "eine kleine Wunde";
+}
+
+static void vampirism(troop at, int damage)
+{
+ static int vampire = -1;
+ if (vampire < 0)
+ vampire = get_param_int(global.parameters, "rules.combat.demon_vampire", 0);
+ if (vampire > 0) {
+ int gain = damage / vampire;
+ int chance = damage - vampire * gain;
+ if (chance > 0 && (rng_int() % vampire < chance))
+ ++gain;
+ if (gain > 0) {
+ int maxhp = unit_max_hp(at.fighter->unit);
+ at.fighter->person[at.index].hp =
+ _min(gain + at.fighter->person[at.index].hp, maxhp);
+ }
+ }
+}
+
+static int natural_armor(unit * du)
+{
+ static int *bonus = 0;
+ int an = u_race(du)->armor;
+ if (bonus == 0) {
+ bonus = calloc(sizeof(int), num_races);
+ }
+ if (bonus[u_race(du)->index] == 0) {
+ bonus[u_race(du)->index] =
+ get_param_int(u_race(du)->parameters, "armor.stamina", -1);
+ if (bonus[u_race(du)->index] == 0)
+ bonus[u_race(du)->index] = -1;
+ }
+ if (bonus[u_race(du)->index] > 0) {
+ int sk = effskill(du, SK_STAMINA);
+ sk /= bonus[u_race(du)->index];
+ an += sk;
+ }
+ return an;
+}
+
+bool
+terminate(troop dt, troop at, int type, const char *damage, bool missile)
+{
+ item **pitm;
+ fighter *df = dt.fighter;
+ fighter *af = at.fighter;
+ unit *au = af->unit;
+ unit *du = df->unit;
+ battle *b = df->side->battle;
+ int heiltrank = 0;
+ static int rule_armor = -1;
+
+ /* Schild */
+ side *ds = df->side;
+ int hp;
+
+ int ar = 0, an, am;
+ const armor_type *armor = select_armor(dt, true);
+ const armor_type *shield = select_armor(dt, false);
+
+ const weapon_type *dwtype = NULL;
+ const weapon_type *awtype = NULL;
+ const weapon *weapon;
+
+ int rda, sk = 0, sd;
+ bool magic = false;
+ int da = dice_rand(damage);
+
+ assert(du->number > 0);
+ ++at.fighter->hits;
+
+ switch (type) {
+ case AT_STANDARD:
+ weapon = select_weapon(at, true, missile);
+ sk = weapon_effskill(at, dt, weapon, true, missile);
+ if (weapon)
+ awtype = weapon->type;
+ if (awtype && fval(awtype, WTF_MAGICAL))
+ magic = true;
+ break;
+ case AT_NATURAL:
+ sk = weapon_effskill(at, dt, NULL, true, missile);
+ break;
+ case AT_SPELL:
+ case AT_COMBATSPELL:
+ magic = true;
+ break;
+ default:
+ break;
+ }
+ weapon = select_weapon(dt, false, true); /* missile=true to get the unmodified best weapon she has */
+ sd = weapon_effskill(dt, at, weapon, false, false);
+ if (weapon != NULL)
+ dwtype = weapon->type;
+
+ if (is_riding(at) && (awtype == NULL || (fval(awtype, WTF_HORSEBONUS)
+ && !fval(awtype, WTF_MISSILE)))) {
+ da += CavalryBonus(au, dt, BONUS_DAMAGE);
+ }
+
+ if (armor) {
+ ar += armor->prot;
+ if (armor->projectile > 0 && chance(armor->projectile)) {
+ return false;
+ }
+ }
+ if (shield) {
+ ar += shield->prot;
+ if (shield->projectile > 0 && chance(shield->projectile)) {
+ return false;
+ }
+ }
+
+ /* natürliche Rüstung */
+ an = natural_armor(du);
+
+ /* magische Rüstung durch Artefakte oder Sprüche */
+ /* Momentan nur Trollgürtel und Werwolf-Eigenschaft */
+ am = select_magicarmor(dt);
+
+#if CHANGED_CROSSBOWS
+ if (awtype && fval(awtype, WTF_ARMORPIERCING)) {
+ /* crossbows */
+ ar /= 2;
+ an /= 2;
+ }
+#endif
+
+ if (rule_armor < 0) {
+ rule_armor = get_param_int(global.parameters, "rules.combat.nat_armor", 0);
+ }
+ if (rule_armor == 0) {
+ /* natürliche Rüstung ist halbkumulativ */
+ if (ar > 0) {
+ ar += an / 2;
+ }
+ else {
+ ar = an;
+ }
+ }
+ else {
+ /* use the higher value, add half the other value */
+ ar = (ar > an) ? (ar + an / 2) : (an + ar / 2);
+ }
+ ar += am;
+
+ if (type != AT_COMBATSPELL && type != AT_SPELL) {
+ if (damage_rules & DAMAGE_CRITICAL) {
+ double kritchance = (sk * 3 - sd) / 200.0;
+
+ kritchance = _max(kritchance, 0.005);
+ kritchance = _min(0.9, kritchance);
+
+ while (chance(kritchance)) {
+ if (bdebug) {
+ fprintf(bdebug, "%s/%d lands a critical hit\n", unitid(au), at.index);
+ }
+ da += dice_rand(damage);
+ }
+ }
+
+ da += rc_specialdamage(u_race(au), u_race(du), awtype);
+
+ if (awtype != NULL && fval(awtype, WTF_MISSILE)) {
+ /* missile weapon bonus */
+ if (damage_rules & DAMAGE_MISSILE_BONUS) {
+ da += af->person[at.index].damage_rear;
+ }
+ }
+ else if (awtype == NULL) {
+ /* skill bonus for unarmed combat */
+ if (damage_rules & DAMAGE_UNARMED_BONUS) {
+ da += effskill(au, SK_WEAPONLESS);
+ }
+ }
+ else {
+ /* melee bonus */
+ if (damage_rules & DAMAGE_MELEE_BONUS) {
+ da += af->person[at.index].damage;
+ }
+ }
+
+ /* Skilldifferenzbonus */
+ if (damage_rules & DAMAGE_SKILL_BONUS) {
+ da += _max(0, (sk - sd) / DAMAGE_QUOTIENT);
+ }
+ }
+
+ if (magic) {
+ /* Magischer Schaden durch Spruch oder magische Waffe */
+ double res = 1.0;
+
+ /* magic_resistance gib x% Resistenzbonus zurück */
+ res -= magic_resistance(du) * 3.0;
+
+ if (u_race(du)->battle_flags & BF_EQUIPMENT) {
+#ifdef TODO_RUNESWORD
+ /* Runenschwert gibt im Kampf 80% Resistenzbonus */
+ if (dwp == WP_RUNESWORD)
+ res -= 0.80;
+#endif
+ /* der Effekt von Laen steigt nicht linear */
+ if (armor && fval(armor, ATF_LAEN))
+ res *= (1 - armor->magres);
+ if (shield && fval(shield, ATF_LAEN))
+ res *= (1 - shield->magres);
+ if (dwtype)
+ res *= (1 - dwtype->magres);
+ }
+
+ if (res > 0) {
+ da = (int)(_max(da * res, 0));
+ }
+ /* gegen Magie wirkt nur natürliche und magische Rüstung */
+ ar = an + am;
+ }
+
+ rda = _max(da - ar, 0);
+
+ if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic)
+ rda = 0;
+ else {
+ int qi;
+ quicklist *ql;
+ unsigned int i = 0;
+
+ if (u_race(du)->battle_flags & BF_RES_PIERCE)
+ i |= WTF_PIERCE;
+ if (u_race(du)->battle_flags & BF_RES_CUT)
+ i |= WTF_CUT;
+ if (u_race(du)->battle_flags & BF_RES_BASH)
+ i |= WTF_BLUNT;
+
+ if (i && awtype && fval(awtype, i))
+ rda /= 2;
+
+ /* Schilde */
+ for (qi = 0, ql = b->meffects; ql; ql_advance(&ql, &qi, 1)) {
+ meffect *me = (meffect *)ql_get(ql, qi);
+ if (meffect_protection(b, me, ds) != 0) {
+ assert(0 <= rda); /* rda sollte hier immer mindestens 0 sein */
+ /* jeder Schaden wird um effect% reduziert bis der Schild duration
+ * Trefferpunkte aufgefangen hat */
+ if (me->typ == SHIELD_REDUCE) {
+ hp = rda * (me->effect / 100);
+ rda -= hp;
+ me->duration -= hp;
+ }
+ /* gibt Rüstung +effect für duration Treffer */
+ if (me->typ == SHIELD_ARMOR) {
+ rda = _max(rda - me->effect, 0);
+ me->duration--;
+ }
+ }
+ }
+ }
+
+ assert(dt.index < du->number);
+ df->person[dt.index].hp -= rda;
+ if (u_race(au) == get_race(RC_DAEMON)) {
+ vampirism(at, rda);
+ }
+
+ if (df->person[dt.index].hp > 0) { /* Hat überlebt */
+ if (bdebug) {
+ fprintf(bdebug, "Damage %d, armor %d: %d -> %d HP\n",
+ da, ar, df->person[dt.index].hp + rda, df->person[dt.index].hp);
+ }
+ if (u_race(au) == get_race(RC_DAEMON)) {
+#ifdef TODO_RUNESWORD
+ if (select_weapon(dt, 0, -1) == WP_RUNESWORD)
+ continue;
+#endif
+ if (!(df->person[dt.index].flags & (FL_COURAGE | FL_DAZZLED))) {
+ df->person[dt.index].flags |= FL_DAZZLED;
+ df->person[dt.index].defence--;
+ }
+ }
+ df->person[dt.index].flags = (df->person[dt.index].flags & ~FL_SLEEPING);
+ return false;
+ }
+
+ /* Sieben Leben */
+ if (u_race(du) == get_race(RC_CAT) && (chance(1.0 / 7))) {
+ assert(dt.index >= 0 && dt.index < du->number);
+ df->person[dt.index].hp = unit_max_hp(du);
+ return false;
+ }
+
+ /* Heiltrank schluerfen und hoffen */
+ if (oldpotiontype[P_HEAL]) {
+ if (get_effect(du, oldpotiontype[P_HEAL]) > 0) {
+ change_effect(du, oldpotiontype[P_HEAL], -1);
+ heiltrank = 1;
+ }
+ else if (i_get(du->items, oldpotiontype[P_HEAL]->itype) > 0) {
+ i_change(&du->items, oldpotiontype[P_HEAL]->itype, -1);
+ change_effect(du, oldpotiontype[P_HEAL], 3);
+ heiltrank = 1;
+ }
+ if (heiltrank && (chance(0.50))) {
+ {
+ message *m = msg_message("battle::potionsave", "unit", du);
+ message_faction(b, du->faction, m);
+ msg_release(m);
+ }
+ assert(dt.index >= 0 && dt.index < du->number);
+ df->person[dt.index].hp = u_race(du)->hitpoints;
+ return false;
+ }
+ }
+ ++at.fighter->kills;
+
+ if (bdebug) {
+ fprintf(bdebug, "Damage %d, armor %d, type %d: %d -> %d HP, tot.\n",
+ da, ar, type, df->person[dt.index].hp + rda, df->person[dt.index].hp);
+ }
+ for (pitm = &du->items; *pitm;) {
+ item *itm = *pitm;
+ const item_type *itype = itm->type;
+ if (!(itype->flags & ITF_CURSED) && dt.index < itm->number) {
+ /* 25% Grundchance, das ein Item kaputtgeht. */
+ if (rng_int() % 4 < 1) {
+ i_change(pitm, itype, -1);
+ }
+ }
+ if (*pitm == itm) {
+ pitm = &itm->next;
+ }
+ }
+ kill_troop(dt);
+
+ return true;
+}
+
+static int
+count_side(const side * s, const side * vs, int minrow, int maxrow, int select)
+{
+ fighter *fig;
+ int people = 0;
+ int unitrow[NUMROWS];
+
+ if (maxrow < FIGHT_ROW)
+ return 0;
+ if (select & SELECT_ADVANCE) {
+ memset(unitrow, -1, sizeof(unitrow));
+ }
+
+ for (fig = s->fighters; fig; fig = fig->next) {
+ if (fig->alive - fig->removed > 0) {
+ int row = statusrow(fig->status);
+ if (select & SELECT_ADVANCE) {
+ if (unitrow[row] == -1) {
+ unitrow[row] = get_unitrow(fig, vs);
+ }
+ row = unitrow[row];
+ }
+ if (row >= minrow && row <= maxrow) {
+ people += fig->alive - fig->removed;
+ if (people > 0 && (select & SELECT_FIND))
+ break;
+ }
+ }
+ }
+ return people;
+}
+
+/* return the number of live allies warning: this function only considers
+* troops that are still alive, not those that are still fighting although
+* dead. */
+int
+count_allies(const side * as, int minrow, int maxrow, int select, int allytype)
+{
+ battle *b = as->battle;
+ side *ds;
+ int count = 0;
+
+ for (ds = b->sides; ds != b->sides + b->nsides; ++ds) {
+ if ((allytype == ALLY_ANY && helping(as, ds)) || (allytype == ALLY_SELF
+ && as->faction == ds->faction)) {
+ count += count_side(ds, NULL, minrow, maxrow, select);
+ if (count > 0 && (select & SELECT_FIND))
+ break;
+ }
+ }
+ return count;
+}
+
+static int
+count_enemies_i(battle * b, const fighter * af, int minrow, int maxrow,
+int select)
+{
+ side *es, *as = af->side;
+ int i = 0;
+
+ for (es = b->sides; es != b->sides + b->nsides; ++es) {
+ if (as == NULL || enemy(es, as)) {
+ int offset = 0;
+ if (select & SELECT_DISTANCE) {
+ offset = get_unitrow(af, es) - FIGHT_ROW;
+ }
+ i += count_side(es, as, minrow - offset, maxrow - offset, select);
+ if (i > 0 && (select & SELECT_FIND))
+ break;
+ }
+ }
+ return i;
+}
+
+int
+count_enemies(battle * b, const fighter * af, int minrow, int maxrow,
+int select)
+{
+ int sr = statusrow(af->status);
+ side *as = af->side;
+
+ if (b->alive == b->fast.alive && as == b->fast.side && sr == b->fast.status
+ && minrow == b->fast.minrow && maxrow == b->fast.maxrow) {
+ if (b->fast.enemies[select] >= 0) {
+#ifdef DEBUG_FAST
+ int i = count_enemies_i(b, af, minrow, maxrow, select);
+ assert(i == b->fast.enemies[select]);
+#endif
+ return b->fast.enemies[select];
+ }
+ else if (select & SELECT_FIND) {
+ if (b->fast.enemies[select - SELECT_FIND] >= 0) {
+#ifdef DEBUG_FAST
+ int i = count_enemies_i(b, af, minrow, maxrow, select);
+ assert((i > 0) == (b->fast.enemies[select - SELECT_FIND] > 0));
+#endif
+ return b->fast.enemies[select - SELECT_FIND];
+ }
+ }
+ }
+ else if (select != SELECT_FIND || b->alive != b->fast.alive) {
+ b->fast.side = as;
+ b->fast.status = sr;
+ b->fast.minrow = minrow;
+ b->fast.alive = b->alive;
+ b->fast.maxrow = maxrow;
+ memset(b->fast.enemies, -1, sizeof(b->fast.enemies));
+ }
+ if (maxrow >= FIRST_ROW) {
+ int i = count_enemies_i(b, af, minrow, maxrow, select);
+ b->fast.enemies[select] = i;
+ return i;
+ }
+ return 0;
+}
+
+troop select_enemy(fighter * af, int minrow, int maxrow, int select)
+{
+ side *as = af->side;
+ battle *b = as->battle;
+ int si, selected;
+ int enemies;
+#ifdef DEBUG_SELECT
+ troop result = no_troop;
+#endif
+ if (u_race(af->unit)->flags & RCF_FLY) {
+ /* flying races ignore min- and maxrow and can attack anyone fighting
+ * them */
+ minrow = FIGHT_ROW;
+ maxrow = BEHIND_ROW;
+ }
+ minrow = _max(minrow, FIGHT_ROW);
+
+ enemies = count_enemies(b, af, minrow, maxrow, select);
+
+ /* Niemand ist in der angegebenen Entfernung? */
+ if (enemies <= 0)
+ return no_troop;
+
+ selected = rng_int() % enemies;
+ for (si = 0; as->enemies[si]; ++si) {
+ side *ds = as->enemies[si];
+ fighter *df;
+ int unitrow[NUMROWS];
+ int offset = 0;
+
+ if (select & SELECT_DISTANCE)
+ offset = get_unitrow(af, ds) - FIGHT_ROW;
+
+ if (select & SELECT_ADVANCE) {
+ int ui;
+ for (ui = 0; ui != NUMROWS; ++ui)
+ unitrow[ui] = -1;
+ }
+
+ for (df = ds->fighters; df; df = df->next) {
+ int dr;
+
+ dr = statusrow(df->status);
+ if (select & SELECT_ADVANCE) {
+ if (unitrow[dr] < 0) {
+ unitrow[dr] = get_unitrow(df, as);
+ }
+ dr = unitrow[dr];
+ }
+
+ if (select & SELECT_DISTANCE)
+ dr += offset;
+ if (dr < minrow || dr > maxrow)
+ continue;
+ if (df->alive - df->removed > selected) {
+#ifdef DEBUG_SELECT
+ if (result.fighter == NULL) {
+ result.index = selected;
+ result.fighter = df;
+ }
+#else
+ troop dt;
+ dt.index = selected;
+ dt.fighter = df;
+ return dt;
+#endif
+ }
+ selected -= (df->alive - df->removed);
+ enemies -= (df->alive - df->removed);
+ }
+ }
+ if (enemies != 0) {
+ log_error("select_enemies has a bug.\n");
+ }
+#ifdef DEBUG_SELECT
+ return result;
+#else
+ assert(!selected);
+ return no_troop;
+#endif
+}
+
+static int get_tactics(const side * as, const side * ds)
+{
+ battle *b = as->battle;
+ side *stac;
+ int result = 0;
+ int defense = 0;
+
+ if (b->max_tactics > 0) {
+ for (stac = b->sides; stac != b->sides + b->nsides; ++stac) {
+ if (stac->leader.value > result && helping(stac, as)) {
+ assert(ds == NULL || !helping(stac, ds));
+ result = stac->leader.value;
+ }
+ if (ds && stac->leader.value > defense && helping(stac, ds)) {
+ assert(!helping(stac, as));
+ defense = stac->leader.value;
+ }
+ }
+ }
+ return result - defense;
+}
+
+static troop select_opponent(battle * b, troop at, int mindist, int maxdist)
+{
+ fighter *af = at.fighter;
+ troop dt;
+
+ if (u_race(af->unit)->flags & RCF_FLY) {
+ /* flying races ignore min- and maxrow and can attack anyone fighting
+ * them */
+ dt = select_enemy(at.fighter, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE);
+ }
+ else {
+ mindist = _max(mindist, FIGHT_ROW);
+ dt = select_enemy(at.fighter, mindist, maxdist, SELECT_ADVANCE);
+ }
+
+ if (b->turn == 0 && dt.fighter) {
+ int tactics_formula = -1;
+
+ if (tactics_formula < 0) {
+ tactics_formula =
+ get_param_int(global.parameters, "rules.tactics.formula", 0);
+ }
+ if (tactics_formula == 1) {
+ int tactics = get_tactics(at.fighter->side, dt.fighter->side);
+
+ /* percentage chance to get this attack */
+ if (tactics > 0) {
+ double tacch = 0.1 * tactics;
+ if (fval(b->region->terrain, SEA_REGION)) {
+ ship *sh = at.fighter->unit->ship;
+ if (sh)
+ tacch *= sh->type->tac_bonus;
+ }
+ if (!chance(tacch)) {
+ dt.fighter = NULL;
+ }
+ }
+ else {
+ dt.fighter = NULL;
+ }
+ }
+ }
+
+ return dt;
+}
+
+quicklist *fighters(battle * b, const side * vs, int minrow, int maxrow,
+ int mask)
+{
+ side *s;
+ quicklist *fightervp = 0;
+
+ assert(vs != NULL);
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+
+ if (mask == FS_ENEMY) {
+ if (!enemy(s, vs))
+ continue;
+ }
+ else if (mask == FS_HELP) {
+ if (enemy(s, vs) || !allysf(s, vs->faction)) {
+ continue;
+ }
+ }
+ else {
+ assert(mask == (FS_HELP | FS_ENEMY) || !"invalid alliance state");
+ }
+ for (fig = s->fighters; fig; fig = fig->next) {
+ int row = get_unitrow(fig, vs);
+ if (row >= minrow && row <= maxrow) {
+ ql_push(&fightervp, fig);
+ }
+ }
+ }
+
+ return fightervp;
+}
+
+static void report_failed_spell(struct battle * b, struct unit * mage, const struct spell *sp)
+{
+ message *m = msg_message("battle::spell_failed", "unit spell", mage, sp);
+ message_all(b, m);
+ msg_release(m);
+}
+
+void do_combatmagic(battle * b, combatmagic_t was)
+{
+ side *s;
+ region *r = b->region;
+ castorder *co;
+ int level, rank, sl;
+ spellrank spellranks[MAX_SPELLRANK];
+
+ memset(spellranks, 0, sizeof(spellranks));
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+ for (fig = s->fighters; fig; fig = fig->next) {
+ unit *mage = fig->unit;
+
+ if (fig->alive <= 0)
+ continue; /* fighter kann im Kampf getötet worden sein */
+
+ level = eff_skill(mage, SK_MAGIC, r);
+ if (level > 0) {
+ float power;
+ const spell *sp;
+ const struct locale *lang = mage->faction->locale;
+ order *ord;
+
+ switch (was) {
+ case DO_PRECOMBATSPELL:
+ sp = get_combatspell(mage, 0);
+ sl = get_combatspelllevel(mage, 0);
+ break;
+ case DO_POSTCOMBATSPELL:
+ sp = get_combatspell(mage, 2);
+ sl = get_combatspelllevel(mage, 2);
+ break;
+ default:
+ /* Fehler! */
+ return;
+ }
+ if (sp == NULL)
+ continue;
+
+ ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang));
+ if (!cancast(mage, sp, 1, 1, ord)) {
+ free_order(ord);
+ continue;
+ }
+
+ level = eff_spelllevel(mage, sp, level, 1);
+ if (sl > 0)
+ level = _min(sl, level);
+ if (level < 0) {
+ report_failed_spell(b, mage, sp);
+ free_order(ord);
+ continue;
+ }
+
+ power = spellpower(r, mage, sp, level, ord);
+ free_order(ord);
+ if (power <= 0) { /* Effekt von Antimagie */
+ report_failed_spell(b, mage, sp);
+ pay_spell(mage, sp, level, 1);
+ }
+ else if (fumble(r, mage, sp, level)) {
+ report_failed_spell(b, mage, sp);
+ pay_spell(mage, sp, level, 1);
+ }
+ else {
+ co = create_castorder(0, fig->unit, 0, sp, r, level, power, 0, 0, 0);
+ co->magician.fig = fig;
+ add_castorder(&spellranks[sp->rank], co);
+ }
+ }
+ }
+ }
+ for (rank = 0; rank < MAX_SPELLRANK; rank++) {
+ for (co = spellranks[rank].begin; co; co = co->next) {
+ fighter *fig = co->magician.fig;
+ const spell *sp = co->sp;
+ int level = co->level;
+
+ if (!sp->cast) {
+ log_error("spell '%s' has no function.\n", sp->sname);
+ }
+ else {
+ level = sp->cast(co);
+ if (level > 0) {
+ pay_spell(fig->unit, sp, level, 1);
+ }
+ }
+ }
+ }
+ for (rank = 0; rank < MAX_SPELLRANK; rank++) {
+ free_castorders(spellranks[rank].begin);
+ }
+}
+
+static int cast_combatspell(troop at, const spell * sp, int level, float force)
+{
+ castorder co;
+
+ create_castorder(&co, at.fighter->unit, 0, sp, at.fighter->unit->region, level, force, 0, 0, 0);
+ co.magician.fig = at.fighter;
+ level = sp->cast(&co);
+ free_castorder(&co);
+ if (level > 0) {
+ pay_spell(at.fighter->unit, sp, level, 1);
+ }
+ return level;
+}
+
+static void do_combatspell(troop at)
+{
+ const spell *sp;
+ fighter *fi = at.fighter;
+ unit *caster = fi->unit;
+ battle *b = fi->side->battle;
+ region *r = b->region;
+ quicklist *ql;
+ int level, qi;
+ float power;
+ int fumblechance = 0;
+ order *ord;
+ int sl;
+ const struct locale *lang = caster->faction->locale;
+
+ sp = get_combatspell(caster, 1);
+ if (sp == NULL) {
+ fi->magic = 0; /* Hat keinen Kampfzauber, kämpft nichtmagisch weiter */
+ return;
+ }
+ ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang));
+ if (!cancast(caster, sp, 1, 1, ord)) {
+ fi->magic = 0; /* Kann nicht mehr Zaubern, kämpft nichtmagisch weiter */
+ return;
+ }
+
+ level = eff_spelllevel(caster, sp, fi->magic, 1);
+ if ((sl = get_combatspelllevel(caster, 1)) > 0)
+ level = _min(level, sl);
+
+ if (fumble(r, caster, sp, level)) {
+ report_failed_spell(b, caster, sp);
+ pay_spell(caster, sp, level, 1);
+ return;
+ }
+
+ for (qi = 0, ql = b->meffects; ql; ql_advance(&ql, &qi, 1)) {
+ meffect *mblock = (meffect *)ql_get(ql, qi);
+ if (mblock->typ == SHIELD_BLOCK) {
+ if (meffect_blocked(b, mblock, fi->side) != 0) {
+ fumblechance += mblock->duration;
+ mblock->duration -= mblock->effect;
+ }
+ }
+ }
+
+ /* Antimagie die Fehlschlag erhöht */
+ if (rng_int() % 100 < fumblechance) {
+ report_failed_spell(b, caster, sp);
+ pay_spell(caster, sp, level, 1);
+ free_order(ord);
+ return;
+ }
+ power = spellpower(r, caster, sp, level, ord);
+ free_order(ord);
+ if (power <= 0) { /* Effekt von Antimagie */
+ report_failed_spell(b, caster, sp);
+ pay_spell(caster, sp, level, 1);
+ return;
+ }
+
+ if (!sp->cast) {
+ log_error("spell '%s' has no function.\n", sp->sname);
+ }
+ else {
+ level = cast_combatspell(at, sp, level, power);
+ }
+}
+
+/* Sonderattacken: Monster patzern nicht und zahlen auch keine
+ * Spruchkosten. Da die Spruchstärke direkt durch den Level bestimmt
+ * wird, wirkt auch keine Antimagie (wird sonst in spellpower
+ * gemacht) */
+
+static void do_extra_spell(troop at, const att * a)
+{
+ const spell *sp = a->data.sp;
+
+ if (sp->cast == NULL) {
+ log_error("spell '%s' has no function.\n", sp->sname);
+ }
+ else {
+ int level = a->level;
+ assert(a->level > 0);
+ cast_combatspell(at, sp, level, level * MagicPower());
+ }
+}
+
+int skilldiff(troop at, troop dt, int dist)
+{
+ fighter *af = at.fighter, *df = dt.fighter;
+ unit *au = af->unit, *du = df->unit;
+ int is_protected = 0, skdiff = 0;
+ weapon *awp = select_weapon(at, true, dist > 1);
+
+ skdiff += af->person[at.index].attack;
+ skdiff -= df->person[dt.index].defence;
+
+ if (df->person[dt.index].flags & FL_SLEEPING)
+ skdiff += 2;
+
+ /* Effekte durch Rassen */
+ if (awp != NULL && u_race(au) == get_race(RC_HALFLING) && dragonrace(u_race(du))) {
+ skdiff += 5;
+ }
+
+ if (u_race(au) == get_race(RC_GOBLIN)) {
+ static int goblin_bonus = -1;
+ if (goblin_bonus < 0)
+ goblin_bonus =
+ get_param_int(global.parameters, "rules.combat.goblinbonus", 10);
+ if (af->side->size[SUM_ROW] >= df->side->size[SUM_ROW] * goblin_bonus) {
+ skdiff += 1;
+ }
+ }
+
+ if (df->building) {
+ bool init = false;
+ static const curse_type *strongwall_ct, *magicwalls_ct;
+ if (!init) {
+ strongwall_ct = ct_find("strongwall");
+ magicwalls_ct = ct_find("magicwalls");
+ init = true;
+ }
+ if (df->building->type->protection) {
+ int beff = df->building->type->protection(df->building, du);
+ if (beff) {
+ skdiff -= beff;
+ is_protected = 2;
+ }
+ }
+ if (strongwall_ct) {
+ curse *c = get_curse(df->building->attribs, strongwall_ct);
+ if (curse_active(c)) {
+ /* wirkt auf alle Gebäude */
+ skdiff -= curse_geteffect_int(c);
+ is_protected = 2;
+ }
+ }
+ if (magicwalls_ct
+ && curse_active(get_curse(df->building->attribs, magicwalls_ct))) {
+ /* Verdoppelt Burgenbonus */
+ skdiff -= buildingeffsize(df->building, false);
+ }
+ }
+ /* Goblin-Verteidigung
+ * ist direkt in der Rassentabelle als df_default
+ */
+
+ /* Effekte der Waffen */
+ skdiff += weapon_effskill(at, dt, awp, true, dist > 1);
+ if (awp && fval(awp->type, WTF_MISSILE)) {
+ skdiff -= is_protected;
+ if (awp->type->modifiers) {
+ int w;
+ for (w = 0; awp->type->modifiers[w].value != 0; ++w) {
+ if (awp->type->modifiers[w].flags & WMF_MISSILE_TARGET) {
+ /* skill decreases by targeting difficulty (bow -2, catapult -4) */
+ skdiff -= awp->type->modifiers[w].value;
+ break;
+ }
+ }
+ }
+ }
+ if (skill_formula == FORMULA_ORIG) {
+ weapon *dwp = select_weapon(dt, false, dist > 1);
+ skdiff -= weapon_effskill(dt, at, dwp, false, dist > 1);
+ }
+ return skdiff;
+}
+
+static int setreload(troop at)
+{
+ fighter *af = at.fighter;
+ const weapon_type *wtype = af->person[at.index].missile->type;
+ if (wtype->reload == 0)
+ return 0;
+ return af->person[at.index].reload = wtype->reload;
+}
+
+int getreload(troop at)
+{
+ return at.fighter->person[at.index].reload;
+}
+
+static void
+debug_hit(troop at, const weapon * awp, troop dt, const weapon * dwp,
+int skdiff, int dist, bool success)
+{
+ fprintf(bdebug, "%.4s/%d [%6s/%d] %s %.4s/%d [%6s/%d] with %d, distance %d\n",
+ unitid(at.fighter->unit), at.index,
+ LOC(default_locale, awp ? resourcename(awp->type->itype->rtype,
+ 0) : "unarmed"), weapon_effskill(at, dt, awp, true, dist > 1),
+ success ? "hits" : "misses", unitid(dt.fighter->unit), dt.index,
+ LOC(default_locale, dwp ? resourcename(dwp->type->itype->rtype,
+ 0) : "unarmed"), weapon_effskill(dt, at, dwp, false, dist > 1), skdiff,
+ dist);
+}
+
+int hits(troop at, troop dt, weapon * awp)
+{
+ fighter *af = at.fighter, *df = dt.fighter;
+ const armor_type *armor, *shield = 0;
+ int skdiff = 0;
+ int dist = get_unitrow(af, df->side) + get_unitrow(df, af->side) - 1;
+ weapon *dwp = select_weapon(dt, false, dist > 1);
+
+ if (!df->alive)
+ return 0;
+ if (getreload(at))
+ return 0;
+ if (dist > 1 && (awp == NULL || !fval(awp->type, WTF_MISSILE)))
+ return 0;
+
+ /* mark this person as hit. */
+ df->person[dt.index].flags |= FL_HIT;
+
+ if (af->person[at.index].flags & FL_STUNNED) {
+ af->person[at.index].flags &= ~FL_STUNNED;
+ return 0;
+ }
+ if ((af->person[at.index].flags & FL_TIRED && rng_int() % 100 < 50)
+ || (af->person[at.index].flags & FL_SLEEPING))
+ return 0;
+
+ /* effect of sp_reeling_arrows combatspell */
+ if (af->side->battle->reelarrow && awp && fval(awp->type, WTF_MISSILE)
+ && rng_double() < 0.5) {
+ return 0;
+ }
+
+ skdiff = skilldiff(at, dt, dist);
+ /* Verteidiger bekommt eine Rüstung */
+ armor = select_armor(dt, true);
+ if (dwp == NULL || (dwp->type->flags & WTF_USESHIELD)) {
+ shield = select_armor(dt, false);
+ }
+ if (contest(skdiff, dt, armor, shield)) {
+ if (bdebug) {
+ debug_hit(at, awp, dt, dwp, skdiff, dist, true);
+ }
+ return 1;
+ }
+ if (bdebug) {
+ debug_hit(at, awp, dt, dwp, skdiff, dist, false);
+ }
+ return 0;
+}
+
+void dazzle(battle * b, troop * td)
+{
+ /* Nicht kumulativ ! */
+ if (td->fighter->person[td->index].flags & FL_DAZZLED)
+ return;
+
+#ifdef TODO_RUNESWORD
+ if (td->fighter->weapon[WP_RUNESWORD].count > td->index) {
+ return;
+ }
+#endif
+ if (td->fighter->person[td->index].flags & FL_COURAGE) {
+ return;
+ }
+
+ if (td->fighter->person[td->index].flags & FL_DAZZLED) {
+ return;
+ }
+
+ td->fighter->person[td->index].flags |= FL_DAZZLED;
+ td->fighter->person[td->index].defence--;
+}
+
+/* TODO: Gebäude/Schiffe sollten auch zerstörbar sein. Schwierig im Kampf,
+ * besonders bei Schiffen. */
+
+void damage_building(battle * b, building * bldg, int damage_abs)
+{
+ bldg->size = _max(1, bldg->size - damage_abs);
+
+ /* Wenn Burg, dann gucken, ob die Leute alle noch in das Gebäude passen. */
+
+ if (bldg->type->protection) {
+ side *s;
+
+ bldg->sizeleft = bldg->size;
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+ for (fig = s->fighters; fig; fig = fig->next) {
+ if (fig->building == bldg) {
+ if (bldg->sizeleft >= fig->unit->number) {
+ fig->building = bldg;
+ bldg->sizeleft -= fig->unit->number;
+ }
+ else {
+ fig->building = NULL;
+ }
+ }
+ }
+ }
+ }
+}
+
+static int attacks_per_round(troop t)
+{
+ return t.fighter->person[t.index].speed;
+}
+
+static void make_heroes(battle * b)
+{
+ side *s;
+ static int hero_speed = 0;
+ if (hero_speed == 0) {
+ hero_speed = get_param_int(global.parameters, "rules.combat.herospeed", 10);
+ }
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+ for (fig = s->fighters; fig; fig = fig->next) {
+ unit *u = fig->unit;
+ if (fval(u, UFL_HERO)) {
+ int i;
+ if (!playerrace(u_race(u))) {
+ log_error("Hero %s is a %s.\n", unitname(u), u_race(u)->_name);
+ }
+ for (i = 0; i != u->number; ++i) {
+ fig->person[i].speed += (hero_speed - 1);
+ }
+ }
+ }
+ }
+}
+
+static void attack(battle * b, troop ta, const att * a, int numattack)
+{
+ fighter *af = ta.fighter;
+ troop td;
+ unit *au = af->unit;
+
+ switch (a->type) {
+ case AT_COMBATSPELL:
+ /* Magier versuchen immer erstmal zu zaubern, erst wenn das
+ * fehlschlägt, wird af->magic == 0 und der Magier kämpft
+ * konventionell weiter */
+ if (numattack == 0 && af->magic > 0) {
+ /* wenn der magier in die potenzielle Reichweite von Attacken des
+ * Feindes kommt, beginnt er auch bei einem Status von KAEMPFE NICHT,
+ * Kampfzauber zu schleudern: */
+ if (count_enemies(b, af, melee_range[0], missile_range[1],
+ SELECT_ADVANCE | SELECT_DISTANCE | SELECT_FIND)) {
+ do_combatspell(ta);
+ }
+ }
+ break;
+ case AT_STANDARD: /* Waffen, mag. Gegenstände, Kampfzauber */
+ if (numattack > 0 || af->magic <= 0) {
+ weapon *wp = ta.fighter->person[ta.index].missile;
+ int melee =
+ count_enemies(b, af, melee_range[0], melee_range[1],
+ SELECT_ADVANCE | SELECT_DISTANCE | SELECT_FIND);
+ if (melee)
+ wp = preferred_weapon(ta, true);
+ /* Sonderbehandlungen */
+
+ if (getreload(ta)) {
+ ta.fighter->person[ta.index].reload--;
+ }
+ else {
+ bool standard_attack = true;
+ bool reload = false;
+ /* spezialattacken der waffe nur, wenn erste attacke in der runde.
+ * sonst helden mit feuerschwertern zu mächtig */
+ if (numattack == 0 && wp && wp->type->attack) {
+ int dead = 0;
+ standard_attack = wp->type->attack(&ta, wp->type, &dead);
+ if (!standard_attack)
+ reload = true;
+ af->catmsg += dead;
+ if (!standard_attack && af->person[ta.index].last_action < b->turn) {
+ af->person[ta.index].last_action = b->turn;
+ }
+ }
+ if (standard_attack) {
+ bool missile = false;
+ if (wp && fval(wp->type, WTF_MISSILE))
+ missile = true;
+ if (missile) {
+ td = select_opponent(b, ta, missile_range[0], missile_range[1]);
+ }
+ else {
+ td = select_opponent(b, ta, melee_range[0], melee_range[1]);
+ }
+ if (!td.fighter)
+ return;
+ if (ta.fighter->person[ta.index].last_action < b->turn) {
+ ta.fighter->person[ta.index].last_action = b->turn;
+ }
+ reload = true;
+ if (hits(ta, td, wp)) {
+ const char *d;
+ if (wp == NULL)
+ d = u_race(au)->def_damage;
+ else if (is_riding(ta))
+ d = wp->type->damage[1];
+ else
+ d = wp->type->damage[0];
+ terminate(td, ta, a->type, d, missile);
+ }
+ }
+ if (reload && wp && wp->type->reload && !getreload(ta)) {
+ int i = setreload(ta);
+ if (bdebug) {
+ fprintf(bdebug, "%s/%d reloading %d turns\n", unitid(au),
+ ta.index, i);
+ }
+ }
+ }
+ }
+ break;
+ case AT_SPELL: /* Extra-Sprüche. Kampfzauber in AT_COMBATSPELL! */
+ do_extra_spell(ta, a);
+ break;
+ case AT_NATURAL:
+ td = select_opponent(b, ta, melee_range[0], melee_range[1]);
+ if (!td.fighter)
+ return;
+ if (ta.fighter->person[ta.index].last_action < b->turn) {
+ ta.fighter->person[ta.index].last_action = b->turn;
+ }
+ if (hits(ta, td, NULL)) {
+ terminate(td, ta, a->type, a->data.dice, false);
+ }
+ break;
+ case AT_DRAIN_ST:
+ td = select_opponent(b, ta, melee_range[0], melee_range[1]);
+ if (!td.fighter)
+ return;
+ if (ta.fighter->person[ta.index].last_action < b->turn) {
+ ta.fighter->person[ta.index].last_action = b->turn;
+ }
+ if (hits(ta, td, NULL)) {
+ int c = dice_rand(a->data.dice);
+ while (c > 0) {
+ if (rng_int() % 2) {
+ td.fighter->person[td.index].attack -= 1;
+ }
+ else {
+ td.fighter->person[td.index].defence -= 1;
+ }
+ c--;
+ }
+ }
+ break;
+ case AT_DRAIN_EXP:
+ td = select_opponent(b, ta, melee_range[0], melee_range[1]);
+ if (!td.fighter)
+ return;
+ if (ta.fighter->person[ta.index].last_action < b->turn) {
+ ta.fighter->person[ta.index].last_action = b->turn;
+ }
+ if (hits(ta, td, NULL)) {
+ drain_exp(td.fighter->unit, dice_rand(a->data.dice));
+ }
+ break;
+ case AT_DAZZLE:
+ td = select_opponent(b, ta, melee_range[0], melee_range[1]);
+ if (!td.fighter)
+ return;
+ if (ta.fighter->person[ta.index].last_action < b->turn) {
+ ta.fighter->person[ta.index].last_action = b->turn;
+ }
+ if (hits(ta, td, NULL)) {
+ dazzle(b, &td);
+ }
+ break;
+ case AT_STRUCTURAL:
+ td = select_opponent(b, ta, melee_range[0], melee_range[1]);
+ if (!td.fighter)
+ return;
+ if (ta.fighter->person[ta.index].last_action < b->turn) {
+ ta.fighter->person[ta.index].last_action = b->turn;
+ }
+ if (td.fighter->unit->ship) {
+ /* FIXME should use damage_ship here? */
+ td.fighter->unit->ship->damage +=
+ DAMAGE_SCALE * dice_rand(a->data.dice);
+ }
+ else if (td.fighter->unit->building) {
+ damage_building(b, td.fighter->unit->building, dice_rand(a->data.dice));
+ }
+ }
+}
+
+void do_attack(fighter * af)
+{
+ troop ta;
+ unit *au = af->unit;
+ side *side = af->side;
+ battle *b = side->battle;
+
+ ta.fighter = af;
+
+ assert(au && au->number);
+ /* Da das Zuschlagen auf Einheiten und nicht auf den einzelnen
+ * Kämpfern beruht, darf die Reihenfolge und Größe der Einheit keine
+ * Rolle spielen, Das tut sie nur dann, wenn jeder, der am Anfang der
+ * Runde lebte, auch zuschlagen darf. Ansonsten ist der, der zufällig
+ * mit einer großen Einheit zuerst drankommt, extrem bevorteilt. */
+ ta.index = af->fighting;
+
+ while (ta.index--) {
+ /* Wir suchen eine beliebige Feind-Einheit aus. An der können
+ * wir feststellen, ob noch jemand da ist. */
+ int apr, attacks = attacks_per_round(ta);
+ if (!count_enemies(b, af, FIGHT_ROW, LAST_ROW, SELECT_FIND))
+ break;
+
+ for (apr = 0; apr != attacks; ++apr) {
+ int a;
+ for (a = 0; a != 10 && u_race(au)->attack[a].type != AT_NONE; ++a) {
+ if (apr > 0) {
+ /* Wenn die Waffe nachladen muss, oder es sich nicht um einen
+ * Waffen-Angriff handelt, dann gilt der Speed nicht. */
+ if (u_race(au)->attack[a].type != AT_STANDARD)
+ continue;
+ else {
+ weapon *wp = preferred_weapon(ta, true);
+ if (wp != NULL && wp->type->reload)
+ continue;
+ }
+ }
+ attack(b, ta, &(u_race(au)->attack[a]), apr);
+ }
+ }
+ }
+ /* Der letzte Katapultschütze setzt die
+ * Ladezeit neu und generiert die Meldung. */
+ if (af->catmsg >= 0) {
+ struct message *m =
+ msg_message("battle::killed", "unit dead", au, af->catmsg);
+ message_all(b, m);
+ msg_release(m);
+ af->catmsg = -1;
+ }
+}
+
+void do_regenerate(fighter * af)
+{
+ troop ta;
+ unit *au = af->unit;
+
+ ta.fighter = af;
+ ta.index = af->fighting;
+
+ while (ta.index--) {
+ af->person[ta.index].hp += effskill(au, SK_STAMINA);
+ af->person[ta.index].hp = _min(unit_max_hp(au), af->person[ta.index].hp);
+ }
+}
+
+static void add_tactics(tactics * ta, fighter * fig, int value)
+{
+ if (value == 0 || value < ta->value)
+ return;
+ if (value > ta->value) {
+ ql_free(ta->fighters);
+ ta->fighters = 0;
+ }
+ ql_push(&ta->fighters, fig);
+ ql_push(&fig->side->battle->leaders, fig);
+ ta->value = value;
+}
+
+static double horsebonus(const unit * u)
+{
+ const item_type *it_horse, *it_elvenhorse, *it_charger;
+ int n1 = 0, n2 = 0, n3 = 0;
+ item *itm;
+ int skl = eff_skill(u, SK_RIDING, u->region);
+ const resource_type *rtype;
+
+ if (skl < 1) return 0.0;
+
+ it_horse = ((rtype = get_resourcetype(R_HORSE)) != NULL) ? rtype->itype : 0;
+ it_elvenhorse = ((rtype = get_resourcetype(R_UNICORN)) != NULL) ? rtype->itype : 0;
+ it_charger = ((rtype = get_resourcetype(R_CHARGER)) != NULL) ? rtype->itype : 0;
+
+ for (itm = u->items; itm; itm = itm->next) {
+ if (itm->type->flags & ITF_ANIMAL) {
+ if (itm->type == it_elvenhorse)
+ n3 += itm->number;
+ else if (itm->type == it_charger)
+ n2 += itm->number;
+ else if (itm->type == it_horse)
+ n1 += itm->number;
+ }
+ }
+ if (skl >= 5 && n3 >= u->number)
+ return 0.30;
+ if (skl >= 3 && n2 + n3 >= u->number)
+ return 0.20;
+ if (skl >= 1 && n1 + n2 + n3 >= u->number)
+ return 0.10;
+ return 0.0F;
+}
+
+double fleechance(unit * u)
+{
+ double c = 0.20; /* Fluchtwahrscheinlichkeit in % */
+ region *r = u->region;
+ attrib *a = a_find(u->attribs, &at_fleechance);
+ /* Einheit u versucht, dem Getümmel zu entkommen */
+
+ c += (eff_skill(u, SK_STEALTH, r) * 0.05);
+ c += horsebonus(u);
+
+ if (u_race(u) == get_race(RC_HALFLING)) {
+ c += 0.20;
+ c = _min(c, 0.90);
+ }
+ else {
+ c = _min(c, 0.75);
+ }
+
+ if (a != NULL)
+ c += a->data.flt;
+
+ return c;
+}
+
+/** add a new army to the conflict
+ * beware: armies need to be added _at the beginning_ of the list because
+ * otherwise join_allies() will get into trouble */
+side *make_side(battle * b, const faction * f, const group * g,
+ unsigned int flags, const faction * stealthfaction)
+{
+ side *s1 = b->sides + b->nsides;
+ bfaction *bf;
+
+ if (fval(b->region->terrain, SEA_REGION)) {
+ /* every fight in an ocean is short */
+ flags |= SIDE_HASGUARDS;
+ }
+ else {
+ unit *u;
+ for (u = b->region->units; u; u = u->next) {
+ if (is_guard(u, HELP_ALL)) {
+ if (alliedunit(u, f, HELP_GUARD)) {
+ flags |= SIDE_HASGUARDS;
+ break;
+ }
+ }
+ }
+ }
+
+ s1->battle = b;
+ s1->group = g;
+ s1->flags = flags;
+ s1->stealthfaction = stealthfaction;
+ for (bf = b->factions; bf; bf = bf->next) {
+ faction *f2 = bf->faction;
+
+ if (f2 == f) {
+ s1->bf = bf;
+ s1->faction = f2;
+ s1->index = b->nsides++;
+ s1->nextF = bf->sides;
+ bf->sides = s1;
+ assert(b->nsides <= MAXSIDES);
+ break;
+ }
+ }
+ assert(bf);
+ return s1;
+}
+
+troop select_ally(fighter * af, int minrow, int maxrow, int allytype)
+{
+ side *as = af->side;
+ battle *b = as->battle;
+ side *ds;
+ int allies = count_allies(as, minrow, maxrow, SELECT_ADVANCE, allytype);
+
+ if (!allies) {
+ return no_troop;
+ }
+ allies = rng_int() % allies;
+
+ for (ds = b->sides; ds != b->sides + b->nsides; ++ds) {
+ if ((allytype == ALLY_ANY && helping(as, ds)) || (allytype == ALLY_SELF
+ && as->faction == ds->faction)) {
+ fighter *df;
+ for (df = ds->fighters; df; df = df->next) {
+ int dr = get_unitrow(df, NULL);
+ if (dr >= minrow && dr <= maxrow) {
+ if (df->alive - df->removed > allies) {
+ troop dt;
+ assert(allies >= 0);
+ dt.index = allies;
+ dt.fighter = df;
+ return dt;
+ }
+ allies -= df->alive;
+ }
+ }
+ }
+ }
+ assert(!"we should never have gotten here");
+ return no_troop;
+}
+
+static int loot_quota(const unit * src, const unit * dst,
+ const item_type * type, int n)
+{
+ static float divisor = -1;
+ if (dst && src && src->faction != dst->faction) {
+ if (divisor < 0) {
+ divisor = get_param_flt(global.parameters, "rules.items.loot_divisor", 1);
+ assert(divisor == 0 || divisor >= 1);
+ }
+ if (divisor >= 1) {
+ double r = n / divisor;
+ int x = (int)r;
+
+ r = r - x;
+ if (chance(r))
+ ++x;
+
+ return x;
+ }
+ }
+ return n;
+}
+
+static void loot_items(fighter * corpse)
+{
+ unit *u = corpse->unit;
+ item *itm = u->items;
+ battle *b = corpse->side->battle;
+ int dead = dead_fighters(corpse);
+
+ if (dead <= 0)
+ return;
+
+ while (itm) {
+ float lootfactor = dead / (float)u->number; /* only loot the dead! */
+ int maxloot = (int)(itm->number * lootfactor);
+ if (maxloot > 0) {
+ int i = _min(10, maxloot);
+ for (; i != 0; --i) {
+ int loot = maxloot / i;
+
+ if (loot > 0) {
+ fighter *fig = NULL;
+ int looting = 0;
+ int maxrow = 0;
+ /* mustloot: we absolutely, positively must have somebody loot this thing */
+ int mustloot = itm->type->flags & (ITF_CURSED | ITF_NOTLOST);
+
+ itm->number -= loot;
+ maxloot -= loot;
+
+ if (is_monsters(u->faction) && (loot_rules & LOOT_MONSTERS)) {
+ looting = 1;
+ }
+ else if (loot_rules & LOOT_OTHERS) {
+ looting = 1;
+ }
+ else if (loot_rules & LOOT_SELF) {
+ looting = 2;
+ }
+ if (looting) {
+ if (mustloot) {
+ maxrow = LAST_ROW;
+ }
+ else if (loot_rules & LOOT_KEEPLOOT) {
+ int lootchance = 50 + b->keeploot;
+ if (rng_int() % 100 < lootchance) {
+ maxrow = BEHIND_ROW;
+ }
+ }
+ else {
+ maxrow = LAST_ROW;
+ }
+ }
+ if (maxrow > 0) {
+ if (looting == 1) {
+ /* enemies get dibs */
+ fig = select_enemy(corpse, FIGHT_ROW, maxrow, 0).fighter;
+ }
+ if (!fig) {
+ /* self and allies get second pick */
+ fig = select_ally(corpse, FIGHT_ROW, LAST_ROW, ALLY_SELF).fighter;
+ }
+ }
+
+ if (fig) {
+ int trueloot =
+ mustloot ? loot : loot_quota(corpse->unit, fig->unit, itm->type,
+ loot);
+ if (trueloot > 0) {
+ item *l = fig->loot;
+ while (l && l->type != itm->type)
+ l = l->next;
+ if (!l) {
+ l = calloc(sizeof(item), 1);
+ l->next = fig->loot;
+ fig->loot = l;
+ l->type = itm->type;
+ }
+ l->number += trueloot;
+ }
+ }
+ }
+ }
+ }
+ itm = itm->next;
+ }
+}
+
+static bool seematrix(const faction * f, const side * s)
+{
+ if (f == s->faction)
+ return true;
+ if (s->flags & SIDE_STEALTH)
+ return false;
+ return true;
+}
+
+static double PopulationDamage(void)
+{
+ static double value = -1.0;
+ if (value < 0) {
+ int damage =
+ get_param_int(global.parameters, "rules.combat.populationdamage",
+ BATTLE_KILLS_PEASANTS);
+ value = damage / 100.0;
+ }
+ return value;
+}
+
+static void battle_effects(battle * b, int dead_players)
+{
+ region *r = b->region;
+ int dead_peasants =
+ _min(rpeasants(r), (int)(dead_players * PopulationDamage()));
+ if (dead_peasants) {
+ deathcounts(r, dead_peasants + dead_players);
+ chaoscounts(r, dead_peasants / 2);
+ rsetpeasants(r, rpeasants(r) - dead_peasants);
+ }
+}
+
+static void reorder_fleeing(region * r)
+{
+ unit **usrc = &r->units;
+ unit **udst = &r->units;
+ unit *ufirst = NULL;
+ unit *u;
+
+ for (; *udst; udst = &u->next) {
+ u = *udst;
+ }
+
+ for (u = *usrc; u != ufirst; u = *usrc) {
+ if (u->next && fval(u, UFL_FLEEING)) {
+ *usrc = u->next;
+ *udst = u;
+ udst = &u->next;
+ if (!ufirst)
+ ufirst = u;
+ }
+ else {
+ usrc = &u->next;
+ }
+ }
+ *udst = NULL;
+}
+
+static void aftermath(battle * b)
+{
+ region *r = b->region;
+ ship *sh;
+ side *s;
+ int dead_players = 0;
+ bfaction *bf;
+ bool ships_damaged = (bool)(b->turn + (b->has_tactics_turn ? 1 : 0) > 2); /* only used for ship damage! */
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *df;
+ s->dead = 0;
+
+ for (df = s->fighters; df; df = df->next) {
+ unit *du = df->unit;
+ int dead = dead_fighters(df);
+
+ /* tote insgesamt: */
+ s->dead += dead;
+ /* Tote, die wiederbelebt werde koennen: */
+ if (playerrace(u_race(df->unit))) {
+ s->casualties += dead;
+ }
+ if (df->hits + df->kills) {
+ struct message *m =
+ msg_message("killsandhits", "unit hits kills", du, df->hits,
+ df->kills);
+ message_faction(b, du->faction, m);
+ msg_release(m);
+ }
+ }
+ }
+
+ /* POSTCOMBAT */
+ do_combatmagic(b, DO_POSTCOMBATSPELL);
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ int snumber = 0;
+ fighter *df;
+ bool relevant = false; /* Kampf relevant für diese Partei? */
+ if (!fval(s, SIDE_HASGUARDS)) {
+ relevant = true;
+ }
+ s->flee = 0;
+
+ for (df = s->fighters; df; df = df->next) {
+ unit *du = df->unit;
+ int dead = dead_fighters(df);
+ int sum_hp = 0;
+ int n;
+ int flags = 0;
+
+ for (n = 0; n != df->alive; ++n) {
+ if (df->person[n].hp > 0) {
+ sum_hp += df->person[n].hp;
+ }
+ }
+ snumber += du->number;
+ if (relevant) {
+ flags = UFL_LONGACTION | UFL_NOTMOVING;
+ if (du->status == ST_FLEE) {
+ flags -= UFL_NOTMOVING;
+ }
+ }
+ if (df->alive == 0) {
+ flags |= UFL_DEAD;
+ }
+ if (flags) {
+ fset(du, flags);
+ }
+ if (sum_hp + df->run.hp < du->hp) {
+ /* someone on the ship got damaged, damage the ship */
+ ship *sh = du->ship ? du->ship : leftship(du);
+ if (sh)
+ fset(sh, SF_DAMAGED);
+ }
+
+ if (df->alive == du->number) {
+ du->hp = sum_hp;
+ continue; /* nichts passiert */
+ }
+ else if (df->run.hp) {
+ if (df->alive == 0) {
+ /* Report the casualties */
+ reportcasualties(b, df, dead);
+
+ /* Zuerst dürfen die Feinde plündern, die mitgenommenen Items
+ * stehen in fig->run.items. Dann werden die Fliehenden auf
+ * die leere (tote) alte Einheit gemapt */
+ if (!fval(df, FIG_NOLOOT)) {
+ loot_items(df);
+ }
+ scale_number(du, df->run.number);
+ du->hp = df->run.hp;
+ setguard(du, GUARD_NONE);
+ /* must leave ships or buildings, or a stealthy hobbit
+ * can hold castles indefinitely */
+ if (!fval(r->terrain, SEA_REGION)) {
+ leave(du, true); /* even region owners have to flee */
+ }
+ fset(du, UFL_FLEEING);
+ }
+ else {
+ /* nur teilweise geflohene Einheiten mergen sich wieder */
+ df->alive += df->run.number;
+ s->size[0] += df->run.number;
+ s->size[statusrow(df->status)] += df->run.number;
+ s->alive += df->run.number;
+ sum_hp += df->run.hp;
+ df->run.number = 0;
+ df->run.hp = 0;
+ /* df->run.region = NULL; */
+
+ reportcasualties(b, df, dead);
+
+ scale_number(du, df->alive);
+ du->hp = sum_hp;
+ }
+ }
+ else {
+ if (df->alive == 0) {
+ /* alle sind tot, niemand geflohen. Einheit auflösen */
+ df->run.number = 0;
+ df->run.hp = 0;
+
+ /* Report the casualties */
+ reportcasualties(b, df, dead);
+
+ /* Distribute Loot */
+ loot_items(df);
+
+ setguard(du, GUARD_NONE);
+ scale_number(du, 0);
+ }
+ else {
+ df->run.number = 0;
+ df->run.hp = 0;
+
+ reportcasualties(b, df, dead);
+
+ scale_number(du, df->alive);
+ du->hp = sum_hp;
+ }
+ }
+ s->flee += df->run.number;
+
+ if (playerrace(u_race(du))) {
+ /* tote im kampf werden zu regionsuntoten:
+ * for each of them, a peasant will die as well */
+ dead_players += dead;
+ }
+ if (du->hp < du->number) {
+ log_error("%s has less hitpoints (%u) than people (%u)\n", itoa36(du->no), du->hp, du->number);
+ du->hp = du->number;
+ }
+ }
+ s->alive += s->healed;
+ assert(snumber == s->flee + s->alive + s->dead);
+ }
+
+ battle_effects(b, dead_players);
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ message *seen = msg_message("battle::army_report",
+ "index abbrev dead fled survived",
+ army_index(s), sideabkz(s, false), s->dead, s->flee, s->alive);
+ message *unseen = msg_message("battle::army_report",
+ "index abbrev dead fled survived",
+ army_index(s), "-?-", s->dead, s->flee, s->alive);
+
+ for (bf = b->factions; bf; bf = bf->next) {
+ faction *f = bf->faction;
+ message *m = seematrix(f, s) ? seen : unseen;
+
+ message_faction(b, f, m);
+ }
+
+ msg_release(seen);
+ msg_release(unseen);
+ }
+
+ /* Wir benutzen drifted, um uns zu merken, ob ein Schiff
+ * schonmal Schaden genommen hat. (moved und drifted
+ * sollten in flags überführt werden */
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *df;
+
+ for (df = s->fighters; df; df = df->next) {
+ unit *du = df->unit;
+ item *l;
+
+ /* Beute verteilen */
+ for (l = df->loot; l; l = l->next) {
+ const item_type *itype = l->type;
+ message *m =
+ msg_message("battle_loot", "unit amount item", du, l->number,
+ itype->rtype);
+ message_faction(b, du->faction, m);
+ msg_release(m);
+ i_change(&du->items, itype, l->number);
+ }
+
+ /* Wenn sich die Einheit auf einem Schiff befindet, wird
+ * dieses Schiff beschädigt. Andernfalls ein Schiff, welches
+ * evt. zuvor verlassen wurde. */
+ if (ships_damaged) {
+ if (du->ship)
+ sh = du->ship;
+ else
+ sh = leftship(du);
+
+ if (sh && fval(sh, SF_DAMAGED)) {
+ int n = b->turn - 2;
+ if (n > 0) {
+ float dmg =
+ get_param_flt(global.parameters, "rules.ship.damage.battleround",
+ 0.05F);
+ damage_ship(sh, dmg * n);
+ freset(sh, SF_DAMAGED);
+ }
+ }
+ }
+ }
+ }
+
+ if (ships_damaged) {
+ ship **sp = &r->ships;
+
+ while (*sp) {
+ ship *sh = *sp;
+ freset(sh, SF_DAMAGED);
+ if (sh->damage >= sh->size * DAMAGE_SCALE) {
+ remove_ship(sp, sh);
+ }
+ if (*sp == sh)
+ sp = &sh->next;
+ }
+ }
+
+ reorder_fleeing(r);
+
+ if (bdebug) {
+ fprintf(bdebug, "The battle lasted %d turns, %s and %s.\n",
+ b->turn,
+ b->has_tactics_turn ? "had a tactic turn" : "had no tactic turn",
+ ships_damaged ? "was relevant" : "was not relevant.");
+ }
+}
+
+static void battle_punit(unit * u, battle * b)
+{
+ bfaction *bf;
+ strlist *S, *x;
+
+ for (bf = b->factions; bf; bf = bf->next) {
+ faction *f = bf->faction;
+
+ S = 0;
+ spunit(&S, f, u, 4, see_battle);
+ for (x = S; x; x = x->next) {
+ fbattlerecord(b, f, x->s);
+ if (bdebug && u->faction == f) {
+ fputs(x->s, bdebug);
+ fputc('\n', bdebug);
+ }
+ }
+ if (S)
+ freestrlist(S);
+ }
+}
+
+static void print_fighters(battle * b, const side * s)
+{
+ fighter *df;
+ int row;
+
+ for (row = 1; row != NUMROWS; ++row) {
+ message *m = NULL;
+
+ for (df = s->fighters; df; df = df->next) {
+ unit *du = df->unit;
+ int thisrow = statusrow(df->unit->status);
+
+ if (row == thisrow) {
+ if (m == NULL) {
+ m = msg_message("battle::row_header", "row", row);
+ message_all(b, m);
+ }
+ battle_punit(du, b);
+ }
+ }
+ if (m != NULL)
+ msg_release(m);
+ }
+}
+
+bool is_attacker(const fighter * fig)
+{
+ return fval(fig, FIG_ATTACKER) != 0;
+}
+
+static void set_attacker(fighter * fig)
+{
+ fset(fig, FIG_ATTACKER);
+}
+
+static void print_header(battle * b)
+{
+ bfaction *bf;
+ char zText[32 * MAXSIDES];
+
+ for (bf = b->factions; bf; bf = bf->next) {
+ message *m;
+ faction *f = bf->faction;
+ const char *lastf = NULL;
+ bool first = false;
+ side *s;
+ char *bufp = zText;
+ size_t size = sizeof(zText) - 1;
+ int bytes;
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *df;
+ for (df = s->fighters; df; df = df->next) {
+ if (is_attacker(df)) {
+ if (first) {
+ bytes = (int)strlcpy(bufp, ", ", size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ }
+ if (lastf) {
+ bytes = (int)strlcpy(bufp, (const char *)lastf, size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ first = true;
+ }
+ if (seematrix(f, s))
+ lastf = sidename(s);
+ else
+ lastf = LOC(f->locale, "unknown_faction_dative");
+ break;
+ }
+ }
+ }
+ if (first) {
+ bytes = (int)strlcpy(bufp, " ", size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ bytes = (int)strlcpy(bufp, (const char *)LOC(f->locale, "and"), size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ bytes = (int)strlcpy(bufp, " ", size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ }
+ if (lastf) {
+ bytes = (int)strlcpy(bufp, (const char *)lastf, size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ }
+
+ m = msg_message("battle::starters", "factions", zText);
+ message_faction(b, f, m);
+ msg_release(m);
+ }
+}
+
+static void print_stats(battle * b)
+{
+ side *s2;
+ side *s;
+ int i = 0;
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ bfaction *bf;
+
+ ++i;
+
+ for (bf = b->factions; bf; bf = bf->next) {
+ faction *f = bf->faction;
+ const char *loc_army = LOC(f->locale, "battle_army");
+ char *bufp;
+ const char *header;
+ size_t rsize, size;
+ int komma;
+ const char *sname =
+ seematrix(f, s) ? sidename(s) : LOC(f->locale, "unknown_faction");
+ message *msg;
+ char buf[1024];
+
+ message_faction(b, f, msg_separator);
+
+ msg = msg_message("battle_army", "index name", army_index(s), sname);
+ message_faction(b, f, msg);
+ msg_release(msg);
+
+ bufp = buf;
+ size = sizeof(buf);
+ komma = 0;
+ header = LOC(f->locale, "battle_opponents");
+
+ for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
+ if (enemy(s2, s)) {
+ const char *abbrev = seematrix(f, s2) ? sideabkz(s2, false) : "-?-";
+ rsize = slprintf(bufp, size, "%s %s %d(%s)",
+ komma++ ? "," : (const char *)header, loc_army, army_index(s2),
+ abbrev);
+ if (rsize > size)
+ rsize = size - 1;
+ size -= rsize;
+ bufp += rsize;
+ }
+ }
+ if (komma)
+ fbattlerecord(b, f, buf);
+
+ bufp = buf;
+ size = sizeof(buf);
+ komma = 0;
+ header = LOC(f->locale, "battle_helpers");
+
+ for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
+ if (friendly(s2, s)) {
+ const char *abbrev = seematrix(f, s2) ? sideabkz(s2, false) : "-?-";
+ rsize = slprintf(bufp, size, "%s %s %d(%s)",
+ komma++ ? "," : (const char *)header, loc_army, army_index(s2),
+ abbrev);
+ if (rsize > size)
+ rsize = size - 1;
+ size -= rsize;
+ bufp += rsize;
+ }
+ }
+ if (komma)
+ fbattlerecord(b, f, buf);
+
+ bufp = buf;
+ size = sizeof(buf);
+ komma = 0;
+ header = LOC(f->locale, "battle_attack");
+
+ for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
+ if (s->relations[s2->index] & E_ATTACKING) {
+ const char *abbrev = seematrix(f, s2) ? sideabkz(s2, false) : "-?-";
+ rsize =
+ slprintf(bufp, size, "%s %s %d(%s)",
+ komma++ ? "," : (const char *)header, loc_army, army_index(s2),
+ abbrev);
+ if (rsize > size)
+ rsize = size - 1;
+ size -= rsize;
+ bufp += rsize;
+ }
+ }
+ if (komma)
+ fbattlerecord(b, f, buf);
+ }
+
+ if (bdebug && s->faction) {
+ if (f_get_alliance(s->faction)) {
+ fprintf(bdebug, "##### %s (%s/%d)\n", s->faction->name,
+ itoa36(s->faction->no),
+ s->faction->alliance ? s->faction->alliance->id : 0);
+ }
+ else {
+ fprintf(bdebug, "##### %s (%s)\n", s->faction->name,
+ itoa36(s->faction->no));
+ }
+ }
+ print_fighters(b, s);
+ }
+
+ message_all(b, msg_separator);
+
+ /* Besten Taktiker ermitteln */
+
+ b->max_tactics = 0;
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ if (!ql_empty(s->leader.fighters)) {
+ b->max_tactics = _max(b->max_tactics, s->leader.value);
+ }
+ }
+
+ if (b->max_tactics > 0) {
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ if (s->leader.value == b->max_tactics) {
+ quicklist *ql;
+ int qi;
+
+ for (qi = 0, ql = s->leader.fighters; ql; ql_advance(&ql, &qi, 1)) {
+ fighter *tf = (fighter *)ql_get(ql, qi);
+ unit *u = tf->unit;
+ message *m = NULL;
+ if (!is_attacker(tf)) {
+ m = msg_message("battle::tactics_lost", "unit", u);
+ }
+ else {
+ m = msg_message("battle::tactics_won", "unit", u);
+ }
+ message_all(b, m);
+ msg_release(m);
+ }
+ }
+ }
+ }
+}
+
+static int weapon_weight(const weapon * w, bool missile)
+{
+ if (missile == i2b(fval(w->type, WTF_MISSILE))) {
+ return w->attackskill + w->defenseskill;
+ }
+ return 0;
+}
+
+side * get_side(battle * b, const struct unit * u)
+{
+ side * s;
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ if (s->faction == u->faction) {
+ fighter * fig;
+ for (fig = s->fighters; fig; fig = fig->next) {
+ if (fig->unit == u) {
+ return s;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+side * find_side(battle * b, const faction * f, const group * g, int flags, const faction * stealthfaction)
+{
+ side * s;
+ static int rule_anon_battle = -1;
+
+ if (rule_anon_battle < 0) {
+ rule_anon_battle = get_param_int(global.parameters, "rules.stealth.anon_battle", 1);
+ }
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ if (s->faction == f && s->group == g) {
+ int s1flags = flags | SIDE_HASGUARDS;
+ int s2flags = s->flags | SIDE_HASGUARDS;
+ if (rule_anon_battle && s->stealthfaction != stealthfaction) {
+ continue;
+ }
+ if (s1flags == s2flags) {
+ return s;
+ }
+ }
+ }
+ return 0;
+}
+
+fighter *make_fighter(battle * b, unit * u, side * s1, bool attack)
+{
+#define WMAX 20
+ weapon weapons[WMAX];
+ int owp[WMAX];
+ int dwp[WMAX];
+ int w = 0;
+ region *r = b->region;
+ item *itm;
+ fighter *fig = NULL;
+ int h, i, tactics = eff_skill(u, SK_TACTICS, r);
+ int berserk;
+ int strongmen;
+ int speeded = 0, speed = 1;
+ bool pr_aid = false;
+ int rest;
+ const group *g = NULL;
+ const attrib *a = a_find(u->attribs, &at_otherfaction);
+ const faction *stealthfaction = a ? get_otherfaction(a) : NULL;
+ unsigned int flags = 0;
+
+ assert(u->number);
+ if (fval(u, UFL_ANON_FACTION) != 0)
+ flags |= SIDE_STEALTH;
+ if (!(AllianceAuto() & HELP_FIGHT) && fval(u, UFL_GROUP)) {
+ const attrib *agroup = a_find(u->attribs, &at_group);
+ if (agroup != NULL)
+ g = (const group *)agroup->data.v;
+ }
+
+ /* Illusionen und Zauber kaempfen nicht */
+ if (fval(u_race(u), RCF_ILLUSIONARY) || idle(u->faction) || u->number == 0) {
+ return NULL;
+ }
+ if (s1 == NULL) {
+ s1 = find_side(b, u->faction, g, flags, stealthfaction);
+ /* aliances are moved out of make_fighter and will be handled later */
+ if (!s1) {
+ s1 = make_side(b, u->faction, g, flags, stealthfaction);
+ }
+ else if (!stealthfaction) {
+ s1->stealthfaction = NULL;
+ }
+ /* Zu diesem Zeitpunkt ist attacked noch 0, da die Einheit für noch
+ * keinen Kampf ausgewählt wurde (sonst würde ein fighter existieren) */
+ }
+ fig = (struct fighter*)calloc(1, sizeof(struct fighter));
+
+ fig->next = s1->fighters;
+ s1->fighters = fig;
+
+ fig->unit = u;
+ /* In einer Burg muß man a) nicht Angreifer sein, und b) drin sein, und
+ * c) noch Platz finden. d) menschanähnlich sein */
+ if (attack) {
+ set_attacker(fig);
+ }
+ else {
+ building *bld = u->building;
+ if (bld && bld->sizeleft >= u->number && playerrace(u_race(u))) {
+ fig->building = bld;
+ fig->building->sizeleft -= u->number;
+ }
+ }
+ fig->status = u->status;
+ fig->side = s1;
+ fig->alive = u->number;
+ fig->side->alive += u->number;
+ fig->side->battle->alive += u->number;
+ fig->catmsg = -1;
+
+ /* Freigeben nicht vergessen! */
+ fig->person = (struct person*)calloc(fig->alive, sizeof(struct person));
+
+ h = u->hp / u->number;
+ assert(h);
+ rest = u->hp % u->number;
+
+ /* Effekte von Sprüchen */
+
+ {
+ static const curse_type *speed_ct;
+ speed_ct = ct_find("speed");
+ if (speed_ct) {
+ curse *c = get_curse(u->attribs, speed_ct);
+ if (c) {
+ speeded = get_cursedmen(u, c);
+ speed = curse_geteffect_int(c);
+ }
+ }
+ }
+
+ /* Effekte von Alchemie */
+ berserk = get_effect(u, oldpotiontype[P_BERSERK]);
+ /* change_effect wird in ageing gemacht */
+
+ /* Effekte von Artefakten */
+ strongmen = _min(fig->unit->number, trollbelts(u));
+
+ /* Hitpoints, Attack- und Defence-Boni für alle Personen */
+ for (i = 0; i < fig->alive; i++) {
+ assert(i < fig->unit->number);
+ fig->person[i].hp = h;
+ if (i < rest)
+ fig->person[i].hp++;
+
+ if (i < speeded)
+ fig->person[i].speed = speed;
+ else
+ fig->person[i].speed = 1;
+
+ if (i < berserk) {
+ fig->person[i].attack++;
+ }
+ /* Leute mit einem Aid-Prayer bekommen +1 auf fast alles. */
+ if (pr_aid) {
+ fig->person[i].attack++;
+ fig->person[i].defence++;
+ fig->person[i].damage++;
+ fig->person[i].damage_rear++;
+ fig->person[i].flags |= FL_COURAGE;
+ }
+ /* Leute mit Kraftzauber machen +2 Schaden im Nahkampf. */
+ if (i < strongmen) {
+ fig->person[i].damage += 2;
+ }
+ }
+
+ /* Für alle Waffengattungen wird bestimmt, wie viele der Personen mit
+ * ihr kämpfen könnten, und was ihr Wert darin ist. */
+ if (u_race(u)->battle_flags & BF_EQUIPMENT) {
+ int oi = 0, di = 0;
+ for (itm = u->items; itm && w != WMAX; itm = itm->next) {
+ const weapon_type *wtype = resource2weapon(itm->type->rtype);
+ if (wtype == NULL || itm->number == 0)
+ continue;
+ weapons[w].attackskill = weapon_skill(wtype, u, true);
+ weapons[w].defenseskill = weapon_skill(wtype, u, false);
+ if (weapons[w].attackskill >= 0 || weapons[w].defenseskill >= 0) {
+ weapons[w].type = wtype;
+ weapons[w].used = 0;
+ weapons[w].count = itm->number;
+ ++w;
+ }
+ assert(w != WMAX);
+ }
+ fig->weapons = (weapon *)calloc(sizeof(weapon), w + 1);
+ memcpy(fig->weapons, weapons, w * sizeof(weapon));
+
+ for (i = 0; i != w; ++i) {
+ int j, o = 0, d = 0;
+ for (j = 0; j != i; ++j) {
+ if (weapon_weight(fig->weapons + j,
+ true) >= weapon_weight(fig->weapons + i, true))
+ ++d;
+ if (weapon_weight(fig->weapons + j,
+ false) >= weapon_weight(fig->weapons + i, false))
+ ++o;
+ }
+ for (j = i + 1; j != w; ++j) {
+ if (weapon_weight(fig->weapons + j,
+ true) > weapon_weight(fig->weapons + i, true))
+ ++d;
+ if (weapon_weight(fig->weapons + j,
+ false) > weapon_weight(fig->weapons + i, false))
+ ++o;
+ }
+ owp[o] = i;
+ dwp[d] = i;
+ }
+ /* jetzt enthalten owp und dwp eine absteigend schlechter werdende Liste der Waffen
+ * oi and di are the current index to the sorted owp/dwp arrays
+ * owp, dwp contain indices to the figther::weapons array */
+
+ /* hand out melee weapons: */
+ for (i = 0; i != fig->alive; ++i) {
+ int wpless = weapon_skill(NULL, u, true);
+ while (oi != w
+ && (fig->weapons[owp[oi]].used == fig->weapons[owp[oi]].count
+ || fval(fig->weapons[owp[oi]].type, WTF_MISSILE))) {
+ ++oi;
+ }
+ if (oi == w)
+ break; /* no more weapons available */
+ if (weapon_weight(fig->weapons + owp[oi], false) <= wpless) {
+ continue; /* we fight better with bare hands */
+ }
+ fig->person[i].melee = &fig->weapons[owp[oi]];
+ ++fig->weapons[owp[oi]].used;
+ }
+ /* hand out missile weapons (from back to front, in case of mixed troops). */
+ for (di = 0, i = fig->alive; i-- != 0;) {
+ while (di != w
+ && (fig->weapons[dwp[di]].used == fig->weapons[dwp[di]].count
+ || !fval(fig->weapons[dwp[di]].type, WTF_MISSILE))) {
+ ++di;
+ }
+ if (di == w)
+ break; /* no more weapons available */
+ if (weapon_weight(fig->weapons + dwp[di], true) > 0) {
+ fig->person[i].missile = &fig->weapons[dwp[di]];
+ ++fig->weapons[dwp[di]].used;
+ }
+ }
+ }
+
+ s1->size[statusrow(fig->status)] += u->number;
+ s1->size[SUM_ROW] += u->number;
+ if (u_race(u)->battle_flags & BF_NOBLOCK) {
+ s1->nonblockers[statusrow(fig->status)] += u->number;
+ }
+
+ if (u_race(fig->unit)->flags & RCF_HORSE) {
+ fig->horses = fig->unit->number;
+ fig->elvenhorses = 0;
+ }
+ else {
+ const resource_type *rt_horse = 0;
+ const resource_type *rt_elvenhorse = 0;
+ rt_elvenhorse = get_resourcetype(R_UNICORN);
+ rt_horse = get_resourcetype(R_CHARGER);
+ if (!rt_horse) {
+ rt_horse = get_resourcetype(R_HORSE);
+ }
+ fig->horses = rt_horse ? i_get(u->items, rt_horse->itype) : 0;
+ fig->elvenhorses = rt_elvenhorse ? i_get(u->items, rt_elvenhorse->itype) : 0;
+ }
+
+ if (u_race(u)->battle_flags & BF_EQUIPMENT) {
+ for (itm = u->items; itm; itm = itm->next) {
+ if (itm->type->rtype->atype) {
+ if (i_canuse(u, itm->type)) {
+ struct armor *adata = (struct armor *)malloc(sizeof(armor)), **aptr;
+ adata->atype = itm->type->rtype->atype;
+ adata->count = itm->number;
+ for (aptr = &fig->armors; *aptr; aptr = &(*aptr)->next) {
+ if (adata->atype->prot > (*aptr)->atype->prot)
+ break;
+ }
+ adata->next = *aptr;
+ *aptr = adata;
+ }
+ }
+ }
+ }
+
+ /* Jetzt muß noch geschaut werden, wo die Einheit die jeweils besten
+ * Werte hat, das kommt aber erst irgendwo später. Ich entscheide
+ * wärend des Kampfes, welche ich nehme, je nach Gegner. Deswegen auch
+ * keine addierten boni. */
+
+ /* Zuerst mal die Spezialbehandlung gewisser Sonderfälle. */
+ fig->magic = eff_skill(u, SK_MAGIC, r);
+
+ if (fig->horses) {
+ if (!fval(r->terrain, CAVALRY_REGION) || r_isforest(r)
+ || eff_skill(u, SK_RIDING, r) < CavalrySkill()
+ || u_race(u) == get_race(RC_TROLL) || fval(u, UFL_WERE))
+ fig->horses = 0;
+ }
+
+ if (fig->elvenhorses) {
+ if (eff_skill(u, SK_RIDING, r) < 5 || u_race(u) == get_race(RC_TROLL)
+ || fval(u, UFL_WERE))
+ fig->elvenhorses = 0;
+ }
+
+ /* Schauen, wie gut wir in Taktik sind. */
+ if (tactics > 0 && u_race(u) == get_race(RC_INSECT))
+ tactics -= 1 - (int)log10(fig->side->size[SUM_ROW]);
+#ifdef TACTICS_MODIFIER
+ if (tactics > 0 && statusrow(fig->status) == FIGHT_ROW)
+ tactics += TACTICS_MODIFIER;
+ if (tactics > 0 && statusrow(fig->status) > BEHIND_ROW) {
+ tactics -= TACTICS_MODIFIER;
+ }
+#endif
+
+ if (tactics > 0) {
+ int bonus = 0;
+
+ for (i = 0; i < fig->alive; i++) {
+ int p_bonus = 0;
+ int rnd;
+
+ do {
+ rnd = rng_int() % 100;
+ if (rnd >= 40 && rnd <= 69)
+ p_bonus += 1;
+ else if (rnd <= 89)
+ p_bonus += 2;
+ else
+ p_bonus += 3;
+ } while (rnd >= 97);
+ bonus = _max(p_bonus, bonus);
+ }
+ tactics += bonus;
+ }
+
+ add_tactics(&fig->side->leader, fig, tactics);
+ ++b->nfighters;
+ return fig;
+}
+
+fighter * get_fighter(battle * b, const struct unit * u)
+{
+ side * s;
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+ if (s->faction == u->faction) {
+ for (fig = s->fighters; fig; fig = fig->next) {
+ if (fig->unit == u) {
+ return fig;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int join_battle(battle * b, unit * u, bool attack, fighter ** cp)
+{
+ side *s;
+ fighter *c = NULL;
+
+ if (!attack) {
+ attrib *a = a_find(u->attribs, &at_fleechance);
+ if (a != NULL) {
+ if (rng_double() <= a->data.flt) {
+ *cp = NULL;
+ return false;
+ }
+ }
+ }
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+ if (s->faction == u->faction) {
+ for (fig = s->fighters; fig; fig = fig->next) {
+ if (fig->unit == u) {
+ c = fig;
+ if (attack) {
+ set_attacker(fig);
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (!c) {
+ *cp = make_fighter(b, u, NULL, attack);
+ return *cp != NULL;
+ }
+ *cp = c;
+ return false;
+}
+
+static const char *simplename(region * r)
+{
+ int i;
+ static char name[17];
+ const char *cp = rname(r, default_locale);
+ for (i = 0; *cp && i != 16; ++i, ++cp) {
+ int c = *(unsigned char *)cp;
+ while (c && !isalpha(c) && !isxspace(c)) {
+ ++cp;
+ c = *(unsigned char *)cp;
+ }
+ if (isxspace(c))
+ name[i] = '_';
+ else
+ name[i] = *cp;
+ if (c == 0)
+ break;
+ }
+ name[i] = 0;
+ return name;
+}
+
+battle *make_battle(region * r)
+{
+ battle *b = (battle *)calloc(1, sizeof(battle));
+ unit *u;
+ bfaction *bf;
+ building * bld;
+ static int max_fac_no = 0; /* need this only once */
+
+ /* Alle Mann raus aus der Burg! */
+ for (bld = r->buildings; bld != NULL; bld = bld->next)
+ bld->sizeleft = bld->size;
+
+ if (battledebug) {
+ char zText[MAX_PATH];
+ char zFilename[MAX_PATH];
+ sprintf(zText, "%s/battles", basepath());
+ _mkdir(zText);
+ sprintf(zFilename, "%s/battle-%d-%s.log", zText, obs_count, simplename(r));
+ bdebug = fopen(zFilename, "w");
+ if (!bdebug)
+ log_error("battles cannot be debugged\n");
+ else {
+ const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf, 0 };
+ fwrite(utf8_bom, 1, 3, bdebug);
+ fprintf(bdebug, "In %s findet ein Kampf statt:\n", rname(r,
+ default_locale));
+ }
+ obs_count++;
+ }
+
+ b->region = r;
+ b->plane = getplane(r);
+ /* Finde alle Parteien, die den Kampf beobachten können: */
+ for (u = r->units; u; u = u->next) {
+ if (u->number > 0) {
+ if (!fval(u->faction, FFL_MARK)) {
+ fset(u->faction, FFL_MARK);
+ for (bf = b->factions; bf; bf = bf->next) {
+ if (bf->faction == u->faction)
+ break;
+ }
+ if (!bf) {
+ bf = (bfaction *)calloc(sizeof(bfaction), 1);
+ ++b->nfactions;
+ bf->faction = u->faction;
+ bf->next = b->factions;
+ b->factions = bf;
+ }
+ }
+ }
+ }
+
+ for (bf = b->factions; bf; bf = bf->next) {
+ faction *f = bf->faction;
+ max_fac_no = _max(max_fac_no, f->no);
+ freset(f, FFL_MARK);
+ }
+ return b;
+}
+
+static void free_side(side * si)
+{
+ ql_free(si->leader.fighters);
+}
+
+static void free_fighter(fighter * fig)
+{
+ while (fig->loot) {
+ i_free(i_remove(&fig->loot, fig->loot));
+ }
+ while (fig->armors) {
+ armor *a = fig->armors;
+ fig->armors = a->next;
+ free(a);
+ }
+ free(fig->person);
+ free(fig->weapons);
+
+}
+
+static void free_battle(battle * b)
+{
+ int max_fac_no = 0;
+
+ if (bdebug) {
+ fclose(bdebug);
+ }
+
+ while (b->factions) {
+ bfaction *bf = b->factions;
+ faction *f = bf->faction;
+ b->factions = bf->next;
+ max_fac_no = _max(max_fac_no, f->no);
+ free(bf);
+ }
+
+ ql_free(b->leaders);
+ ql_foreach(b->meffects, free);
+ ql_free(b->meffects);
+
+ battle_free(b);
+}
+
+static int *get_alive(side * s)
+{
+#if 0
+ static int alive[NUMROWS];
+ fighter *fig;
+ memset(alive, 0, NUMROWS * sizeof(int));
+ for (fig = s->fighters; fig; fig = fig->next) {
+ if (fig->alive > 0) {
+ int row = statusrow(fig);
+ alive[row] += fig->alive;
+ }
+ }
+ return alive;
+#endif
+ return s->size;
+}
+
+static int battle_report(battle * b)
+{
+ side *s, *s2;
+ bool cont = false;
+ bool komma;
+ bfaction *bf;
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ if (s->alive - s->removed > 0) {
+ for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
+ if (s2->alive - s2->removed > 0 && enemy(s, s2)) {
+ cont = true;
+ break;
+ }
+ }
+ if (cont)
+ break;
+ }
+ }
+
+ if (verbosity > 0)
+ log_printf(stdout, " %d", b->turn);
+ fflush(stdout);
+
+ for (bf = b->factions; bf; bf = bf->next) {
+ faction *fac = bf->faction;
+ char buf[32 * MAXSIDES];
+ char *bufp = buf;
+ int bytes;
+ size_t size = sizeof(buf) - 1;
+ message *m;
+
+ message_faction(b, fac, msg_separator);
+
+ if (cont)
+ m = msg_message("battle::lineup", "turn", b->turn);
+ else
+ m = msg_message("battle::after", "");
+ message_faction(b, fac, m);
+ msg_release(m);
+
+ komma = false;
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ if (s->alive) {
+ int r, k = 0, *alive = get_alive(s);
+ int l = FIGHT_ROW;
+ const char *abbrev = seematrix(fac, s) ? sideabkz(s, false) : "-?-";
+ const char *loc_army = LOC(fac->locale, "battle_army");
+ char buffer[32];
+
+ if (komma) {
+ bytes = (int)strlcpy(bufp, ", ", size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ }
+ slprintf(buffer, sizeof(buffer), "%s %2d(%s): ",
+ loc_army, army_index(s), abbrev);
+
+ bytes = (int)strlcpy(bufp, buffer, size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+
+ for (r = FIGHT_ROW; r != NUMROWS; ++r) {
+ if (alive[r]) {
+ if (l != FIGHT_ROW) {
+ bytes = (int)strlcpy(bufp, "+", size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ }
+ while (k--) {
+ bytes = (int)strlcpy(bufp, "0+", size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+ }
+ sprintf(buffer, "%d", alive[r]);
+
+ bytes = (int)strlcpy(bufp, buffer, size);
+ if (wrptr(&bufp, &size, bytes) != 0)
+ WARN_STATIC_BUFFER();
+
+ k = 0;
+ l = r + 1;
+ }
+ else
+ ++k;
+ }
+
+ komma = true;
+ }
+ }
+ *bufp = 0;
+ fbattlerecord(b, fac, buf);
+ }
+ return cont;
+}
+
+static void join_allies(battle * b)
+{
+ region *r = b->region;
+ unit *u;
+ side *s, *s_end = b->sides + b->nsides;
+ /* make_side might be adding a new faction, but it adds them to the end
+ * of the list, so we're safe in our iteration here if we remember the end
+ * up front. */
+ for (u = r->units; u; u = u->next) {
+ /* Was ist mit Schiffen? */
+ if (u->status != ST_FLEE && u->status != ST_AVOID
+ && !fval(u, UFL_LONGACTION | UFL_ISNEW) && u->number > 0) {
+ faction *f = u->faction;
+ fighter *c = NULL;
+
+ for (s = b->sides; s != s_end; ++s) {
+ side *se;
+ /* Wenn alle attackierten noch FFL_NOAID haben, dann kämpfe nicht mit. */
+ if (fval(s->faction, FFL_NOAID))
+ continue;
+ if (s->faction != f) {
+ /* Wenn wir attackiert haben, kommt niemand mehr hinzu: */
+ if (s->bf->attacker)
+ continue;
+ /* alliiert müssen wir schon sein, sonst ist's eh egal : */
+ if (!alliedunit(u, s->faction, HELP_FIGHT))
+ continue;
+ /* wenn die partei verborgen ist, oder gar eine andere
+ * vorgespiegelt wird, und er sich uns gegenüber nicht zu
+ * erkennen gibt, helfen wir ihm nicht */
+ if (s->stealthfaction) {
+ if (!allysfm(s, u->faction, HELP_FSTEALTH)) {
+ continue;
+ }
+ }
+ }
+ /* einen alliierten angreifen dürfen sie nicht, es sei denn, der
+ * ist mit einem alliierten verfeindet, der nicht attackiert
+ * hat: */
+ for (se = b->sides; se != s_end; ++se) {
+ if (u->faction == se->faction)
+ continue;
+ if (alliedunit(u, se->faction, HELP_FIGHT) && !se->bf->attacker) {
+ continue;
+ }
+ if (enemy(s, se))
+ break;
+ }
+ if (se == s_end)
+ continue;
+ /* Wenn die Einheit belagert ist, muß auch einer der Alliierten belagert sein: */
+ if (besieged(u)) {
+ fighter *ally;
+ for (ally = s->fighters; ally; ally = ally->next) {
+ if (besieged(ally->unit)) {
+ break;
+ }
+ }
+ if (ally == NULL)
+ continue;
+ }
+ /* keine Einwände, also soll er mitmachen: */
+ if (c == NULL) {
+ if (join_battle(b, u, false, &c)) {
+ if (battledebug) {
+ fprintf(bdebug, "%s joins to help %s against %s.\n",
+ unitname(u), factionname(s->faction), factionname(se->faction));
+ }
+ }
+ else if (c == NULL) {
+ continue;
+ }
+ }
+
+ /* the enemy of my friend is my enemy: */
+ for (se = b->sides; se != s_end; ++se) {
+ if (se->faction != u->faction && enemy(s, se)) {
+ if (set_enemy(se, c->side, false) && battledebug) {
+ fprintf(bdebug,
+ "%u/%s hates %u/%s because they are enemies with %u/%s.\n",
+ c->side->index, sidename(c->side), se->index, sidename(se),
+ s->index, sidename(s));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ int si;
+ side *sa;
+ faction *f = s->faction;
+
+ /* Den Feinden meiner Feinde gebe ich Deckung (gegen gemeinsame Feinde): */
+ for (si = 0; s->enemies[si]; ++si) {
+ side *se = s->enemies[si];
+ int ai;
+ for (ai = 0; se->enemies[ai]; ++ai) {
+ side *as = se->enemies[ai];
+ if (as == s || !enemy(as, s)) {
+ set_friendly(as, s);
+ }
+ }
+ }
+
+ for (sa = s + 1; sa != b->sides + b->nsides; ++sa) {
+ plane *pl = rplane(r);
+ if (enemy(s, sa))
+ continue;
+ if (friendly(s, sa))
+ continue;
+ if (!alliedgroup(pl, f, sa->faction, f->allies, HELP_FIGHT))
+ continue;
+ if (!alliedgroup(pl, sa->faction, f, sa->faction->allies, HELP_FIGHT))
+ continue;
+
+ set_friendly(s, sa);
+ }
+ }
+}
+
+static void flee(const troop dt)
+{
+ fighter *fig = dt.fighter;
+ unit *u = fig->unit;
+
+ fig->run.hp += fig->person[dt.index].hp;
+ ++fig->run.number;
+
+ setguard(u, GUARD_NONE);
+
+ kill_troop(dt);
+}
+
+static bool start_battle(region * r, battle ** bp)
+{
+ battle *b = NULL;
+ unit *u;
+ bool fighting = false;
+
+ /* list_foreach geht nicht, wegen flucht */
+ for (u = r->units; u != NULL; u = u->next) {
+ if (fval(u, UFL_LONGACTION))
+ continue;
+ if (u->number > 0) {
+ order *ord;
+
+ for (ord = u->orders; ord; ord = ord->next) {
+ static bool init = false;
+ static const curse_type *peace_ct, *slave_ct, *calm_ct;
+
+ if (!init) {
+ init = true;
+ peace_ct = ct_find("peacezone");
+ slave_ct = ct_find("slavery");
+ calm_ct = ct_find("calmmonster");
+ }
+ if (getkeyword(ord) == K_ATTACK) {
+ unit *u2;
+ fighter *c1, *c2;
+ ship *lsh = NULL;
+ plane *pl = rplane(r);
+
+ if (pl && fval(pl, PFL_NOATTACK)) {
+ cmistake(u, ord, 271, MSG_BATTLE);
+ continue;
+ }
+
+ if ((u_race(u)->battle_flags & BF_CANATTACK) == 0) {
+ ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "race_no_attack",
+ "race", u_race(u)));
+ continue;
+ }
+ /**
+ ** Fehlerbehandlung Angreifer
+ **/
+ if (LongHunger(u)) {
+ cmistake(u, ord, 225, MSG_BATTLE);
+ continue;
+ }
+
+ if (u->status == ST_AVOID || u->status == ST_FLEE) {
+ cmistake(u, ord, 226, MSG_BATTLE);
+ continue;
+ }
+
+ /* ist ein Flüchtling aus einem andern Kampf */
+ if (fval(u, UFL_LONGACTION))
+ continue;
+
+ if (peace_ct && curse_active(get_curse(r->attribs, peace_ct))) {
+ ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "peace_active", ""));
+ continue;
+ }
+
+ if (slave_ct && curse_active(get_curse(u->attribs, slave_ct))) {
+ ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "slave_active", ""));
+ continue;
+ }
+
+ if ((u->ship != NULL && !fval(r->terrain, SEA_REGION))
+ || (lsh = leftship(u)) != NULL) {
+ if (is_guarded(r, u, GUARD_TRAVELTHRU)) {
+ if (lsh) {
+ cmistake(u, ord, 234, MSG_BATTLE);
+ }
+ else {
+ /* Fehler: "Das Schiff muß erst verlassen werden" */
+ cmistake(u, ord, 19, MSG_BATTLE);
+ }
+ continue;
+ }
+ }
+
+ /* Ende Fehlerbehandlung Angreifer */
+
+ init_order(ord);
+ /* attackierte Einheit ermitteln */
+ u2 = getunit(r, u->faction);
+
+ /* Beginn Fehlerbehandlung */
+ /* Fehler: "Die Einheit wurde nicht gefunden" */
+ if (!u2 || u2->number == 0 || !cansee(u->faction, u->region, u2, 0)) {
+ ADDMSG(&u->faction->msgs, msg_feedback(u, ord,
+ "feedback_unit_not_found", ""));
+ continue;
+ }
+ /* Fehler: "Die Einheit ist eine der unsrigen" */
+ if (u2->faction == u->faction) {
+ cmistake(u, ord, 45, MSG_BATTLE);
+ continue;
+ }
+ /* Fehler: "Die Einheit ist mit uns alliert" */
+ if (alliedunit(u, u2->faction, HELP_FIGHT)) {
+ cmistake(u, ord, 47, MSG_BATTLE);
+ continue;
+ }
+ if (IsImmune(u2->faction)) {
+ add_message(&u->faction->msgs,
+ msg_feedback(u, u->thisorder, "newbie_immunity_error", "turns",
+ NewbieImmunity()));
+ continue;
+ }
+ /* Fehler: "Die Einheit ist mit uns alliert" */
+
+ if (calm_ct) {
+ attrib *a = a_find(u->attribs, &at_curse);
+ bool calm = false;
+ while (a && a->type == &at_curse) {
+ curse *c = (curse *)a->data.v;
+ if (c->type == calm_ct
+ && curse_geteffect(c) == u2->faction->subscription) {
+ if (curse_active(c)) {
+ calm = true;
+ break;
+ }
+ }
+ a = a->next;
+ }
+ if (calm) {
+ cmistake(u, ord, 47, MSG_BATTLE);
+ continue;
+ }
+ }
+ /* Ende Fehlerbehandlung */
+ if (b == NULL) {
+ unit *utmp;
+ for (utmp = r->units; utmp != NULL; utmp = utmp->next) {
+ fset(utmp->faction, FFL_NOAID);
+ }
+ b = make_battle(r);
+ }
+ if (join_battle(b, u, true, &c1)) {
+ if (battledebug) {
+ fprintf(bdebug, "%s joins by attacking %s.\n",
+ unitname(u), unitname(u2));
+ }
+ }
+ if (join_battle(b, u2, false, &c2)) {
+ if (battledebug) {
+ fprintf(bdebug, "%s joins because of an attack from %s.\n",
+ unitname(u2), unitname(u));
+ }
+ }
+
+ /* Hat die attackierte Einheit keinen Noaid-Status,
+ * wird das Flag von der Faction genommen, andere
+ * Einheiten greifen ein. */
+ if (!fval(u2, UFL_NOAID))
+ freset(u2->faction, FFL_NOAID);
+
+ if (c1 != NULL && c2 != NULL) {
+ /* Merken, wer Angreifer ist, für die Rückzahlung der
+ * Präcombataura bei kurzem Kampf. */
+ c1->side->bf->attacker = true;
+
+ if (set_enemy(c1->side, c2->side, true) && battledebug) {
+ fprintf(bdebug, "%u/%s hates %u/%s because they attacked them.\n",
+ c2->side->index, sidename(c2->side),
+ c1->side->index, sidename(c1->side));
+ }
+ fighting = true;
+ }
+ }
+ }
+ }
+ }
+ *bp = b;
+ return fighting;
+}
+
+/** execute one round of attacks
+ * fig->fighting is used to determine who attacks, not fig->alive, since
+ * the latter may be influenced by attacks that already took place.
+ */
+static void battle_attacks(battle * b)
+{
+ side *s;
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+
+ if (b->turn != 0 || (b->max_tactics > 0
+ && get_tactics(s, NULL) == b->max_tactics)) {
+ for (fig = s->fighters; fig; fig = fig->next) {
+
+ /* ist in dieser Einheit noch jemand handlungsfähig? */
+ if (fig->fighting <= 0)
+ continue;
+
+ /* Handle the unit's attack on someone */
+ do_attack(fig);
+ }
+ }
+ }
+}
+
+/** updates the number of attacking troops in each fighter struct.
+ * this has to be calculated _before_ the actual attacks take
+ * place because otherwise dead troops would not strike in the
+ * round they die. */
+static void battle_update(battle * b)
+{
+ side *s;
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+ for (fig = s->fighters; fig; fig = fig->next) {
+ fig->fighting = fig->alive - fig->removed;
+ }
+ }
+}
+
+/** attempt to flee from battle before the next round begins
+ * there's a double attempt before the first round, but only
+ * one attempt before round zero, the potential tactics round. */
+static void battle_flee(battle * b)
+{
+ int attempt, flee_ops = 1;
+
+ if (b->turn == 1)
+ flee_ops = 2;
+
+ for (attempt = 1; attempt <= flee_ops; ++attempt) {
+ side *s;
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fig;
+ for (fig = s->fighters; fig; fig = fig->next) {
+ unit *u = fig->unit;
+ troop dt;
+ int runners = 0;
+ /* Flucht nicht bei mehr als 600 HP. Damit Wyrme tötbar bleiben. */
+ int runhp = _min(600, (int)(0.9 + unit_max_hp(u) * hpflee(u->status)));
+
+ if (u->ship && fval(u->region->terrain, SEA_REGION)) {
+ /* keine Flucht von Schiffen auf hoher See */
+ continue;
+ }
+ if (fval(u_race(u), RCF_UNDEAD) || u_race(u) == get_race(RC_SHADOWKNIGHT)) {
+ /* Untote fliehen nicht. Warum eigentlich? */
+ continue;
+ }
+
+ dt.fighter = fig;
+ dt.index = fig->alive - fig->removed;
+ while (s->size[SUM_ROW] && dt.index != 0) {
+ double ispaniced = 0.0;
+ --dt.index;
+ assert(dt.index >= 0 && dt.index < fig->unit->number);
+ assert(fig->person[dt.index].hp > 0);
+
+ /* Versuche zu fliehen, wenn
+ * - Kampfstatus fliehe
+ * - schwer verwundet und nicht erste kampfrunde
+ * - in panik (Zauber)
+ * aber nicht, wenn der Zaubereffekt Held auf dir liegt!
+ */
+ switch (u->status) {
+ case ST_FLEE:
+ break;
+ default:
+ if ((fig->person[dt.index].flags & FL_HIT) == 0)
+ continue;
+ if (b->turn <= 1)
+ continue;
+ if (fig->person[dt.index].hp <= runhp)
+ break;
+ if (fig->person[dt.index].flags & FL_PANICED) {
+ if ((fig->person[dt.index].flags & FL_COURAGE) == 0)
+ break;
+ }
+ continue;
+ }
+
+ if (fig->person[dt.index].flags & FL_PANICED) {
+ ispaniced = EFFECT_PANIC_SPELL;
+ }
+ if (chance(_min(fleechance(u) + ispaniced, 0.90))) {
+ ++runners;
+ flee(dt);
+ }
+ }
+ if (bdebug && runners > 0) {
+ fprintf(bdebug, "Fleeing: %d from %s\n", runners,
+ itoa36(fig->unit->no));
+ }
+ }
+ }
+ }
+}
+
+void do_battle(region * r)
+{
+ battle *b = NULL;
+ bool fighting = false;
+ ship *sh;
+ static int init_rules = 0;
+
+ if (!init_rules) {
+ static_rules();
+ init_rules = 1;
+ }
+ if (msg_separator == NULL) {
+ msg_separator = msg_message("battle::section", "");
+ }
+
+ fighting = start_battle(r, &b);
+
+ if (b == NULL)
+ return;
+
+ /* Bevor wir die alliierten hineinziehen, sollten wir schauen, *
+ * Ob jemand fliehen kann. Dann erübrigt sich das ganze ja
+ * vielleicht schon. */
+ print_header(b);
+ if (!fighting) {
+ /* Niemand mehr da, Kampf kann nicht stattfinden. */
+ message *m = msg_message("battle::aborted", "");
+ message_all(b, m);
+ msg_release(m);
+ free_battle(b);
+ free(b);
+ return;
+ }
+ join_allies(b);
+ make_heroes(b);
+
+ /* make sure no ships are damaged initially */
+ for (sh = r->ships; sh; sh = sh->next)
+ freset(sh, SF_DAMAGED);
+
+ /* Gibt es eine Taktikrunde ? */
+ if (!ql_empty(b->leaders)) {
+ b->turn = 0;
+ b->has_tactics_turn = true;
+ }
+ else {
+ b->turn = 1;
+ b->has_tactics_turn = false;
+ }
+
+ /* PRECOMBATSPELLS */
+ do_combatmagic(b, DO_PRECOMBATSPELL);
+
+ print_stats(b); /* gibt die Kampfaufstellung aus */
+ if (verbosity > 0)
+ log_printf(stdout, "%s (%d, %d) : ", rname(r, default_locale), r->x, r->y);
+
+ for (; battle_report(b) && b->turn <= max_turns; ++b->turn) {
+ if (bdebug) {
+ fprintf(bdebug, "*** Turn: %d\n", b->turn);
+ }
+ battle_flee(b);
+ battle_update(b);
+ battle_attacks(b);
+
+ }
+
+ if (verbosity > 0)
+ log_printf(stdout, "\n");
+
+ /* Auswirkungen berechnen: */
+ aftermath(b);
+ /* Hier ist das Gefecht beendet, und wir können die
+ * Hilfsstrukturen * wieder löschen: */
+
+ if (b) {
+ free_battle(b);
+ free(b);
+ }
+}
+
+void battle_free(battle * b) {
+ side *s;
+
+ assert(b);
+
+ for (s = b->sides; s != b->sides + b->nsides; ++s) {
+ fighter *fnext = s->fighters;
+ while (fnext) {
+ fighter *fig = fnext;
+ fnext = fig->next;
+ free_fighter(fig);
+ free(fig);
+ }
+ free_side(s);
+ }
+}
+
diff --git a/src/kernel/battle.h b/src/battle.h
similarity index 100%
rename from src/kernel/battle.h
rename to src/battle.h
diff --git a/src/kernel/battle.test.c b/src/battle.test.c
similarity index 96%
rename from src/kernel/battle.test.c
rename to src/battle.test.c
index efe1d8623..47e0d69d8 100644
--- a/src/kernel/battle.test.c
+++ b/src/battle.test.c
@@ -1,13 +1,15 @@
#include
#include
+
#include "battle.h"
-#include "building.h"
-#include "faction.h"
-#include "item.h"
-#include "race.h"
-#include "region.h"
#include "skill.h"
-#include "unit.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
#include
#include "tests.h"
diff --git a/src/bind_message.c b/src/bind_message.c
index 4b1b8069d..07fb471dc 100644
--- a/src/bind_message.c
+++ b/src/bind_message.c
@@ -1,11 +1,12 @@
#include
#include
+#include "spells.h"
+
/* kernel includes */
#include
#include
#include
-#include
#include
#include
diff --git a/src/bind_process.c b/src/bind_process.c
index e6018e69f..3bcfd835b 100755
--- a/src/bind_process.c
+++ b/src/bind_process.c
@@ -6,11 +6,11 @@
#include
#include
#include
-#include
#include
#include
#include
-#include
+#include "battle.h"
+#include "move.h"
#include "economy.h"
#include "laws.h"
#include "market.h"
diff --git a/src/bind_ship.c b/src/bind_ship.c
index d38857b20..a089ab084 100644
--- a/src/bind_ship.c
+++ b/src/bind_ship.c
@@ -11,12 +11,14 @@ without prior permission by the authors of Eressea.
*/
#include
+#include
#include "bind_ship.h"
#include "bind_unit.h"
+#include "move.h"
+
#include
#include
-#include
#include
#include
diff --git a/src/bind_unit.c b/src/bind_unit.c
index 66be03a0c..a7dc1cab5 100755
--- a/src/bind_unit.c
+++ b/src/bind_unit.c
@@ -17,14 +17,15 @@ without prior permission by the authors of Eressea.
#ifdef BSON_ATTRIB
# include "bind_attrib.h"
#endif
+#include "alchemy.h"
#include "bindings.h"
+#include "move.h"
/* attributes includes */
#include
#include
/* kernel includes */
-#include
#include
#include
#include
@@ -32,7 +33,6 @@ without prior permission by the authors of Eressea.
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/bindings.c b/src/bindings.c
index 140e738d3..94caa2d42 100755
--- a/src/bindings.c
+++ b/src/bindings.c
@@ -11,6 +11,7 @@ without prior permission by the authors of Eressea.
*/
#include
+#include
#include "bindings.h"
#include "bind_unit.h"
#include "bind_storage.h"
@@ -24,6 +25,7 @@ without prior permission by the authors of Eressea.
#include "bind_region.h"
#include "helpers.h"
#include "console.h"
+#include "reports.h"
#include
@@ -36,7 +38,6 @@ without prior permission by the authors of Eressea.
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/creation.c b/src/creation.c
index eb4e2fde6..aa555d708 100644
--- a/src/creation.c
+++ b/src/creation.c
@@ -20,9 +20,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include "creation.h"
#include "monster.h"
+#include "alchemy.h"
/* kernel includes */
-#include
#include
#include
#include
diff --git a/src/creport.c b/src/creport.c
index 278c96bd7..a56c28888 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -30,9 +30,12 @@ without prior permission by the authors of Eressea.
/* gamecode includes */
#include "laws.h"
#include "economy.h"
+#include "stealth.h"
+#include "move.h"
+#include "reports.h"
+#include "alchemy.h"
/* kernel includes */
-#include
#include
#include
#include
@@ -42,12 +45,10 @@ without prior permission by the authors of Eressea.
#include
#include
#include
-#include
#include
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/direction.test.c b/src/direction.test.c
index b9bb06d65..e47ab5034 100644
--- a/src/direction.test.c
+++ b/src/direction.test.c
@@ -1,9 +1,11 @@
#include
-#include "kernel/types.h"
+#include
+
#include "direction.h"
-#include "util/language.h"
#include "tests.h"
+#include
+
#include
static void test_init_directions(CuTest *tc) {
diff --git a/src/economy.c b/src/economy.c
index 271482f89..49f4153a6 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -22,14 +22,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include "economy.h"
+#include "alchemy.h"
#include "direction.h"
#include "give.h"
#include "laws.h"
#include "randenc.h"
#include "spy.h"
+#include "move.h"
+#include "reports.h"
/* kernel includes */
-#include
#include
#include
#include
@@ -38,13 +40,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include
-#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/eressea.c b/src/eressea.c
index f62c601a6..a0d16ebd1 100755
--- a/src/eressea.c
+++ b/src/eressea.c
@@ -14,10 +14,7 @@
#include
#include
#include
-#include
#include
-#include
-#include
#include
#include
#include
@@ -27,6 +24,7 @@
#include "report.h"
#include "items.h"
#include "creport.h"
+#include "names.h"
void game_done(void)
{
diff --git a/src/give.c b/src/give.c
index feb28a877..a3dd08f98 100644
--- a/src/give.c
+++ b/src/give.c
@@ -26,7 +26,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/gmtool.c b/src/gmtool.c
index 53cb33efd..ec1a77ab3 100644
--- a/src/gmtool.c
+++ b/src/gmtool.c
@@ -39,7 +39,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/helpers.c b/src/helpers.c
index 8ec55e7de..5f0b3a12d 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -10,8 +10,9 @@ This program may not be used, modified or distributed
without prior permission by the authors of Eressea.
*/
-#include "helpers.h"
#include
+#include "helpers.h"
+#include "vortex.h"
#include
#include
@@ -541,6 +542,7 @@ int tolua_toid(lua_State * L, int idx, int def)
void register_tolua_helpers(void)
{
+ at_register(&at_direction);
at_register(&at_building_action);
register_function((pf_generic) & lua_building_protection,
diff --git a/src/items.c b/src/items.c
index 983284ea8..0ce2ecb8e 100644
--- a/src/items.c
+++ b/src/items.c
@@ -3,6 +3,7 @@
#include "items.h"
#include "study.h"
+#include "move.h"
#include
#include
@@ -10,7 +11,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/items/speedsail.c b/src/items/speedsail.c
index 254bc1e6a..8f76cca00 100644
--- a/src/items/speedsail.c
+++ b/src/items/speedsail.c
@@ -24,12 +24,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include
-#include
#include
#include
#include
#include
+#include
+
/* util includes */
#include
#include
diff --git a/src/items/weapons.c b/src/items/weapons.c
index 5dab42fa3..a7114d321 100644
--- a/src/items/weapons.c
+++ b/src/items/weapons.c
@@ -19,13 +19,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include "weapons.h"
+#include "battle.h"
#include
#include
#include
#include
#include
-#include
#include
/* util includes */
diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt
index b2bdece72..9566317e7 100644
--- a/src/kernel/CMakeLists.txt
+++ b/src/kernel/CMakeLists.txt
@@ -4,31 +4,27 @@ SET(_TEST_FILES
build.test.c
config.test.c
faction.test.c
+unit.test.c
save.test.c
ship.test.c
spell.test.c
ally.test.c
-battle.test.c
building.test.c
magic.test.c
equipment.test.c
curse.test.c
item.test.c
-move.test.c
order.test.c
pool.test.c
race.test.c
-reports.test.c
spellbook.test.c
curse.test.c
jsonconf.test.c
)
SET(_FILES
-alchemy.c
alliance.c
ally.c
-battle.c
build.c
building.c
calendar.c
@@ -42,15 +38,12 @@ group.c
item.c
magic.c
messages.c
-move.c
-names.c
order.c
pathfinder.c
plane.c
pool.c
race.c
region.c
-reports.c
resources.c
save.c
ship.c
diff --git a/src/kernel/battle.c b/src/kernel/battle.c
deleted file mode 100644
index 11d4aeb02..000000000
--- a/src/kernel/battle.c
+++ /dev/null
@@ -1,4280 +0,0 @@
-/*
-Copyright (c) 1998-2010, Enno Rehling
- Katja Zedel
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-**/
-
-#include
-#include
-#include "battle.h"
-
-#include "alchemy.h"
-#include "alliance.h"
-#include "build.h"
-#include "building.h"
-#include "curse.h"
-#include "equipment.h"
-#include "faction.h"
-#include "group.h"
-#include "item.h"
-#include "magic.h"
-#include "messages.h"
-#include "move.h"
-#include "names.h"
-#include "order.h"
-#include "plane.h"
-#include "race.h"
-#include "region.h"
-#include "reports.h"
-#include "ship.h"
-#include "skill.h"
-#include "spell.h"
-#include "terrain.h"
-#include "unit.h"
-
-/* attributes includes */
-#include
-#include
-#include
-#include
-#include
-
-/* util includes */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* libc includes */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-static FILE *bdebug;
-
-#define TACTICS_BONUS 1 /* when undefined, we have a tactics round. else this is the bonus tactics give */
-#define TACTICS_MODIFIER 1 /* modifier for generals in the front/rear */
-
-#define CATAPULT_INITIAL_RELOAD 4 /* erster schuss in runde 1 + rng_int() % INITIAL */
-#define CATAPULT_STRUCTURAL_DAMAGE
-
-#define BASE_CHANCE 70 /* 70% Basis-Überlebenschance */
-#ifdef NEW_COMBATSKILLS_RULE
-#define TDIFF_CHANGE 5 /* 5% höher pro Stufe */
-#define DAMAGE_QUOTIENT 2 /* damage += skilldiff/DAMAGE_QUOTIENT */
-#else
-#define TDIFF_CHANGE 10
-# define DAMAGE_QUOTIENT 1 /* damage += skilldiff/DAMAGE_QUOTIENT */
-#endif
-
-#undef DEBUG_FAST /* should be disabled when b->fast and b->rowcache works */
-#define DEBUG_SELECT /* should be disabled if select_enemy works */
-
-typedef enum combatmagic {
- DO_PRECOMBATSPELL,
- DO_POSTCOMBATSPELL
-} combatmagic_t;
-
-/* globals */
-static int obs_count = 0;
-
-#define MINSPELLRANGE 1
-#define MAXSPELLRANGE 7
-
-#ifndef ROW_FACTOR
-# define ROW_FACTOR 10
-#endif
-#define EFFECT_PANIC_SPELL 0.25
-#define TROLL_REGENERATION 0.10
-
-/* Nach dem alten System: */
-static int missile_range[2] = { FIGHT_ROW, BEHIND_ROW };
-static int melee_range[2] = { FIGHT_ROW, FIGHT_ROW };
-
-static message *msg_separator;
-
-const troop no_troop = { 0, 0 };
-
-static int max_turns = 0;
-static int damage_rules = 0;
-static int loot_rules = 0;
-static int skill_formula = 0;
-
-#define FORMULA_ORIG 0
-#define FORMULA_NEW 1
-
-#define LOOT_MONSTERS (1<<0)
-#define LOOT_SELF (1<<1) /* code is mutually exclusive with LOOT_OTHERS */
-#define LOOT_OTHERS (1<<2)
-#define LOOT_KEEPLOOT (1<<4)
-
-#define DAMAGE_CRITICAL (1<<0)
-#define DAMAGE_MELEE_BONUS (1<<1)
-#define DAMAGE_MISSILE_BONUS (1<<2)
-#define DAMAGE_UNARMED_BONUS (1<<3)
-#define DAMAGE_SKILL_BONUS (1<<4)
-/** initialize rules from configuration.
- */
-static void static_rules(void)
-{
- loot_rules =
- get_param_int(global.parameters, "rules.combat.loot",
- LOOT_MONSTERS | LOOT_OTHERS | LOOT_KEEPLOOT);
- /* new formula to calculate to-hit-chance */
- skill_formula =
- get_param_int(global.parameters, "rules.combat.skill_formula",
- FORMULA_ORIG);
- /* maximum number of combat turns */
- max_turns =
- get_param_int(global.parameters, "rules.combat.turns", COMBAT_TURNS);
- /* damage calculation */
- if (get_param_int(global.parameters, "rules.combat.critical", 1)) {
- damage_rules |= DAMAGE_CRITICAL;
- }
- if (get_param_int(global.parameters, "rules.combat.melee_bonus", 1)) {
- damage_rules |= DAMAGE_MELEE_BONUS;
- }
- if (get_param_int(global.parameters, "rules.combat.missile_bonus", 1)) {
- damage_rules |= DAMAGE_MISSILE_BONUS;
- }
- if (get_param_int(global.parameters, "rules.combat.unarmed_bonus", 1)) {
- damage_rules |= DAMAGE_UNARMED_BONUS;
- }
- if (get_param_int(global.parameters, "rules.combat.skill_bonus", 1)) {
- damage_rules |= DAMAGE_SKILL_BONUS;
- }
-}
-
-static int army_index(side * s)
-{
- return s->index;
-}
-
-static char *sidename(side * s)
-{
-#define SIDENAMEBUFLEN 256
- static int bufno; /* STATIC_XCALL: used across calls */
- static char sidename_buf[4][SIDENAMEBUFLEN]; /* STATIC_RESULT: used for return, not across calls */
-
- bufno = bufno % 4;
- strlcpy(sidename_buf[bufno], factionname(s->stealthfaction?s->stealthfaction:s->faction), SIDENAMEBUFLEN);
- return sidename_buf[bufno++];
-}
-
-static const char *sideabkz(side * s, bool truename)
-{
- static char sideabkz_buf[8]; /* STATIC_RESULT: used for return, not across calls */
- const faction *f = (s->stealthfaction
- && !truename) ? s->stealthfaction : s->faction;
-
-#undef SIDE_ABKZ
-#ifdef SIDE_ABKZ
- abkz(f->name, sideabkz_buf, sizeof(sideabkz_buf), 3);
-#else
- strcpy(sideabkz_buf, itoa36(f->no));
-#endif
- return sideabkz_buf;
-}
-
-static void message_faction(battle * b, faction * f, struct message *m)
-{
- region *r = b->region;
-
- if (f->battles == NULL || f->battles->r != r) {
- struct bmsg *bm = (struct bmsg *)calloc(1, sizeof(struct bmsg));
- bm->next = f->battles;
- f->battles = bm;
- bm->r = r;
- }
- add_message(&f->battles->msgs, m);
-}
-
-int armedmen(const unit * u, bool siege_weapons)
-{
- item *itm;
- int n = 0;
- if (!(urace(u)->flags & RCF_NOWEAPONS)) {
- if (effskill(u, SK_WEAPONLESS) >= 1) {
- /* kann ohne waffen bewachen: fuer drachen */
- n = u->number;
- } else {
- /* alle Waffen werden gezaehlt, und dann wird auf die Anzahl
- * Personen minimiert */
- for (itm = u->items; itm; itm = itm->next) {
- const weapon_type *wtype = resource2weapon(itm->type->rtype);
- if (wtype == NULL || (!siege_weapons && (wtype->flags & WTF_SIEGE)))
- continue;
- if (effskill(u, wtype->skill) >= 1)
- n += itm->number;
- /* if (effskill(u, wtype->skill) >= wtype->minskill) n += itm->number; */
- if (n > u->number)
- break;
- }
- n = _min(n, u->number);
- }
- }
- return n;
-}
-
-void message_all(battle * b, message * m)
-{
- bfaction *bf;
- plane *p = rplane(b->region);
- watcher *w;
-
- for (bf = b->factions; bf; bf = bf->next) {
- message_faction(b, bf->faction, m);
- }
- if (p)
- for (w = p->watchers; w; w = w->next) {
- for (bf = b->factions; bf; bf = bf->next)
- if (bf->faction == w->faction)
- break;
- if (bf == NULL)
- message_faction(b, w->faction, m);
- }
-}
-
-static void fbattlerecord(battle * b, faction * f, const char *s)
-{
- message *m = msg_message("battle_msg", "string", s);
- message_faction(b, f, m);
- msg_release(m);
-}
-
-/* being an enemy or a friend is (and must always be!) symmetrical */
-#define enemy_i(as, di) (as->relations[di]&E_ENEMY)
-#define friendly_i(as, di) (as->relations[di]&E_FRIEND)
-#define enemy(as, ds) (as->relations[ds->index]&E_ENEMY)
-#define friendly(as, ds) (as->relations[ds->index]&E_FRIEND)
-
-static bool set_enemy(side * as, side * ds, bool attacking)
-{
- int i;
- for (i = 0; i != MAXSIDES; ++i) {
- if (ds->enemies[i] == NULL)
- ds->enemies[i] = as;
- if (ds->enemies[i] == as)
- break;
- }
- for (i = 0; i != MAXSIDES; ++i) {
- if (as->enemies[i] == NULL)
- as->enemies[i] = ds;
- if (as->enemies[i] == ds)
- break;
- }
- assert(i != MAXSIDES);
- if (attacking)
- as->relations[ds->index] |= E_ATTACKING;
- if ((ds->relations[as->index] & E_ENEMY) == 0) {
- /* enemy-relation are always symmetrical */
- assert((as->relations[ds->index] & (E_ENEMY | E_FRIEND)) == 0);
- ds->relations[as->index] |= E_ENEMY;
- as->relations[ds->index] |= E_ENEMY;
- return true;
- }
- return false;
-}
-
-static void set_friendly(side * as, side * ds)
-{
- assert((as->relations[ds->index] & E_ENEMY) == 0);
- ds->relations[as->index] |= E_FRIEND;
- as->relations[ds->index] |= E_FRIEND;
-}
-
-static int allysfm(const side * s, const faction * f, int mode)
-{
- if (s->faction == f)
- return mode;
- if (s->group) {
- return alliedgroup(s->battle->plane, s->faction, f, s->group->allies, mode);
- }
- return alliedfaction(s->battle->plane, s->faction, f, mode);
-}
-
-static int allysf(const side * s, const faction * f)
-{
- return allysfm(s, f, HELP_FIGHT);
-}
-
-static int dead_fighters(const fighter * df)
-{
- return df->unit->number - df->alive - df->run.number;
-}
-
-fighter *select_corpse(battle * b, fighter * af)
-/* Wählt eine Leiche aus, der af hilft. casualties ist die Anzahl der
- * Toten auf allen Seiten (im Array). Wenn af == NULL, wird die
- * Parteizugehörigkeit ignoriert, und irgendeine Leiche genommen.
- *
- * Untote werden nicht ausgewählt (casualties, not dead) */
-{
- int si, di, maxcasualties = 0;
- fighter *df;
- side *s;
-
- for (si = 0; si != b->nsides; ++si) {
- side *s = b->sides + si;
- if (af == NULL || (!enemy_i(af->side, si) && allysf(af->side, s->faction))) {
- maxcasualties += s->casualties;
- }
- }
- di = rng_int() % maxcasualties;
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- for (df = s->fighters; df; df = df->next) {
- /* Geflohene haben auch 0 hp, dürfen hier aber nicht ausgewählt
- * werden! */
- int dead = dead_fighters(df);
- if (!playerrace(u_race(df->unit)))
- continue;
-
- if (af && !helping(af->side, df->side))
- continue;
- if (di < dead) {
- return df;
- }
- di -= dead;
- }
- }
-
- return NULL;
-}
-
-bool helping(const side * as, const side * ds)
-{
- if (as->faction == ds->faction)
- return true;
- return (bool) (!enemy(as, ds) && allysf(as, ds->faction));
-}
-
-int statusrow(int status)
-{
- switch (status) {
- case ST_AGGRO:
- case ST_FIGHT:
- return FIGHT_ROW;
- case ST_BEHIND:
- case ST_CHICKEN:
- return BEHIND_ROW;
- case ST_AVOID:
- return AVOID_ROW;
- case ST_FLEE:
- return FLEE_ROW;
- default:
- assert(!"unknown combatrow");
- }
- return FIGHT_ROW;
-}
-
-static double hpflee(int status)
- /* if hp drop below this percentage, run away */
-{
- switch (status) {
- case ST_AGGRO:
- return 0.0;
- case ST_FIGHT:
- case ST_BEHIND:
- return 0.2;
- case ST_CHICKEN:
- case ST_AVOID:
- return 0.9;
- case ST_FLEE:
- return 1.0;
- default:
- assert(!"unknown combatrow");
- }
- return 0.0;
-}
-
-static int get_row(const side * s, int row, const side * vs)
-{
- bool counted[MAXSIDES];
- int enemyfront = 0;
- int line, result;
- int retreat = 0;
- int size[NUMROWS];
- int front = 0;
- battle *b = s->battle;
-
- memset(counted, 0, sizeof(counted));
- memset(size, 0, sizeof(size));
- for (line = FIRST_ROW; line != NUMROWS; ++line) {
- int si, sa_i;
- /* how many enemies are there in the first row? */
- for (si = 0; s->enemies[si]; ++si) {
- side *se = s->enemies[si];
- if (se->size[line] > 0) {
- enemyfront += se->size[line];
- /* - s->nonblockers[line] (nicht, weil angreifer) */
- }
- }
- for (sa_i = 0; sa_i != b->nsides; ++sa_i) {
- side *sa = b->sides + sa_i;
- /* count people that like me, but don't like my enemy */
- if (friendly_i(s, sa_i) && enemy_i(vs, sa_i)) {
- if (!counted[sa_i]) {
- int i;
-
- for (i = 0; i != NUMROWS; ++i) {
- size[i] += sa->size[i] - sa->nonblockers[i];
- }
- counted[sa_i] = true;
- }
- }
- }
- if (enemyfront)
- break;
- }
- if (enemyfront) {
- for (line = FIRST_ROW; line != NUMROWS; ++line) {
- front += size[line];
- if (!front || front < enemyfront / ROW_FACTOR)
- ++retreat;
- else if (front)
- break;
- }
- }
-
- /* every entry in the size[] array means someone trying to defend us.
- * 'retreat' is the number of rows falling.
- */
- result = _max(FIRST_ROW, row - retreat);
-
- return result;
-}
-
-int get_unitrow(const fighter * af, const side * vs)
-{
- int row = statusrow(af->status);
- if (vs == NULL) {
- int i;
- for (i = FIGHT_ROW; i != row; ++i)
- if (af->side->size[i])
- break;
- return FIGHT_ROW + (row - i);
- } else {
- battle *b = vs->battle;
- if (row != b->rowcache.row || b->alive != b->rowcache.alive
- || af->side != b->rowcache.as || vs != b->rowcache.vs) {
- b->rowcache.alive = b->alive;
- b->rowcache.as = af->side;
- b->rowcache.vs = vs;
- b->rowcache.row = row;
- b->rowcache.result = get_row(af->side, row, vs);
- return b->rowcache.result;
- }
-#ifdef DEBUG_FAST /* validation code */
- {
- int i = get_row(af->side, row, vs);
- assert(i == b->rowcache.result);
- }
-#endif
- return b->rowcache.result;
- }
-}
-
-static void reportcasualties(battle * b, fighter * fig, int dead)
-{
- struct message *m;
- region *r = NULL;
- if (fig->alive == fig->unit->number)
- return;
- m = msg_message("casualties", "unit runto run alive fallen",
- fig->unit, r, fig->run.number, fig->alive, dead);
- message_all(b, m);
- msg_release(m);
-}
-
-static int
-contest_classic(int skilldiff, const armor_type * ar, const armor_type * sh)
-{
- int p, vw = BASE_CHANCE - TDIFF_CHANGE * skilldiff;
- double mod = 1.0;
-
- if (ar != NULL)
- mod *= (1 + ar->penalty);
- if (sh != NULL)
- mod *= (1 + sh->penalty);
- vw = (int)(100 - ((100 - vw) * mod));
-
- do {
- p = rng_int() % 100;
- vw -= p;
- }
- while (vw >= 0 && p >= 90);
- return (vw <= 0);
-}
-
-/** new rule for Eressea 1.5
- * \param skilldiff - the attack skill with every modifier applied
- */
-static int
-contest_new(int skilldiff, const troop dt, const armor_type * ar,
- const armor_type * sh)
-{
- double tohit = 0.5 + skilldiff * 0.1;
- if (tohit < 0.5)
- tohit = 0.5;
- if (chance(tohit)) {
- int defense = effskill(dt.fighter->unit, SK_STAMINA);
- double tosave = defense * 0.05;
- return !chance(tosave);
- }
- return 0;
-}
-
-static int
-contest(int skdiff, const troop dt, const armor_type * ar,
- const armor_type * sh)
-{
- if (skill_formula == FORMULA_ORIG) {
- return contest_classic(skdiff, ar, sh);
- } else {
- return contest_new(skdiff, dt, ar, sh);
- }
-}
-
-static bool is_riding(const troop t)
-{
- if (t.fighter->building != NULL)
- return false;
- if (t.fighter->horses + t.fighter->elvenhorses > t.index)
- return true;
- return false;
-}
-
-static weapon *preferred_weapon(const troop t, bool attacking)
-{
- weapon *missile = t.fighter->person[t.index].missile;
- weapon *melee = t.fighter->person[t.index].melee;
- if (attacking) {
- if (melee == NULL || (missile && missile->attackskill > melee->attackskill)) {
- return missile;
- }
- } else {
- if (melee == NULL || (missile
- && missile->defenseskill > melee->defenseskill)) {
- return missile;
- }
- }
- return melee;
-}
-
-static weapon *select_weapon(const troop t, bool attacking,
- bool ismissile)
- /* select the primary weapon for this trooper */
-{
- if (attacking) {
- if (ismissile) {
- /* from the back rows, have to use your missile weapon */
- return t.fighter->person[t.index].missile;
- }
- } else {
- if (!ismissile) {
- /* have to use your melee weapon if it's melee */
- return t.fighter->person[t.index].melee;
- }
- }
- return preferred_weapon(t, attacking);
-}
-
-static bool i_canuse(const unit * u, const item_type * itype)
-{
- if (itype->canuse) {
- return itype->canuse(u, itype);
- }
- return true;
-}
-
-static int
-weapon_skill(const weapon_type * wtype, const unit * u, bool attacking)
- /* the 'pure' skill when using this weapon to attack or defend.
- * only undiscriminate modifiers (not affected by troops or enemies)
- * are taken into account, e.g. no horses, magic, etc. */
-{
- int skill;
-
- if (wtype == NULL) {
- skill = effskill(u, SK_WEAPONLESS);
- if (skill <= 0) {
- /* wenn kein waffenloser kampf, dann den rassen-defaultwert */
- if (u_race(u) == get_race(RC_ORC)) {
- int sword = effskill(u, SK_MELEE);
- int spear = effskill(u, SK_SPEAR);
- skill = _max(sword, spear) - 3;
- if (attacking) {
- skill = _max(skill, u_race(u)->at_default);
- } else {
- skill = _max(skill, u_race(u)->df_default);
- }
- } else {
- if (attacking) {
- skill = u_race(u)->at_default;
- } else {
- skill = u_race(u)->df_default;
- }
- }
- } else {
- /* der rassen-defaultwert kann höher sein als der Talentwert von
- * waffenloser kampf */
- if (attacking) {
- if (skill < u_race(u)->at_default)
- skill = u_race(u)->at_default;
- } else {
- if (skill < u_race(u)->df_default)
- skill = u_race(u)->df_default;
- }
- }
- if (attacking) {
- skill += u_race(u)->at_bonus;
- if (fval(u->region->terrain, SEA_REGION) && u->ship)
- skill += u->ship->type->at_bonus;
- } else {
- skill += u_race(u)->df_bonus;
- if (fval(u->region->terrain, SEA_REGION) && u->ship)
- skill += u->ship->type->df_bonus;
- }
- } else {
- /* changed: if we own a weapon, we have at least a skill of 0 */
- if (!i_canuse(u, wtype->itype))
- return -1;
- skill = effskill(u, wtype->skill);
- if (skill < wtype->minskill)
- skill = 0;
- if (skill > 0) {
- if (attacking) {
- skill += u_race(u)->at_bonus;
- } else {
- skill += u_race(u)->df_bonus;
- }
- }
- if (attacking) {
- skill += wtype->offmod;
- } else {
- skill += wtype->defmod;
- }
- }
-
- return skill;
-}
-
-static int CavalrySkill(void)
-{
- static int skill = -1;
-
- if (skill < 0) {
- skill = get_param_int(global.parameters, "rules.cavalry.skill", 2);
- }
- return skill;
-}
-
-#define BONUS_SKILL 1
-#define BONUS_DAMAGE 2
-static int CavalryBonus(const unit * u, troop enemy, int type)
-{
- static int mode = -1;
-
- if (mode < 0) {
- mode = get_param_int(global.parameters, "rules.cavalry.mode", 1);
- }
- if (mode == 0) {
- /* old rule, Eressea 1.0 compat */
- return (type == BONUS_SKILL) ? 2 : 0;
- } else {
- /* new rule, chargers in Eressea 1.1 */
- int skl = effskill(u, SK_RIDING);
- /* only half against trolls */
- if (skl > 0) {
- if (type == BONUS_DAMAGE) {
- int dmg = _min(skl, 8);
- if (u_race(enemy.fighter->unit) == get_race(RC_TROLL)) {
- dmg = dmg / 4;
- } else {
- dmg = dmg / 2;
- }
- return dmg;
- } else {
- skl = skl / 2;
- return _min(skl, 4);
- }
- }
- }
- return 0;
-}
-
-static int
-weapon_effskill(troop t, troop enemy, const weapon * w, bool attacking,
- bool missile)
- /* effektiver Waffenskill während des Kampfes */
-{
- /* In dieser Runde alle die Modifier berechnen, die fig durch die
- * Waffen bekommt. */
- fighter *tf = t.fighter;
- unit *tu = t.fighter->unit;
- int skill;
- const weapon_type *wtype = w ? w->type : NULL;
-
- if (wtype == NULL) {
- /* Ohne Waffe: Waffenlose Angriffe */
- skill = weapon_skill(NULL, tu, attacking);
- } else {
- if (attacking) {
- skill = w->attackskill;
- } else {
- skill = w->defenseskill;
- }
- if (wtype->modifiers != NULL) {
- /* Pferdebonus, Lanzenbonus, usw. */
- int m;
- unsigned int flags =
- WMF_SKILL | (attacking ? WMF_OFFENSIVE : WMF_DEFENSIVE);
-
- if (is_riding(t))
- flags |= WMF_RIDING;
- else
- flags |= WMF_WALKING;
- if (is_riding(enemy))
- flags |= WMF_AGAINST_RIDING;
- else
- flags |= WMF_AGAINST_WALKING;
-
- for (m = 0; wtype->modifiers[m].value; ++m) {
- if ((wtype->modifiers[m].flags & flags) == flags) {
- race_list *rlist = wtype->modifiers[m].races;
- if (rlist != NULL) {
- while (rlist) {
- if (rlist->data == u_race(tu))
- break;
- rlist = rlist->next;
- }
- if (rlist == NULL)
- continue;
- }
- skill += wtype->modifiers[m].value;
- }
- }
- }
- }
-
- /* Burgenbonus, Pferdebonus */
- if (is_riding(t) && (wtype == NULL || (fval(wtype, WTF_HORSEBONUS)
- && !fval(wtype, WTF_MISSILE)))) {
- skill += CavalryBonus(tu, enemy, BONUS_SKILL);
- if (wtype)
- skill =
- skillmod(urace(tu)->attribs, tu, tu->region, wtype->skill, skill,
- SMF_RIDING);
- }
-
- if (t.index < tf->elvenhorses) {
- /* Elfenpferde: Helfen dem Reiter, egal ob und welche Waffe. Das ist
- * eleganter, und vor allem einfacher, sonst muß man noch ein
- * WMF_ELVENHORSE einbauen. */
- skill += 2;
- }
-
- if (skill > 0 && !attacking && missile) {
- /*
- * Wenn ich verteidige, und nicht direkt meinem Feind gegenüberstehe,
- * halbiert sich mein Skill: (z.B. gegen Fernkämpfer. Nahkämpfer
- * können mich eh nicht treffen)
- */
- skill /= 2;
- }
- return skill;
-}
-
-static const armor_type *select_armor(troop t, bool shield)
-{
- unsigned int type = shield ? ATF_SHIELD : 0;
- unit *u = t.fighter->unit;
- const armor *a = t.fighter->armors;
- int geschuetzt = 0;
-
- /* some monsters should not use armor (dragons in chainmail? ha!) */
- if (!(u_race(u)->battle_flags & BF_EQUIPMENT))
- return NULL;
-
- /* ... neither do werewolves */
- if (fval(u, UFL_WERE)) {
- return NULL;
- }
-
- for (; a; a = a->next) {
- if ((a->atype->flags & ATF_SHIELD) == type) {
- geschuetzt += a->count;
- if (geschuetzt > t.index) {
- /* unser Kandidat wird geschuetzt */
- return a->atype;
- }
- }
- }
- return NULL;
-}
-
-/* Hier ist zu beachten, ob und wie sich Zauber und Artefakte, die
- * Rüstungschutz geben, addieren.
- * - Artefakt "trollbelt" gibt Rüstung +1
- * - Zauber Rindenhaut gibt Rüstung +3
- */
-static int trollbelts(const unit *u) {
- const struct resource_type *belt = rt_find("trollbelt");
- return belt ? i_get(u->items, belt->itype) : 0;
-}
-
-int select_magicarmor(troop t)
-{
- unit *u = t.fighter->unit;
- int ma = 0;
-
- if (trollbelts(u) > t.index) /* unser Kandidat wird geschuetzt */
- ma += 1;
-
- return ma;
-}
-
-/* Sind side ds und Magier des meffect verbündet, dann return 1*/
-bool meffect_protection(battle * b, meffect * s, side * ds)
-{
- if (!s->magician->alive)
- return false;
- if (s->duration <= 0)
- return false;
- if (enemy(s->magician->side, ds))
- return false;
- if (allysf(s->magician->side, ds->faction))
- return true;
- return false;
-}
-
-/* Sind side as und Magier des meffect verfeindet, dann return 1*/
-bool meffect_blocked(battle * b, meffect * s, side * as)
-{
- if (!s->magician->alive)
- return false;
- if (s->duration <= 0)
- return false;
- if (enemy(s->magician->side, as))
- return true;
- return false;
-}
-
-/* rmfighter wird schon im PRAECOMBAT gebraucht, da gibt es noch keine
- * troops */
-void rmfighter(fighter * df, int i)
-{
- side *ds = df->side;
-
- /* nicht mehr personen abziehen, als in der Einheit am Leben sind */
- assert(df->alive >= i);
- assert(df->alive <= df->unit->number);
-
- /* erst ziehen wir die Anzahl der Personen von den Kämpfern in der
- * Schlacht, dann von denen auf dieser Seite ab*/
- df->side->alive -= i;
- df->side->battle->alive -= i;
-
- /* Dann die Kampfreihen aktualisieren */
- ds->size[SUM_ROW] -= i;
- ds->size[statusrow(df->status)] -= i;
-
- /* Spezialwirkungen, z.B. Schattenritter */
- if (u_race(df->unit)->battle_flags & BF_NOBLOCK) {
- ds->nonblockers[SUM_ROW] -= i;
- ds->nonblockers[statusrow(df->status)] -= i;
- }
-
- /* und die Einheit selbst aktualisieren */
- df->alive -= i;
-}
-
-static void rmtroop(troop dt)
-{
- fighter *df = dt.fighter;
-
- /* troop ist immer eine einzele Person */
- rmfighter(df, 1);
-
- assert(dt.index >= 0 && dt.index < df->unit->number);
- df->person[dt.index] = df->person[df->alive - df->removed];
- if (df->removed) {
- df->person[df->alive - df->removed] = df->person[df->alive];
- }
- df->person[df->alive].hp = 0;
-}
-
-void remove_troop(troop dt)
-{
- fighter *df = dt.fighter;
- struct person p = df->person[dt.index];
- battle *b = df->side->battle;
- b->fast.alive = -1; /* invalidate cached value */
- b->rowcache.alive = -1; /* invalidate cached value */
- ++df->removed;
- ++df->side->removed;
- df->person[dt.index] = df->person[df->alive - df->removed];
- df->person[df->alive - df->removed] = p;
-}
-
-void kill_troop(troop dt)
-{
- fighter *df = dt.fighter;
- unit *du = df->unit;
-
- rmtroop(dt);
- if (!df->alive) {
- char eqname[64];
- const struct equipment *eq;
- if (u_race(du)->itemdrop) {
- item *drops = u_race(du)->itemdrop(u_race(du), du->number - df->run.number);
-
- if (drops != NULL) {
- i_merge(&du->items, &drops);
- }
- }
- sprintf(eqname, "%s_spoils", u_race(du)->_name);
- eq = get_equipment(eqname);
- if (eq != NULL) {
- equip_items(&du->items, eq);
- }
- }
-}
-
-/** reduces the target's exp by an equivalent of n points learning
- * 30 points = 1 week
- */
-void drain_exp(struct unit *u, int n)
-{
- skill_t sk = (skill_t) (rng_int() % MAXSKILLS);
- skill_t ssk;
-
- ssk = sk;
-
- while (get_level(u, sk) == 0) {
- sk++;
- if (sk == MAXSKILLS)
- sk = 0;
- if (sk == ssk) {
- sk = NOSKILL;
- break;
- }
- }
- if (sk != NOSKILL) {
- skill *sv = unit_skill(u, sk);
- while (n > 0) {
- if (n >= 30 * u->number) {
- reduce_skill(u, sv, 1);
- n -= 30;
- } else {
- if (rng_int() % (30 * u->number) < n)
- reduce_skill(u, sv, 1);
- n = 0;
- }
- }
- }
-}
-
-const char *rel_dam(int dam, int hp)
-{
- double q = (double)dam / (double)hp;
-
- if (q > 0.75) {
- return "eine klaffende Wunde";
- } else if (q > 0.5) {
- return "eine schwere Wunde";
- } else if (q > 0.25) {
- return "eine Wunde";
- }
- return "eine kleine Wunde";
-}
-
-static void vampirism(troop at, int damage)
-{
- static int vampire = -1;
- if (vampire < 0)
- vampire = get_param_int(global.parameters, "rules.combat.demon_vampire", 0);
- if (vampire > 0) {
- int gain = damage / vampire;
- int chance = damage - vampire * gain;
- if (chance > 0 && (rng_int() % vampire < chance))
- ++gain;
- if (gain > 0) {
- int maxhp = unit_max_hp(at.fighter->unit);
- at.fighter->person[at.index].hp =
- _min(gain + at.fighter->person[at.index].hp, maxhp);
- }
- }
-}
-
-static int natural_armor(unit * du)
-{
- static int *bonus = 0;
- int an = u_race(du)->armor;
- if (bonus == 0) {
- bonus = calloc(sizeof(int), num_races);
- }
- if (bonus[u_race(du)->index] == 0) {
- bonus[u_race(du)->index] =
- get_param_int(u_race(du)->parameters, "armor.stamina", -1);
- if (bonus[u_race(du)->index] == 0)
- bonus[u_race(du)->index] = -1;
- }
- if (bonus[u_race(du)->index] > 0) {
- int sk = effskill(du, SK_STAMINA);
- sk /= bonus[u_race(du)->index];
- an += sk;
- }
- return an;
-}
-
-bool
-terminate(troop dt, troop at, int type, const char *damage, bool missile)
-{
- item **pitm;
- fighter *df = dt.fighter;
- fighter *af = at.fighter;
- unit *au = af->unit;
- unit *du = df->unit;
- battle *b = df->side->battle;
- int heiltrank = 0;
- static int rule_armor = -1;
-
- /* Schild */
- side *ds = df->side;
- int hp;
-
- int ar = 0, an, am;
- const armor_type *armor = select_armor(dt, true);
- const armor_type *shield = select_armor(dt, false);
-
- const weapon_type *dwtype = NULL;
- const weapon_type *awtype = NULL;
- const weapon *weapon;
-
- int rda, sk = 0, sd;
- bool magic = false;
- int da = dice_rand(damage);
-
- assert(du->number > 0);
- ++at.fighter->hits;
-
- switch (type) {
- case AT_STANDARD:
- weapon = select_weapon(at, true, missile);
- sk = weapon_effskill(at, dt, weapon, true, missile);
- if (weapon)
- awtype = weapon->type;
- if (awtype && fval(awtype, WTF_MAGICAL))
- magic = true;
- break;
- case AT_NATURAL:
- sk = weapon_effskill(at, dt, NULL, true, missile);
- break;
- case AT_SPELL:
- case AT_COMBATSPELL:
- magic = true;
- break;
- default:
- break;
- }
- weapon = select_weapon(dt, false, true); /* missile=true to get the unmodified best weapon she has */
- sd = weapon_effskill(dt, at, weapon, false, false);
- if (weapon != NULL)
- dwtype = weapon->type;
-
- if (is_riding(at) && (awtype == NULL || (fval(awtype, WTF_HORSEBONUS)
- && !fval(awtype, WTF_MISSILE)))) {
- da += CavalryBonus(au, dt, BONUS_DAMAGE);
- }
-
- if (armor) {
- ar += armor->prot;
- if (armor->projectile > 0 && chance(armor->projectile)) {
- return false;
- }
- }
- if (shield) {
- ar += shield->prot;
- if (shield->projectile > 0 && chance(shield->projectile)) {
- return false;
- }
- }
-
- /* natürliche Rüstung */
- an = natural_armor(du);
-
- /* magische Rüstung durch Artefakte oder Sprüche */
- /* Momentan nur Trollgürtel und Werwolf-Eigenschaft */
- am = select_magicarmor(dt);
-
-#if CHANGED_CROSSBOWS
- if (awtype && fval(awtype, WTF_ARMORPIERCING)) {
- /* crossbows */
- ar /= 2;
- an /= 2;
- }
-#endif
-
- if (rule_armor < 0) {
- rule_armor = get_param_int(global.parameters, "rules.combat.nat_armor", 0);
- }
- if (rule_armor == 0) {
- /* natürliche Rüstung ist halbkumulativ */
- if (ar > 0) {
- ar += an / 2;
- } else {
- ar = an;
- }
- } else {
- /* use the higher value, add half the other value */
- ar = (ar > an) ? (ar + an / 2) : (an + ar / 2);
- }
- ar += am;
-
- if (type != AT_COMBATSPELL && type != AT_SPELL) {
- if (damage_rules & DAMAGE_CRITICAL) {
- double kritchance = (sk * 3 - sd) / 200.0;
-
- kritchance = _max(kritchance, 0.005);
- kritchance = _min(0.9, kritchance);
-
- while (chance(kritchance)) {
- if (bdebug) {
- fprintf(bdebug, "%s/%d lands a critical hit\n", unitid(au), at.index);
- }
- da += dice_rand(damage);
- }
- }
-
- da += rc_specialdamage(u_race(au), u_race(du), awtype);
-
- if (awtype != NULL && fval(awtype, WTF_MISSILE)) {
- /* missile weapon bonus */
- if (damage_rules & DAMAGE_MISSILE_BONUS) {
- da += af->person[at.index].damage_rear;
- }
- } else if (awtype == NULL) {
- /* skill bonus for unarmed combat */
- if (damage_rules & DAMAGE_UNARMED_BONUS) {
- da += effskill(au, SK_WEAPONLESS);
- }
- } else {
- /* melee bonus */
- if (damage_rules & DAMAGE_MELEE_BONUS) {
- da += af->person[at.index].damage;
- }
- }
-
- /* Skilldifferenzbonus */
- if (damage_rules & DAMAGE_SKILL_BONUS) {
- da += _max(0, (sk - sd) / DAMAGE_QUOTIENT);
- }
- }
-
- if (magic) {
- /* Magischer Schaden durch Spruch oder magische Waffe */
- double res = 1.0;
-
- /* magic_resistance gib x% Resistenzbonus zurück */
- res -= magic_resistance(du) * 3.0;
-
- if (u_race(du)->battle_flags & BF_EQUIPMENT) {
-#ifdef TODO_RUNESWORD
- /* Runenschwert gibt im Kampf 80% Resistenzbonus */
- if (dwp == WP_RUNESWORD)
- res -= 0.80;
-#endif
- /* der Effekt von Laen steigt nicht linear */
- if (armor && fval(armor, ATF_LAEN))
- res *= (1 - armor->magres);
- if (shield && fval(shield, ATF_LAEN))
- res *= (1 - shield->magres);
- if (dwtype)
- res *= (1 - dwtype->magres);
- }
-
- if (res > 0) {
- da = (int)(_max(da * res, 0));
- }
- /* gegen Magie wirkt nur natürliche und magische Rüstung */
- ar = an + am;
- }
-
- rda = _max(da - ar, 0);
-
- if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic)
- rda = 0;
- else {
- int qi;
- quicklist *ql;
- unsigned int i = 0;
-
- if (u_race(du)->battle_flags & BF_RES_PIERCE)
- i |= WTF_PIERCE;
- if (u_race(du)->battle_flags & BF_RES_CUT)
- i |= WTF_CUT;
- if (u_race(du)->battle_flags & BF_RES_BASH)
- i |= WTF_BLUNT;
-
- if (i && awtype && fval(awtype, i))
- rda /= 2;
-
- /* Schilde */
- for (qi = 0, ql = b->meffects; ql; ql_advance(&ql, &qi, 1)) {
- meffect *me = (meffect *) ql_get(ql, qi);
- if (meffect_protection(b, me, ds) != 0) {
- assert(0 <= rda); /* rda sollte hier immer mindestens 0 sein */
- /* jeder Schaden wird um effect% reduziert bis der Schild duration
- * Trefferpunkte aufgefangen hat */
- if (me->typ == SHIELD_REDUCE) {
- hp = rda * (me->effect / 100);
- rda -= hp;
- me->duration -= hp;
- }
- /* gibt Rüstung +effect für duration Treffer */
- if (me->typ == SHIELD_ARMOR) {
- rda = _max(rda - me->effect, 0);
- me->duration--;
- }
- }
- }
- }
-
- assert(dt.index < du->number);
- df->person[dt.index].hp -= rda;
- if (u_race(au) == get_race(RC_DAEMON)) {
- vampirism(at, rda);
- }
-
- if (df->person[dt.index].hp > 0) { /* Hat überlebt */
- if (bdebug) {
- fprintf(bdebug, "Damage %d, armor %d: %d -> %d HP\n",
- da, ar, df->person[dt.index].hp + rda, df->person[dt.index].hp);
- }
- if (u_race(au) == get_race(RC_DAEMON)) {
-#ifdef TODO_RUNESWORD
- if (select_weapon(dt, 0, -1) == WP_RUNESWORD)
- continue;
-#endif
- if (!(df->person[dt.index].flags & (FL_COURAGE | FL_DAZZLED))) {
- df->person[dt.index].flags |= FL_DAZZLED;
- df->person[dt.index].defence--;
- }
- }
- df->person[dt.index].flags = (df->person[dt.index].flags & ~FL_SLEEPING);
- return false;
- }
-
- /* Sieben Leben */
- if (u_race(du) == get_race(RC_CAT) && (chance(1.0 / 7))) {
- assert(dt.index >= 0 && dt.index < du->number);
- df->person[dt.index].hp = unit_max_hp(du);
- return false;
- }
-
- /* Heiltrank schluerfen und hoffen */
- if (oldpotiontype[P_HEAL]) {
- if (get_effect(du, oldpotiontype[P_HEAL]) > 0) {
- change_effect(du, oldpotiontype[P_HEAL], -1);
- heiltrank = 1;
- } else if (i_get(du->items, oldpotiontype[P_HEAL]->itype) > 0) {
- i_change(&du->items, oldpotiontype[P_HEAL]->itype, -1);
- change_effect(du, oldpotiontype[P_HEAL], 3);
- heiltrank = 1;
- }
- if (heiltrank && (chance(0.50))) {
- {
- message *m = msg_message("battle::potionsave", "unit", du);
- message_faction(b, du->faction, m);
- msg_release(m);
- }
- assert(dt.index >= 0 && dt.index < du->number);
- df->person[dt.index].hp = u_race(du)->hitpoints;
- return false;
- }
- }
- ++at.fighter->kills;
-
- if (bdebug) {
- fprintf(bdebug, "Damage %d, armor %d, type %d: %d -> %d HP, tot.\n",
- da, ar, type, df->person[dt.index].hp + rda, df->person[dt.index].hp);
- }
- for (pitm = &du->items; *pitm;) {
- item *itm = *pitm;
- const item_type *itype = itm->type;
- if (!(itype->flags & ITF_CURSED) && dt.index < itm->number) {
- /* 25% Grundchance, das ein Item kaputtgeht. */
- if (rng_int() % 4 < 1) {
- i_change(pitm, itype, -1);
- }
- }
- if (*pitm == itm) {
- pitm = &itm->next;
- }
- }
- kill_troop(dt);
-
- return true;
-}
-
-static int
-count_side(const side * s, const side * vs, int minrow, int maxrow, int select)
-{
- fighter *fig;
- int people = 0;
- int unitrow[NUMROWS];
-
- if (maxrow < FIGHT_ROW)
- return 0;
- if (select & SELECT_ADVANCE) {
- memset(unitrow, -1, sizeof(unitrow));
- }
-
- for (fig = s->fighters; fig; fig = fig->next) {
- if (fig->alive - fig->removed > 0) {
- int row = statusrow(fig->status);
- if (select & SELECT_ADVANCE) {
- if (unitrow[row] == -1) {
- unitrow[row] = get_unitrow(fig, vs);
- }
- row = unitrow[row];
- }
- if (row >= minrow && row <= maxrow) {
- people += fig->alive - fig->removed;
- if (people > 0 && (select & SELECT_FIND))
- break;
- }
- }
- }
- return people;
-}
-
-/* return the number of live allies warning: this function only considers
-* troops that are still alive, not those that are still fighting although
-* dead. */
-int
-count_allies(const side * as, int minrow, int maxrow, int select, int allytype)
-{
- battle *b = as->battle;
- side *ds;
- int count = 0;
-
- for (ds = b->sides; ds != b->sides + b->nsides; ++ds) {
- if ((allytype == ALLY_ANY && helping(as, ds)) || (allytype == ALLY_SELF
- && as->faction == ds->faction)) {
- count += count_side(ds, NULL, minrow, maxrow, select);
- if (count > 0 && (select & SELECT_FIND))
- break;
- }
- }
- return count;
-}
-
-static int
-count_enemies_i(battle * b, const fighter * af, int minrow, int maxrow,
- int select)
-{
- side *es, *as = af->side;
- int i = 0;
-
- for (es = b->sides; es != b->sides + b->nsides; ++es) {
- if (as == NULL || enemy(es, as)) {
- int offset = 0;
- if (select & SELECT_DISTANCE) {
- offset = get_unitrow(af, es) - FIGHT_ROW;
- }
- i += count_side(es, as, minrow - offset, maxrow - offset, select);
- if (i > 0 && (select & SELECT_FIND))
- break;
- }
- }
- return i;
-}
-
-int
-count_enemies(battle * b, const fighter * af, int minrow, int maxrow,
- int select)
-{
- int sr = statusrow(af->status);
- side *as = af->side;
-
- if (b->alive == b->fast.alive && as == b->fast.side && sr == b->fast.status
- && minrow == b->fast.minrow && maxrow == b->fast.maxrow) {
- if (b->fast.enemies[select] >= 0) {
-#ifdef DEBUG_FAST
- int i = count_enemies_i(b, af, minrow, maxrow, select);
- assert(i == b->fast.enemies[select]);
-#endif
- return b->fast.enemies[select];
- } else if (select & SELECT_FIND) {
- if (b->fast.enemies[select - SELECT_FIND] >= 0) {
-#ifdef DEBUG_FAST
- int i = count_enemies_i(b, af, minrow, maxrow, select);
- assert((i > 0) == (b->fast.enemies[select - SELECT_FIND] > 0));
-#endif
- return b->fast.enemies[select - SELECT_FIND];
- }
- }
- } else if (select != SELECT_FIND || b->alive != b->fast.alive) {
- b->fast.side = as;
- b->fast.status = sr;
- b->fast.minrow = minrow;
- b->fast.alive = b->alive;
- b->fast.maxrow = maxrow;
- memset(b->fast.enemies, -1, sizeof(b->fast.enemies));
- }
- if (maxrow >= FIRST_ROW) {
- int i = count_enemies_i(b, af, minrow, maxrow, select);
- b->fast.enemies[select] = i;
- return i;
- }
- return 0;
-}
-
-troop select_enemy(fighter * af, int minrow, int maxrow, int select)
-{
- side *as = af->side;
- battle *b = as->battle;
- int si, selected;
- int enemies;
-#ifdef DEBUG_SELECT
- troop result = no_troop;
-#endif
- if (u_race(af->unit)->flags & RCF_FLY) {
- /* flying races ignore min- and maxrow and can attack anyone fighting
- * them */
- minrow = FIGHT_ROW;
- maxrow = BEHIND_ROW;
- }
- minrow = _max(minrow, FIGHT_ROW);
-
- enemies = count_enemies(b, af, minrow, maxrow, select);
-
- /* Niemand ist in der angegebenen Entfernung? */
- if (enemies <= 0)
- return no_troop;
-
- selected = rng_int() % enemies;
- for (si = 0; as->enemies[si]; ++si) {
- side *ds = as->enemies[si];
- fighter *df;
- int unitrow[NUMROWS];
- int offset = 0;
-
- if (select & SELECT_DISTANCE)
- offset = get_unitrow(af, ds) - FIGHT_ROW;
-
- if (select & SELECT_ADVANCE) {
- int ui;
- for (ui = 0; ui != NUMROWS; ++ui)
- unitrow[ui] = -1;
- }
-
- for (df = ds->fighters; df; df = df->next) {
- int dr;
-
- dr = statusrow(df->status);
- if (select & SELECT_ADVANCE) {
- if (unitrow[dr] < 0) {
- unitrow[dr] = get_unitrow(df, as);
- }
- dr = unitrow[dr];
- }
-
- if (select & SELECT_DISTANCE)
- dr += offset;
- if (dr < minrow || dr > maxrow)
- continue;
- if (df->alive - df->removed > selected) {
-#ifdef DEBUG_SELECT
- if (result.fighter == NULL) {
- result.index = selected;
- result.fighter = df;
- }
-#else
- troop dt;
- dt.index = selected;
- dt.fighter = df;
- return dt;
-#endif
- }
- selected -= (df->alive - df->removed);
- enemies -= (df->alive - df->removed);
- }
- }
- if (enemies != 0) {
- log_error("select_enemies has a bug.\n");
- }
-#ifdef DEBUG_SELECT
- return result;
-#else
- assert(!selected);
- return no_troop;
-#endif
-}
-
-static int get_tactics(const side * as, const side * ds)
-{
- battle *b = as->battle;
- side *stac;
- int result = 0;
- int defense = 0;
-
- if (b->max_tactics > 0) {
- for (stac = b->sides; stac != b->sides + b->nsides; ++stac) {
- if (stac->leader.value > result && helping(stac, as)) {
- assert(ds == NULL || !helping(stac, ds));
- result = stac->leader.value;
- }
- if (ds && stac->leader.value > defense && helping(stac, ds)) {
- assert(!helping(stac, as));
- defense = stac->leader.value;
- }
- }
- }
- return result - defense;
-}
-
-static troop select_opponent(battle * b, troop at, int mindist, int maxdist)
-{
- fighter *af = at.fighter;
- troop dt;
-
- if (u_race(af->unit)->flags & RCF_FLY) {
- /* flying races ignore min- and maxrow and can attack anyone fighting
- * them */
- dt = select_enemy(at.fighter, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE);
- } else {
- mindist = _max(mindist, FIGHT_ROW);
- dt = select_enemy(at.fighter, mindist, maxdist, SELECT_ADVANCE);
- }
-
- if (b->turn == 0 && dt.fighter) {
- int tactics_formula = -1;
-
- if (tactics_formula < 0) {
- tactics_formula =
- get_param_int(global.parameters, "rules.tactics.formula", 0);
- }
- if (tactics_formula == 1) {
- int tactics = get_tactics(at.fighter->side, dt.fighter->side);
-
- /* percentage chance to get this attack */
- if (tactics > 0) {
- double tacch = 0.1 * tactics;
- if (fval(b->region->terrain, SEA_REGION)) {
- ship *sh = at.fighter->unit->ship;
- if (sh)
- tacch *= sh->type->tac_bonus;
- }
- if (!chance(tacch)) {
- dt.fighter = NULL;
- }
- } else {
- dt.fighter = NULL;
- }
- }
- }
-
- return dt;
-}
-
-quicklist *fighters(battle * b, const side * vs, int minrow, int maxrow,
- int mask)
-{
- side *s;
- quicklist *fightervp = 0;
-
- assert(vs != NULL);
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
-
- if (mask == FS_ENEMY) {
- if (!enemy(s, vs))
- continue;
- } else if (mask == FS_HELP) {
- if (enemy(s, vs) || !allysf(s, vs->faction)) {
- continue;
- }
- } else {
- assert(mask == (FS_HELP | FS_ENEMY) || !"invalid alliance state");
- }
- for (fig = s->fighters; fig; fig = fig->next) {
- int row = get_unitrow(fig, vs);
- if (row >= minrow && row <= maxrow) {
- ql_push(&fightervp, fig);
- }
- }
- }
-
- return fightervp;
-}
-
-static void report_failed_spell(battle * b, unit * mage, const spell * sp)
-{
- message *m = msg_message("battle::spell_failed", "unit spell", mage, sp);
- message_all(b, m);
- msg_release(m);
-}
-
-void do_combatmagic(battle * b, combatmagic_t was)
-{
- side *s;
- region *r = b->region;
- castorder *co;
- int level, rank, sl;
- spellrank spellranks[MAX_SPELLRANK];
-
- memset(spellranks, 0, sizeof(spellranks));
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
- for (fig = s->fighters; fig; fig = fig->next) {
- unit *mage = fig->unit;
-
- if (fig->alive <= 0)
- continue; /* fighter kann im Kampf getötet worden sein */
-
- level = eff_skill(mage, SK_MAGIC, r);
- if (level > 0) {
- float power;
- const spell *sp;
- const struct locale *lang = mage->faction->locale;
- order *ord;
-
- switch (was) {
- case DO_PRECOMBATSPELL:
- sp = get_combatspell(mage, 0);
- sl = get_combatspelllevel(mage, 0);
- break;
- case DO_POSTCOMBATSPELL:
- sp = get_combatspell(mage, 2);
- sl = get_combatspelllevel(mage, 2);
- break;
- default:
- /* Fehler! */
- return;
- }
- if (sp == NULL)
- continue;
-
- ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang));
- if (!cancast(mage, sp, 1, 1, ord)) {
- free_order(ord);
- continue;
- }
-
- level = eff_spelllevel(mage, sp, level, 1);
- if (sl > 0)
- level = _min(sl, level);
- if (level < 0) {
- report_failed_spell(b, mage, sp);
- free_order(ord);
- continue;
- }
-
- power = spellpower(r, mage, sp, level, ord);
- free_order(ord);
- if (power <= 0) { /* Effekt von Antimagie */
- report_failed_spell(b, mage, sp);
- pay_spell(mage, sp, level, 1);
- } else if (fumble(r, mage, sp, level)) {
- report_failed_spell(b, mage, sp);
- pay_spell(mage, sp, level, 1);
- } else {
- co = create_castorder(0, fig->unit, 0, sp, r, level, power, 0, 0, 0);
- co->magician.fig = fig;
- add_castorder(&spellranks[sp->rank], co);
- }
- }
- }
- }
- for (rank = 0; rank < MAX_SPELLRANK; rank++) {
- for (co = spellranks[rank].begin; co; co = co->next) {
- fighter *fig = co->magician.fig;
- const spell *sp = co->sp;
- int level = co->level;
-
- if (!sp->cast) {
- log_error("spell '%s' has no function.\n", sp->sname);
- } else {
- level = sp->cast(co);
- if (level > 0) {
- pay_spell(fig->unit, sp, level, 1);
- }
- }
- }
- }
- for (rank = 0; rank < MAX_SPELLRANK; rank++) {
- free_castorders(spellranks[rank].begin);
- }
-}
-
-static int cast_combatspell(troop at, const spell * sp, int level, float force)
-{
- castorder co;
-
- create_castorder(&co, at.fighter->unit, 0, sp, at.fighter->unit->region, level, force, 0, 0, 0);
- co.magician.fig = at.fighter;
- level = sp->cast(&co);
- free_castorder(&co);
- if (level > 0) {
- pay_spell(at.fighter->unit, sp, level, 1);
- }
- return level;
-}
-
-static void do_combatspell(troop at)
-{
- const spell *sp;
- fighter *fi = at.fighter;
- unit *caster = fi->unit;
- battle *b = fi->side->battle;
- region *r = b->region;
- quicklist *ql;
- int level, qi;
- float power;
- int fumblechance = 0;
- order *ord;
- int sl;
- const struct locale *lang = caster->faction->locale;
-
- sp = get_combatspell(caster, 1);
- if (sp == NULL) {
- fi->magic = 0; /* Hat keinen Kampfzauber, kämpft nichtmagisch weiter */
- return;
- }
- ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang));
- if (!cancast(caster, sp, 1, 1, ord)) {
- fi->magic = 0; /* Kann nicht mehr Zaubern, kämpft nichtmagisch weiter */
- return;
- }
-
- level = eff_spelllevel(caster, sp, fi->magic, 1);
- if ((sl = get_combatspelllevel(caster, 1)) > 0)
- level = _min(level, sl);
-
- if (fumble(r, caster, sp, level)) {
- report_failed_spell(b, caster, sp);
- pay_spell(caster, sp, level, 1);
- return;
- }
-
- for (qi = 0, ql = b->meffects; ql; ql_advance(&ql, &qi, 1)) {
- meffect *mblock = (meffect *) ql_get(ql, qi);
- if (mblock->typ == SHIELD_BLOCK) {
- if (meffect_blocked(b, mblock, fi->side) != 0) {
- fumblechance += mblock->duration;
- mblock->duration -= mblock->effect;
- }
- }
- }
-
- /* Antimagie die Fehlschlag erhöht */
- if (rng_int() % 100 < fumblechance) {
- report_failed_spell(b, caster, sp);
- pay_spell(caster, sp, level, 1);
- free_order(ord);
- return;
- }
- power = spellpower(r, caster, sp, level, ord);
- free_order(ord);
- if (power <= 0) { /* Effekt von Antimagie */
- report_failed_spell(b, caster, sp);
- pay_spell(caster, sp, level, 1);
- return;
- }
-
- if (!sp->cast) {
- log_error("spell '%s' has no function.\n", sp->sname);
- } else {
- level = cast_combatspell(at, sp, level, power);
- }
-}
-
-/* Sonderattacken: Monster patzern nicht und zahlen auch keine
- * Spruchkosten. Da die Spruchstärke direkt durch den Level bestimmt
- * wird, wirkt auch keine Antimagie (wird sonst in spellpower
- * gemacht) */
-
-static void do_extra_spell(troop at, const att * a)
-{
- const spell *sp = a->data.sp;
-
- if (sp->cast == NULL) {
- log_error("spell '%s' has no function.\n", sp->sname);
- } else {
- int level = a->level;
- assert(a->level>0);
- cast_combatspell(at, sp, level, level * MagicPower());
- }
-}
-
-int skilldiff(troop at, troop dt, int dist)
-{
- fighter *af = at.fighter, *df = dt.fighter;
- unit *au = af->unit, *du = df->unit;
- int is_protected = 0, skdiff = 0;
- weapon *awp = select_weapon(at, true, dist > 1);
-
- skdiff += af->person[at.index].attack;
- skdiff -= df->person[dt.index].defence;
-
- if (df->person[dt.index].flags & FL_SLEEPING)
- skdiff += 2;
-
- /* Effekte durch Rassen */
- if (awp != NULL && u_race(au) == get_race(RC_HALFLING) && dragonrace(u_race(du))) {
- skdiff += 5;
- }
-
- if (u_race(au) == get_race(RC_GOBLIN)) {
- static int goblin_bonus = -1;
- if (goblin_bonus < 0)
- goblin_bonus =
- get_param_int(global.parameters, "rules.combat.goblinbonus", 10);
- if (af->side->size[SUM_ROW] >= df->side->size[SUM_ROW] * goblin_bonus) {
- skdiff += 1;
- }
- }
-
- if (df->building) {
- bool init = false;
- static const curse_type *strongwall_ct, *magicwalls_ct;
- if (!init) {
- strongwall_ct = ct_find("strongwall");
- magicwalls_ct = ct_find("magicwalls");
- init = true;
- }
- if (df->building->type->protection) {
- int beff = df->building->type->protection(df->building, du);
- if (beff) {
- skdiff -= beff;
- is_protected = 2;
- }
- }
- if (strongwall_ct) {
- curse *c = get_curse(df->building->attribs, strongwall_ct);
- if (curse_active(c)) {
- /* wirkt auf alle Gebäude */
- skdiff -= curse_geteffect_int(c);
- is_protected = 2;
- }
- }
- if (magicwalls_ct
- && curse_active(get_curse(df->building->attribs, magicwalls_ct))) {
- /* Verdoppelt Burgenbonus */
- skdiff -= buildingeffsize(df->building, false);
- }
- }
- /* Goblin-Verteidigung
- * ist direkt in der Rassentabelle als df_default
- */
-
- /* Effekte der Waffen */
- skdiff += weapon_effskill(at, dt, awp, true, dist > 1);
- if (awp && fval(awp->type, WTF_MISSILE)) {
- skdiff -= is_protected;
- if (awp->type->modifiers) {
- int w;
- for (w = 0; awp->type->modifiers[w].value != 0; ++w) {
- if (awp->type->modifiers[w].flags & WMF_MISSILE_TARGET) {
- /* skill decreases by targeting difficulty (bow -2, catapult -4) */
- skdiff -= awp->type->modifiers[w].value;
- break;
- }
- }
- }
- }
- if (skill_formula == FORMULA_ORIG) {
- weapon *dwp = select_weapon(dt, false, dist > 1);
- skdiff -= weapon_effskill(dt, at, dwp, false, dist > 1);
- }
- return skdiff;
-}
-
-static int setreload(troop at)
-{
- fighter *af = at.fighter;
- const weapon_type *wtype = af->person[at.index].missile->type;
- if (wtype->reload == 0)
- return 0;
- return af->person[at.index].reload = wtype->reload;
-}
-
-int getreload(troop at)
-{
- return at.fighter->person[at.index].reload;
-}
-
-static void
-debug_hit(troop at, const weapon * awp, troop dt, const weapon * dwp,
- int skdiff, int dist, bool success)
-{
- fprintf(bdebug, "%.4s/%d [%6s/%d] %s %.4s/%d [%6s/%d] with %d, distance %d\n",
- unitid(at.fighter->unit), at.index,
- LOC(default_locale, awp ? resourcename(awp->type->itype->rtype,
- 0) : "unarmed"), weapon_effskill(at, dt, awp, true, dist > 1),
- success ? "hits" : "misses", unitid(dt.fighter->unit), dt.index,
- LOC(default_locale, dwp ? resourcename(dwp->type->itype->rtype,
- 0) : "unarmed"), weapon_effskill(dt, at, dwp, false, dist > 1), skdiff,
- dist);
-}
-
-int hits(troop at, troop dt, weapon * awp)
-{
- fighter *af = at.fighter, *df = dt.fighter;
- const armor_type *armor, *shield = 0;
- int skdiff = 0;
- int dist = get_unitrow(af, df->side) + get_unitrow(df, af->side) - 1;
- weapon *dwp = select_weapon(dt, false, dist > 1);
-
- if (!df->alive)
- return 0;
- if (getreload(at))
- return 0;
- if (dist > 1 && (awp == NULL || !fval(awp->type, WTF_MISSILE)))
- return 0;
-
- /* mark this person as hit. */
- df->person[dt.index].flags |= FL_HIT;
-
- if (af->person[at.index].flags & FL_STUNNED) {
- af->person[at.index].flags &= ~FL_STUNNED;
- return 0;
- }
- if ((af->person[at.index].flags & FL_TIRED && rng_int() % 100 < 50)
- || (af->person[at.index].flags & FL_SLEEPING))
- return 0;
-
- /* effect of sp_reeling_arrows combatspell */
- if (af->side->battle->reelarrow && awp && fval(awp->type, WTF_MISSILE)
- && rng_double() < 0.5) {
- return 0;
- }
-
- skdiff = skilldiff(at, dt, dist);
- /* Verteidiger bekommt eine Rüstung */
- armor = select_armor(dt, true);
- if (dwp == NULL || (dwp->type->flags & WTF_USESHIELD)) {
- shield = select_armor(dt, false);
- }
- if (contest(skdiff, dt, armor, shield)) {
- if (bdebug) {
- debug_hit(at, awp, dt, dwp, skdiff, dist, true);
- }
- return 1;
- }
- if (bdebug) {
- debug_hit(at, awp, dt, dwp, skdiff, dist, false);
- }
- return 0;
-}
-
-void dazzle(battle * b, troop * td)
-{
- /* Nicht kumulativ ! */
- if (td->fighter->person[td->index].flags & FL_DAZZLED)
- return;
-
-#ifdef TODO_RUNESWORD
- if (td->fighter->weapon[WP_RUNESWORD].count > td->index) {
- return;
- }
-#endif
- if (td->fighter->person[td->index].flags & FL_COURAGE) {
- return;
- }
-
- if (td->fighter->person[td->index].flags & FL_DAZZLED) {
- return;
- }
-
- td->fighter->person[td->index].flags |= FL_DAZZLED;
- td->fighter->person[td->index].defence--;
-}
-
-/* TODO: Gebäude/Schiffe sollten auch zerstörbar sein. Schwierig im Kampf,
- * besonders bei Schiffen. */
-
-void damage_building(battle * b, building * bldg, int damage_abs)
-{
- bldg->size = _max(1, bldg->size - damage_abs);
-
- /* Wenn Burg, dann gucken, ob die Leute alle noch in das Gebäude passen. */
-
- if (bldg->type->protection) {
- side *s;
-
- bldg->sizeleft = bldg->size;
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
- for (fig = s->fighters; fig; fig = fig->next) {
- if (fig->building == bldg) {
- if (bldg->sizeleft >= fig->unit->number) {
- fig->building = bldg;
- bldg->sizeleft -= fig->unit->number;
- } else {
- fig->building = NULL;
- }
- }
- }
- }
- }
-}
-
-static int attacks_per_round(troop t)
-{
- return t.fighter->person[t.index].speed;
-}
-
-static void make_heroes(battle * b)
-{
- side *s;
- static int hero_speed = 0;
- if (hero_speed == 0) {
- hero_speed = get_param_int(global.parameters, "rules.combat.herospeed", 10);
- }
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
- for (fig = s->fighters; fig; fig = fig->next) {
- unit *u = fig->unit;
- if (fval(u, UFL_HERO)) {
- int i;
- if (!playerrace(u_race(u))) {
- log_error("Hero %s is a %s.\n", unitname(u), u_race(u)->_name);
- }
- for (i = 0; i != u->number; ++i) {
- fig->person[i].speed += (hero_speed - 1);
- }
- }
- }
- }
-}
-
-static void attack(battle * b, troop ta, const att * a, int numattack)
-{
- fighter *af = ta.fighter;
- troop td;
- unit *au = af->unit;
-
- switch (a->type) {
- case AT_COMBATSPELL:
- /* Magier versuchen immer erstmal zu zaubern, erst wenn das
- * fehlschlägt, wird af->magic == 0 und der Magier kämpft
- * konventionell weiter */
- if (numattack == 0 && af->magic > 0) {
- /* wenn der magier in die potenzielle Reichweite von Attacken des
- * Feindes kommt, beginnt er auch bei einem Status von KAEMPFE NICHT,
- * Kampfzauber zu schleudern: */
- if (count_enemies(b, af, melee_range[0], missile_range[1],
- SELECT_ADVANCE | SELECT_DISTANCE | SELECT_FIND)) {
- do_combatspell(ta);
- }
- }
- break;
- case AT_STANDARD: /* Waffen, mag. Gegenstände, Kampfzauber */
- if (numattack > 0 || af->magic <= 0) {
- weapon *wp = ta.fighter->person[ta.index].missile;
- int melee =
- count_enemies(b, af, melee_range[0], melee_range[1],
- SELECT_ADVANCE | SELECT_DISTANCE | SELECT_FIND);
- if (melee)
- wp = preferred_weapon(ta, true);
- /* Sonderbehandlungen */
-
- if (getreload(ta)) {
- ta.fighter->person[ta.index].reload--;
- } else {
- bool standard_attack = true;
- bool reload = false;
- /* spezialattacken der waffe nur, wenn erste attacke in der runde.
- * sonst helden mit feuerschwertern zu mächtig */
- if (numattack == 0 && wp && wp->type->attack) {
- int dead = 0;
- standard_attack = wp->type->attack(&ta, wp->type, &dead);
- if (!standard_attack)
- reload = true;
- af->catmsg += dead;
- if (!standard_attack && af->person[ta.index].last_action < b->turn) {
- af->person[ta.index].last_action = b->turn;
- }
- }
- if (standard_attack) {
- bool missile = false;
- if (wp && fval(wp->type, WTF_MISSILE))
- missile = true;
- if (missile) {
- td = select_opponent(b, ta, missile_range[0], missile_range[1]);
- } else {
- td = select_opponent(b, ta, melee_range[0], melee_range[1]);
- }
- if (!td.fighter)
- return;
- if (ta.fighter->person[ta.index].last_action < b->turn) {
- ta.fighter->person[ta.index].last_action = b->turn;
- }
- reload = true;
- if (hits(ta, td, wp)) {
- const char *d;
- if (wp == NULL)
- d = u_race(au)->def_damage;
- else if (is_riding(ta))
- d = wp->type->damage[1];
- else
- d = wp->type->damage[0];
- terminate(td, ta, a->type, d, missile);
- }
- }
- if (reload && wp && wp->type->reload && !getreload(ta)) {
- int i = setreload(ta);
- if (bdebug) {
- fprintf(bdebug, "%s/%d reloading %d turns\n", unitid(au),
- ta.index, i);
- }
- }
- }
- }
- break;
- case AT_SPELL: /* Extra-Sprüche. Kampfzauber in AT_COMBATSPELL! */
- do_extra_spell(ta, a);
- break;
- case AT_NATURAL:
- td = select_opponent(b, ta, melee_range[0], melee_range[1]);
- if (!td.fighter)
- return;
- if (ta.fighter->person[ta.index].last_action < b->turn) {
- ta.fighter->person[ta.index].last_action = b->turn;
- }
- if (hits(ta, td, NULL)) {
- terminate(td, ta, a->type, a->data.dice, false);
- }
- break;
- case AT_DRAIN_ST:
- td = select_opponent(b, ta, melee_range[0], melee_range[1]);
- if (!td.fighter)
- return;
- if (ta.fighter->person[ta.index].last_action < b->turn) {
- ta.fighter->person[ta.index].last_action = b->turn;
- }
- if (hits(ta, td, NULL)) {
- int c = dice_rand(a->data.dice);
- while (c > 0) {
- if (rng_int() % 2) {
- td.fighter->person[td.index].attack -= 1;
- } else {
- td.fighter->person[td.index].defence -= 1;
- }
- c--;
- }
- }
- break;
- case AT_DRAIN_EXP:
- td = select_opponent(b, ta, melee_range[0], melee_range[1]);
- if (!td.fighter)
- return;
- if (ta.fighter->person[ta.index].last_action < b->turn) {
- ta.fighter->person[ta.index].last_action = b->turn;
- }
- if (hits(ta, td, NULL)) {
- drain_exp(td.fighter->unit, dice_rand(a->data.dice));
- }
- break;
- case AT_DAZZLE:
- td = select_opponent(b, ta, melee_range[0], melee_range[1]);
- if (!td.fighter)
- return;
- if (ta.fighter->person[ta.index].last_action < b->turn) {
- ta.fighter->person[ta.index].last_action = b->turn;
- }
- if (hits(ta, td, NULL)) {
- dazzle(b, &td);
- }
- break;
- case AT_STRUCTURAL:
- td = select_opponent(b, ta, melee_range[0], melee_range[1]);
- if (!td.fighter)
- return;
- if (ta.fighter->person[ta.index].last_action < b->turn) {
- ta.fighter->person[ta.index].last_action = b->turn;
- }
- if (td.fighter->unit->ship) {
- /* FIXME should use damage_ship here? */
- td.fighter->unit->ship->damage +=
- DAMAGE_SCALE * dice_rand(a->data.dice);
- } else if (td.fighter->unit->building) {
- damage_building(b, td.fighter->unit->building, dice_rand(a->data.dice));
- }
- }
-}
-
-void do_attack(fighter * af)
-{
- troop ta;
- unit *au = af->unit;
- side *side = af->side;
- battle *b = side->battle;
-
- ta.fighter = af;
-
- assert(au && au->number);
- /* Da das Zuschlagen auf Einheiten und nicht auf den einzelnen
- * Kämpfern beruht, darf die Reihenfolge und Größe der Einheit keine
- * Rolle spielen, Das tut sie nur dann, wenn jeder, der am Anfang der
- * Runde lebte, auch zuschlagen darf. Ansonsten ist der, der zufällig
- * mit einer großen Einheit zuerst drankommt, extrem bevorteilt. */
- ta.index = af->fighting;
-
- while (ta.index--) {
- /* Wir suchen eine beliebige Feind-Einheit aus. An der können
- * wir feststellen, ob noch jemand da ist. */
- int apr, attacks = attacks_per_round(ta);
- if (!count_enemies(b, af, FIGHT_ROW, LAST_ROW, SELECT_FIND))
- break;
-
- for (apr = 0; apr != attacks; ++apr) {
- int a;
- for (a = 0; a != 10 && u_race(au)->attack[a].type != AT_NONE; ++a) {
- if (apr > 0) {
- /* Wenn die Waffe nachladen muss, oder es sich nicht um einen
- * Waffen-Angriff handelt, dann gilt der Speed nicht. */
- if (u_race(au)->attack[a].type != AT_STANDARD)
- continue;
- else {
- weapon *wp = preferred_weapon(ta, true);
- if (wp != NULL && wp->type->reload)
- continue;
- }
- }
- attack(b, ta, &(u_race(au)->attack[a]), apr);
- }
- }
- }
- /* Der letzte Katapultschütze setzt die
- * Ladezeit neu und generiert die Meldung. */
- if (af->catmsg >= 0) {
- struct message *m =
- msg_message("battle::killed", "unit dead", au, af->catmsg);
- message_all(b, m);
- msg_release(m);
- af->catmsg = -1;
- }
-}
-
-void do_regenerate(fighter * af)
-{
- troop ta;
- unit *au = af->unit;
-
- ta.fighter = af;
- ta.index = af->fighting;
-
- while (ta.index--) {
- af->person[ta.index].hp += effskill(au, SK_STAMINA);
- af->person[ta.index].hp = _min(unit_max_hp(au), af->person[ta.index].hp);
- }
-}
-
-static void add_tactics(tactics * ta, fighter * fig, int value)
-{
- if (value == 0 || value < ta->value)
- return;
- if (value > ta->value) {
- ql_free(ta->fighters);
- ta->fighters = 0;
- }
- ql_push(&ta->fighters, fig);
- ql_push(&fig->side->battle->leaders, fig);
- ta->value = value;
-}
-
-static double horsebonus(const unit * u)
-{
- const item_type *it_horse, *it_elvenhorse, *it_charger;
- int n1 = 0, n2 = 0, n3 = 0;
- item *itm;
- int skl = eff_skill(u, SK_RIDING, u->region);
- const resource_type *rtype;
-
- if (skl < 1) return 0.0;
-
- it_horse = ((rtype = get_resourcetype(R_HORSE))!=NULL) ? rtype->itype : 0;
- it_elvenhorse = ((rtype = get_resourcetype(R_UNICORN))!=NULL) ? rtype->itype : 0;
- it_charger = ((rtype = get_resourcetype(R_CHARGER))!=NULL) ? rtype->itype : 0;
-
- for (itm=u->items; itm; itm = itm->next) {
- if (itm->type->flags & ITF_ANIMAL) {
- if (itm->type == it_elvenhorse)
- n3 += itm->number;
- else if (itm->type == it_charger)
- n2 += itm->number;
- else if (itm->type == it_horse)
- n1 += itm->number;
- }
- }
- if (skl >= 5 && n3 >= u->number)
- return 0.30;
- if (skl >= 3 && n2 + n3 >= u->number)
- return 0.20;
- if (skl >= 1 && n1 + n2 + n3 >= u->number)
- return 0.10;
- return 0.0F;
-}
-
-double fleechance(unit * u)
-{
- double c = 0.20; /* Fluchtwahrscheinlichkeit in % */
- region *r = u->region;
- attrib *a = a_find(u->attribs, &at_fleechance);
- /* Einheit u versucht, dem Getümmel zu entkommen */
-
- c += (eff_skill(u, SK_STEALTH, r) * 0.05);
- c += horsebonus(u);
-
- if (u_race(u) == get_race(RC_HALFLING)) {
- c += 0.20;
- c = _min(c, 0.90);
- } else {
- c = _min(c, 0.75);
- }
-
- if (a != NULL)
- c += a->data.flt;
-
- return c;
-}
-
-/** add a new army to the conflict
- * beware: armies need to be added _at the beginning_ of the list because
- * otherwise join_allies() will get into trouble */
-side *make_side(battle * b, const faction * f, const group * g,
- unsigned int flags, const faction * stealthfaction)
-{
- side *s1 = b->sides + b->nsides;
- bfaction *bf;
-
- if (fval(b->region->terrain, SEA_REGION)) {
- /* every fight in an ocean is short */
- flags |= SIDE_HASGUARDS;
- } else {
- unit *u;
- for (u = b->region->units; u; u = u->next) {
- if (is_guard(u, HELP_ALL)) {
- if (alliedunit(u, f, HELP_GUARD)) {
- flags |= SIDE_HASGUARDS;
- break;
- }
- }
- }
- }
-
- s1->battle = b;
- s1->group = g;
- s1->flags = flags;
- s1->stealthfaction = stealthfaction;
- for (bf = b->factions; bf; bf = bf->next) {
- faction *f2 = bf->faction;
-
- if (f2 == f) {
- s1->bf = bf;
- s1->faction = f2;
- s1->index = b->nsides++;
- s1->nextF = bf->sides;
- bf->sides = s1;
- assert(b->nsides <= MAXSIDES);
- break;
- }
- }
- assert(bf);
- return s1;
-}
-
-troop select_ally(fighter * af, int minrow, int maxrow, int allytype)
-{
- side *as = af->side;
- battle *b = as->battle;
- side *ds;
- int allies = count_allies(as, minrow, maxrow, SELECT_ADVANCE, allytype);
-
- if (!allies) {
- return no_troop;
- }
- allies = rng_int() % allies;
-
- for (ds = b->sides; ds != b->sides + b->nsides; ++ds) {
- if ((allytype == ALLY_ANY && helping(as, ds)) || (allytype == ALLY_SELF
- && as->faction == ds->faction)) {
- fighter *df;
- for (df = ds->fighters; df; df = df->next) {
- int dr = get_unitrow(df, NULL);
- if (dr >= minrow && dr <= maxrow) {
- if (df->alive - df->removed > allies) {
- troop dt;
- assert(allies >= 0);
- dt.index = allies;
- dt.fighter = df;
- return dt;
- }
- allies -= df->alive;
- }
- }
- }
- }
- assert(!"we should never have gotten here");
- return no_troop;
-}
-
-static int loot_quota(const unit * src, const unit * dst,
- const item_type * type, int n)
-{
- static float divisor = -1;
- if (dst && src && src->faction != dst->faction) {
- if (divisor < 0) {
- divisor = get_param_flt(global.parameters, "rules.items.loot_divisor", 1);
- assert(divisor == 0 || divisor >= 1);
- }
- if (divisor >= 1) {
- double r = n / divisor;
- int x = (int)r;
-
- r = r - x;
- if (chance(r))
- ++x;
-
- return x;
- }
- }
- return n;
-}
-
-static void loot_items(fighter * corpse)
-{
- unit *u = corpse->unit;
- item *itm = u->items;
- battle *b = corpse->side->battle;
- int dead = dead_fighters(corpse);
-
- if (dead <= 0)
- return;
-
- while (itm) {
- float lootfactor = dead / (float)u->number; /* only loot the dead! */
- int maxloot = (int)(itm->number * lootfactor);
- if (maxloot > 0) {
- int i = _min(10, maxloot);
- for (; i != 0; --i) {
- int loot = maxloot / i;
-
- if (loot > 0) {
- fighter *fig = NULL;
- int looting = 0;
- int maxrow = 0;
- /* mustloot: we absolutely, positively must have somebody loot this thing */
- int mustloot = itm->type->flags & (ITF_CURSED | ITF_NOTLOST);
-
- itm->number -= loot;
- maxloot -= loot;
-
- if (is_monsters(u->faction) && (loot_rules & LOOT_MONSTERS)) {
- looting = 1;
- } else if (loot_rules & LOOT_OTHERS) {
- looting = 1;
- } else if (loot_rules & LOOT_SELF) {
- looting = 2;
- }
- if (looting) {
- if (mustloot) {
- maxrow = LAST_ROW;
- } else if (loot_rules & LOOT_KEEPLOOT) {
- int lootchance = 50 + b->keeploot;
- if (rng_int() % 100 < lootchance) {
- maxrow = BEHIND_ROW;
- }
- } else {
- maxrow = LAST_ROW;
- }
- }
- if (maxrow > 0) {
- if (looting == 1) {
- /* enemies get dibs */
- fig = select_enemy(corpse, FIGHT_ROW, maxrow, 0).fighter;
- }
- if (!fig) {
- /* self and allies get second pick */
- fig = select_ally(corpse, FIGHT_ROW, LAST_ROW, ALLY_SELF).fighter;
- }
- }
-
- if (fig) {
- int trueloot =
- mustloot ? loot : loot_quota(corpse->unit, fig->unit, itm->type,
- loot);
- if (trueloot > 0) {
- item *l = fig->loot;
- while (l && l->type != itm->type)
- l = l->next;
- if (!l) {
- l = calloc(sizeof(item), 1);
- l->next = fig->loot;
- fig->loot = l;
- l->type = itm->type;
- }
- l->number += trueloot;
- }
- }
- }
- }
- }
- itm = itm->next;
- }
-}
-
-static bool seematrix(const faction * f, const side * s)
-{
- if (f == s->faction)
- return true;
- if (s->flags & SIDE_STEALTH)
- return false;
- return true;
-}
-
-static double PopulationDamage(void)
-{
- static double value = -1.0;
- if (value < 0) {
- int damage =
- get_param_int(global.parameters, "rules.combat.populationdamage",
- BATTLE_KILLS_PEASANTS);
- value = damage / 100.0;
- }
- return value;
-}
-
-static void battle_effects(battle * b, int dead_players)
-{
- region *r = b->region;
- int dead_peasants =
- _min(rpeasants(r), (int)(dead_players * PopulationDamage()));
- if (dead_peasants) {
- deathcounts(r, dead_peasants + dead_players);
- chaoscounts(r, dead_peasants / 2);
- rsetpeasants(r, rpeasants(r) - dead_peasants);
- }
-}
-
-static void reorder_fleeing(region * r)
-{
- unit **usrc = &r->units;
- unit **udst = &r->units;
- unit *ufirst = NULL;
- unit *u;
-
- for (; *udst; udst = &u->next) {
- u = *udst;
- }
-
- for (u = *usrc; u != ufirst; u = *usrc) {
- if (u->next && fval(u, UFL_FLEEING)) {
- *usrc = u->next;
- *udst = u;
- udst = &u->next;
- if (!ufirst)
- ufirst = u;
- } else {
- usrc = &u->next;
- }
- }
- *udst = NULL;
-}
-
-static void aftermath(battle * b)
-{
- region *r = b->region;
- ship *sh;
- side *s;
- int dead_players = 0;
- bfaction *bf;
- bool ships_damaged = (bool) (b->turn + (b->has_tactics_turn ? 1 : 0) > 2); /* only used for ship damage! */
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *df;
- s->dead = 0;
-
- for (df = s->fighters; df; df = df->next) {
- unit *du = df->unit;
- int dead = dead_fighters(df);
-
- /* tote insgesamt: */
- s->dead += dead;
- /* Tote, die wiederbelebt werde koennen: */
- if (playerrace(u_race(df->unit))) {
- s->casualties += dead;
- }
- if (df->hits + df->kills) {
- struct message *m =
- msg_message("killsandhits", "unit hits kills", du, df->hits,
- df->kills);
- message_faction(b, du->faction, m);
- msg_release(m);
- }
- }
- }
-
- /* POSTCOMBAT */
- do_combatmagic(b, DO_POSTCOMBATSPELL);
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- int snumber = 0;
- fighter *df;
- bool relevant = false; /* Kampf relevant für diese Partei? */
- if (!fval(s, SIDE_HASGUARDS)) {
- relevant = true;
- }
- s->flee = 0;
-
- for (df = s->fighters; df; df = df->next) {
- unit *du = df->unit;
- int dead = dead_fighters(df);
- int sum_hp = 0;
- int n;
- int flags = 0;
-
- for (n = 0; n != df->alive; ++n) {
- if (df->person[n].hp > 0) {
- sum_hp += df->person[n].hp;
- }
- }
- snumber += du->number;
- if (relevant) {
- flags = UFL_LONGACTION | UFL_NOTMOVING;
- if (du->status == ST_FLEE) {
- flags -= UFL_NOTMOVING;
- }
- }
- if (df->alive == 0) {
- flags |= UFL_DEAD;
- }
- if (flags) {
- fset(du, flags);
- }
- if (sum_hp + df->run.hp < du->hp) {
- /* someone on the ship got damaged, damage the ship */
- ship *sh = du->ship ? du->ship : leftship(du);
- if (sh)
- fset(sh, SF_DAMAGED);
- }
-
- if (df->alive == du->number) {
- du->hp = sum_hp;
- continue; /* nichts passiert */
- } else if (df->run.hp) {
- if (df->alive == 0) {
- /* Report the casualties */
- reportcasualties(b, df, dead);
-
- /* Zuerst dürfen die Feinde plündern, die mitgenommenen Items
- * stehen in fig->run.items. Dann werden die Fliehenden auf
- * die leere (tote) alte Einheit gemapt */
- if (!fval(df, FIG_NOLOOT)) {
- loot_items(df);
- }
- scale_number(du, df->run.number);
- du->hp = df->run.hp;
- setguard(du, GUARD_NONE);
- /* must leave ships or buildings, or a stealthy hobbit
- * can hold castles indefinitely */
- if (!fval(r->terrain, SEA_REGION)) {
- leave(du, true); /* even region owners have to flee */
- }
- fset(du, UFL_FLEEING);
- } else {
- /* nur teilweise geflohene Einheiten mergen sich wieder */
- df->alive += df->run.number;
- s->size[0] += df->run.number;
- s->size[statusrow(df->status)] += df->run.number;
- s->alive += df->run.number;
- sum_hp += df->run.hp;
- df->run.number = 0;
- df->run.hp = 0;
- /* df->run.region = NULL; */
-
- reportcasualties(b, df, dead);
-
- scale_number(du, df->alive);
- du->hp = sum_hp;
- }
- } else {
- if (df->alive == 0) {
- /* alle sind tot, niemand geflohen. Einheit auflösen */
- df->run.number = 0;
- df->run.hp = 0;
-
- /* Report the casualties */
- reportcasualties(b, df, dead);
-
- /* Distribute Loot */
- loot_items(df);
-
- setguard(du, GUARD_NONE);
- scale_number(du, 0);
- } else {
- df->run.number = 0;
- df->run.hp = 0;
-
- reportcasualties(b, df, dead);
-
- scale_number(du, df->alive);
- du->hp = sum_hp;
- }
- }
- s->flee += df->run.number;
-
- if (playerrace(u_race(du))) {
- /* tote im kampf werden zu regionsuntoten:
- * for each of them, a peasant will die as well */
- dead_players += dead;
- }
- if (du->hp < du->number) {
- log_error("%s has less hitpoints (%u) than people (%u)\n", itoa36(du->no), du->hp, du->number);
- du->hp = du->number;
- }
- }
- s->alive += s->healed;
- assert(snumber == s->flee + s->alive + s->dead);
- }
-
- battle_effects(b, dead_players);
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- message *seen = msg_message("battle::army_report",
- "index abbrev dead fled survived",
- army_index(s), sideabkz(s, false), s->dead, s->flee, s->alive);
- message *unseen = msg_message("battle::army_report",
- "index abbrev dead fled survived",
- army_index(s), "-?-", s->dead, s->flee, s->alive);
-
- for (bf = b->factions; bf; bf = bf->next) {
- faction *f = bf->faction;
- message *m = seematrix(f, s) ? seen : unseen;
-
- message_faction(b, f, m);
- }
-
- msg_release(seen);
- msg_release(unseen);
- }
-
- /* Wir benutzen drifted, um uns zu merken, ob ein Schiff
- * schonmal Schaden genommen hat. (moved und drifted
- * sollten in flags überführt werden */
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *df;
-
- for (df = s->fighters; df; df = df->next) {
- unit *du = df->unit;
- item *l;
-
- /* Beute verteilen */
- for (l = df->loot; l; l = l->next) {
- const item_type *itype = l->type;
- message *m =
- msg_message("battle_loot", "unit amount item", du, l->number,
- itype->rtype);
- message_faction(b, du->faction, m);
- msg_release(m);
- i_change(&du->items, itype, l->number);
- }
-
- /* Wenn sich die Einheit auf einem Schiff befindet, wird
- * dieses Schiff beschädigt. Andernfalls ein Schiff, welches
- * evt. zuvor verlassen wurde. */
- if (ships_damaged) {
- if (du->ship)
- sh = du->ship;
- else
- sh = leftship(du);
-
- if (sh && fval(sh, SF_DAMAGED)) {
- int n = b->turn - 2;
- if (n > 0) {
- float dmg =
- get_param_flt(global.parameters, "rules.ship.damage.battleround",
- 0.05F);
- damage_ship(sh, dmg * n);
- freset(sh, SF_DAMAGED);
- }
- }
- }
- }
- }
-
- if (ships_damaged) {
- ship **sp = &r->ships;
-
- while (*sp) {
- ship *sh = *sp;
- freset(sh, SF_DAMAGED);
- if (sh->damage >= sh->size * DAMAGE_SCALE) {
- remove_ship(sp, sh);
- }
- if (*sp == sh)
- sp = &sh->next;
- }
- }
-
- reorder_fleeing(r);
-
- if (bdebug) {
- fprintf(bdebug, "The battle lasted %d turns, %s and %s.\n",
- b->turn,
- b->has_tactics_turn ? "had a tactic turn" : "had no tactic turn",
- ships_damaged ? "was relevant" : "was not relevant.");
- }
-}
-
-static void battle_punit(unit * u, battle * b)
-{
- bfaction *bf;
- strlist *S, *x;
-
- for (bf = b->factions; bf; bf = bf->next) {
- faction *f = bf->faction;
-
- S = 0;
- spunit(&S, f, u, 4, see_battle);
- for (x = S; x; x = x->next) {
- fbattlerecord(b, f, x->s);
- if (bdebug && u->faction == f) {
- fputs(x->s, bdebug);
- fputc('\n', bdebug);
- }
- }
- if (S)
- freestrlist(S);
- }
-}
-
-static void print_fighters(battle * b, const side * s)
-{
- fighter *df;
- int row;
-
- for (row = 1; row != NUMROWS; ++row) {
- message *m = NULL;
-
- for (df = s->fighters; df; df = df->next) {
- unit *du = df->unit;
- int thisrow = statusrow(df->unit->status);
-
- if (row == thisrow) {
- if (m == NULL) {
- m = msg_message("battle::row_header", "row", row);
- message_all(b, m);
- }
- battle_punit(du, b);
- }
- }
- if (m != NULL)
- msg_release(m);
- }
-}
-
-bool is_attacker(const fighter * fig)
-{
- return fval(fig, FIG_ATTACKER) != 0;
-}
-
-static void set_attacker(fighter * fig)
-{
- fset(fig, FIG_ATTACKER);
-}
-
-static void print_header(battle * b)
-{
- bfaction *bf;
- char zText[32 * MAXSIDES];
-
- for (bf = b->factions; bf; bf = bf->next) {
- message *m;
- faction *f = bf->faction;
- const char *lastf = NULL;
- bool first = false;
- side *s;
- char *bufp = zText;
- size_t size = sizeof(zText) - 1;
- int bytes;
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *df;
- for (df = s->fighters; df; df = df->next) {
- if (is_attacker(df)) {
- if (first) {
- bytes = (int)strlcpy(bufp, ", ", size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- }
- if (lastf) {
- bytes = (int)strlcpy(bufp, (const char *)lastf, size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- first = true;
- }
- if (seematrix(f, s))
- lastf = sidename(s);
- else
- lastf = LOC(f->locale, "unknown_faction_dative");
- break;
- }
- }
- }
- if (first) {
- bytes = (int)strlcpy(bufp, " ", size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- bytes = (int)strlcpy(bufp, (const char *)LOC(f->locale, "and"), size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- bytes = (int)strlcpy(bufp, " ", size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- }
- if (lastf) {
- bytes = (int)strlcpy(bufp, (const char *)lastf, size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- }
-
- m = msg_message("battle::starters", "factions", zText);
- message_faction(b, f, m);
- msg_release(m);
- }
-}
-
-static void print_stats(battle * b)
-{
- side *s2;
- side *s;
- int i = 0;
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- bfaction *bf;
-
- ++i;
-
- for (bf = b->factions; bf; bf = bf->next) {
- faction *f = bf->faction;
- const char *loc_army = LOC(f->locale, "battle_army");
- char *bufp;
- const char *header;
- size_t rsize, size;
- int komma;
- const char *sname =
- seematrix(f, s) ? sidename(s) : LOC(f->locale, "unknown_faction");
- message *msg;
- char buf[1024];
-
- message_faction(b, f, msg_separator);
-
- msg = msg_message("battle_army", "index name", army_index(s), sname);
- message_faction(b, f, msg);
- msg_release(msg);
-
- bufp = buf;
- size = sizeof(buf);
- komma = 0;
- header = LOC(f->locale, "battle_opponents");
-
- for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
- if (enemy(s2, s)) {
- const char *abbrev = seematrix(f, s2) ? sideabkz(s2, false) : "-?-";
- rsize = slprintf(bufp, size, "%s %s %d(%s)",
- komma++ ? "," : (const char *)header, loc_army, army_index(s2),
- abbrev);
- if (rsize > size)
- rsize = size - 1;
- size -= rsize;
- bufp += rsize;
- }
- }
- if (komma)
- fbattlerecord(b, f, buf);
-
- bufp = buf;
- size = sizeof(buf);
- komma = 0;
- header = LOC(f->locale, "battle_helpers");
-
- for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
- if (friendly(s2, s)) {
- const char *abbrev = seematrix(f, s2) ? sideabkz(s2, false) : "-?-";
- rsize = slprintf(bufp, size, "%s %s %d(%s)",
- komma++ ? "," : (const char *)header, loc_army, army_index(s2),
- abbrev);
- if (rsize > size)
- rsize = size - 1;
- size -= rsize;
- bufp += rsize;
- }
- }
- if (komma)
- fbattlerecord(b, f, buf);
-
- bufp = buf;
- size = sizeof(buf);
- komma = 0;
- header = LOC(f->locale, "battle_attack");
-
- for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
- if (s->relations[s2->index] & E_ATTACKING) {
- const char *abbrev = seematrix(f, s2) ? sideabkz(s2, false) : "-?-";
- rsize =
- slprintf(bufp, size, "%s %s %d(%s)",
- komma++ ? "," : (const char *)header, loc_army, army_index(s2),
- abbrev);
- if (rsize > size)
- rsize = size - 1;
- size -= rsize;
- bufp += rsize;
- }
- }
- if (komma)
- fbattlerecord(b, f, buf);
- }
-
- if (bdebug && s->faction) {
- if (f_get_alliance(s->faction)) {
- fprintf(bdebug, "##### %s (%s/%d)\n", s->faction->name,
- itoa36(s->faction->no),
- s->faction->alliance ? s->faction->alliance->id : 0);
- } else {
- fprintf(bdebug, "##### %s (%s)\n", s->faction->name,
- itoa36(s->faction->no));
- }
- }
- print_fighters(b, s);
- }
-
- message_all(b, msg_separator);
-
- /* Besten Taktiker ermitteln */
-
- b->max_tactics = 0;
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- if (!ql_empty(s->leader.fighters)) {
- b->max_tactics = _max(b->max_tactics, s->leader.value);
- }
- }
-
- if (b->max_tactics > 0) {
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- if (s->leader.value == b->max_tactics) {
- quicklist *ql;
- int qi;
-
- for (qi = 0, ql = s->leader.fighters; ql; ql_advance(&ql, &qi, 1)) {
- fighter *tf = (fighter *) ql_get(ql, qi);
- unit *u = tf->unit;
- message *m = NULL;
- if (!is_attacker(tf)) {
- m = msg_message("battle::tactics_lost", "unit", u);
- } else {
- m = msg_message("battle::tactics_won", "unit", u);
- }
- message_all(b, m);
- msg_release(m);
- }
- }
- }
- }
-}
-
-static int weapon_weight(const weapon * w, bool missile)
-{
- if (missile == i2b(fval(w->type, WTF_MISSILE))) {
- return w->attackskill + w->defenseskill;
- }
- return 0;
-}
-
-side * get_side(battle * b, const struct unit * u)
-{
- side * s;
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- if (s->faction==u->faction) {
- fighter * fig;
- for (fig=s->fighters;fig;fig=fig->next) {
- if (fig->unit==u) {
- return s;
- }
- }
- }
- }
- return 0;
-}
-
-side * find_side(battle * b, const faction * f, const group * g, int flags, const faction * stealthfaction)
-{
- side * s;
- static int rule_anon_battle = -1;
-
- if (rule_anon_battle < 0) {
- rule_anon_battle = get_param_int(global.parameters, "rules.stealth.anon_battle", 1);
- }
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- if (s->faction == f && s->group == g) {
- int s1flags = flags | SIDE_HASGUARDS;
- int s2flags = s->flags | SIDE_HASGUARDS;
- if (rule_anon_battle && s->stealthfaction != stealthfaction) {
- continue;
- }
- if (s1flags == s2flags) {
- return s;
- }
- }
- }
- return 0;
-}
-
-fighter *make_fighter(battle * b, unit * u, side * s1, bool attack)
-{
-#define WMAX 20
- weapon weapons[WMAX];
- int owp[WMAX];
- int dwp[WMAX];
- int w = 0;
- region *r = b->region;
- item *itm;
- fighter *fig = NULL;
- int h, i, tactics = eff_skill(u, SK_TACTICS, r);
- int berserk;
- int strongmen;
- int speeded = 0, speed = 1;
- bool pr_aid = false;
- int rest;
- const group *g = NULL;
- const attrib *a = a_find(u->attribs, &at_otherfaction);
- const faction *stealthfaction = a ? get_otherfaction(a) : NULL;
- unsigned int flags = 0;
-
- assert(u->number);
- if (fval(u, UFL_ANON_FACTION) != 0)
- flags |= SIDE_STEALTH;
- if (!(AllianceAuto() & HELP_FIGHT) && fval(u, UFL_GROUP)) {
- const attrib *agroup = a_find(u->attribs, &at_group);
- if (agroup != NULL)
- g = (const group *)agroup->data.v;
- }
-
- /* Illusionen und Zauber kaempfen nicht */
- if (fval(u_race(u), RCF_ILLUSIONARY) || idle(u->faction) || u->number == 0) {
- return NULL;
- }
- if (s1 == NULL) {
- s1 = find_side(b, u->faction, g, flags, stealthfaction);
- /* aliances are moved out of make_fighter and will be handled later */
- if (!s1) {
- s1 = make_side(b, u->faction, g, flags, stealthfaction);
- } else if (!stealthfaction) {
- s1->stealthfaction = NULL;
- }
- /* Zu diesem Zeitpunkt ist attacked noch 0, da die Einheit für noch
- * keinen Kampf ausgewählt wurde (sonst würde ein fighter existieren) */
- }
- fig = (struct fighter*)calloc(1, sizeof(struct fighter));
-
- fig->next = s1->fighters;
- s1->fighters = fig;
-
- fig->unit = u;
- /* In einer Burg muß man a) nicht Angreifer sein, und b) drin sein, und
- * c) noch Platz finden. d) menschanähnlich sein */
- if (attack) {
- set_attacker(fig);
- } else {
- building *bld = u->building;
- if (bld && bld->sizeleft >= u->number && playerrace(u_race(u))) {
- fig->building = bld;
- fig->building->sizeleft -= u->number;
- }
- }
- fig->status = u->status;
- fig->side = s1;
- fig->alive = u->number;
- fig->side->alive += u->number;
- fig->side->battle->alive += u->number;
- fig->catmsg = -1;
-
- /* Freigeben nicht vergessen! */
- fig->person = (struct person*)calloc(fig->alive, sizeof(struct person));
-
- h = u->hp / u->number;
- assert(h);
- rest = u->hp % u->number;
-
- /* Effekte von Sprüchen */
-
- {
- static const curse_type *speed_ct;
- speed_ct = ct_find("speed");
- if (speed_ct) {
- curse *c = get_curse(u->attribs, speed_ct);
- if (c) {
- speeded = get_cursedmen(u, c);
- speed = curse_geteffect_int(c);
- }
- }
- }
-
- /* Effekte von Alchemie */
- berserk = get_effect(u, oldpotiontype[P_BERSERK]);
- /* change_effect wird in ageing gemacht */
-
- /* Effekte von Artefakten */
- strongmen = _min(fig->unit->number, trollbelts(u));
-
- /* Hitpoints, Attack- und Defence-Boni für alle Personen */
- for (i = 0; i < fig->alive; i++) {
- assert(i < fig->unit->number);
- fig->person[i].hp = h;
- if (i < rest)
- fig->person[i].hp++;
-
- if (i < speeded)
- fig->person[i].speed = speed;
- else
- fig->person[i].speed = 1;
-
- if (i < berserk) {
- fig->person[i].attack++;
- }
- /* Leute mit einem Aid-Prayer bekommen +1 auf fast alles. */
- if (pr_aid) {
- fig->person[i].attack++;
- fig->person[i].defence++;
- fig->person[i].damage++;
- fig->person[i].damage_rear++;
- fig->person[i].flags |= FL_COURAGE;
- }
- /* Leute mit Kraftzauber machen +2 Schaden im Nahkampf. */
- if (i < strongmen) {
- fig->person[i].damage += 2;
- }
- }
-
- /* Für alle Waffengattungen wird bestimmt, wie viele der Personen mit
- * ihr kämpfen könnten, und was ihr Wert darin ist. */
- if (u_race(u)->battle_flags & BF_EQUIPMENT) {
- int oi = 0, di = 0;
- for (itm = u->items; itm && w != WMAX; itm = itm->next) {
- const weapon_type *wtype = resource2weapon(itm->type->rtype);
- if (wtype == NULL || itm->number == 0)
- continue;
- weapons[w].attackskill = weapon_skill(wtype, u, true);
- weapons[w].defenseskill = weapon_skill(wtype, u, false);
- if (weapons[w].attackskill >= 0 || weapons[w].defenseskill >= 0) {
- weapons[w].type = wtype;
- weapons[w].used = 0;
- weapons[w].count = itm->number;
- ++w;
- }
- assert(w != WMAX);
- }
- fig->weapons = (weapon *) calloc(sizeof(weapon), w + 1);
- memcpy(fig->weapons, weapons, w * sizeof(weapon));
-
- for (i = 0; i != w; ++i) {
- int j, o = 0, d = 0;
- for (j = 0; j != i; ++j) {
- if (weapon_weight(fig->weapons + j,
- true) >= weapon_weight(fig->weapons + i, true))
- ++d;
- if (weapon_weight(fig->weapons + j,
- false) >= weapon_weight(fig->weapons + i, false))
- ++o;
- }
- for (j = i + 1; j != w; ++j) {
- if (weapon_weight(fig->weapons + j,
- true) > weapon_weight(fig->weapons + i, true))
- ++d;
- if (weapon_weight(fig->weapons + j,
- false) > weapon_weight(fig->weapons + i, false))
- ++o;
- }
- owp[o] = i;
- dwp[d] = i;
- }
- /* jetzt enthalten owp und dwp eine absteigend schlechter werdende Liste der Waffen
- * oi and di are the current index to the sorted owp/dwp arrays
- * owp, dwp contain indices to the figther::weapons array */
-
- /* hand out melee weapons: */
- for (i = 0; i != fig->alive; ++i) {
- int wpless = weapon_skill(NULL, u, true);
- while (oi != w
- && (fig->weapons[owp[oi]].used == fig->weapons[owp[oi]].count
- || fval(fig->weapons[owp[oi]].type, WTF_MISSILE))) {
- ++oi;
- }
- if (oi == w)
- break; /* no more weapons available */
- if (weapon_weight(fig->weapons + owp[oi], false) <= wpless) {
- continue; /* we fight better with bare hands */
- }
- fig->person[i].melee = &fig->weapons[owp[oi]];
- ++fig->weapons[owp[oi]].used;
- }
- /* hand out missile weapons (from back to front, in case of mixed troops). */
- for (di = 0, i = fig->alive; i-- != 0;) {
- while (di != w
- && (fig->weapons[dwp[di]].used == fig->weapons[dwp[di]].count
- || !fval(fig->weapons[dwp[di]].type, WTF_MISSILE))) {
- ++di;
- }
- if (di == w)
- break; /* no more weapons available */
- if (weapon_weight(fig->weapons + dwp[di], true) > 0) {
- fig->person[i].missile = &fig->weapons[dwp[di]];
- ++fig->weapons[dwp[di]].used;
- }
- }
- }
-
- s1->size[statusrow(fig->status)] += u->number;
- s1->size[SUM_ROW] += u->number;
- if (u_race(u)->battle_flags & BF_NOBLOCK) {
- s1->nonblockers[statusrow(fig->status)] += u->number;
- }
-
- if (u_race(fig->unit)->flags & RCF_HORSE) {
- fig->horses = fig->unit->number;
- fig->elvenhorses = 0;
- } else {
- const resource_type *rt_horse = 0;
- const resource_type *rt_elvenhorse = 0;
- rt_elvenhorse = get_resourcetype(R_UNICORN);
- rt_horse = get_resourcetype(R_CHARGER);
- if (!rt_horse) {
- rt_horse = get_resourcetype(R_HORSE);
- }
- fig->horses = rt_horse ? i_get(u->items, rt_horse->itype) : 0;
- fig->elvenhorses = rt_elvenhorse ? i_get(u->items, rt_elvenhorse->itype) : 0;
- }
-
- if (u_race(u)->battle_flags & BF_EQUIPMENT) {
- for (itm = u->items; itm; itm = itm->next) {
- if (itm->type->rtype->atype) {
- if (i_canuse(u, itm->type)) {
- struct armor *adata = (struct armor *)malloc(sizeof(armor)), **aptr;
- adata->atype = itm->type->rtype->atype;
- adata->count = itm->number;
- for (aptr = &fig->armors; *aptr; aptr = &(*aptr)->next) {
- if (adata->atype->prot > (*aptr)->atype->prot)
- break;
- }
- adata->next = *aptr;
- *aptr = adata;
- }
- }
- }
- }
-
- /* Jetzt muß noch geschaut werden, wo die Einheit die jeweils besten
- * Werte hat, das kommt aber erst irgendwo später. Ich entscheide
- * wärend des Kampfes, welche ich nehme, je nach Gegner. Deswegen auch
- * keine addierten boni. */
-
- /* Zuerst mal die Spezialbehandlung gewisser Sonderfälle. */
- fig->magic = eff_skill(u, SK_MAGIC, r);
-
- if (fig->horses) {
- if (!fval(r->terrain, CAVALRY_REGION) || r_isforest(r)
- || eff_skill(u, SK_RIDING, r) < CavalrySkill()
- || u_race(u) == get_race(RC_TROLL) || fval(u, UFL_WERE))
- fig->horses = 0;
- }
-
- if (fig->elvenhorses) {
- if (eff_skill(u, SK_RIDING, r) < 5 || u_race(u) == get_race(RC_TROLL)
- || fval(u, UFL_WERE))
- fig->elvenhorses = 0;
- }
-
- /* Schauen, wie gut wir in Taktik sind. */
- if (tactics > 0 && u_race(u) == get_race(RC_INSECT))
- tactics -= 1 - (int)log10(fig->side->size[SUM_ROW]);
-#ifdef TACTICS_MODIFIER
- if (tactics > 0 && statusrow(fig->status) == FIGHT_ROW)
- tactics += TACTICS_MODIFIER;
- if (tactics > 0 && statusrow(fig->status) > BEHIND_ROW) {
- tactics -= TACTICS_MODIFIER;
- }
-#endif
-
- if (tactics > 0) {
- int bonus = 0;
-
- for (i = 0; i < fig->alive; i++) {
- int p_bonus = 0;
- int rnd;
-
- do {
- rnd = rng_int() % 100;
- if (rnd >= 40 && rnd <= 69)
- p_bonus += 1;
- else if (rnd <= 89)
- p_bonus += 2;
- else
- p_bonus += 3;
- } while (rnd >= 97);
- bonus = _max(p_bonus, bonus);
- }
- tactics += bonus;
- }
-
- add_tactics(&fig->side->leader, fig, tactics);
- ++b->nfighters;
- return fig;
-}
-
-fighter * get_fighter(battle * b, const struct unit * u)
-{
- side * s;
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
- if (s->faction == u->faction) {
- for (fig = s->fighters; fig; fig = fig->next) {
- if (fig->unit == u) {
- return fig;
- }
- }
- }
- }
- return 0;
-}
-
-static int join_battle(battle * b, unit * u, bool attack, fighter ** cp)
-{
- side *s;
- fighter *c = NULL;
-
- if (!attack) {
- attrib *a = a_find(u->attribs, &at_fleechance);
- if (a != NULL) {
- if (rng_double() <= a->data.flt) {
- *cp = NULL;
- return false;
- }
- }
- }
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
- if (s->faction == u->faction) {
- for (fig = s->fighters; fig; fig = fig->next) {
- if (fig->unit == u) {
- c = fig;
- if (attack) {
- set_attacker(fig);
- }
- break;
- }
- }
- }
- }
- if (!c) {
- *cp = make_fighter(b, u, NULL, attack);
- return *cp != NULL;
- }
- *cp = c;
- return false;
-}
-
-static const char *simplename(region * r)
-{
- int i;
- static char name[17];
- const char *cp = rname(r, default_locale);
- for (i = 0; *cp && i != 16; ++i, ++cp) {
- int c = *(unsigned char *)cp;
- while (c && !isalpha(c) && !isxspace(c)) {
- ++cp;
- c = *(unsigned char *)cp;
- }
- if (isxspace(c))
- name[i] = '_';
- else
- name[i] = *cp;
- if (c == 0)
- break;
- }
- name[i] = 0;
- return name;
-}
-
-battle *make_battle(region * r)
-{
- battle *b = (battle *)calloc(1, sizeof(battle));
- unit *u;
- bfaction *bf;
- building * bld;
- static int max_fac_no = 0; /* need this only once */
-
- /* Alle Mann raus aus der Burg! */
- for (bld = r->buildings; bld != NULL; bld = bld->next)
- bld->sizeleft = bld->size;
-
- if (battledebug) {
- char zText[MAX_PATH];
- char zFilename[MAX_PATH];
- sprintf(zText, "%s/battles", basepath());
- _mkdir(zText);
- sprintf(zFilename, "%s/battle-%d-%s.log", zText, obs_count, simplename(r));
- bdebug = fopen(zFilename, "w");
- if (!bdebug)
- log_error("battles cannot be debugged\n");
- else {
- const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf, 0 };
- fwrite(utf8_bom, 1, 3, bdebug);
- fprintf(bdebug, "In %s findet ein Kampf statt:\n", rname(r,
- default_locale));
- }
- obs_count++;
- }
-
- b->region = r;
- b->plane = getplane(r);
- /* Finde alle Parteien, die den Kampf beobachten können: */
- for (u = r->units; u; u = u->next) {
- if (u->number > 0) {
- if (!fval(u->faction, FFL_MARK)) {
- fset(u->faction, FFL_MARK);
- for (bf = b->factions; bf; bf = bf->next) {
- if (bf->faction == u->faction)
- break;
- }
- if (!bf) {
- bf = (bfaction *)calloc(sizeof(bfaction), 1);
- ++b->nfactions;
- bf->faction = u->faction;
- bf->next = b->factions;
- b->factions = bf;
- }
- }
- }
- }
-
- for (bf = b->factions; bf; bf = bf->next) {
- faction *f = bf->faction;
- max_fac_no = _max(max_fac_no, f->no);
- freset(f, FFL_MARK);
- }
- return b;
-}
-
-static void free_side(side * si)
-{
- ql_free(si->leader.fighters);
-}
-
-static void free_fighter(fighter * fig)
-{
- while (fig->loot) {
- i_free(i_remove(&fig->loot, fig->loot));
- }
- while (fig->armors) {
- armor *a = fig->armors;
- fig->armors = a->next;
- free(a);
- }
- free(fig->person);
- free(fig->weapons);
-
-}
-
-static void free_battle(battle * b)
-{
- int max_fac_no = 0;
-
- if (bdebug) {
- fclose(bdebug);
- }
-
- while (b->factions) {
- bfaction *bf = b->factions;
- faction *f = bf->faction;
- b->factions = bf->next;
- max_fac_no = _max(max_fac_no, f->no);
- free(bf);
- }
-
- ql_free(b->leaders);
- ql_foreach(b->meffects, free);
- ql_free(b->meffects);
-
- battle_free(b);
-}
-
-static int *get_alive(side * s)
-{
-#if 0
- static int alive[NUMROWS];
- fighter *fig;
- memset(alive, 0, NUMROWS * sizeof(int));
- for (fig = s->fighters; fig; fig = fig->next) {
- if (fig->alive > 0) {
- int row = statusrow(fig);
- alive[row] += fig->alive;
- }
- }
- return alive;
-#endif
- return s->size;
-}
-
-static int battle_report(battle * b)
-{
- side *s, *s2;
- bool cont = false;
- bool komma;
- bfaction *bf;
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- if (s->alive - s->removed > 0) {
- for (s2 = b->sides; s2 != b->sides + b->nsides; ++s2) {
- if (s2->alive - s2->removed > 0 && enemy(s, s2)) {
- cont = true;
- break;
- }
- }
- if (cont)
- break;
- }
- }
-
- if (verbosity > 0)
- log_printf(stdout, " %d", b->turn);
- fflush(stdout);
-
- for (bf = b->factions; bf; bf = bf->next) {
- faction *fac = bf->faction;
- char buf[32 * MAXSIDES];
- char *bufp = buf;
- int bytes;
- size_t size = sizeof(buf) - 1;
- message *m;
-
- message_faction(b, fac, msg_separator);
-
- if (cont)
- m = msg_message("battle::lineup", "turn", b->turn);
- else
- m = msg_message("battle::after", "");
- message_faction(b, fac, m);
- msg_release(m);
-
- komma = false;
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- if (s->alive) {
- int r, k = 0, *alive = get_alive(s);
- int l = FIGHT_ROW;
- const char *abbrev = seematrix(fac, s) ? sideabkz(s, false) : "-?-";
- const char *loc_army = LOC(fac->locale, "battle_army");
- char buffer[32];
-
- if (komma) {
- bytes = (int)strlcpy(bufp, ", ", size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- }
- slprintf(buffer, sizeof(buffer), "%s %2d(%s): ",
- loc_army, army_index(s), abbrev);
-
- bytes = (int)strlcpy(bufp, buffer, size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
-
- for (r = FIGHT_ROW; r != NUMROWS; ++r) {
- if (alive[r]) {
- if (l != FIGHT_ROW) {
- bytes = (int)strlcpy(bufp, "+", size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- }
- while (k--) {
- bytes = (int)strlcpy(bufp, "0+", size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- }
- sprintf(buffer, "%d", alive[r]);
-
- bytes = (int)strlcpy(bufp, buffer, size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
-
- k = 0;
- l = r + 1;
- } else
- ++k;
- }
-
- komma = true;
- }
- }
- *bufp = 0;
- fbattlerecord(b, fac, buf);
- }
- return cont;
-}
-
-static void join_allies(battle * b)
-{
- region *r = b->region;
- unit *u;
- side *s, *s_end = b->sides + b->nsides;
- /* make_side might be adding a new faction, but it adds them to the end
- * of the list, so we're safe in our iteration here if we remember the end
- * up front. */
- for (u = r->units; u; u = u->next) {
- /* Was ist mit Schiffen? */
- if (u->status != ST_FLEE && u->status != ST_AVOID
- && !fval(u, UFL_LONGACTION | UFL_ISNEW) && u->number > 0) {
- faction *f = u->faction;
- fighter *c = NULL;
-
- for (s = b->sides; s != s_end; ++s) {
- side *se;
- /* Wenn alle attackierten noch FFL_NOAID haben, dann kämpfe nicht mit. */
- if (fval(s->faction, FFL_NOAID))
- continue;
- if (s->faction != f) {
- /* Wenn wir attackiert haben, kommt niemand mehr hinzu: */
- if (s->bf->attacker)
- continue;
- /* alliiert müssen wir schon sein, sonst ist's eh egal : */
- if (!alliedunit(u, s->faction, HELP_FIGHT))
- continue;
- /* wenn die partei verborgen ist, oder gar eine andere
- * vorgespiegelt wird, und er sich uns gegenüber nicht zu
- * erkennen gibt, helfen wir ihm nicht */
- if (s->stealthfaction) {
- if (!allysfm(s, u->faction, HELP_FSTEALTH)) {
- continue;
- }
- }
- }
- /* einen alliierten angreifen dürfen sie nicht, es sei denn, der
- * ist mit einem alliierten verfeindet, der nicht attackiert
- * hat: */
- for (se = b->sides; se != s_end; ++se) {
- if (u->faction == se->faction)
- continue;
- if (alliedunit(u, se->faction, HELP_FIGHT) && !se->bf->attacker) {
- continue;
- }
- if (enemy(s, se))
- break;
- }
- if (se == s_end)
- continue;
- /* Wenn die Einheit belagert ist, muß auch einer der Alliierten belagert sein: */
- if (besieged(u)) {
- fighter *ally;
- for (ally = s->fighters; ally; ally = ally->next) {
- if (besieged(ally->unit)) {
- break;
- }
- }
- if (ally == NULL)
- continue;
- }
- /* keine Einwände, also soll er mitmachen: */
- if (c == NULL) {
- if (join_battle(b, u, false, &c)) {
- if (battledebug) {
- fprintf(bdebug, "%s joins to help %s against %s.\n",
- unitname(u), factionname(s->faction), factionname(se->faction));
- }
- } else if (c == NULL) {
- continue;
- }
- }
-
- /* the enemy of my friend is my enemy: */
- for (se = b->sides; se != s_end; ++se) {
- if (se->faction != u->faction && enemy(s, se)) {
- if (set_enemy(se, c->side, false) && battledebug) {
- fprintf(bdebug,
- "%u/%s hates %u/%s because they are enemies with %u/%s.\n",
- c->side->index, sidename(c->side), se->index, sidename(se),
- s->index, sidename(s));
- }
- }
- }
- }
- }
- }
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- int si;
- side *sa;
- faction *f = s->faction;
-
- /* Den Feinden meiner Feinde gebe ich Deckung (gegen gemeinsame Feinde): */
- for (si = 0; s->enemies[si]; ++si) {
- side *se = s->enemies[si];
- int ai;
- for (ai = 0; se->enemies[ai]; ++ai) {
- side *as = se->enemies[ai];
- if (as == s || !enemy(as, s)) {
- set_friendly(as, s);
- }
- }
- }
-
- for (sa = s + 1; sa != b->sides + b->nsides; ++sa) {
- plane *pl = rplane(r);
- if (enemy(s, sa))
- continue;
- if (friendly(s, sa))
- continue;
- if (!alliedgroup(pl, f, sa->faction, f->allies, HELP_FIGHT))
- continue;
- if (!alliedgroup(pl, sa->faction, f, sa->faction->allies, HELP_FIGHT))
- continue;
-
- set_friendly(s, sa);
- }
- }
-}
-
-static void flee(const troop dt)
-{
- fighter *fig = dt.fighter;
- unit *u = fig->unit;
-
- fig->run.hp += fig->person[dt.index].hp;
- ++fig->run.number;
-
- setguard(u, GUARD_NONE);
-
- kill_troop(dt);
-}
-
-static bool start_battle(region * r, battle ** bp)
-{
- battle *b = NULL;
- unit *u;
- bool fighting = false;
-
- /* list_foreach geht nicht, wegen flucht */
- for (u = r->units; u != NULL; u = u->next) {
- if (fval(u, UFL_LONGACTION))
- continue;
- if (u->number > 0) {
- order *ord;
-
- for (ord = u->orders; ord; ord = ord->next) {
- static bool init = false;
- static const curse_type *peace_ct, *slave_ct, *calm_ct;
-
- if (!init) {
- init = true;
- peace_ct = ct_find("peacezone");
- slave_ct = ct_find("slavery");
- calm_ct = ct_find("calmmonster");
- }
- if (getkeyword(ord) == K_ATTACK) {
- unit *u2;
- fighter *c1, *c2;
- ship *lsh = NULL;
- plane *pl = rplane(r);
-
- if (pl && fval(pl, PFL_NOATTACK)) {
- cmistake(u, ord, 271, MSG_BATTLE);
- continue;
- }
-
- if ((u_race(u)->battle_flags & BF_CANATTACK) == 0) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "race_no_attack",
- "race", u_race(u)));
- continue;
- }
- /**
- ** Fehlerbehandlung Angreifer
- **/
- if (LongHunger(u)) {
- cmistake(u, ord, 225, MSG_BATTLE);
- continue;
- }
-
- if (u->status == ST_AVOID || u->status == ST_FLEE) {
- cmistake(u, ord, 226, MSG_BATTLE);
- continue;
- }
-
- /* ist ein Flüchtling aus einem andern Kampf */
- if (fval(u, UFL_LONGACTION))
- continue;
-
- if (peace_ct && curse_active(get_curse(r->attribs, peace_ct))) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "peace_active", ""));
- continue;
- }
-
- if (slave_ct && curse_active(get_curse(u->attribs, slave_ct))) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "slave_active", ""));
- continue;
- }
-
- if ((u->ship != NULL && !fval(r->terrain, SEA_REGION))
- || (lsh = leftship(u)) != NULL) {
- if (is_guarded(r, u, GUARD_TRAVELTHRU)) {
- if (lsh) {
- cmistake(u, ord, 234, MSG_BATTLE);
- } else {
- /* Fehler: "Das Schiff muß erst verlassen werden" */
- cmistake(u, ord, 19, MSG_BATTLE);
- }
- continue;
- }
- }
-
- /* Ende Fehlerbehandlung Angreifer */
-
- init_order(ord);
- /* attackierte Einheit ermitteln */
- u2 = getunit(r, u->faction);
-
- /* Beginn Fehlerbehandlung */
- /* Fehler: "Die Einheit wurde nicht gefunden" */
- if (!u2 || u2->number == 0 || !cansee(u->faction, u->region, u2, 0)) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord,
- "feedback_unit_not_found", ""));
- continue;
- }
- /* Fehler: "Die Einheit ist eine der unsrigen" */
- if (u2->faction == u->faction) {
- cmistake(u, ord, 45, MSG_BATTLE);
- continue;
- }
- /* Fehler: "Die Einheit ist mit uns alliert" */
- if (alliedunit(u, u2->faction, HELP_FIGHT)) {
- cmistake(u, ord, 47, MSG_BATTLE);
- continue;
- }
- if (IsImmune(u2->faction)) {
- add_message(&u->faction->msgs,
- msg_feedback(u, u->thisorder, "newbie_immunity_error", "turns",
- NewbieImmunity()));
- continue;
- }
- /* Fehler: "Die Einheit ist mit uns alliert" */
-
- if (calm_ct) {
- attrib *a = a_find(u->attribs, &at_curse);
- bool calm = false;
- while (a && a->type == &at_curse) {
- curse *c = (curse *) a->data.v;
- if (c->type == calm_ct
- && curse_geteffect(c) == u2->faction->subscription) {
- if (curse_active(c)) {
- calm = true;
- break;
- }
- }
- a = a->next;
- }
- if (calm) {
- cmistake(u, ord, 47, MSG_BATTLE);
- continue;
- }
- }
- /* Ende Fehlerbehandlung */
- if (b == NULL) {
- unit *utmp;
- for (utmp = r->units; utmp != NULL; utmp = utmp->next) {
- fset(utmp->faction, FFL_NOAID);
- }
- b = make_battle(r);
- }
- if (join_battle(b, u, true, &c1)) {
- if (battledebug) {
- fprintf(bdebug, "%s joins by attacking %s.\n",
- unitname(u), unitname(u2));
- }
- }
- if (join_battle(b, u2, false, &c2)) {
- if (battledebug) {
- fprintf(bdebug, "%s joins because of an attack from %s.\n",
- unitname(u2), unitname(u));
- }
- }
-
- /* Hat die attackierte Einheit keinen Noaid-Status,
- * wird das Flag von der Faction genommen, andere
- * Einheiten greifen ein. */
- if (!fval(u2, UFL_NOAID))
- freset(u2->faction, FFL_NOAID);
-
- if (c1 != NULL && c2 != NULL) {
- /* Merken, wer Angreifer ist, für die Rückzahlung der
- * Präcombataura bei kurzem Kampf. */
- c1->side->bf->attacker = true;
-
- if (set_enemy(c1->side, c2->side, true) && battledebug) {
- fprintf(bdebug, "%u/%s hates %u/%s because they attacked them.\n",
- c2->side->index, sidename(c2->side),
- c1->side->index, sidename(c1->side));
- }
- fighting = true;
- }
- }
- }
- }
- }
- *bp = b;
- return fighting;
-}
-
-/** execute one round of attacks
- * fig->fighting is used to determine who attacks, not fig->alive, since
- * the latter may be influenced by attacks that already took place.
- */
-static void battle_attacks(battle * b)
-{
- side *s;
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
-
- if (b->turn != 0 || (b->max_tactics > 0
- && get_tactics(s, NULL) == b->max_tactics)) {
- for (fig = s->fighters; fig; fig = fig->next) {
-
- /* ist in dieser Einheit noch jemand handlungsfähig? */
- if (fig->fighting <= 0)
- continue;
-
- /* Handle the unit's attack on someone */
- do_attack(fig);
- }
- }
- }
-}
-
-/** updates the number of attacking troops in each fighter struct.
- * this has to be calculated _before_ the actual attacks take
- * place because otherwise dead troops would not strike in the
- * round they die. */
-static void battle_update(battle * b)
-{
- side *s;
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
- for (fig = s->fighters; fig; fig = fig->next) {
- fig->fighting = fig->alive - fig->removed;
- }
- }
-}
-
-/** attempt to flee from battle before the next round begins
- * there's a double attempt before the first round, but only
- * one attempt before round zero, the potential tactics round. */
-static void battle_flee(battle * b)
-{
- int attempt, flee_ops = 1;
-
- if (b->turn == 1)
- flee_ops = 2;
-
- for (attempt = 1; attempt <= flee_ops; ++attempt) {
- side *s;
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fig;
- for (fig = s->fighters; fig; fig = fig->next) {
- unit *u = fig->unit;
- troop dt;
- int runners = 0;
- /* Flucht nicht bei mehr als 600 HP. Damit Wyrme tötbar bleiben. */
- int runhp = _min(600, (int)(0.9 + unit_max_hp(u) * hpflee(u->status)));
-
- if (u->ship && fval(u->region->terrain, SEA_REGION)) {
- /* keine Flucht von Schiffen auf hoher See */
- continue;
- }
- if (fval(u_race(u), RCF_UNDEAD) || u_race(u) == get_race(RC_SHADOWKNIGHT)) {
- /* Untote fliehen nicht. Warum eigentlich? */
- continue;
- }
-
- dt.fighter = fig;
- dt.index = fig->alive - fig->removed;
- while (s->size[SUM_ROW] && dt.index != 0) {
- double ispaniced = 0.0;
- --dt.index;
- assert(dt.index >= 0 && dt.index < fig->unit->number);
- assert(fig->person[dt.index].hp > 0);
-
- /* Versuche zu fliehen, wenn
- * - Kampfstatus fliehe
- * - schwer verwundet und nicht erste kampfrunde
- * - in panik (Zauber)
- * aber nicht, wenn der Zaubereffekt Held auf dir liegt!
- */
- switch (u->status) {
- case ST_FLEE:
- break;
- default:
- if ((fig->person[dt.index].flags & FL_HIT) == 0)
- continue;
- if (b->turn <= 1)
- continue;
- if (fig->person[dt.index].hp <= runhp)
- break;
- if (fig->person[dt.index].flags & FL_PANICED) {
- if ((fig->person[dt.index].flags & FL_COURAGE) == 0)
- break;
- }
- continue;
- }
-
- if (fig->person[dt.index].flags & FL_PANICED) {
- ispaniced = EFFECT_PANIC_SPELL;
- }
- if (chance(_min(fleechance(u) + ispaniced, 0.90))) {
- ++runners;
- flee(dt);
- }
- }
- if (bdebug && runners > 0) {
- fprintf(bdebug, "Fleeing: %d from %s\n", runners,
- itoa36(fig->unit->no));
- }
- }
- }
- }
-}
-
-void do_battle(region * r)
-{
- battle *b = NULL;
- bool fighting = false;
- ship *sh;
- static int init_rules = 0;
-
- if (!init_rules) {
- static_rules();
- init_rules = 1;
- }
- if (msg_separator == NULL) {
- msg_separator = msg_message("battle::section", "");
- }
-
- fighting = start_battle(r, &b);
-
- if (b == NULL)
- return;
-
- /* Bevor wir die alliierten hineinziehen, sollten wir schauen, *
- * Ob jemand fliehen kann. Dann erübrigt sich das ganze ja
- * vielleicht schon. */
- print_header(b);
- if (!fighting) {
- /* Niemand mehr da, Kampf kann nicht stattfinden. */
- message *m = msg_message("battle::aborted", "");
- message_all(b, m);
- msg_release(m);
- free_battle(b);
- free(b);
- return;
- }
- join_allies(b);
- make_heroes(b);
-
- /* make sure no ships are damaged initially */
- for (sh = r->ships; sh; sh = sh->next)
- freset(sh, SF_DAMAGED);
-
- /* Gibt es eine Taktikrunde ? */
- if (!ql_empty(b->leaders)) {
- b->turn = 0;
- b->has_tactics_turn = true;
- } else {
- b->turn = 1;
- b->has_tactics_turn = false;
- }
-
- /* PRECOMBATSPELLS */
- do_combatmagic(b, DO_PRECOMBATSPELL);
-
- print_stats(b); /* gibt die Kampfaufstellung aus */
- if (verbosity > 0)
- log_printf(stdout, "%s (%d, %d) : ", rname(r, default_locale), r->x, r->y);
-
- for (; battle_report(b) && b->turn <= max_turns; ++b->turn) {
- if (bdebug) {
- fprintf(bdebug, "*** Turn: %d\n", b->turn);
- }
- battle_flee(b);
- battle_update(b);
- battle_attacks(b);
-
- }
-
- if (verbosity > 0)
- log_printf(stdout, "\n");
-
- /* Auswirkungen berechnen: */
- aftermath(b);
- /* Hier ist das Gefecht beendet, und wir können die
- * Hilfsstrukturen * wieder löschen: */
-
- if (b) {
- free_battle(b);
- free(b);
- }
-}
-
-void battle_free(battle * b) {
- side *s;
-
- assert(b);
-
- for (s = b->sides; s != b->sides + b->nsides; ++s) {
- fighter *fnext = s->fighters;
- while (fnext) {
- fighter *fig = fnext;
- fnext = fig->next;
- free_fighter(fig);
- free(fig);
- }
- free_side(s);
- }
-}
-
diff --git a/src/kernel/build.c b/src/kernel/build.c
index 8e977fc91..44352e2c3 100644
--- a/src/kernel/build.c
+++ b/src/kernel/build.c
@@ -521,9 +521,7 @@ int build(unit * u, const construction * ctype, int completed, int want)
}
}
- if (want > 0) {
- n = _min(want, n);
- }
+ if (want < n) n = want;
if (type->maxsize > 0) {
n = _min(type->maxsize - completed, n);
diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c
index efac217ae..1dc8e86e8 100644
--- a/src/kernel/build.test.c
+++ b/src/kernel/build.test.c
@@ -1,5 +1,6 @@
#include
#include
+#include "alchemy.h"
#include "types.h"
#include "build.h"
#include "order.h"
@@ -21,6 +22,7 @@ typedef struct build_fixture {
unit *u;
region *r;
race *rc;
+ construction cons;
} build_fixture;
static unit * setup_build(build_fixture *bf) {
@@ -32,37 +34,159 @@ static unit * setup_build(build_fixture *bf) {
assert(bf->rc && bf->f && bf->r);
bf->u = test_create_unit(bf->f, bf->r);
assert(bf->u);
+
+ bf->cons.materials = calloc(2, sizeof(requirement));
+ bf->cons.materials[0].number = 1;
+ bf->cons.materials[0].rtype = get_resourcetype(R_SILVER);
+ bf->cons.skill = SK_ARMORER;
+ bf->cons.minskill = 2;
+ bf->cons.maxsize = -1;
+ bf->cons.reqsize = 1;
return bf->u;
}
-static void test_build(CuTest *tc) {
- build_fixture bf;
+static void test_build_requires_materials(CuTest *tc) {
+ build_fixture bf = { 0 };
+ unit *u;
+ const struct item_type *itype;
+
+ u = setup_build(&bf);
+ set_level(u, SK_ARMORER, 2);
+ CuAssertIntEquals(tc, ENOMATERIALS, build(u, &bf.cons, 0, 1));
+ itype = bf.cons.materials[0].rtype->itype;
+ i_change(&u->items, itype, 2);
+ CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1));
+ CuAssertIntEquals(tc, 1, i_get(u->items, itype));
+}
+
+static void test_build_requires_building(CuTest *tc) {
+ build_fixture bf = { 0 };
+ unit *u;
+ const struct resource_type *rtype;
+ building_type *btype;
+
+ u = setup_build(&bf);
+ rtype = bf.cons.materials[0].rtype;
+ i_change(&u->items, rtype->itype, 1);
+ set_level(u, SK_ARMORER, 2);
+ bf.cons.btype = btype = bt_get_or_create("hodor");
+ btype->maxcapacity = 1;
+ btype->capacity = 1;
+ CuAssertIntEquals_Msg(tc, "must be inside a production building", EBUILDINGREQ, build(u, &bf.cons, 0, 1));
+ u->building = test_create_building(u->region, btype);
+ fset(u->building, BLD_WORKING);
+ CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1));
+ btype->maxcapacity = 0;
+ CuAssertIntEquals_Msg(tc, "cannot build when production building capacity exceeded", EBUILDINGREQ, build(u, &bf.cons, 0, 1));
+}
+
+static void test_build_failure_missing_skill(CuTest *tc) {
+ build_fixture bf = { 0 };
unit *u;
- construction cons = { 0 };
const struct resource_type *rtype;
u = setup_build(&bf);
- rtype = get_resourcetype(R_SILVER);
- assert(rtype);
-
- cons.materials = calloc(2, sizeof(requirement));
- cons.materials[0].number = 1;
- cons.materials[0].rtype = rtype;
- cons.skill = SK_ARMORER;
- cons.minskill = 2;
- cons.reqsize = 1;
- CuAssertIntEquals(tc, ENEEDSKILL, build(u, &cons, 1, 1));
- set_level(u, SK_ARMORER, 1);
- CuAssertIntEquals(tc, ELOWSKILL, build(u, &cons, 1, 1));
- set_level(u, SK_ARMORER, 2);
- CuAssertIntEquals(tc, ENOMATERIALS, build(u, &cons, 1, 1));
+ rtype = bf.cons.materials[0].rtype;
i_change(&u->items, rtype->itype, 1);
- CuAssertIntEquals(tc, 1, build(u, &cons, 1, 1));
+ CuAssertIntEquals(tc, ENEEDSKILL, build(u, &bf.cons, 1, 1));
+}
+
+static void test_build_failure_low_skill(CuTest *tc) {
+ build_fixture bf = { 0 };
+ unit *u;
+ const struct resource_type *rtype;
+
+ u = setup_build(&bf);
+ rtype = bf.cons.materials[0].rtype;
+ i_change(&u->items, rtype->itype, 1);
+ set_level(u, SK_ARMORER, bf.cons.minskill-1);
+ CuAssertIntEquals(tc, ELOWSKILL, build(u, &bf.cons, 0, 10));
+}
+
+static void test_build_failure_completed(CuTest *tc) {
+ build_fixture bf = { 0 };
+ unit *u;
+ const struct resource_type *rtype;
+
+ u = setup_build(&bf);
+ rtype = bf.cons.materials[0].rtype;
+ i_change(&u->items, rtype->itype, 1);
+ set_level(u, SK_ARMORER, bf.cons.minskill);
+ bf.cons.maxsize = 1;
+ CuAssertIntEquals(tc, ECOMPLETE, build(u, &bf.cons, bf.cons.maxsize, 10));
+ CuAssertIntEquals(tc, 1, i_get(u->items, rtype->itype));
+}
+
+static void test_build_limits(CuTest *tc) {
+ build_fixture bf = { 0 };
+ unit *u;
+ const struct resource_type *rtype;
+
+ u = setup_build(&bf);
+ rtype = bf.cons.materials[0].rtype;
+ i_change(&u->items, rtype->itype, 1);
+ set_level(u, SK_ARMORER, bf.cons.minskill);
+ CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 10));
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
+
scale_number(u, 2);
+ set_level(u, SK_ARMORER, bf.cons.minskill);
i_change(&u->items, rtype->itype, 2);
- CuAssertIntEquals(tc, 2, build(u, &cons, 2, 2));
+ CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 10));
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
+
+ scale_number(u, 2);
+ set_level(u, SK_ARMORER, bf.cons.minskill * 2);
+ i_change(&u->items, rtype->itype, 4);
+ CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 10));
+ CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
+ test_cleanup();
+}
+
+static void test_build_with_ring(CuTest *tc) {
+ build_fixture bf = { 0 };
+ unit *u;
+ item_type *ring;
+ const struct resource_type *rtype;
+
+ u = setup_build(&bf);
+ rtype = bf.cons.materials[0].rtype;
+ ring = it_get_or_create(rt_get_or_create("roqf"));
+ assert(rtype && ring);
+
+ set_level(u, SK_ARMORER, bf.cons.minskill);
+ i_change(&u->items, rtype->itype, 20);
+ i_change(&u->items, ring, 1);
+ CuAssertIntEquals(tc, 10, build(u, &bf.cons, 0, 20));
+ CuAssertIntEquals(tc, 10, i_get(u->items, rtype->itype));
+ test_cleanup();
+}
+
+static void test_build_with_potion(CuTest *tc) {
+ build_fixture bf = { 0 };
+ unit *u;
+ const potion_type *ptype;
+ const struct resource_type *rtype;
+
+ u = setup_build(&bf);
+ rtype = bf.cons.materials[0].rtype;
+ oldpotiontype[P_DOMORE] = ptype = new_potiontype(it_get_or_create(rt_get_or_create("hodor")), 1);
+ assert(rtype && ptype);
+
+ i_change(&u->items, rtype->itype, 20);
+ change_effect(u, ptype, 4);
+ set_level(u, SK_ARMORER, bf.cons.minskill);
+ CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 20));
+ CuAssertIntEquals(tc, 18, i_get(u->items, rtype->itype));
+ CuAssertIntEquals(tc, 3, get_effect(u, ptype));
+ set_level(u, SK_ARMORER, bf.cons.minskill*2);
+ CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20));
+ CuAssertIntEquals(tc, 2, get_effect(u, ptype));
+ set_level(u, SK_ARMORER, bf.cons.minskill);
+ scale_number(u, 2); // OBS: this scales the effects, too:
+ CuAssertIntEquals(tc, 4, get_effect(u, ptype));
+ CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20));
+ CuAssertIntEquals(tc, 2, get_effect(u, ptype));
test_cleanup();
}
@@ -82,7 +206,7 @@ static void test_build_building_no_materials(CuTest *tc) {
static void test_build_building_with_golem(CuTest *tc) {
unit *u;
- build_fixture bf;
+ build_fixture bf = { 0 };
const building_type *btype;
u = setup_build(&bf);
@@ -92,7 +216,7 @@ static void test_build_building_with_golem(CuTest *tc) {
assert(btype->construction);
set_level(bf.u, SK_BUILDING, 1);
- CuAssertIntEquals(tc, 1, build_building(u, btype, 0, 4, 0));
+ CuAssertIntEquals(tc, 1, build_building(u, btype, 0, 1, 0));
CuAssertPtrNotNull(tc, u->region->buildings);
CuAssertIntEquals(tc, 1, u->region->buildings->size);
CuAssertIntEquals(tc, 0, u->number);
@@ -124,7 +248,14 @@ static void test_build_building_success(CuTest *tc) {
CuSuite *get_build_suite(void)
{
CuSuite *suite = CuSuiteNew();
- SUITE_ADD_TEST(suite, test_build);
+ SUITE_ADD_TEST(suite, test_build_limits);
+ SUITE_ADD_TEST(suite, test_build_failure_low_skill);
+ SUITE_ADD_TEST(suite, test_build_failure_missing_skill);
+ SUITE_ADD_TEST(suite, test_build_requires_materials);
+ SUITE_ADD_TEST(suite, test_build_requires_building);
+ SUITE_ADD_TEST(suite, test_build_failure_completed);
+ SUITE_ADD_TEST(suite, test_build_with_ring);
+ SUITE_ADD_TEST(suite, test_build_with_potion);
SUITE_ADD_TEST(suite, test_build_building_success);
SUITE_ADD_TEST(suite, test_build_building_with_golem);
SUITE_ADD_TEST(suite, test_build_building_no_materials);
diff --git a/src/kernel/config.c b/src/kernel/config.c
index 90cf0765d..10ab406b9 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -27,7 +27,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "alliance.h"
#include "ally.h"
#include "alchemy.h"
-#include "battle.h"
#include "connection.h"
#include "building.h"
#include "calendar.h"
@@ -40,7 +39,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "magic.h"
#include "messages.h"
#include "move.h"
-#include "names.h"
#include "objtypes.h"
#include "order.h"
#include "plane.h"
@@ -76,6 +74,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
+
#ifdef USE_LIBXML2
/* libxml includes */
#include
@@ -669,23 +669,6 @@ region *findunitregion(const unit * su)
#endif
}
-int eff_stealth(const unit * u, const region * r)
-{
- int e = 0;
-
- /* Auf Schiffen keine Tarnung! */
- if (!u->ship && skill_enabled(SK_STEALTH)) {
- e = eff_skill(u, SK_STEALTH, r);
-
- if (fval(u, UFL_STEALTH)) {
- int es = u_geteffstealth(u);
- if (es >= 0 && es < e)
- return es;
- }
- }
- return e;
-}
-
bool unit_has_cursed_item(unit * u)
{
item *itm = u->items;
@@ -2684,40 +2667,6 @@ message *movement_error(unit * u, const char *token, order * ord,
return NULL;
}
-int movewhere(const unit * u, const char *token, region * r, region ** resultp)
-{
- region *r2;
- direction_t d;
-
- if (!token || *token == '\0') {
- *resultp = NULL;
- return E_MOVE_OK;
- }
-
- d = get_direction(token, u->faction->locale);
- switch (d) {
- case D_PAUSE:
- *resultp = r;
- break;
-
- case NODIRECTION:
- r2 = find_special_direction(r, token, u->faction->locale);
- if (r2 == NULL) {
- return E_MOVE_NOREGION;
- }
- *resultp = r2;
- break;
-
- default:
- r2 = rconnect(r, d);
- if (r2 == NULL || move_blocked(u, r, r2)) {
- return E_MOVE_BLOCKED;
- }
- *resultp = r2;
- }
- return E_MOVE_OK;
-}
-
bool move_blocked(const unit * u, const region * r, const region * r2)
{
connection *b;
@@ -2796,7 +2745,6 @@ void attrib_init(void)
at_register(&at_seenspell);
/* neue REGION-Attribute */
- at_register(&at_direction);
at_register(&at_moveblock);
at_register(&at_deathcount);
at_register(&at_chaoscount);
diff --git a/src/kernel/config.h b/src/kernel/config.h
index bca500ab9..02cf6e8f0 100644
--- a/src/kernel/config.h
+++ b/src/kernel/config.h
@@ -251,7 +251,6 @@ extern "C" {
bool has_limited_skills(const struct unit *u);
const struct race *findrace(const char *, const struct locale *);
- int eff_stealth(const struct unit *u, const struct region *r);
int ispresent(const struct faction *f, const struct region *r);
int check_option(struct faction *f, int option);
@@ -340,15 +339,6 @@ extern "C" {
const struct region *dest);
void add_income(struct unit *u, int type, int want, int qty);
- /* movewhere error codes */
- enum {
- E_MOVE_OK = 0, /* possible to move */
- E_MOVE_NOREGION, /* no region exists in this direction */
- E_MOVE_BLOCKED /* cannot see this region, there is a blocking connection. */
- };
- int movewhere(const struct unit *u, const char *token,
- struct region *r, struct region **resultp);
-
const char *datapath(void);
void set_datapath(const char *path);
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 94b3b8b48..72ca65885 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -838,7 +838,7 @@ static int
use_bloodpotion(struct unit *u, const struct item_type *itype, int amount,
struct order *ord)
{
- if (u_race(u) == get_race(RC_DAEMON)) {
+ if (u->number == 0 || u_race(u) == get_race(RC_DAEMON)) {
change_effect(u, itype->rtype->ptype, 100 * amount);
}
else {
diff --git a/src/kernel/move.h b/src/kernel/move.h
deleted file mode 100644
index c76c362c1..000000000
--- a/src/kernel/move.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-Copyright (c) 1998-2010, Enno Rehling
- Katja Zedel
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-**/
-
-#ifndef H_KRNL_MOVEMENT
-#define H_KRNL_MOVEMENT
-#ifdef __cplusplus
-extern "C" {
-#endif
-
- struct unit;
- struct ship;
- struct building_type;
-
-/* die Zahlen sind genau äquivalent zu den race Flags */
-#define MV_CANNOTMOVE (1<<5)
-#define MV_FLY (1<<7) /* kann fliegen */
-#define MV_SWIM (1<<8) /* kann schwimmen */
-#define MV_WALK (1<<9) /* kann über Land gehen */
-
-/* Die tragekapaz. ist hardcodiert mit defines, da es bis jetzt sowieso nur 2
-** objekte gibt, die etwas tragen. */
-#define SILVERWEIGHT 1
-#define SCALEWEIGHT 100 /* Faktor, um den die Anzeige von gewichten
- * * skaliert wird */
-#define HORSECAPACITY 7000
-#define WAGONCAPACITY 14000
-
-#define HORSESNEEDED 2
-
-/* ein mensch wiegt 10, traegt also 5, ein pferd wiegt 50, traegt also 20. ein
-** wagen wird von zwei pferden gezogen und traegt total 140, davon 40 die
-** pferde, macht nur noch 100, aber samt eigenem gewicht (40) macht also 140. */
-
- int personcapacity(const struct unit *u);
- void movement(void);
- void run_to(struct unit *u, struct region *to);
- struct unit *is_guarded(struct region *r, struct unit *u, unsigned int mask);
- bool is_guard(const struct unit *u, int mask);
- int enoughsailors(const struct ship *sh, const struct region *r);
- bool canswim(struct unit *u);
- bool canfly(struct unit *u);
- struct unit *get_captain(const struct ship *sh);
- void travelthru(const struct unit *u, struct region *r);
- struct ship *move_ship(struct ship *sh, struct region *from,
- struct region *to, struct region_list *route);
- int walkingcapacity(const struct unit *u);
- void follow_unit(struct unit *u);
- bool buildingtype_exists(const struct region *r,
- const struct building_type *bt, bool working);
- struct unit *owner_buildingtyp(const struct region *r,
- const struct building_type *bt);
-
- extern struct attrib_type at_speedup;
-
-#define SA_HARBOUR 2
-#define SA_COAST 1
-#define SA_NO_INSECT -1
-#define SA_NO_COAST -2
-
- extern int check_ship_allowed(struct ship *sh, const struct region * r);
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/kernel/region.c b/src/kernel/region.c
index f75bfb46f..8c57a220f 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -189,163 +189,6 @@ void chaoscounts(region * r, int fallen)
a_remove(&r->attribs, a);
}
-/********************/
-/* at_direction */
-/********************/
-static void a_initdirection(attrib * a)
-{
- a->data.v = calloc(1, sizeof(spec_direction));
-}
-
-static void a_freedirection(attrib * a)
-{
- free(a->data.v);
-}
-
-static int a_agedirection(attrib * a)
-{
- spec_direction *d = (spec_direction *) (a->data.v);
- --d->duration;
- return (d->duration > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE;
-}
-
-typedef struct dir_lookup {
- char *name;
- const char *oldname;
- struct dir_lookup *next;
-} dir_lookup;
-
-static dir_lookup *dir_name_lookup;
-
-void register_special_direction(const char *name)
-{
- struct locale *lang;
- char *str = _strdup(name);
-
- for (lang = locales; lang; lang = nextlocale(lang)) {
- void **tokens = get_translations(lang, UT_SPECDIR);
- const char *token = LOC(lang, name);
-
- if (token) {
- variant var;
-
- var.v = str;
- addtoken(tokens, token, var);
-
- if (lang == default_locale) {
- dir_lookup *dl = malloc(sizeof(dir_lookup));
- dl->name = str;
- dl->oldname = token;
- dl->next = dir_name_lookup;
- dir_name_lookup = dl;
- }
- } else {
- log_error("no translation for spec_direction '%s' in locale '%s'\n", name, locale_name(lang));
- }
- }
-}
-
-static int a_readdirection(attrib * a, void *owner, struct storage *store)
-{
- spec_direction *d = (spec_direction *) (a->data.v);
-
- READ_INT(store, &d->x);
- READ_INT(store, &d->y);
- READ_INT(store, &d->duration);
- if (global.data_version < UNICODE_VERSION) {
- char lbuf[16];
- dir_lookup *dl = dir_name_lookup;
-
- READ_TOK(store, NULL, 0);
- READ_TOK(store, lbuf, sizeof(lbuf));
-
- cstring_i(lbuf);
- for (; dl; dl = dl->next) {
- if (strcmp(lbuf, dl->oldname) == 0) {
- d->keyword = _strdup(dl->name);
- sprintf(lbuf, "%s_desc", d->keyword);
- d->desc = _strdup(dl->name);
- break;
- }
- }
- if (dl == NULL) {
- log_error("unknown spec_direction '%s'\n", lbuf);
- assert(!"not implemented");
- }
- } else {
- char lbuf[32];
- READ_TOK(store, lbuf, sizeof(lbuf));
- d->desc = _strdup(lbuf);
- READ_TOK(store, lbuf, sizeof(lbuf));
- d->keyword = _strdup(lbuf);
- }
- d->active = true;
- return AT_READ_OK;
-}
-
-static void
-a_writedirection(const attrib * a, const void *owner, struct storage *store)
-{
- spec_direction *d = (spec_direction *) (a->data.v);
-
- WRITE_INT(store, d->x);
- WRITE_INT(store, d->y);
- WRITE_INT(store, d->duration);
- WRITE_TOK(store, d->desc);
- WRITE_TOK(store, d->keyword);
-}
-
-attrib_type at_direction = {
- "direction",
- a_initdirection,
- a_freedirection,
- a_agedirection,
- a_writedirection,
- a_readdirection
-};
-
-region *find_special_direction(const region * r, const char *token,
- const struct locale *lang)
-{
- attrib *a;
- spec_direction *d;
-
- if (strlen(token) == 0)
- return NULL;
- for (a = a_find(r->attribs, &at_direction); a && a->type == &at_direction;
- a = a->next) {
- d = (spec_direction *) (a->data.v);
-
- if (d->active) {
- void **tokens = get_translations(lang, UT_SPECDIR);
- variant var;
- if (findtoken(*tokens, token, &var) == E_TOK_SUCCESS) {
- if (strcmp((const char *)var.v, d->keyword) == 0) {
- return findregion(d->x, d->y);
- }
- }
- }
- }
-
- return NULL;
-}
-
-attrib *create_special_direction(region * r, region * rt, int duration,
- const char *desc, const char *keyword)
-{
- attrib *a = a_add(&r->attribs, a_new(&at_direction));
- spec_direction *d = (spec_direction *) (a->data.v);
-
- d->active = false;
- d->x = rt->x;
- d->y = rt->y;
- d->duration = duration;
- d->desc = _strdup(desc);
- d->keyword = _strdup(keyword);
-
- return a;
-}
-
/* Moveblock wird zur Zeit nicht über Attribute, sondern ein Bitfeld
r->moveblock gemacht. Sollte umgestellt werden, wenn kompliziertere
Dinge gefragt werden. */
@@ -630,49 +473,6 @@ int distance(const region * r1, const region * r2)
return koor_distance(r1->x, r1->y, r2->x, r2->y);
}
-static direction_t
-koor_reldirection(int ax, int ay, int bx, int by, const struct plane *pl)
-{
- int dir;
- for (dir = 0; dir != MAXDIRECTIONS; ++dir) {
- int x = ax + delta_x[dir];
- int y = ay + delta_y[dir];
- pnormalize(&x, &y, pl);
- if (bx == x && by == y)
- return (direction_t)dir;
- }
- return NODIRECTION;
-}
-
-spec_direction *special_direction(const region * from, const region * to)
-{
- const attrib *a = a_findc(from->attribs, &at_direction);
-
- while (a != NULL && a->type == &at_direction) {
- spec_direction *sd = (spec_direction *) a->data.v;
- if (sd->x == to->x && sd->y == to->y)
- return sd;
- a = a->next;
- }
- return NULL;
-}
-
-direction_t reldirection(const region * from, const region * to)
-{
- plane *pl = rplane(from);
- if (pl == rplane(to)) {
- direction_t dir = koor_reldirection(from->x, from->y, to->x, to->y, pl);
-
- if (dir == NODIRECTION) {
- spec_direction *sd = special_direction(from, to);
- if (sd != NULL && sd->active)
- return D_SPECIAL;
- }
- return dir;
- }
- return NODIRECTION;
-}
-
void free_regionlist(region_list * rl)
{
while (rl) {
diff --git a/src/kernel/region.h b/src/kernel/region.h
index 367d77385..aa05853f0 100644
--- a/src/kernel/region.h
+++ b/src/kernel/region.h
@@ -153,14 +153,6 @@ extern "C" {
struct message *r_addmessage(struct region *r, const struct faction *viewer,
struct message *msg);
- typedef struct spec_direction {
- int x, y;
- int duration;
- bool active;
- char *desc;
- char *keyword;
- } spec_direction;
-
typedef struct {
direction_t dir;
} moveblock;
@@ -169,11 +161,9 @@ extern "C" {
int distance(const struct region *, const struct region *);
int koor_distance(int ax, int ay, int bx, int by);
- direction_t reldirection(const struct region *from, const struct region *to);
struct region *findregion(int x, int y);
struct region *findregionbyid(int uid);
- extern struct attrib_type at_direction;
extern struct attrib_type at_moveblock;
extern struct attrib_type at_peasantluck;
extern struct attrib_type at_horseluck;
@@ -189,14 +179,6 @@ extern "C" {
void free_regionlist(region_list * rl);
void add_regionlist(region_list ** rl, struct region *r);
- struct region *find_special_direction(const struct region *r,
- const char *token, const struct locale *lang);
- void register_special_direction(const char *name);
- struct spec_direction *special_direction(const region * from,
- const region * to);
- struct attrib *create_special_direction(struct region *r, struct region *rt,
- int duration, const char *desc, const char *keyword);
-
int deathcount(const struct region *r);
int chaoscount(const struct region *r);
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index 967ff5db5..375500f15 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -1,7 +1,7 @@
/*
Copyright (c) 1998-2010, Enno Rehling
- Katja Zedel
+Katja Zedel
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -56,6 +56,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
+
#include
/* libc includes */
@@ -68,13 +70,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define FIND_FOREIGN_TEMP
attrib_type at_creator = {
- "creator"
+ "creator"
/* Rest ist NULL; temporaeres, nicht alterndes Attribut */
};
#define UMAXHASH MAXUNITS
static unit *unithash[UMAXHASH];
-static unit *delmarker = (unit *) unithash; /* a funny hack */
+static unit *delmarker = (unit *)unithash; /* a funny hack */
#define HASH_STATISTICS 1
#if HASH_STATISTICS
@@ -84,134 +86,136 @@ static int hash_misses;
void uhash(unit * u)
{
- int key = HASH1(u->no, UMAXHASH), gk = HASH2(u->no, UMAXHASH);
- while (unithash[key] != NULL && unithash[key] != delmarker
- && unithash[key] != u) {
- key = (key + gk) % UMAXHASH;
- }
- assert(unithash[key] != u || !"trying to add the same unit twice");
- unithash[key] = u;
+ int key = HASH1(u->no, UMAXHASH), gk = HASH2(u->no, UMAXHASH);
+ while (unithash[key] != NULL && unithash[key] != delmarker
+ && unithash[key] != u) {
+ key = (key + gk) % UMAXHASH;
+ }
+ assert(unithash[key] != u || !"trying to add the same unit twice");
+ unithash[key] = u;
}
void uunhash(unit * u)
{
- int key = HASH1(u->no, UMAXHASH), gk = HASH2(u->no, UMAXHASH);
- while (unithash[key] != NULL && unithash[key] != u) {
- key = (key + gk) % UMAXHASH;
- }
- assert(unithash[key] == u || !"trying to remove a unit that is not hashed");
- unithash[key] = delmarker;
+ int key = HASH1(u->no, UMAXHASH), gk = HASH2(u->no, UMAXHASH);
+ while (unithash[key] != NULL && unithash[key] != u) {
+ key = (key + gk) % UMAXHASH;
+ }
+ assert(unithash[key] == u || !"trying to remove a unit that is not hashed");
+ unithash[key] = delmarker;
}
unit *ufindhash(int uid)
{
- assert(uid >= 0);
+ assert(uid >= 0);
#if HASH_STATISTICS
- ++hash_requests;
+ ++hash_requests;
#endif
- if (uid >= 0) {
- int key = HASH1(uid, UMAXHASH), gk = HASH2(uid, UMAXHASH);
- while (unithash[key] != NULL && (unithash[key] == delmarker
- || unithash[key]->no != uid)) {
- key = (key + gk) % UMAXHASH;
+ if (uid >= 0) {
+ int key = HASH1(uid, UMAXHASH), gk = HASH2(uid, UMAXHASH);
+ while (unithash[key] != NULL && (unithash[key] == delmarker
+ || unithash[key]->no != uid)) {
+ key = (key + gk) % UMAXHASH;
#if HASH_STATISTICS
- ++hash_misses;
+ ++hash_misses;
#endif
+ }
+ return unithash[key];
}
- return unithash[key];
- }
- return NULL;
+ return NULL;
}
#define DMAXHASH 7919
typedef struct dead {
- struct dead *nexthash;
- faction *f;
- int no;
+ struct dead *nexthash;
+ faction *f;
+ int no;
} dead;
static dead *deadhash[DMAXHASH];
static void dhash(int no, faction * f)
{
- dead *hash = (dead *) calloc(1, sizeof(dead));
- dead *old = deadhash[no % DMAXHASH];
- hash->no = no;
- hash->f = f;
- deadhash[no % DMAXHASH] = hash;
- hash->nexthash = old;
+ dead *hash = (dead *)calloc(1, sizeof(dead));
+ dead *old = deadhash[no % DMAXHASH];
+ hash->no = no;
+ hash->f = f;
+ deadhash[no % DMAXHASH] = hash;
+ hash->nexthash = old;
}
faction *dfindhash(int no)
{
- dead *old;
+ dead *old;
- if (no < 0)
- return 0;
+ if (no < 0)
+ return 0;
- for (old = deadhash[no % DMAXHASH]; old; old = old->nexthash) {
- if (old->no == no) {
- return old->f;
+ for (old = deadhash[no % DMAXHASH]; old; old = old->nexthash) {
+ if (old->no == no) {
+ return old->f;
+ }
}
- }
- return 0;
+ return 0;
}
typedef struct buddy {
- struct buddy *next;
- int number;
- faction *faction;
- unit *unit;
+ struct buddy *next;
+ int number;
+ faction *faction;
+ unit *unit;
} buddy;
static buddy *get_friends(const unit * u, int *numfriends)
{
- buddy *friends = 0;
- faction *f = u->faction;
- region *r = u->region;
- int number = 0;
- unit *u2;
+ buddy *friends = 0;
+ faction *f = u->faction;
+ region *r = u->region;
+ int number = 0;
+ unit *u2;
- for (u2 = r->units; u2; u2 = u2->next) {
- if (u2->faction != f && u2->number > 0) {
- int allied = 0;
- if (get_param_int(global.parameters, "rules.alliances", 0) != 0) {
- allied = (f->alliance && f->alliance == u2->faction->alliance);
- } else if (alliedunit(u, u2->faction, HELP_MONEY)
- && alliedunit(u2, f, HELP_GIVE)) {
- allied = 1;
- }
- if (allied) {
- buddy *nf, **fr = &friends;
-
- /* some units won't take stuff: */
- if (u_race(u2)->ec_flags & GETITEM) {
- while (*fr && (*fr)->faction->no < u2->faction->no)
- fr = &(*fr)->next;
- nf = *fr;
- if (nf == NULL || nf->faction != u2->faction) {
- nf = malloc(sizeof(buddy));
- nf->next = *fr;
- nf->faction = u2->faction;
- nf->unit = u2;
- nf->number = 0;
- *fr = nf;
- } else if (nf->faction == u2->faction
- && (u_race(u2)->ec_flags & GIVEITEM)) {
- /* we don't like to gift it to units that won't give it back */
- if ((u_race(nf->unit)->ec_flags & GIVEITEM) == 0) {
- nf->unit = u2;
+ for (u2 = r->units; u2; u2 = u2->next) {
+ if (u2->faction != f && u2->number > 0) {
+ int allied = 0;
+ if (get_param_int(global.parameters, "rules.alliances", 0) != 0) {
+ allied = (f->alliance && f->alliance == u2->faction->alliance);
+ }
+ else if (alliedunit(u, u2->faction, HELP_MONEY)
+ && alliedunit(u2, f, HELP_GIVE)) {
+ allied = 1;
+ }
+ if (allied) {
+ buddy *nf, **fr = &friends;
+
+ /* some units won't take stuff: */
+ if (u_race(u2)->ec_flags & GETITEM) {
+ while (*fr && (*fr)->faction->no < u2->faction->no)
+ fr = &(*fr)->next;
+ nf = *fr;
+ if (nf == NULL || nf->faction != u2->faction) {
+ nf = malloc(sizeof(buddy));
+ nf->next = *fr;
+ nf->faction = u2->faction;
+ nf->unit = u2;
+ nf->number = 0;
+ *fr = nf;
+ }
+ else if (nf->faction == u2->faction
+ && (u_race(u2)->ec_flags & GIVEITEM)) {
+ /* we don't like to gift it to units that won't give it back */
+ if ((u_race(nf->unit)->ec_flags & GIVEITEM) == 0) {
+ nf->unit = u2;
+ }
+ }
+ nf->number += u2->number;
+ number += u2->number;
+ }
}
- }
- nf->number += u2->number;
- number += u2->number;
}
- }
}
- }
- if (numfriends)
- *numfriends = number;
- return friends;
+ if (numfriends)
+ *numfriends = number;
+ return friends;
}
/** give all items to friends or peasants.
@@ -226,7 +230,7 @@ int gift_items(unit * u, int flags)
item **itm_p = &u->items;
int retval = 0;
int rule = rule_give();
-
+
assert(u->region);
assert(u->faction);
@@ -243,100 +247,102 @@ int gift_items(unit * u, int flags)
return 0;
if ((u_race(u)->ec_flags & GIVEITEM) == 0)
return 0;
-
- /* at first, I should try giving my crap to my own units in this region */
- if (u->faction && (u->faction->flags & FFL_QUIT) == 0 && (flags & GIFT_SELF)) {
- unit *u2, *u3 = NULL;
- for (u2 = r->units; u2; u2 = u2->next) {
- if (u2 != u && u2->faction == u->faction && u2->number > 0) {
- /* some units won't take stuff: */
- if (u_race(u2)->ec_flags & GETITEM) {
- /* we don't like to gift it to units that won't give it back */
- if (u_race(u2)->ec_flags & GIVEITEM) {
- i_merge(&u2->items, &u->items);
+
+ /* at first, I should try giving my crap to my own units in this region */
+ if (u->faction && (u->faction->flags & FFL_QUIT) == 0 && (flags & GIFT_SELF)) {
+ unit *u2, *u3 = NULL;
+ for (u2 = r->units; u2; u2 = u2->next) {
+ if (u2 != u && u2->faction == u->faction && u2->number > 0) {
+ /* some units won't take stuff: */
+ if (u_race(u2)->ec_flags & GETITEM) {
+ /* we don't like to gift it to units that won't give it back */
+ if (u_race(u2)->ec_flags & GIVEITEM) {
+ i_merge(&u2->items, &u->items);
+ u->items = NULL;
+ break;
+ }
+ else {
+ u3 = u2;
+ }
+ }
+ }
+ }
+ if (u->items && u3) {
+ /* if nobody else takes it, we give it to a unit that has issues */
+ i_merge(&u3->items, &u->items);
u->items = NULL;
- break;
- } else {
- u3 = u2;
- }
}
- }
+ if (u->items == NULL)
+ return 0;
}
- if (u->items && u3) {
- /* if nobody else takes it, we give it to a unit that has issues */
- i_merge(&u3->items, &u->items);
- u->items = NULL;
- }
- if (u->items == NULL)
- return 0;
- }
- /* if I have friends, I'll try to give my stuff to them */
- if (u->faction && (flags & GIFT_FRIENDS)) {
- int number = 0;
- buddy *friends = get_friends(u, &number);
+ /* if I have friends, I'll try to give my stuff to them */
+ if (u->faction && (flags & GIFT_FRIENDS)) {
+ int number = 0;
+ buddy *friends = get_friends(u, &number);
- while (friends) {
- struct buddy *nf = friends;
- unit *u2 = nf->unit;
- item *itm = u->items;
- while (itm != NULL) {
- const item_type *itype = itm->type;
- item *itn = itm->next;
- int n = itm->number;
- n = n * nf->number / number;
- if (n > 0) {
- i_change(&u->items, itype, -n);
- i_change(&u2->items, itype, n);
+ while (friends) {
+ struct buddy *nf = friends;
+ unit *u2 = nf->unit;
+ item *itm = u->items;
+ while (itm != NULL) {
+ const item_type *itype = itm->type;
+ item *itn = itm->next;
+ int n = itm->number;
+ n = n * nf->number / number;
+ if (n > 0) {
+ i_change(&u->items, itype, -n);
+ i_change(&u2->items, itype, n);
+ }
+ itm = itn;
+ }
+ number -= nf->number;
+ friends = nf->next;
+ free(nf);
}
- itm = itn;
- }
- number -= nf->number;
- friends = nf->next;
- free(nf);
+ if (u->items == NULL)
+ return 0;
}
- if (u->items == NULL)
- return 0;
- }
- /* last, but not least, give money and horses to peasants */
- while (*itm_p) {
- item *itm = *itm_p;
+ /* last, but not least, give money and horses to peasants */
+ while (*itm_p) {
+ item *itm = *itm_p;
- if (flags & GIFT_PEASANTS) {
- if (!fval(u->region->terrain, SEA_REGION)) {
- if (itm->type->rtype == rsilver) {
- rsetmoney(r, rmoney(r) + itm->number);
- itm->number = 0;
+ if (flags & GIFT_PEASANTS) {
+ if (!fval(u->region->terrain, SEA_REGION)) {
+ if (itm->type->rtype == rsilver) {
+ rsetmoney(r, rmoney(r) + itm->number);
+ itm->number = 0;
+ }
+ else if (itm->type->rtype == rhorse) {
+ rsethorses(r, rhorses(r) + itm->number);
+ itm->number = 0;
+ }
+ }
}
- else if (itm->type->rtype == rhorse) {
- rsethorses(r, rhorses(r) + itm->number);
- itm->number = 0;
+ if (itm->number > 0 && (itm->type->flags & ITF_NOTLOST)) {
+ itm_p = &itm->next;
+ retval = -1;
+ }
+ else {
+ i_remove(itm_p, itm);
+ i_free(itm);
}
- }
}
- if (itm->number > 0 && (itm->type->flags & ITF_NOTLOST)) {
- itm_p = &itm->next;
- retval = -1;
- } else {
- i_remove(itm_p, itm);
- i_free(itm);
- }
- }
- return retval;
+ return retval;
}
void make_zombie(unit * u)
{
- u_setfaction(u, get_monsters());
- scale_number(u, 1);
- u_setrace(u, get_race(RC_ZOMBIE));
- u->irace = NULL;
+ u_setfaction(u, get_monsters());
+ scale_number(u, 1);
+ u_setrace(u, get_race(RC_ZOMBIE));
+ u->irace = NULL;
}
/** remove the unit from the list of active units.
* the unit is not actually freed, because there may still be references
- * dangling to it (from messages, for example). To free all removed units,
+ * dangling to it (from messages, for example). To free all removed units,
* call free_units().
* returns 0 on success, or -1 if unit could not be removed.
*/
@@ -345,57 +351,57 @@ static unit *deleted_units = NULL;
int remove_unit(unit ** ulist, unit * u)
{
- int result;
+ int result;
- assert(ufindhash(u->no));
- handle_event(u->attribs, "destroy", u);
+ assert(ufindhash(u->no));
+ handle_event(u->attribs, "destroy", u);
- result = gift_items(u, GIFT_SELF | GIFT_FRIENDS | GIFT_PEASANTS);
- if (result != 0) {
- make_zombie(u);
- return -1;
- }
-
- if (u->number)
- set_number(u, 0);
- leave(u, true);
- u->region = NULL;
-
- uunhash(u);
- if (ulist) {
- while (*ulist != u) {
- ulist = &(*ulist)->next;
+ result = gift_items(u, GIFT_SELF | GIFT_FRIENDS | GIFT_PEASANTS);
+ if (result != 0) {
+ make_zombie(u);
+ return -1;
}
- assert(*ulist == u);
- *ulist = u->next;
- }
- u->next = deleted_units;
- deleted_units = u;
- dhash(u->no, u->faction);
+ if (u->number)
+ set_number(u, 0);
+ leave(u, true);
+ u->region = NULL;
- u_setfaction(u, NULL);
- u->region = NULL;
+ uunhash(u);
+ if (ulist) {
+ while (*ulist != u) {
+ ulist = &(*ulist)->next;
+ }
+ assert(*ulist == u);
+ *ulist = u->next;
+ }
- return 0;
+ u->next = deleted_units;
+ deleted_units = u;
+ dhash(u->no, u->faction);
+
+ u_setfaction(u, NULL);
+ u->region = NULL;
+
+ return 0;
}
unit *findnewunit(const region * r, const faction * f, int n)
{
- unit *u2;
+ unit *u2;
- if (n == 0)
- return 0;
+ if (n == 0)
+ return 0;
- for (u2 = r->units; u2; u2 = u2->next)
- if (u2->faction == f && ualias(u2) == n)
- return u2;
+ for (u2 = r->units; u2; u2 = u2->next)
+ if (u2->faction == f && ualias(u2) == n)
+ return u2;
#ifdef FIND_FOREIGN_TEMP
- for (u2 = r->units; u2; u2 = u2->next)
- if (ualias(u2) == n)
- return u2;
+ for (u2 = r->units; u2; u2 = u2->next)
+ if (ualias(u2) == n)
+ return u2;
#endif
- return 0;
+ return 0;
}
/* ------------------------------------------------------------- */
@@ -404,74 +410,75 @@ unit *findnewunit(const region * r, const faction * f, int n)
/* at_alias */
/*********************/
attrib_type at_alias = {
- "alias",
- DEFAULT_INIT,
- DEFAULT_FINALIZE,
- DEFAULT_AGE,
- NO_WRITE,
- NO_READ
+ "alias",
+ DEFAULT_INIT,
+ DEFAULT_FINALIZE,
+ DEFAULT_AGE,
+ NO_WRITE,
+ NO_READ
};
int ualias(const unit * u)
{
- attrib *a = a_find(u->attribs, &at_alias);
- if (!a)
- return 0;
- return a->data.i;
+ attrib *a = a_find(u->attribs, &at_alias);
+ if (!a)
+ return 0;
+ return a->data.i;
}
int a_readprivate(attrib * a, void *owner, struct storage *store)
{
- char lbuf[DISPLAYSIZE];
- READ_STR(store, lbuf, sizeof(lbuf));
- a->data.v = _strdup(lbuf);
- return (a->data.v) ? AT_READ_OK : AT_READ_FAIL;
+ char lbuf[DISPLAYSIZE];
+ READ_STR(store, lbuf, sizeof(lbuf));
+ a->data.v = _strdup(lbuf);
+ return (a->data.v) ? AT_READ_OK : AT_READ_FAIL;
}
/*********************/
/* at_private */
/*********************/
attrib_type at_private = {
- "private",
- DEFAULT_INIT,
- a_finalizestring,
- DEFAULT_AGE,
- a_writestring,
- a_readprivate
+ "private",
+ DEFAULT_INIT,
+ a_finalizestring,
+ DEFAULT_AGE,
+ a_writestring,
+ a_readprivate
};
const char *u_description(const unit * u, const struct locale *lang)
{
- if (u->display && u->display[0]) {
- return u->display;
- } else if (u_race(u)->describe) {
- return u_race(u)->describe(u, lang);
- }
- return NULL;
+ if (u->display && u->display[0]) {
+ return u->display;
+ }
+ else if (u_race(u)->describe) {
+ return u_race(u)->describe(u, lang);
+ }
+ return NULL;
}
const char *uprivate(const unit * u)
{
- attrib *a = a_find(u->attribs, &at_private);
- if (!a)
- return NULL;
- return a->data.v;
+ attrib *a = a_find(u->attribs, &at_private);
+ if (!a)
+ return NULL;
+ return a->data.v;
}
void usetprivate(unit * u, const char *str)
{
- attrib *a = a_find(u->attribs, &at_private);
+ attrib *a = a_find(u->attribs, &at_private);
- if (str == NULL) {
- if (a)
- a_remove(&u->attribs, a);
- return;
- }
- if (!a)
- a = a_add(&u->attribs, a_new(&at_private));
- if (a->data.v)
- free(a->data.v);
- a->data.v = _strdup((const char *)str);
+ if (str == NULL) {
+ if (a)
+ a_remove(&u->attribs, a);
+ return;
+ }
+ if (!a)
+ a = a_add(&u->attribs, a_new(&at_private));
+ if (a->data.v)
+ free(a->data.v);
+ a->data.v = _strdup((const char *)str);
}
/*********************/
@@ -479,66 +486,67 @@ void usetprivate(unit * u, const char *str)
/*********************/
/* Einheit BENUTZT einen Trank */
attrib_type at_potionuser = {
- "potionuser",
- DEFAULT_INIT,
- DEFAULT_FINALIZE,
- DEFAULT_AGE,
- NO_WRITE,
- NO_READ
+ "potionuser",
+ DEFAULT_INIT,
+ DEFAULT_FINALIZE,
+ DEFAULT_AGE,
+ NO_WRITE,
+ NO_READ
};
void usetpotionuse(unit * u, const potion_type * ptype)
{
- attrib *a = a_find(u->attribs, &at_potionuser);
- if (!a)
- a = a_add(&u->attribs, a_new(&at_potionuser));
- a->data.v = (void *)ptype;
+ attrib *a = a_find(u->attribs, &at_potionuser);
+ if (!a)
+ a = a_add(&u->attribs, a_new(&at_potionuser));
+ a->data.v = (void *)ptype;
}
const potion_type *ugetpotionuse(const unit * u)
{
- attrib *a = a_find(u->attribs, &at_potionuser);
- if (!a)
- return NULL;
- return (const potion_type *)a->data.v;
+ attrib *a = a_find(u->attribs, &at_potionuser);
+ if (!a)
+ return NULL;
+ return (const potion_type *)a->data.v;
}
/*********************/
/* at_target */
/*********************/
attrib_type at_target = {
- "target",
- DEFAULT_INIT,
- DEFAULT_FINALIZE,
- DEFAULT_AGE,
- NO_WRITE,
- NO_READ
+ "target",
+ DEFAULT_INIT,
+ DEFAULT_FINALIZE,
+ DEFAULT_AGE,
+ NO_WRITE,
+ NO_READ
};
unit *utarget(const unit * u)
{
- attrib *a;
- if (!fval(u, UFL_TARGET))
- return NULL;
- a = a_find(u->attribs, &at_target);
- assert(a || !"flag set, but no target found");
- return (unit *) a->data.v;
+ attrib *a;
+ if (!fval(u, UFL_TARGET))
+ return NULL;
+ a = a_find(u->attribs, &at_target);
+ assert(a || !"flag set, but no target found");
+ return (unit *)a->data.v;
}
void usettarget(unit * u, const unit * t)
{
- attrib *a = a_find(u->attribs, &at_target);
- if (!a && t)
- a = a_add(&u->attribs, a_new(&at_target));
- if (a) {
- if (!t) {
- a_remove(&u->attribs, a);
- freset(u, UFL_TARGET);
- } else {
- a->data.v = (void *)t;
- fset(u, UFL_TARGET);
+ attrib *a = a_find(u->attribs, &at_target);
+ if (!a && t)
+ a = a_add(&u->attribs, a_new(&at_target));
+ if (a) {
+ if (!t) {
+ a_remove(&u->attribs, a);
+ freset(u, UFL_TARGET);
+ }
+ else {
+ a->data.v = (void *)t;
+ fset(u, UFL_TARGET);
+ }
}
- }
}
/*********************/
@@ -547,93 +555,94 @@ void usettarget(unit * u, const unit * t)
void a_writesiege(const attrib * a, const void *owner, struct storage *store)
{
- struct building *b = (struct building *)a->data.v;
- write_building_reference(b, store);
+ struct building *b = (struct building *)a->data.v;
+ write_building_reference(b, store);
}
int a_readsiege(attrib * a, void *owner, struct storage *store)
{
- int result = read_reference(&a->data.v, store, read_building_reference,
- resolve_building);
- if (result == 0 && !a->data.v) {
- return AT_READ_FAIL;
- }
- return AT_READ_OK;
+ int result = read_reference(&a->data.v, store, read_building_reference,
+ resolve_building);
+ if (result == 0 && !a->data.v) {
+ return AT_READ_FAIL;
+ }
+ return AT_READ_OK;
}
attrib_type at_siege = {
- "siege",
- DEFAULT_INIT,
- DEFAULT_FINALIZE,
- DEFAULT_AGE,
- a_writesiege,
- a_readsiege
+ "siege",
+ DEFAULT_INIT,
+ DEFAULT_FINALIZE,
+ DEFAULT_AGE,
+ a_writesiege,
+ a_readsiege
};
struct building *usiege(const unit * u)
{
- attrib *a;
- if (!fval(u, UFL_SIEGE))
- return NULL;
- a = a_find(u->attribs, &at_siege);
- assert(a || !"flag set, but no siege found");
- return (struct building *)a->data.v;
+ attrib *a;
+ if (!fval(u, UFL_SIEGE))
+ return NULL;
+ a = a_find(u->attribs, &at_siege);
+ assert(a || !"flag set, but no siege found");
+ return (struct building *)a->data.v;
}
void usetsiege(unit * u, const struct building *t)
{
- attrib *a = a_find(u->attribs, &at_siege);
- if (!a && t)
- a = a_add(&u->attribs, a_new(&at_siege));
- if (a) {
- if (!t) {
- a_remove(&u->attribs, a);
- freset(u, UFL_SIEGE);
- } else {
- a->data.v = (void *)t;
- fset(u, UFL_SIEGE);
+ attrib *a = a_find(u->attribs, &at_siege);
+ if (!a && t)
+ a = a_add(&u->attribs, a_new(&at_siege));
+ if (a) {
+ if (!t) {
+ a_remove(&u->attribs, a);
+ freset(u, UFL_SIEGE);
+ }
+ else {
+ a->data.v = (void *)t;
+ fset(u, UFL_SIEGE);
+ }
}
- }
}
/*********************/
/* at_contact */
/*********************/
attrib_type at_contact = {
- "contact",
- DEFAULT_INIT,
- DEFAULT_FINALIZE,
- DEFAULT_AGE,
- NO_WRITE,
- NO_READ
+ "contact",
+ DEFAULT_INIT,
+ DEFAULT_FINALIZE,
+ DEFAULT_AGE,
+ NO_WRITE,
+ NO_READ
};
void usetcontact(unit * u, const unit * u2)
{
- attrib *a = a_find(u->attribs, &at_contact);
- while (a && a->type == &at_contact && a->data.v != u2)
- a = a->next;
- if (a && a->type == &at_contact)
- return;
- a_add(&u->attribs, a_new(&at_contact))->data.v = (void *)u2;
+ attrib *a = a_find(u->attribs, &at_contact);
+ while (a && a->type == &at_contact && a->data.v != u2)
+ a = a->next;
+ if (a && a->type == &at_contact)
+ return;
+ a_add(&u->attribs, a_new(&at_contact))->data.v = (void *)u2;
}
bool ucontact(const unit * u, const unit * u2)
/* Prueft, ob u den Kontaktiere-Befehl zu u2 gesetzt hat. */
{
- attrib *ru;
- if (u->faction == u2->faction)
- return true;
+ attrib *ru;
+ if (u->faction == u2->faction)
+ return true;
- /* Explizites KONTAKTIERE */
- for (ru = a_find(u->attribs, &at_contact); ru && ru->type == &at_contact;
- ru = ru->next) {
- if (((unit *) ru->data.v) == u2) {
- return true;
+ /* Explizites KONTAKTIERE */
+ for (ru = a_find(u->attribs, &at_contact); ru && ru->type == &at_contact;
+ ru = ru->next) {
+ if (((unit *)ru->data.v) == u2) {
+ return true;
+ }
}
- }
- return false;
+ return false;
}
/***
@@ -642,280 +651,244 @@ bool ucontact(const unit * u, const unit * u2)
void free_units(void)
{
- while (deleted_units) {
- unit *u = deleted_units;
- deleted_units = deleted_units->next;
- free_unit(u);
- free(u);
- }
+ while (deleted_units) {
+ unit *u = deleted_units;
+ deleted_units = deleted_units->next;
+ free_unit(u);
+ free(u);
+ }
}
void write_unit_reference(const unit * u, struct storage *store)
{
- WRITE_INT(store, (u && u->region) ? u->no : 0);
+ WRITE_INT(store, (u && u->region) ? u->no : 0);
}
int resolve_unit(variant id, void *address)
{
- unit *u = NULL;
- if (id.i != 0) {
- u = findunit(id.i);
- if (u == NULL) {
- *(unit **) address = NULL;
- return -1;
+ unit *u = NULL;
+ if (id.i != 0) {
+ u = findunit(id.i);
+ if (u == NULL) {
+ *(unit **)address = NULL;
+ return -1;
+ }
}
- }
- *(unit **) address = u;
- return 0;
+ *(unit **)address = u;
+ return 0;
}
variant read_unit_reference(struct storage * store)
{
- variant var;
- READ_INT(store, &var.i);
- return var;
-}
-
-attrib_type at_stealth = {
- "stealth", NULL, NULL, NULL, a_writeint, a_readint
-};
-
-void u_seteffstealth(unit * u, int value)
-{
- if (skill_enabled(SK_STEALTH)) {
- attrib *a = NULL;
- if (fval(u, UFL_STEALTH)) {
- a = a_find(u->attribs, &at_stealth);
- }
- if (value < 0) {
- if (a != NULL) {
- freset(u, UFL_STEALTH);
- a_remove(&u->attribs, a);
- }
- return;
- }
- if (a == NULL) {
- a = a_add(&u->attribs, a_new(&at_stealth));
- fset(u, UFL_STEALTH);
- }
- a->data.i = value;
- }
-}
-
-int u_geteffstealth(const struct unit *u)
-{
- if (skill_enabled(SK_STEALTH)) {
- if (fval(u, UFL_STEALTH)) {
- attrib *a = a_find(u->attribs, &at_stealth);
- if (a != NULL)
- return a->data.i;
- }
- }
- return -1;
+ variant var;
+ READ_INT(store, &var.i);
+ return var;
}
int get_level(const unit * u, skill_t id)
{
- if (skill_enabled(id)) {
- skill *sv = u->skills;
- while (sv != u->skills + u->skill_size) {
- if (sv->id == id) {
- return sv->level;
- }
- ++sv;
+ if (skill_enabled(id)) {
+ skill *sv = u->skills;
+ while (sv != u->skills + u->skill_size) {
+ if (sv->id == id) {
+ return sv->level;
+ }
+ ++sv;
+ }
}
- }
- return 0;
+ return 0;
}
void set_level(unit * u, skill_t sk, int value)
{
- skill *sv = u->skills;
+ skill *sv = u->skills;
- if (!skill_enabled(sk))
- return;
+ if (!skill_enabled(sk))
+ return;
- if (value == 0) {
- remove_skill(u, sk);
- return;
- }
- while (sv != u->skills + u->skill_size) {
- if (sv->id == sk) {
- sk_set(sv, value);
- return;
+ if (value == 0) {
+ remove_skill(u, sk);
+ return;
}
- ++sv;
- }
- sk_set(add_skill(u, sk), value);
+ while (sv != u->skills + u->skill_size) {
+ if (sv->id == sk) {
+ sk_set(sv, value);
+ return;
+ }
+ ++sv;
+ }
+ sk_set(add_skill(u, sk), value);
}
static int leftship_age(struct attrib *a)
{
- /* must be aged, so it doesn't affect report generation (cansee) */
- unused_arg(a);
- return AT_AGE_REMOVE; /* remove me */
+ /* must be aged, so it doesn't affect report generation (cansee) */
+ unused_arg(a);
+ return AT_AGE_REMOVE; /* remove me */
}
static attrib_type at_leftship = {
- "leftship", NULL, NULL, leftship_age
+ "leftship", NULL, NULL, leftship_age
};
static attrib *make_leftship(struct ship *leftship)
{
- attrib *a = a_new(&at_leftship);
- a->data.v = leftship;
- return a;
+ attrib *a = a_new(&at_leftship);
+ a->data.v = leftship;
+ return a;
}
void set_leftship(unit * u, ship * sh)
{
- a_add(&u->attribs, make_leftship(sh));
+ a_add(&u->attribs, make_leftship(sh));
}
ship *leftship(const unit * u)
{
- attrib *a = a_find(u->attribs, &at_leftship);
+ attrib *a = a_find(u->attribs, &at_leftship);
- /* Achtung: Es ist nicht garantiert, dass der Rueckgabewert zu jedem
- * Zeitpunkt noch auf ein existierendes Schiff zeigt! */
+ /* Achtung: Es ist nicht garantiert, dass der Rueckgabewert zu jedem
+ * Zeitpunkt noch auf ein existierendes Schiff zeigt! */
- if (a)
- return (ship *) (a->data.v);
+ if (a)
+ return (ship *)(a->data.v);
- return NULL;
+ return NULL;
}
void u_set_building(unit * u, building * b)
{
- assert(!u->building); /* you must leave first */
- u->building = b;
- if (b && (!b->_owner || b->_owner->number <= 0)) {
- building_set_owner(u);
- }
+ assert(!u->building); /* you must leave first */
+ u->building = b;
+ if (b && (!b->_owner || b->_owner->number <= 0)) {
+ building_set_owner(u);
+ }
}
void u_set_ship(unit * u, ship * sh)
{
- assert(!u->ship); /* you must leave_ship */
- u->ship = sh;
- if (sh && (!sh->_owner || sh->_owner->number <= 0)) {
- ship_set_owner(u);
- }
+ assert(!u->ship); /* you must leave_ship */
+ u->ship = sh;
+ if (sh && (!sh->_owner || sh->_owner->number <= 0)) {
+ ship_set_owner(u);
+ }
}
void leave_ship(unit * u)
{
- struct ship *sh = u->ship;
+ struct ship *sh = u->ship;
- u->ship = 0;
- if (sh->_owner==u) {
- ship_update_owner(sh);
- sh->_owner = ship_owner(sh);
- }
- set_leftship(u, sh);
+ u->ship = 0;
+ if (sh->_owner == u) {
+ ship_update_owner(sh);
+ sh->_owner = ship_owner(sh);
+ }
+ set_leftship(u, sh);
}
void leave_building(unit * u)
{
- building * b = u->building;
+ building * b = u->building;
- u->building = 0;
- if (b->_owner==u) {
- building_update_owner(b);
- assert(b->_owner!=u);
- }
+ u->building = 0;
+ if (b->_owner == u) {
+ building_update_owner(b);
+ assert(b->_owner != u);
+ }
}
bool can_leave(unit * u)
{
- static int gamecookie = -1;
- static int rule_leave = -1;
+ static int gamecookie = -1;
+ static int rule_leave = -1;
- if (!u->building) {
+ if (!u->building) {
+ return true;
+ }
+
+ if (rule_leave < 0 || gamecookie != global.cookie) {
+ gamecookie = global.cookie;
+ rule_leave = get_param_int(global.parameters, "rules.move.owner_leave", 0);
+ }
+
+ if (rule_leave && u->building && u == building_owner(u->building)) {
+ return false;
+ }
return true;
- }
-
- if (rule_leave < 0 || gamecookie != global.cookie) {
- gamecookie = global.cookie;
- rule_leave = get_param_int(global.parameters, "rules.move.owner_leave", 0);
- }
-
- if (rule_leave && u->building && u == building_owner(u->building)) {
- return false;
- }
- return true;
}
bool leave(unit * u, bool force)
{
- if (!force) {
- if (!can_leave(u)) {
- return false;
+ if (!force) {
+ if (!can_leave(u)) {
+ return false;
+ }
}
- }
- if (u->building) {
- leave_building(u);
- } else if (u->ship) {
- leave_ship(u);
- }
- return true;
+ if (u->building) {
+ leave_building(u);
+ }
+ else if (u->ship) {
+ leave_ship(u);
+ }
+ return true;
}
const struct race *urace(const struct unit *u)
{
- return u->race_;
+ return u->race_;
}
bool can_survive(const unit * u, const region * r)
{
- if ((fval(r->terrain, WALK_INTO) && (u_race(u)->flags & RCF_WALK))
- || (fval(r->terrain, SWIM_INTO) && (u_race(u)->flags & RCF_SWIM))
- || (fval(r->terrain, FLY_INTO) && (u_race(u)->flags & RCF_FLY))) {
- static const curse_type *ctype = NULL;
+ if ((fval(r->terrain, WALK_INTO) && (u_race(u)->flags & RCF_WALK))
+ || (fval(r->terrain, SWIM_INTO) && (u_race(u)->flags & RCF_SWIM))
+ || (fval(r->terrain, FLY_INTO) && (u_race(u)->flags & RCF_FLY))) {
+ static const curse_type *ctype = NULL;
- if (has_horses(u) && !fval(r->terrain, WALK_INTO))
- return false;
+ if (has_horses(u) && !fval(r->terrain, WALK_INTO))
+ return false;
- if (!ctype)
- ctype = ct_find("holyground");
- if (fval(u_race(u), RCF_UNDEAD) && curse_active(get_curse(r->attribs, ctype)))
- return false;
+ if (!ctype)
+ ctype = ct_find("holyground");
+ if (fval(u_race(u), RCF_UNDEAD) && curse_active(get_curse(r->attribs, ctype)))
+ return false;
- return true;
- }
- return false;
+ return true;
+ }
+ return false;
}
void move_unit(unit * u, region * r, unit ** ulist)
{
- assert(u && r);
+ assert(u && r);
- assert(u->faction || !"this unit is dead");
- if (u->region == r)
- return;
- if (!ulist)
- ulist = (&r->units);
- if (u->region) {
- setguard(u, GUARD_NONE);
- fset(u, UFL_MOVED);
- if (u->ship || u->building) {
- /* can_leave must be checked in travel_i */
+ assert(u->faction || !"this unit is dead");
+ if (u->region == r)
+ return;
+ if (!ulist)
+ ulist = (&r->units);
+ if (u->region) {
+ setguard(u, GUARD_NONE);
+ fset(u, UFL_MOVED);
+ if (u->ship || u->building) {
+ /* can_leave must be checked in travel_i */
#ifndef NDEBUG
- bool result = leave(u, false);
- assert(result);
+ bool result = leave(u, false);
+ assert(result);
#else
- leave(u, false);
+ leave(u, false);
#endif
+ }
+ translist(&u->region->units, ulist, u);
+ }
+ else {
+ addlist(ulist, u);
}
- translist(&u->region->units, ulist, u);
- } else {
- addlist(ulist, u);
- }
#ifdef SMART_INTERVALS
- update_interval(u->faction, r);
+ update_interval(u->faction, r);
#endif
- u->region = r;
+ u->region = r;
}
/* ist mist, aber wegen nicht skalierender attribute notwendig: */
@@ -923,152 +896,159 @@ void move_unit(unit * u, region * r, unit ** ulist)
void transfermen(unit * u, unit * u2, int n)
{
- const attrib *a;
- int hp = u->hp;
- region *r = u->region;
+ const attrib *a;
+ int hp = u->hp;
+ region *r = u->region;
- if (n == 0)
- return;
- assert(n > 0);
- /* "hat attackiert"-status wird uebergeben */
+ if (n == 0)
+ return;
+ assert(n > 0);
+ /* "hat attackiert"-status wird uebergeben */
- if (u2) {
- skill *sv, *sn;
- skill_t sk;
- ship *sh;
+ if (u2) {
+ skill *sv, *sn;
+ skill_t sk;
+ ship *sh;
- assert(u2->number + n > 0);
+ assert(u2->number + n > 0);
- for (sk = 0; sk != MAXSKILLS; ++sk) {
- int weeks, level = 0;
+ for (sk = 0; sk != MAXSKILLS; ++sk) {
+ int weeks, level = 0;
- sv = unit_skill(u, sk);
- sn = unit_skill(u2, sk);
+ sv = unit_skill(u, sk);
+ sn = unit_skill(u2, sk);
- if (sv == NULL && sn == NULL)
- continue;
- if (sn == NULL && u2->number == 0) {
- /* new unit, easy to solve */
- level = sv->level;
- weeks = sv->weeks;
- } else {
- double dlevel = 0.0;
+ if (sv == NULL && sn == NULL)
+ continue;
+ if (sn == NULL && u2->number == 0) {
+ /* new unit, easy to solve */
+ level = sv->level;
+ weeks = sv->weeks;
+ }
+ else {
+ double dlevel = 0.0;
- if (sv && sv->level) {
- dlevel += (sv->level + 1 - sv->weeks / (sv->level + 1.0)) * n;
- level += sv->level * n;
+ if (sv && sv->level) {
+ dlevel += (sv->level + 1 - sv->weeks / (sv->level + 1.0)) * n;
+ level += sv->level * n;
+ }
+ if (sn && sn->level) {
+ dlevel +=
+ (sn->level + 1 - sn->weeks / (sn->level + 1.0)) * u2->number;
+ level += sn->level * u2->number;
+ }
+
+ dlevel = dlevel / (n + u2->number);
+ level = level / (n + u2->number);
+ if (level <= dlevel) {
+ /* apply the remaining fraction to the number of weeks to go.
+ * subtract the according number of weeks, getting closer to the
+ * next level */
+ level = (int)dlevel;
+ weeks = (level + 1) - (int)((dlevel - level) * (level + 1));
+ }
+ else {
+ /* make it harder to reach the next level.
+ * weeks+level is the max difficulty, 1 - the fraction between
+ * level and dlevel applied to the number of weeks between this
+ * and the previous level is the added difficutly */
+ level = (int)dlevel + 1;
+ weeks = 1 + 2 * level - (int)((1 + dlevel - level) * level);
+ }
+ }
+ if (level) {
+ if (sn == NULL)
+ sn = add_skill(u2, sk);
+ sn->level = (unsigned char)level;
+ sn->weeks = (unsigned char)weeks;
+ assert(sn->weeks > 0 && sn->weeks <= sn->level * 2 + 1);
+ assert(u2->number != 0 || (sn->level == sv->level
+ && sn->weeks == sv->weeks));
+ }
+ else if (sn) {
+ remove_skill(u2, sk);
+ sn = NULL;
+ }
}
- if (sn && sn->level) {
- dlevel +=
- (sn->level + 1 - sn->weeks / (sn->level + 1.0)) * u2->number;
- level += sn->level * u2->number;
+ a = a_find(u->attribs, &at_effect);
+ while (a && a->type == &at_effect) {
+ effect_data *olde = (effect_data *)a->data.v;
+ if (olde->value)
+ change_effect(u2, olde->type, olde->value);
+ a = a->next;
}
-
- dlevel = dlevel / (n + u2->number);
- level = level / (n + u2->number);
- if (level <= dlevel) {
- /* apply the remaining fraction to the number of weeks to go.
- * subtract the according number of weeks, getting closer to the
- * next level */
- level = (int)dlevel;
- weeks = (level + 1) - (int)((dlevel - level) * (level + 1));
- } else {
- /* make it harder to reach the next level.
- * weeks+level is the max difficulty, 1 - the fraction between
- * level and dlevel applied to the number of weeks between this
- * and the previous level is the added difficutly */
- level = (int)dlevel + 1;
- weeks = 1 + 2 * level - (int)((1 + dlevel - level) * level);
+ sh = leftship(u);
+ if (sh != NULL)
+ set_leftship(u2, sh);
+ u2->flags |=
+ u->flags & (UFL_LONGACTION | UFL_NOTMOVING | UFL_HUNGER | UFL_MOVED |
+ UFL_ENTER);
+ if (u->attribs) {
+ transfer_curse(u, u2, n);
}
- }
- if (level) {
- if (sn == NULL)
- sn = add_skill(u2, sk);
- sn->level = (unsigned char)level;
- sn->weeks = (unsigned char)weeks;
- assert(sn->weeks > 0 && sn->weeks <= sn->level * 2 + 1);
- assert(u2->number != 0 || (sn->level == sv->level
- && sn->weeks == sv->weeks));
- } else if (sn) {
- remove_skill(u2, sk);
- sn = NULL;
- }
}
- a = a_find(u->attribs, &at_effect);
- while (a && a->type == &at_effect) {
- effect_data *olde = (effect_data *) a->data.v;
- if (olde->value)
- change_effect(u2, olde->type, olde->value);
- a = a->next;
+ scale_number(u, u->number - n);
+ if (u2) {
+ set_number(u2, u2->number + n);
+ hp -= u->hp;
+ u2->hp += hp;
+ /* TODO: Das ist schnarchlahm! und gehoert nicht hierhin */
+ a = a_find(u2->attribs, &at_effect);
+ while (a && a->type == &at_effect) {
+ attrib *an = a->next;
+ effect_data *olde = (effect_data *)a->data.v;
+ int e = get_effect(u, olde->type);
+ if (e != 0)
+ change_effect(u2, olde->type, -e);
+ a = an;
+ }
}
- sh = leftship(u);
- if (sh != NULL)
- set_leftship(u2, sh);
- u2->flags |=
- u->flags & (UFL_LONGACTION | UFL_NOTMOVING | UFL_HUNGER | UFL_MOVED |
- UFL_ENTER);
- if (u->attribs) {
- transfer_curse(u, u2, n);
+ else if (r->land) {
+ if ((u_race(u)->ec_flags & ECF_REC_ETHEREAL) == 0) {
+ const race *rc = u_race(u);
+ if (rc->ec_flags & ECF_REC_HORSES) { /* Zentauren an die Pferde */
+ int h = rhorses(r) + n;
+ rsethorses(r, h);
+ }
+ else {
+ int p = rpeasants(r);
+ p += (int)(n * rc->recruit_multi);
+ rsetpeasants(r, p);
+ }
+ }
}
- }
- scale_number(u, u->number - n);
- if (u2) {
- set_number(u2, u2->number + n);
- hp -= u->hp;
- u2->hp += hp;
- /* TODO: Das ist schnarchlahm! und gehoert nicht hierhin */
- a = a_find(u2->attribs, &at_effect);
- while (a && a->type == &at_effect) {
- attrib *an = a->next;
- effect_data *olde = (effect_data *) a->data.v;
- int e = get_effect(u, olde->type);
- if (e != 0)
- change_effect(u2, olde->type, -e);
- a = an;
- }
- } else if (r->land) {
- if ((u_race(u)->ec_flags & ECF_REC_ETHEREAL) == 0) {
- const race *rc = u_race(u);
- if (rc->ec_flags & ECF_REC_HORSES) { /* Zentauren an die Pferde */
- int h = rhorses(r) + n;
- rsethorses(r, h);
- } else {
- int p = rpeasants(r);
- p += (int)(n * rc->recruit_multi);
- rsetpeasants(r, p);
- }
- }
- }
}
struct building *inside_building(const struct unit *u)
{
- if (u->building == NULL)
- return NULL;
+ if (u->building == NULL)
+ return NULL;
- if (!fval(u->building, BLD_WORKING)) {
- /* Unterhalt nicht bezahlt */
- return NULL;
- } else if (u->building->size < u->building->type->maxsize) {
- /* Gebaeude noch nicht fertig */
- return NULL;
- } else {
- int p = 0, cap = buildingcapacity(u->building);
- const unit *u2;
- for (u2 = u->region->units; u2; u2 = u2->next) {
- if (u2->building == u->building) {
- p += u2->number;
- if (u2 == u) {
- if (p <= cap)
- return u->building;
- return NULL;
- }
- if (p > cap)
- return NULL;
- }
+ if (!fval(u->building, BLD_WORKING)) {
+ /* Unterhalt nicht bezahlt */
+ return NULL;
}
- }
- return NULL;
+ else if (u->building->size < u->building->type->maxsize) {
+ /* Gebaeude noch nicht fertig */
+ return NULL;
+ }
+ else {
+ int p = 0, cap = buildingcapacity(u->building);
+ const unit *u2;
+ for (u2 = u->region->units; u2; u2 = u2->next) {
+ if (u2->building == u->building) {
+ p += u2->number;
+ if (u2 == u) {
+ if (p <= cap)
+ return u->building;
+ return NULL;
+ }
+ if (p > cap)
+ return NULL;
+ }
+ }
+ }
+ return NULL;
}
void u_setfaction(unit * u, faction * f)
@@ -1088,7 +1068,7 @@ void u_setfaction(unit * u, faction * f)
}
if (u->prevF) {
u->prevF->nextF = u->nextF;
- }
+ }
else {
u->faction->units = u->nextF;
}
@@ -1121,101 +1101,102 @@ void u_setfaction(unit * u, faction * f)
/* vorsicht Sprueche koennen u->number == RS_FARVISION haben! */
void set_number(unit * u, int count)
{
- assert(count >= 0);
- assert(count <= UNIT_MAXSIZE);
+ assert(count >= 0);
+ assert(count <= UNIT_MAXSIZE);
- if (count == 0) {
- u->flags &= ~(UFL_HERO);
- }
- if (u->faction) {
- u->faction->num_people += count - u->number;
- }
- u->number = (unsigned short)count;
+ if (count == 0) {
+ u->flags &= ~(UFL_HERO);
+ }
+ if (u->faction) {
+ u->faction->num_people += count - u->number;
+ }
+ u->number = (unsigned short)count;
}
bool learn_skill(unit * u, skill_t sk, double chance)
{
- skill *sv = u->skills;
- if (chance < 1.0 && rng_int() % 10000 >= chance * 10000)
- return false;
- while (sv != u->skills + u->skill_size) {
- assert(sv->weeks > 0);
- if (sv->id == sk) {
- if (sv->weeks <= 1) {
- sk_set(sv, sv->level + 1);
- } else {
- sv->weeks--;
- }
- return true;
+ skill *sv = u->skills;
+ if (chance < 1.0 && rng_int() % 10000 >= chance * 10000)
+ return false;
+ while (sv != u->skills + u->skill_size) {
+ assert(sv->weeks > 0);
+ if (sv->id == sk) {
+ if (sv->weeks <= 1) {
+ sk_set(sv, sv->level + 1);
+ }
+ else {
+ sv->weeks--;
+ }
+ return true;
+ }
+ ++sv;
}
- ++sv;
- }
- sv = add_skill(u, sk);
- sk_set(sv, 1);
- return true;
+ sv = add_skill(u, sk);
+ sk_set(sv, 1);
+ return true;
}
void remove_skill(unit * u, skill_t sk)
{
- skill *sv = u->skills;
- for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
- if (sv->id == sk) {
- skill *sl = u->skills + u->skill_size - 1;
- if (sl != sv) {
- *sv = *sl;
- }
- --u->skill_size;
- return;
+ skill *sv = u->skills;
+ for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
+ if (sv->id == sk) {
+ skill *sl = u->skills + u->skill_size - 1;
+ if (sl != sv) {
+ *sv = *sl;
+ }
+ --u->skill_size;
+ return;
+ }
}
- }
}
skill *add_skill(unit * u, skill_t id)
{
- skill *sv = u->skills;
+ skill *sv = u->skills;
#ifndef NDEBUG
- for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
- assert(sv->id != id);
- }
+ for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
+ assert(sv->id != id);
+ }
#endif
- ++u->skill_size;
- u->skills = realloc(u->skills, u->skill_size * sizeof(skill));
- sv = (u->skills + u->skill_size - 1);
- sv->level = (unsigned char)0;
- sv->weeks = (unsigned char)1;
- sv->old = (unsigned char)0;
- sv->id = (unsigned char)id;
- return sv;
+ ++u->skill_size;
+ u->skills = realloc(u->skills, u->skill_size * sizeof(skill));
+ sv = (u->skills + u->skill_size - 1);
+ sv->level = (unsigned char)0;
+ sv->weeks = (unsigned char)1;
+ sv->old = (unsigned char)0;
+ sv->id = (unsigned char)id;
+ return sv;
}
skill *unit_skill(const unit * u, skill_t sk)
{
- skill *sv = u->skills;
- while (sv != u->skills + u->skill_size) {
- if (sv->id == sk)
- return sv;
- ++sv;
- }
- return NULL;
+ skill *sv = u->skills;
+ while (sv != u->skills + u->skill_size) {
+ if (sv->id == sk)
+ return sv;
+ ++sv;
+ }
+ return NULL;
}
bool has_skill(const unit * u, skill_t sk)
{
- skill *sv = u->skills;
- while (sv != u->skills + u->skill_size) {
- if (sv->id == sk) {
- return (sv->level > 0);
+ skill *sv = u->skills;
+ while (sv != u->skills + u->skill_size) {
+ if (sv->id == sk) {
+ return (sv->level > 0);
+ }
+ ++sv;
}
- ++sv;
- }
- return false;
+ return false;
}
static int item_invis(const unit *u) {
const struct resource_type *rring = get_resourcetype(R_RING_OF_INVISIBILITY);
const struct resource_type *rsphere = get_resourcetype(R_SPHERE_OF_INVISIBILITY);
return (rring ? i_get(u->items, rring->itype) : 0)
- + (rsphere ? i_get(u->items, rsphere->itype) * 100 : 0);
+ + (rsphere ? i_get(u->items, rsphere->itype) * 100 : 0);
}
static int item_modification(const unit * u, skill_t sk, int val)
@@ -1240,123 +1221,124 @@ static int item_modification(const unit * u, skill_t sk, int val)
static int att_modification(const unit * u, skill_t sk)
{
- double result = 0;
- static bool init = false;
- static const curse_type *skillmod_ct, *gbdream_ct, *worse_ct;
- curse *c;
+ double result = 0;
+ static bool init = false;
+ static const curse_type *skillmod_ct, *gbdream_ct, *worse_ct;
+ curse *c;
- if (!init) {
- init = true;
- skillmod_ct = ct_find("skillmod");
- gbdream_ct = ct_find("gbdream");
- worse_ct = ct_find("worse");
- }
+ if (!init) {
+ init = true;
+ skillmod_ct = ct_find("skillmod");
+ gbdream_ct = ct_find("gbdream");
+ worse_ct = ct_find("worse");
+ }
- c = get_curse(u->attribs, worse_ct);
- if (c != NULL)
- result += curse_geteffect(c);
- if (skillmod_ct) {
- attrib *a = a_find(u->attribs, &at_curse);
- while (a && a->type == &at_curse) {
- curse *c = (curse *) a->data.v;
- if (c->type == skillmod_ct && c->data.i == sk) {
+ c = get_curse(u->attribs, worse_ct);
+ if (c != NULL)
result += curse_geteffect(c);
- break;
- }
- a = a->next;
- }
- }
-
- /* TODO hier kann nicht mit get/iscursed gearbeitet werden, da nur der
- * jeweils erste vom Typ C_GBDREAM zurueckgegen wird, wir aber alle
- * durchsuchen und aufaddieren muessen */
- if (u->region) {
- double bonus = 0, malus = 0;
- attrib *a = a_find(u->region->attribs, &at_curse);
- while (a && a->type == &at_curse) {
- curse *c = (curse *) a->data.v;
- if (curse_active(c) && c->type == gbdream_ct) {
- double mod = curse_geteffect(c);
- unit *mage = c->magician;
- /* wir suchen jeweils den groesten Bonus und den groesten Malus */
- if (mod > bonus) {
- if (mage == NULL || mage->number == 0
- || alliedunit(mage, u->faction, HELP_GUARD)) {
- bonus = mod;
- }
- } else if (mod < malus) {
- if (mage == NULL || !alliedunit(mage, u->faction, HELP_GUARD)) {
- malus = mod;
- }
+ if (skillmod_ct) {
+ attrib *a = a_find(u->attribs, &at_curse);
+ while (a && a->type == &at_curse) {
+ curse *c = (curse *)a->data.v;
+ if (c->type == skillmod_ct && c->data.i == sk) {
+ result += curse_geteffect(c);
+ break;
+ }
+ a = a->next;
}
- }
- a = a->next;
}
- result = result + bonus + malus;
- }
- return (int)result;
+ /* TODO hier kann nicht mit get/iscursed gearbeitet werden, da nur der
+ * jeweils erste vom Typ C_GBDREAM zurueckgegen wird, wir aber alle
+ * durchsuchen und aufaddieren muessen */
+ if (u->region) {
+ double bonus = 0, malus = 0;
+ attrib *a = a_find(u->region->attribs, &at_curse);
+ while (a && a->type == &at_curse) {
+ curse *c = (curse *)a->data.v;
+ if (curse_active(c) && c->type == gbdream_ct) {
+ double mod = curse_geteffect(c);
+ unit *mage = c->magician;
+ /* wir suchen jeweils den groesten Bonus und den groesten Malus */
+ if (mod > bonus) {
+ if (mage == NULL || mage->number == 0
+ || alliedunit(mage, u->faction, HELP_GUARD)) {
+ bonus = mod;
+ }
+ }
+ else if (mod < malus) {
+ if (mage == NULL || !alliedunit(mage, u->faction, HELP_GUARD)) {
+ malus = mod;
+ }
+ }
+ }
+ a = a->next;
+ }
+ result = result + bonus + malus;
+ }
+
+ return (int)result;
}
int
get_modifier(const unit * u, skill_t sk, int level, const region * r,
- bool noitem)
+bool noitem)
{
- int bskill = level;
- int skill = bskill;
+ int bskill = level;
+ int skill = bskill;
- if (r && sk == SK_STEALTH) {
- plane *pl = rplane(r);
- if (pl && fval(pl, PFL_NOSTEALTH)) {
- return 0;
+ if (r && sk == SK_STEALTH) {
+ plane *pl = rplane(r);
+ if (pl && fval(pl, PFL_NOSTEALTH)) {
+ return 0;
+ }
}
- }
- skill += rc_skillmod(u_race(u), r, sk);
- skill += att_modification(u, sk);
+ skill += rc_skillmod(u_race(u), r, sk);
+ skill += att_modification(u, sk);
- if (!noitem) {
- skill = item_modification(u, sk, skill);
- }
- skill = skillmod(u->attribs, u, r, sk, skill, SMF_ALWAYS);
+ if (!noitem) {
+ skill = item_modification(u, sk, skill);
+ }
+ skill = skillmod(u->attribs, u, r, sk, skill, SMF_ALWAYS);
#ifdef HUNGER_REDUCES_SKILL
- if (fval(u, UFL_HUNGER)) {
- skill = skill / 2;
- }
+ if (fval(u, UFL_HUNGER)) {
+ skill = skill / 2;
+ }
#endif
- return skill - bskill;
+ return skill - bskill;
}
int eff_skill(const unit * u, skill_t sk, const region * r)
{
- if (skill_enabled(sk)) {
- int level = get_level(u, sk);
- if (level > 0) {
- int mlevel = level + get_modifier(u, sk, level, r, false);
+ if (skill_enabled(sk)) {
+ int level = get_level(u, sk);
+ if (level > 0) {
+ int mlevel = level + get_modifier(u, sk, level, r, false);
- if (mlevel > 0) {
- int skillcap = SkillCap(sk);
- if (skillcap && mlevel > skillcap) {
- return skillcap;
+ if (mlevel > 0) {
+ int skillcap = SkillCap(sk);
+ if (skillcap && mlevel > skillcap) {
+ return skillcap;
+ }
+ return mlevel;
+ }
}
- return mlevel;
- }
}
- }
- return 0;
+ return 0;
}
int eff_skill_study(const unit * u, skill_t sk, const region * r)
{
- int level = get_level(u, sk);
- if (level > 0) {
- int mlevel = level + get_modifier(u, sk, level, r, true);
+ int level = get_level(u, sk);
+ if (level > 0) {
+ int mlevel = level + get_modifier(u, sk, level, r, true);
- if (mlevel > 0)
- return mlevel;
- }
- return 0;
+ if (mlevel > 0)
+ return mlevel;
+ }
+ return 0;
}
int invisible(const unit * target, const unit * viewer)
@@ -1387,68 +1369,71 @@ int invisible(const unit * target, const unit * viewer)
*/
void free_unit(unit * u)
{
- free(u->name);
- free(u->display);
- free_order(u->thisorder);
- free_orders(&u->orders);
- if (u->skills)
- free(u->skills);
- while (u->items) {
- item *it = u->items->next;
- u->items->next = NULL;
- i_free(u->items);
- u->items = it;
- }
- while (u->attribs)
- a_remove(&u->attribs, u->attribs);
- while (u->reservations) {
- struct reservation *res = u->reservations;
- u->reservations = res->next;
- free(res);
- }
+ free(u->name);
+ free(u->display);
+ free_order(u->thisorder);
+ free_orders(&u->orders);
+ if (u->skills)
+ free(u->skills);
+ while (u->items) {
+ item *it = u->items->next;
+ u->items->next = NULL;
+ i_free(u->items);
+ u->items = it;
+ }
+ while (u->attribs)
+ a_remove(&u->attribs, u->attribs);
+ while (u->reservations) {
+ struct reservation *res = u->reservations;
+ u->reservations = res->next;
+ free(res);
+ }
}
static void createunitid(unit * u, int id)
{
- if (id <= 0 || id > MAX_UNIT_NR || ufindhash(id) || dfindhash(id)
- || forbiddenid(id))
- u->no = newunitid();
- else
- u->no = id;
- uhash(u);
+ if (id <= 0 || id > MAX_UNIT_NR || ufindhash(id) || dfindhash(id)
+ || forbiddenid(id))
+ u->no = newunitid();
+ else
+ u->no = id;
+ uhash(u);
}
void name_unit(unit * u)
{
- if (u_race(u)->generate_name) {
- const char *gen_name = u_race(u)->generate_name(u);
- if (gen_name) {
- unit_setname(u, gen_name);
- } else {
- unit_setname(u, racename(u->faction->locale, u, u_race(u)));
- }
- } else {
- char name[32];
- const char * result;
- const struct locale * lang = u->faction ? u->faction->locale : default_locale;
- if (lang) {
- static const char * prefix[MAXLOCALES];
- int i = locale_index(lang);
- if (!prefix[i]) {
- prefix[i] = LOC(lang, "unitdefault");
- if (!prefix[i]) {
- prefix[i] = parameters[P_UNIT];
+ if (u_race(u)->generate_name) {
+ const char *gen_name = u_race(u)->generate_name(u);
+ if (gen_name) {
+ unit_setname(u, gen_name);
+ }
+ else {
+ unit_setname(u, racename(u->faction->locale, u, u_race(u)));
}
- }
- result = prefix[i];
- } else {
- result = parameters[P_UNIT];
}
- strlcpy(name, result, sizeof(name));
- strlcat(name, " ", sizeof(name));
- strlcat(name, itoa36(u->no), sizeof(name));
- unit_setname(u, name);
- }
+ else {
+ char name[32];
+ const char * result;
+ const struct locale * lang = u->faction ? u->faction->locale : default_locale;
+ if (lang) {
+ static const char * prefix[MAXLOCALES];
+ int i = locale_index(lang);
+ if (!prefix[i]) {
+ prefix[i] = LOC(lang, "unitdefault");
+ if (!prefix[i]) {
+ prefix[i] = parameters[P_UNIT];
+ }
+ }
+ result = prefix[i];
+ }
+ else {
+ result = parameters[P_UNIT];
+ }
+ strlcpy(name, result, sizeof(name));
+ strlcat(name, " ", sizeof(name));
+ strlcat(name, itoa36(u->no), sizeof(name));
+ unit_setname(u, name);
+ }
}
/** creates a new unit.
@@ -1457,340 +1442,341 @@ void name_unit(unit * u)
* @param creator: unit to inherit stealth, group, building, ship, etc. from
*/
unit *create_unit(region * r, faction * f, int number, const struct race *urace,
- int id, const char *dname, unit * creator)
+ int id, const char *dname, unit * creator)
{
- unit *u = (unit *)calloc(1, sizeof(unit));
+ unit *u = (unit *)calloc(1, sizeof(unit));
- assert(urace);
- if (f) {
- assert(f->alive);
- u_setfaction(u, f);
+ assert(urace);
+ if (f) {
+ assert(f->alive);
+ u_setfaction(u, f);
- if (f->locale) {
- order *deford = default_order(f->locale);
- if (deford) {
- set_order(&u->thisorder, NULL);
- addlist(&u->orders, deford);
- }
+ if (f->locale) {
+ order *deford = default_order(f->locale);
+ if (deford) {
+ set_order(&u->thisorder, NULL);
+ addlist(&u->orders, deford);
+ }
+ }
}
- }
- u_seteffstealth(u, -1);
- u_setrace(u, urace);
- u->irace = NULL;
+ u_setrace(u, urace);
+ u->irace = NULL;
- set_number(u, number);
+ set_number(u, number);
- /* die nummer der neuen einheit muss vor name_unit generiert werden,
- * da der default name immer noch 'Nummer u->no' ist */
- createunitid(u, id);
+ /* die nummer der neuen einheit muss vor name_unit generiert werden,
+ * da der default name immer noch 'Nummer u->no' ist */
+ createunitid(u, id);
- /* zuerst in die Region setzen, da zb Drachennamen den Regionsnamen
- * enthalten */
- if (r)
- move_unit(u, r, NULL);
+ /* zuerst in die Region setzen, da zb Drachennamen den Regionsnamen
+ * enthalten */
+ if (r)
+ move_unit(u, r, NULL);
- /* u->race muss bereits gesetzt sein, wird fuer default-hp gebraucht */
- /* u->region auch */
- u->hp = unit_max_hp(u) * number;
+ /* u->race muss bereits gesetzt sein, wird fuer default-hp gebraucht */
+ /* u->region auch */
+ u->hp = unit_max_hp(u) * number;
- if (!dname) {
- name_unit(u);
- } else {
- u->name = _strdup(dname);
- }
-
- if (creator) {
- attrib *a;
-
- /* erbt Kampfstatus */
- setstatus(u, creator->status);
-
- /* erbt Gebaeude/Schiff */
- if (creator->region == r) {
- if (creator->building) {
- u_set_building(u, creator->building);
- }
- if (creator->ship && fval(u_race(u), RCF_CANSAIL)) {
- u_set_ship(u, creator->ship);
- }
+ if (!dname) {
+ name_unit(u);
+ }
+ else {
+ u->name = _strdup(dname);
}
- /* Tarnlimit wird vererbt */
- if (fval(creator, UFL_STEALTH)) {
- attrib *a = a_find(creator->attribs, &at_stealth);
- if (a) {
- int stealth = a->data.i;
- a = a_add(&u->attribs, a_new(&at_stealth));
- a->data.i = stealth;
- }
+ if (creator) {
+ attrib *a;
+
+ /* erbt Kampfstatus */
+ setstatus(u, creator->status);
+
+ /* erbt Gebaeude/Schiff */
+ if (creator->region == r) {
+ if (creator->building) {
+ u_set_building(u, creator->building);
+ }
+ if (creator->ship && fval(u_race(u), RCF_CANSAIL)) {
+ u_set_ship(u, creator->ship);
+ }
+ }
+
+ /* Tarnlimit wird vererbt */
+ if (fval(creator, UFL_STEALTH)) {
+ attrib *a = a_find(creator->attribs, &at_stealth);
+ if (a) {
+ int stealth = a->data.i;
+ a = a_add(&u->attribs, a_new(&at_stealth));
+ a->data.i = stealth;
+ }
+ }
+
+ /* Temps von parteigetarnten Einheiten sind wieder parteigetarnt */
+ if (fval(creator, UFL_ANON_FACTION)) {
+ fset(u, UFL_ANON_FACTION);
+ }
+ /* Daemonentarnung */
+ set_racename(&u->attribs, get_racename(creator->attribs));
+ if (fval(u_race(u), RCF_SHAPESHIFT) && fval(u_race(creator), RCF_SHAPESHIFT)) {
+ u->irace = creator->irace;
+ }
+
+ /* Gruppen */
+ if (creator->faction == f && fval(creator, UFL_GROUP)) {
+ a = a_find(creator->attribs, &at_group);
+ if (a) {
+ group *g = (group *)a->data.v;
+ set_group(u, g);
+ }
+ }
+ a = a_find(creator->attribs, &at_otherfaction);
+ if (a) {
+ a_add(&u->attribs, make_otherfaction(get_otherfaction(a)));
+ }
+
+ a = a_add(&u->attribs, a_new(&at_creator));
+ a->data.v = creator;
}
- /* Temps von parteigetarnten Einheiten sind wieder parteigetarnt */
- if (fval(creator, UFL_ANON_FACTION)) {
- fset(u, UFL_ANON_FACTION);
- }
- /* Daemonentarnung */
- set_racename(&u->attribs, get_racename(creator->attribs));
- if (fval(u_race(u), RCF_SHAPESHIFT) && fval(u_race(creator), RCF_SHAPESHIFT)) {
- u->irace = creator->irace;
- }
-
- /* Gruppen */
- if (creator->faction == f && fval(creator, UFL_GROUP)) {
- a = a_find(creator->attribs, &at_group);
- if (a) {
- group *g = (group *) a->data.v;
- set_group(u, g);
- }
- }
- a = a_find(creator->attribs, &at_otherfaction);
- if (a) {
- a_add(&u->attribs, make_otherfaction(get_otherfaction(a)));
- }
-
- a = a_add(&u->attribs, a_new(&at_creator));
- a->data.v = creator;
- }
-
- return u;
+ return u;
}
int maxheroes(const struct faction *f)
{
- int nsize = count_all(f);
- if (nsize == 0)
- return 0;
- else {
- int nmax = (int)(log10(nsize / 50.0) * 20);
- return (nmax < 0) ? 0 : nmax;
- }
+ int nsize = count_all(f);
+ if (nsize == 0)
+ return 0;
+ else {
+ int nmax = (int)(log10(nsize / 50.0) * 20);
+ return (nmax < 0) ? 0 : nmax;
+ }
}
int countheroes(const struct faction *f)
{
- const unit *u = f->units;
- int n = 0;
+ const unit *u = f->units;
+ int n = 0;
- while (u) {
- if (fval(u, UFL_HERO))
- n += u->number;
- u = u->nextF;
- }
+ while (u) {
+ if (fval(u, UFL_HERO))
+ n += u->number;
+ u = u->nextF;
+ }
#ifdef DEBUG_MAXHEROES
- int m = maxheroes(f);
- if (n > m) {
- log_warning("%s has %d of %d heroes\n", factionname(f), n, m);
- }
+ int m = maxheroes(f);
+ if (n > m) {
+ log_warning("%s has %d of %d heroes\n", factionname(f), n, m);
+ }
#endif
- return n;
+ return n;
}
const char *unit_getname(const unit * u)
{
- return (const char *)u->name;
+ return (const char *)u->name;
}
void unit_setname(unit * u, const char *name)
{
- free(u->name);
- if (name)
- u->name = _strdup(name);
- else
- u->name = NULL;
+ free(u->name);
+ if (name)
+ u->name = _strdup(name);
+ else
+ u->name = NULL;
}
const char *unit_getinfo(const unit * u)
{
- return (const char *)u->display;
+ return (const char *)u->display;
}
void unit_setinfo(unit * u, const char *info)
{
- free(u->display);
- if (info)
- u->display = _strdup(info);
- else
- u->display = NULL;
+ free(u->display);
+ if (info)
+ u->display = _strdup(info);
+ else
+ u->display = NULL;
}
int unit_getid(const unit * u)
{
- return u->no;
+ return u->no;
}
void unit_setid(unit * u, int id)
{
- unit *nu = findunit(id);
- if (nu == NULL) {
- uunhash(u);
- u->no = id;
- uhash(u);
- }
+ unit *nu = findunit(id);
+ if (nu == NULL) {
+ uunhash(u);
+ u->no = id;
+ uhash(u);
+ }
}
int unit_gethp(const unit * u)
{
- return u->hp;
+ return u->hp;
}
void unit_sethp(unit * u, int hp)
{
- u->hp = hp;
+ u->hp = hp;
}
status_t unit_getstatus(const unit * u)
{
- return u->status;
+ return u->status;
}
void unit_setstatus(unit * u, status_t status)
{
- u->status = status;
+ u->status = status;
}
int unit_getweight(const unit * u)
{
- return weight(u);
+ return weight(u);
}
int unit_getcapacity(const unit * u)
{
- return walkingcapacity(u);
+ return walkingcapacity(u);
}
void unit_addorder(unit * u, order * ord)
{
- order **ordp = &u->orders;
- while (*ordp)
- ordp = &(*ordp)->next;
- *ordp = ord;
- u->faction->lastorders = turn;
+ order **ordp = &u->orders;
+ while (*ordp)
+ ordp = &(*ordp)->next;
+ *ordp = ord;
+ u->faction->lastorders = turn;
}
int unit_max_hp(const unit * u)
{
- static int rules_stamina = -1;
- int h;
- double p;
- static const curse_type *heal_ct = NULL;
+ static int rules_stamina = -1;
+ int h;
+ double p;
+ static const curse_type *heal_ct = NULL;
- if (rules_stamina < 0) {
- rules_stamina =
- get_param_int(global.parameters, "rules.stamina", STAMINA_AFFECTS_HP);
- }
- h = u_race(u)->hitpoints;
- if (heal_ct == NULL)
- heal_ct = ct_find("healingzone");
-
- if (rules_stamina & 1) {
- p = pow(effskill(u, SK_STAMINA) / 2.0, 1.5) * 0.2;
- h += (int)(h * p + 0.5);
- }
- /* der healing curse veraendert die maximalen hp */
- if (heal_ct) {
- curse *c = get_curse(u->region->attribs, heal_ct);
- if (c) {
- h = (int)(h * (1.0 + (curse_geteffect(c) / 100)));
+ if (rules_stamina < 0) {
+ rules_stamina =
+ get_param_int(global.parameters, "rules.stamina", STAMINA_AFFECTS_HP);
}
- }
+ h = u_race(u)->hitpoints;
+ if (heal_ct == NULL)
+ heal_ct = ct_find("healingzone");
- return h;
+ if (rules_stamina & 1) {
+ p = pow(effskill(u, SK_STAMINA) / 2.0, 1.5) * 0.2;
+ h += (int)(h * p + 0.5);
+ }
+ /* der healing curse veraendert die maximalen hp */
+ if (heal_ct) {
+ curse *c = get_curse(u->region->attribs, heal_ct);
+ if (c) {
+ h = (int)(h * (1.0 + (curse_geteffect(c) / 100)));
+ }
+ }
+
+ return h;
}
void scale_number(unit * u, int n)
{
- skill_t sk;
- const attrib *a;
- int remain;
+ const attrib *a;
+ int remain;
- if (n == u->number)
- return;
- if (n && u->number > 0) {
- int full;
- remain = ((u->hp % u->number) * (n % u->number)) % u->number;
+ if (n == u->number)
+ return;
+ if (n && u->number > 0) {
+ int full;
+ remain = ((u->hp % u->number) * (n % u->number)) % u->number;
- full = u->hp / u->number; /* wieviel kriegt jede person mindestens */
- u->hp = full * n + (u->hp - full * u->number) * n / u->number;
- assert(u->hp >= 0);
- if ((rng_int() % u->number) < remain)
- ++u->hp; /* Nachkommastellen */
- } else {
- remain = 0;
- u->hp = 0;
- }
- if (u->number > 0) {
- for (a = a_find(u->attribs, &at_effect); a && a->type == &at_effect;
- a = a->next) {
- effect_data *data = (effect_data *) a->data.v;
- int snew = data->value / u->number * n;
- if (n) {
- remain = data->value - snew / n * u->number;
- snew += remain * n / u->number;
- remain = (remain * n) % u->number;
+ full = u->hp / u->number; /* wieviel kriegt jede person mindestens */
+ u->hp = full * n + (u->hp - full * u->number) * n / u->number;
+ assert(u->hp >= 0);
if ((rng_int() % u->number) < remain)
- ++snew; /* Nachkommastellen */
- }
- data->value = snew;
+ ++u->hp; /* Nachkommastellen */
}
- }
- if (u->number == 0 || n == 0) {
- for (sk = 0; sk < MAXSKILLS; sk++) {
- remove_skill(u, sk);
+ else {
+ remain = 0;
+ u->hp = 0;
+ }
+ if (u->number > 0) {
+ for (a = a_find(u->attribs, &at_effect); a && a->type == &at_effect;
+ a = a->next) {
+ effect_data *data = (effect_data *)a->data.v;
+ int snew = data->value / u->number * n;
+ if (n) {
+ remain = data->value - snew / n * u->number;
+ snew += remain * n / u->number;
+ remain = (remain * n) % u->number;
+ if ((rng_int() % u->number) < remain)
+ ++snew; /* Nachkommastellen */
+ }
+ data->value = snew;
+ }
+ }
+ if (u->number == 0 || n == 0) {
+ skill_t sk;
+ for (sk = 0; sk < MAXSKILLS; sk++) {
+ remove_skill(u, sk);
+ }
}
- }
- set_number(u, n);
+ set_number(u, n);
}
const struct race *u_irace(const struct unit *u)
{
- if (u->irace && skill_enabled(SK_STEALTH)) {
- return u->irace;
- }
- return u->race_;
+ if (u->irace && skill_enabled(SK_STEALTH)) {
+ return u->irace;
+ }
+ return u->race_;
}
const struct race *u_race(const struct unit *u)
{
- return u->race_;
+ return u->race_;
}
void u_setrace(struct unit *u, const struct race *rc)
{
- assert(rc);
- u->race_ = rc;
+ assert(rc);
+ u->race_ = rc;
}
void unit_add_spell(unit * u, sc_mage * m, struct spell * sp, int level)
{
- sc_mage *mage = m ? m : get_mage(u);
+ sc_mage *mage = m ? m : get_mage(u);
- if (!mage) {
- log_debug("adding new spell %s to a previously non-mage unit %s\n", sp->sname, unitname(u));
- mage = create_mage(u, u->faction?u->faction->magiegebiet:M_GRAY);
- }
- if (!mage->spellbook) {
- mage->spellbook = create_spellbook(0);
- }
- spellbook_add(mage->spellbook, sp, level);
+ if (!mage) {
+ log_debug("adding new spell %s to a previously non-mage unit %s\n", sp->sname, unitname(u));
+ mage = create_mage(u, u->faction ? u->faction->magiegebiet : M_GRAY);
+ }
+ if (!mage->spellbook) {
+ mage->spellbook = create_spellbook(0);
+ }
+ spellbook_add(mage->spellbook, sp, level);
}
struct spellbook * unit_get_spellbook(const struct unit * u)
{
- sc_mage * mage = get_mage(u);
- if (mage) {
- if (mage->spellbook) {
- return mage->spellbook;
+ sc_mage * mage = get_mage(u);
+ if (mage) {
+ if (mage->spellbook) {
+ return mage->spellbook;
+ }
+ if (mage->magietyp != M_GRAY) {
+ return faction_get_spellbook(u->faction);
+ }
}
- if (mage->magietyp!=M_GRAY) {
- return faction_get_spellbook(u->faction);
- }
- }
- return 0;
+ return 0;
}
int effskill(const unit * u, skill_t sk)
{
- return eff_skill(u, sk, u->region);
+ return eff_skill(u, sk, u->region);
}
diff --git a/src/kernel/unit.h b/src/kernel/unit.h
index 4c232da5e..de90db3b2 100644
--- a/src/kernel/unit.h
+++ b/src/kernel/unit.h
@@ -125,10 +125,6 @@ extern "C" {
int ualias(const struct unit *u);
- extern struct attrib_type at_stealth;
-
- void u_seteffstealth(struct unit *u, int value);
- int u_geteffstealth(const struct unit *u);
const struct race *u_irace(const struct unit *u);
const struct race *u_race(const struct unit *u);
void u_setrace(struct unit *u, const struct race *);
diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c
new file mode 100644
index 000000000..6f3618e73
--- /dev/null
+++ b/src/kernel/unit.test.c
@@ -0,0 +1,41 @@
+#include
+#include
+#include "alchemy.h"
+#include "unit.h"
+#include "item.h"
+#include "region.h"
+
+#include
+#include