diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4d39d0559..8b7cfa11d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -6,7 +6,7 @@ name: CI
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
- branches: [ master, develop ]
+ branches: [ master, develop, gh ]
pull_request:
branches: [ master, develop ]
@@ -45,3 +45,8 @@ jobs:
with:
args: "-DCMAKE_BUILD_TYPE=Debug"
run-test: true
+ - name: Integration Test
+ run: |
+ cd tests
+ ./run-turn.sh
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2b0c9969a..d712fded2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.13)
+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
+
set(CMAKE_C_STANDARD 99)
if (WIN32)
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH )
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..0363d010f
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,21 @@
+# Contributing to Eressea
+
+Since Eressea is for the most part a one-person show run by
+[me](https://enno.horse/), contributions that fix bugs,
+add features or improve usability are always welcome. Due to the
+one-person thing however, my time to work on this project and review
+PRs is fairly limited.
+
+## Use of large language models / generative "AI"
+
+Eressea is something I do for fun, and to relax. I love programming,
+games, communities, and everything else that's involved in running
+online games.
+
+Receiving a LLM-generated PR however takes the joy out of this and makes
+reviewing the PR a wasted effort. If you didn't bother writing and
+thinking about it, I won't bother reading it.
+
+Therefore, LLM-generated pull requests will not be accepted in this
+project.
+
diff --git a/conf/e2/terrains.json b/conf/e2/terrains.json
index df9afc6fd..0ba8b7174 100644
--- a/conf/e2/terrains.json
+++ b/conf/e2/terrains.json
@@ -285,7 +285,7 @@
"flags": [ "land", "walk", "sail" ]
},
"wall1": {
- "flags": [ "forbidden", "land" ]
+ "flags": [ "forbidden" ]
}
}
}
diff --git a/docker/start-container b/docker/start-container
index 287675062..a0e3d2aeb 100644
--- a/docker/start-container
+++ b/docker/start-container
@@ -6,4 +6,4 @@ fi
export LUA_PATH="/usr/local/share/eressea/scripts/?.lua;/usr/local/share/eressea/scripts/?/init.lua;$LUA_PATH"
export ERESSEA_INSTALL=/usr/local/share/eressea
-exec eressea -i "$ERESSEA_INSTALL" "$@"
+exec eressea "$@"
diff --git a/process/compress.py b/process/compress.py
index ade1a6c0d..8743895b5 100755
--- a/process/compress.py
+++ b/process/compress.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python3
+
from sys import argv, exit
-import os
+import os, subprocess, shlex
import io
import os.path
@@ -63,7 +64,8 @@
filename = "%s%s" % (prefix, extension)
if os.path.isfile(filename):
parameters = parameters + [ filename ]
- os.system("zip %s -q -m -j %s" % (output, ' '.join(parameters)))
+ subprocess.run(["zip", shlex.quote(output), "-q", "-m", "-j",
+ ' '.join(shlex.quote(p) for p in parameters)], check=True)
else:
files = []
for extension in reports:
@@ -74,7 +76,7 @@
if os.path.isfile(filename):
if os.path.isfile(output):
continue
- os.system("bzip2 %s" % filename)
+ subprocess.run(["bzip2", shlex.quote(filename)], check=True)
for extra in extras:
if os.path.isfile(extra):
files = files + [extra]
diff --git a/process/cron/run-eressea.cron b/process/cron/run-eressea.cron
index 5a241ddfb..88157b845 100755
--- a/process/cron/run-eressea.cron
+++ b/process/cron/run-eressea.cron
@@ -62,7 +62,7 @@ backup()
export ERESSEA
eval "$(luarocks path)"
-export LUA_PATH="${ERESSEA}/server/scripts/?.lua;${ERESSEA}/git/scripts/?/init.lua;$LUA_PATH"
+export LUA_PATH="${ERESSEA}/server/scripts/?.lua;$LUA_PATH"
BIN="$ERESSEA/server/bin"
TURN=$(cat "$ERESSEA/game-$GAME/turn")
if [ ! -e "$ERESSEA/game-$GAME/data/$TURN.dat" ]; then
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 0c71f8703..d179aae94 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -2639,7 +2639,7 @@
-
+
@@ -5863,18 +5863,32 @@
-
+
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
-
+
diff --git a/res/e3a/races.xml b/res/e3a/races.xml
index ad2e6c1a9..95c46852a 100644
--- a/res/e3a/races.xml
+++ b/res/e3a/races.xml
@@ -714,7 +714,7 @@
-
+
diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po
index 8b671572e..57cccf9df 100644
--- a/res/translations/messages.de.po
+++ b/res/translations/messages.de.po
@@ -61,8 +61,11 @@ msgstr "\"$unit($unit) hat Schwierigkeiten seine magischen Energien zu sammeln.
msgid "harbor_trade"
msgstr "\"$unit($unit) erhielt $resources($items) von der $ship($ship).\""
-msgid "reduce_spell"
-msgstr "\"$unit($self) schwächt in $region($region) einen Zauber von $unit.dative($mage) durch Antimagie ab.\""
+msgid "spell_reduced_by"
+msgstr "\"$unit($mage) schwächt in $region($region) einen Zauber von $unit.dative($target) durch Antimagie ab.\""
+
+msgid "spell_reduced"
+msgstr "In $region($region) wird ein Zauber von $unit.dative($target) durch Antimagie abgeschwächt.\""
msgid "raised"
msgstr "\"$unit($unit) züchtet $int($amount) Pferde.\""
@@ -338,8 +341,11 @@ msgstr "\"Moral der Bauern: $int($morale)\""
msgid "changemail_invalid"
msgstr "\"Die Reportadresse wurde nicht geändert, '${value}' ist keine gültige email.\""
-msgid "block_spell"
-msgstr "\"Antimagie von $unit.dative($self) blockiert in $region($region) einen Zauber von $unit.dative($mage).\""
+msgid "spell_blocked_by"
+msgstr "\"Antimagie von $unit.dative($mage) blockiert in $region($region) einen Zauber von $unit.dative($target).\""
+
+msgid "spell_blocked"
+msgstr "\"In $region($region) wird ein Zauber von $unit.dative($target) durch Antimagie blockiert.\""
msgid "enter_overload"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit darf nicht an Bord kommen, da sie das Schiff überladen würde.\""
diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po
index 67d8c9e3b..fef6f019d 100644
--- a/res/translations/messages.en.po
+++ b/res/translations/messages.en.po
@@ -61,8 +61,11 @@ msgstr "\"$unit($unit) finds it difficult to gather its magical energies. ($int3
msgid "harbor_trade"
msgstr "\"$unit($unit) received $resources($items) from the $ship($ship).\""
-msgid "reduce_spell"
-msgstr "\"In $region($region), anti-magic from $unit($self) reduces the effect of $unit($mage)'s spell.\""
+msgid "spell_reduced_by"
+msgstr "\"In $region($region), anti-magic from $unit($mage) reduces the effect of $unit($target)'s spell.\""
+
+msgid "spell_reduced"
+msgstr "\"In $region($region), anti-magic reduces the effect of $unit($target)'s spell.\""
msgid "raised"
msgstr "\"$unit($unit) breeds $int($amount) horses.\""
@@ -338,8 +341,11 @@ msgstr "\"peasant morale: $int($morale)\""
msgid "changemail_invalid"
msgstr "\"Address not changed, '$value' is an invalid email.\""
-msgid "block_spell"
-msgstr "\"In $region($region), anti-magic from $unit($self) blocks the spell of $unit($mage).\""
+msgid "spell_blocked"
+msgstr "\"In $region($region), anti-magic blocks a spell by $unit($target).\""
+
+msgid "spell_blocked_by"
+msgstr "\"In $region($region), anti-magic from $unit($mage) blocks a spell by $unit($target).\""
msgid "enter_overload"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit cannot go aboard, the ship would be overloaded.\""
diff --git a/s/cmake-init b/s/cmake-init
index 08780a497..d80f52d84 100755
--- a/s/cmake-init
+++ b/s/cmake-init
@@ -2,8 +2,9 @@
WITHOUT_LUA=OFF
ERESSEA_DB=memory
+if [ -n "$(which pkg-config)" ]; then
pkg-config --exists sqlite3 && ERESSEA_DB=sqlite
-
+fi
usage()
{
echo "usage: $0 --help --db=DB"
diff --git a/s/install b/s/install
index cb67c019d..db9c8a9b9 100755
--- a/s/install
+++ b/s/install
@@ -13,15 +13,14 @@ done
if [ "$FORCE" != "yes" ] ; then
DESCRIBE=$(git describe --tags)
VERSION=$(build/eressea/eressea --version | sed -e 's/Eressea version \([^,]*\),.*/\1/')
- if [ "v$VERSION" != "$DESCRIBE" ] ; then
- echo "You must buld the server before installing it"
- echo "Current build is $VERSION, source is $DESCRIBE"
- exit
- fi
BRANCH=$(git status -s -b | head -1 | cut -d\ -f 2 | sed 's/\..*//')
if [ "$BRANCH" != "master" ] ; then
echo "You should only install stable versions from the master branch. Use -f to override."
exit
+ elseif [ "v$VERSION" != "$DESCRIBE" ]
+ echo "You must build the server before installing it"
+ echo "Current build is $VERSION, source is $DESCRIBE"
+ exit
fi
fi
[ -z "$CC" ] && [ -n "$(which clang)" ] && CC="clang"
@@ -31,7 +30,9 @@ fi
BIN_DIR="build"
ROOT=$(git rev-parse --show-toplevel)
+if [ -z "$PREFIX" ]; then
PREFIX=$(dirname "$ROOT")/server
+fi
cd "$ROOT" || exit
cmake --install "$BIN_DIR" --prefix "$PREFIX"
if [ ! -e "$PREFIX/bin/backup-eressea" ] ; then
diff --git a/s/preview b/s/preview
index 86cfe947b..919a13f23 100755
--- a/s/preview
+++ b/s/preview
@@ -57,7 +57,6 @@ cat >| eressea.ini < turn
touch newfactions
ln -sf "$SOURCE/bin/eressea" .
-ln -sf "$SOURCE/scripts/run-turn.lua" .
-ln -sf "$SOURCE/scripts/reports.lua" .
-ln -sf "$SOURCE/scripts/config.lua" .
diff --git a/scripts/debug.lua b/scripts/debug.lua
deleted file mode 100644
index 69e830a87..000000000
--- a/scripts/debug.lua
+++ /dev/null
@@ -1,13 +0,0 @@
-print("LUA_PATH is " .. os.getenv('LUA_PATH'))
-print("LUA_CPATH is " .. os.getenv('LUA_CPATH'))
-print("PACKAGE.PATH is " .. package.path)
-lunit = require('lunit')
-
-module('tests.example', lunit.testcase, package.seeall)
-function test_example()
- assert_true(true)
-end
-
-result = lunit.main()
-return result.errors + result.failed
-
diff --git a/scripts/eressea/embassy.lua b/scripts/eressea/embassy.lua
index 2d3e44a71..fc68cfe11 100644
--- a/scripts/eressea/embassy.lua
+++ b/scripts/eressea/embassy.lua
@@ -27,13 +27,6 @@ function use_ring_of_levitation(u, amount, token, ord)
return 0
end
--- Muschelplateau
-
-if not config.embassy or config.embassy==0 then return nil end
-
-local embassy = {}
-local home = nil
-
-- global exports (use item)
function use_seashell(u, amount)
-- Muschelplateau...
@@ -50,6 +43,12 @@ function use_seashell(u, amount)
return -4
end
+-- Muschelplateau
+if not config.embassy or config.embassy==0 then return nil end
+
+local embassy = {}
+local home = nil
+
function embassy.init()
home = get_region(165,30)
if home==nil then
diff --git a/scripts/eressea/path.lua b/scripts/eressea/path.lua
index ad60f7d6f..c240a2cce 100644
--- a/scripts/eressea/path.lua
+++ b/scripts/eressea/path.lua
@@ -1,7 +1,7 @@
-local install = os.getenv("ERESSEA_INSTALL") or config.install
+local install = config.install
if install then
eressea.config.add_authority("conf", install .. '/conf')
eressea.config.add_authority("res", install .. '/res')
else
- print("eressea.ini does not set an install directory")
+ print("no install directory set")
end
diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua
index 4c4fedb0b..d6e890e9f 100644
--- a/scripts/run-turn.lua
+++ b/scripts/run-turn.lua
@@ -210,4 +210,5 @@ if config.rules then
elseif not config.config then
eressea.log.warning('no rule modules loaded, specify a game in eressea.ini or with -r')
end
+assert(use_seashell)
run_turn(rules)
diff --git a/scripts/tests/e2/guard.lua b/scripts/tests/e2/guard.lua
index 0907ef712..8d07e1c0a 100644
--- a/scripts/tests/e2/guard.lua
+++ b/scripts/tests/e2/guard.lua
@@ -139,3 +139,25 @@ function test_move_to_same_region_stops_guarding()
assert_equal(r1, u.region)
assert_false(u.guard)
end
+
+function test_flee_guard()
+ local r1 = region.create(0, 0, "plain")
+
+ local f1 = faction.create("human", "guardflee@eressea.de", "de")
+
+ local u11 = unit.create(f1, r1, 1)
+
+ u11:add_item("sword", 1)
+ u11:set_skill("melee", 1)
+ u11.guard = true
+ u11:add_order("BEWACHE")
+ process_orders()
+ assert_equal(true, u11.guard)
+
+ u11:clear_orders()
+ u11:add_order("KÄMPFE FLIEHE")
+
+ process_orders()
+ -- write_reports()
+ assert_equal(false, u11.guard)
+end
diff --git a/scripts/tests/e2/xmas.lua b/scripts/tests/e2/xmas.lua
index 7c37dceb4..497957f37 100644
--- a/scripts/tests/e2/xmas.lua
+++ b/scripts/tests/e2/xmas.lua
@@ -84,11 +84,13 @@ function test_xmastree()
assert_equal(10, r:get_resource("tree"))
-- intermittent?
local k = r:get_key("xm06")
- local t = get_turn()
- if (t ~= k) then
- print("turn " .. t .. ", xm06 " .. k)
- end
- assert_equal(t, k)
+ if k then
+ local t = get_turn()
+ if (t ~= k) then
+ print("turn " .. t .. ", xm06 " .. k)
+ end
+ assert_equal(t, k)
+ end
end
function test_stardust()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3134ad595..7ca1aeb6f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -241,10 +241,7 @@ target_link_libraries(eressea
${LUA_LIBRARIES}
)
-add_test(NAME lua-debug
- COMMAND eressea -v1 ../scripts/debug.lua
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
-set_property(TEST lua-debug PROPERTY RUN_SERIAL)
+include (CTest)
add_test(NAME lua-core
COMMAND eressea -v1 ../scripts/run-tests.lua
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
@@ -257,7 +254,7 @@ add_test(NAME lua-e3
COMMAND eressea -v1 ../scripts/run-tests-e3.lua
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
set_property(TEST lua-e3 PROPERTY RUN_SERIAL)
-set_tests_properties(lua-debug lua-core lua-e2 lua-e3 PROPERTIES LABELS "lua;ci-only")
+set_tests_properties(lua-core lua-e2 lua-e3 PROPERTIES LABELS "lua;ci-only")
find_program(LUAROCKS_EXECUTABLE luarocks)
if(LUAROCKS_EXECUTABLE)
@@ -269,7 +266,7 @@ string(REPLACE ";" "\;" LUA_PATH
"${CMAKE_SOURCE_DIR}/scripts/?.lua;${CMAKE_SOURCE_DIR}/scripts/?/init.lua;${LR_PATH}")
string(REPLACE ";" "\;" LUA_CPATH
"${LR_CPATH}")
-set_tests_properties(lua-debug lua-core lua-e2 lua-e3 PROPERTIES
+set_tests_properties(lua-core lua-e2 lua-e3 PROPERTIES
ENVIRONMENT "LUA_PATH=${LUA_PATH};LUA_CPATH=${LUA_CPATH}"
)
endif(LUAROCKS_EXECUTABLE)
@@ -287,6 +284,7 @@ set(TESTS_SRC
economy.test.c
give.test.c
guard.test.c
+ items.test.c
json.test.c
jsonconf.test.c
laws.test.c
@@ -335,7 +333,6 @@ target_link_libraries(test_eressea
game
cutest
${LUA_LIBRARIES}
- ${CLIBS_LIBRARIES}
${STORAGE_LIBRARIES}
${CJSON_LIBRARY}
${INIPARSER_LIBRARY}
diff --git a/src/alchemy.c b/src/alchemy.c
index f0462a831..9ebe14455 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -12,6 +12,7 @@
#include
#include
#include "kernel/skill.h"
+#include "kernel/skills.h"
#include "kernel/unit.h"
/* util includes */
@@ -21,7 +22,9 @@
#include "util/message.h"
#include
-#include
+#include
+
+#include
/* libc includes */
#include
@@ -321,34 +324,28 @@ void scale_effects(unit* u, int n)
}
}
-int use_foolpotion(unit *user, const item_type *itype, int amount,
- struct order *ord)
+void potion_effects(unit *u)
{
- int targetno = read_unitid(user->faction, user->region);
- unit *u = findunit(targetno);
- int max_effects;
- if (u == NULL || user->region != u->region) {
- ADDMSG(&user->faction->msgs, msg_feedback(user, ord, "feedback_unit_not_found",
- ""));
- return ECUSTOM;
- }
- if (effskill(user, SK_STEALTH, NULL) <= effskill(u, SK_PERCEPTION, NULL)) {
- cmistake(user, ord, 64, MSG_EVENT);
- return ECUSTOM;
- }
- max_effects = u->number * 10 - get_effect(u, itype);
- if (max_effects > 0) {
- int use = (max_effects + 9) / 10;
- int effects = max_effects;
- if (use > amount) {
- use = amount;
- effects = use * 10;
+ int effect = get_effect(u, oldpotiontype[P_FOOL]);
+ if (effect > 0) { /* Trank "Dumpfbackenbrot" */
+ int weeks = u->number;
+ if (weeks > effect) weeks = effect;
+ ptrdiff_t s, n = arrlen(u->skills);
+ skill *sb = NULL;
+ for (s = 0; s != n; ++s) {
+ skill *sv = u->skills + s;
+ if (sb == NULL || skill_compare(sv, sb) > 0) {
+ sb = sv;
+ }
+ ++sv;
}
- change_effect(u, itype, effects);
- ADDMSG(&user->faction->msgs, msg_message("givedumb",
- "unit recipient amount", user, u, use));
- return use;
+ /* bestes Talent raussuchen */
+ if (sb != NULL) {
+ change_skill(u, sb, -SKILL_DAYS_PER_WEEK * weeks);
+ ADDMSG(&u->faction->msgs, msg_message("dumbeffect",
+ "unit weeks skill", u, weeks, (skill_t)sb->id));
+ } /* sonst Glueck gehabt: wer nix weiss, kann nix vergessen... */
+ change_effect(u, oldpotiontype[P_FOOL], -weeks);
}
- return 0;
}
diff --git a/src/alchemy.h b/src/alchemy.h
index f0d292000..5e1dfff6a 100644
--- a/src/alchemy.h
+++ b/src/alchemy.h
@@ -51,6 +51,4 @@ bool display_potions(struct unit* u);
int effect_value(const struct attrib* a);
const struct item_type *effect_type(const struct attrib* a);
-int use_foolpotion(struct unit *user, const struct item_type *itype,
- int amount, struct order *ord);
-
+void potion_effects(struct unit *u);
diff --git a/src/alchemy.test.c b/src/alchemy.test.c
index c7892d5a0..b5d3ad776 100644
--- a/src/alchemy.test.c
+++ b/src/alchemy.test.c
@@ -1,26 +1,13 @@
#include "alchemy.h"
#include "guard.h"
-#include "laws.h"
-#include "items.h"
-#include
-#include
-
-#include
-#include
-#include
-
-#include
-#include
#include
#include
-#include
#include
+#include
#include
#include
-#include
-#include
#include
#include "tests.h"
@@ -94,71 +81,6 @@ static void test_herbsearch(CuTest * tc)
test_teardown();
}
-static void test_foolpotion_effect(CuTest *tc) {
- unit *u;
- const struct item_type *itype;
-
- test_setup();
- itype = oldpotiontype[P_FOOL] = test_create_potiontype("balm", 1);
- u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
- test_set_skill(u, SK_MAGIC, 3, 1);
- test_set_skill(u, SK_CROSSBOW, 2, 1);
- change_effect(u, itype, 2);
- CuAssertIntEquals(tc, 2, get_effect(u, itype));
- age_unit(&u);
- CuAssertIntEquals(tc, 1, get_effect(u, itype));
- CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, unit_skill(u, SK_MAGIC)->days);
- CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, unit_skill(u, SK_CROSSBOW)->days);
- test_teardown();
-}
-
-static void test_use_foolpotion(CuTest *tc) {
- unit *u, *u2;
- const struct item_type *itype;
-
- test_setup();
- itype = oldpotiontype[P_FOOL] = test_create_potiontype("balm", 1);
- u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
- u2 = test_create_unit(test_create_faction(), u->region);
- u->thisorder = create_order(K_USE, u->faction->locale, itoa36(u2->no), NULL);
-
- init_order(u->thisorder, u->faction->locale);
- CuAssertIntEquals(tc, ECUSTOM, use_foolpotion(u, itype, 2, u->thisorder));
-
- /* Maximal 10 Wirkungen pro Person: */
- test_set_skill(u, SK_STEALTH, 1, 1);
- init_order(u->thisorder, u->faction->locale);
- CuAssertIntEquals(tc, 1, use_foolpotion(u, itype, 2, u->thisorder));
- CuAssertIntEquals(tc, 0, get_effect(u, itype));
- CuAssertIntEquals(tc, 10, get_effect(u2, itype));
-
- a_removeall(&u2->attribs, &at_effect);
- scale_number(u2, 2);
- init_order(u->thisorder, u->faction->locale);
- CuAssertIntEquals(tc, 2, use_foolpotion(u, itype, 3, u->thisorder));
- CuAssertIntEquals(tc, 20, get_effect(u2, itype));
-
- a_removeall(&u2->attribs, &at_effect);
- scale_number(u2, 10);
- init_order(u->thisorder, u->faction->locale);
- CuAssertIntEquals(tc, 10, use_foolpotion(u, itype, 20, u->thisorder));
- CuAssertIntEquals(tc, 100, get_effect(u2, itype));
-
- /* limited use: */
- a_removeall(&u2->attribs, &at_effect);
- scale_number(u2, 2);
- init_order(u->thisorder, u->faction->locale);
- CuAssertIntEquals(tc, 1, use_foolpotion(u, itype, 1, u->thisorder));
- CuAssertIntEquals(tc, 10, get_effect(u2, itype));
-
- /* stacking only up to 10 effect/person: */
- init_order(u->thisorder, u->faction->locale);
- CuAssertIntEquals(tc, 1, use_foolpotion(u, itype, 2, u->thisorder));
- CuAssertIntEquals(tc, 20, get_effect(u2, itype));
-
- test_teardown();
-}
-
static void test_scale_effects(CuTest *tc)
{
unit* u;
@@ -196,10 +118,10 @@ static void test_scale_effects(CuTest *tc)
test_teardown();
}
-static void test_scale_bloodpotion(CuTest* tc)
+static void test_scale_bloodpotion(CuTest *tc)
{
- unit* u;
- const struct item_type* it_blood;
+ unit *u;
+ const struct item_type *it_blood;
test_setup();
test_create_race("demon");
@@ -212,62 +134,11 @@ static void test_scale_bloodpotion(CuTest* tc)
CuAssertIntEquals(tc, 0, get_effect(u, it_blood));
}
-static void test_bloodpotion(CuTest* tc) {
- unit* u;
- struct item_type* itype;
- struct race* rc_demon, * rc_toad;
-
- test_setup();
- itype = test_create_potiontype("bloodpotion", 1);
- rc_demon = test_create_race("demon");
- rc_toad = test_create_race("toad");
- CuAssertPtrEquals(tc, rc_demon, (race*)get_race(RC_DAEMON));
- CuAssertPtrEquals(tc, rc_toad, (race*)get_race(RC_TOAD));
- u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
- u_setrace(u, rc_demon);
- use_bloodpotion(u, itype, 5, NULL);
- CuAssertPtrEquals(tc, NULL, get_timeout(u->attribs, "timer", &tt_changerace));
- CuAssertIntEquals(tc, 500, get_effect(u, itype));
- CuAssertPtrEquals(tc, rc_demon, (race*)u_race(u));
-
- test_teardown();
-}
-
-static void test_bloodpotion_fail(CuTest* tc) {
- unit* u;
- struct item_type* itype;
- struct race* rc_demon, * rc_smurf;
- const struct race* rc;
- trigger* t;
- changerace_data* crd;
-
- test_setup();
- itype = test_create_potiontype("bloodpotion", 1);
- rc_demon = test_create_race("demon");
- rc_smurf = test_create_race("smurf");
- CuAssertPtrEquals(tc, rc_demon, (race*)get_race(RC_DAEMON));
- u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
- rc = u_race(u);
- CuAssertTrue(tc, rc_demon != u_race(u));
- use_bloodpotion(u, itype, 1, NULL);
- CuAssertIntEquals(tc, 0, get_effect(u, itype));
- CuAssertPtrEquals(tc, rc_smurf, (race*)u_race(u));
- CuAssertPtrNotNull(tc, t = get_timeout(u->attribs, "timer", &tt_changerace));
- CuAssertPtrNotNull(tc, crd = (changerace_data*)t->data.v);
- CuAssertPtrEquals(tc, (race*)rc, (race *)crd->race);
- CuAssertPtrEquals(tc, NULL, (race*)crd->irace);
- test_teardown();
-}
-
CuSuite *get_alchemy_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_herbsearch);
SUITE_ADD_TEST(suite, test_scale_effects);
SUITE_ADD_TEST(suite, test_scale_bloodpotion);
- SUITE_ADD_TEST(suite, test_foolpotion_effect);
- SUITE_ADD_TEST(suite, test_use_foolpotion);
- SUITE_ADD_TEST(suite, test_bloodpotion);
- SUITE_ADD_TEST(suite, test_bloodpotion_fail);
return suite;
}
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index e214c8655..70e5effae 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -36,7 +36,8 @@
#include
#include
-#include
+#include
+
#include
typedef struct obs_data {
diff --git a/src/attributes/dict.c b/src/attributes/dict.c
index dda8abf03..618bf7daa 100644
--- a/src/attributes/dict.c
+++ b/src/attributes/dict.c
@@ -2,21 +2,22 @@
#include "key.h"
/* kernel includes */
+#include
#include
#include
+#include
#include
#include
#include
/* util includes */
-#include
#include
#include
-#include
#include
-#include
-#include
+#include
+
+#include
/* stdc includes */
#include
diff --git a/src/attributes/follow.c b/src/attributes/follow.c
index 2f8afc746..7818f7b53 100644
--- a/src/attributes/follow.c
+++ b/src/attributes/follow.c
@@ -1,12 +1,12 @@
#include "follow.h"
-#include
-
#include
#include
+#include
+
#include
-#include
+#include
static int read_follow(variant * var, void *owner, gamedata *data)
{
diff --git a/src/attributes/hate.c b/src/attributes/hate.c
index c1a723696..8de926e52 100644
--- a/src/attributes/hate.c
+++ b/src/attributes/hate.c
@@ -1,13 +1,13 @@
#include "hate.h"
-#include
-
#include
#include
+#include
+
#include
#include
-#include
+#include
static int verify_hate(attrib * a, void *owner)
{
diff --git a/src/attributes/key.c b/src/attributes/key.c
index 55d1d88e2..c08a144e3 100644
--- a/src/attributes/key.c
+++ b/src/attributes/key.c
@@ -5,8 +5,10 @@
#include
#include
+
#include
-#include
+
+#include
#include
#include
diff --git a/src/attributes/movement.c b/src/attributes/movement.c
index 439b56d54..c84772ad5 100644
--- a/src/attributes/movement.c
+++ b/src/attributes/movement.c
@@ -2,7 +2,7 @@
#include
-#include
+#include
#include
#include
diff --git a/src/attributes/otherfaction.c b/src/attributes/otherfaction.c
index 15881db59..af20d5d5c 100644
--- a/src/attributes/otherfaction.c
+++ b/src/attributes/otherfaction.c
@@ -6,9 +6,10 @@
#include
#include
+#include
+
#include
#include // for true, false
-#include
/*
* simple attributes that do not yet have their own file
diff --git a/src/attributes/racename.c b/src/attributes/racename.c
index 83df6cb3c..31631f36a 100644
--- a/src/attributes/racename.c
+++ b/src/attributes/racename.c
@@ -2,7 +2,7 @@
#include
-#include
+#include
/* libc includes */
#include
diff --git a/src/attributes/raceprefix.c b/src/attributes/raceprefix.c
index 62e704f64..9b0363084 100644
--- a/src/attributes/raceprefix.c
+++ b/src/attributes/raceprefix.c
@@ -2,7 +2,7 @@
#include
-#include
+#include
#include
#include
diff --git a/src/attributes/seenspell.c b/src/attributes/seenspell.c
index f79fe47f8..e3660f603 100644
--- a/src/attributes/seenspell.c
+++ b/src/attributes/seenspell.c
@@ -1,3 +1,5 @@
+#include "seenspell.h"
+
#include
#include
#include
@@ -6,10 +8,10 @@
#include
#include
-#include "seenspell.h"
-#include
-#include
+#include
+
+#include
#include
#include
diff --git a/src/attributes/targetregion.c b/src/attributes/targetregion.c
index fcea49c56..9fe9dc2f3 100644
--- a/src/attributes/targetregion.c
+++ b/src/attributes/targetregion.c
@@ -1,12 +1,13 @@
#include "targetregion.h"
-#include
#include
#include
+#include
+
#include
-#include
+#include
static void
write_targetregion(const variant *var, const void *owner, struct storage *store)
diff --git a/src/automate.test.c b/src/automate.test.c
index 0104de66e..5f3b9fa1e 100644
--- a/src/automate.test.c
+++ b/src/automate.test.c
@@ -6,6 +6,7 @@
#include "kernel/race.h"
#include "kernel/region.h"
#include "kernel/skill.h"
+#include "kernel/skills.h"
#include "kernel/unit.h"
#include "util/keyword.h"
@@ -479,6 +480,33 @@ static void test_autostudy_batches(CuTest *tc) {
test_teardown();
}
+static void test_do_autostudy_speed(CuTest *tc) {
+ region *r;
+ unit *u1, *u2;
+ race *rc;
+ faction *f;
+
+ test_setup();
+ config_set("study.random_progress", "0");
+
+ r = test_create_plain(0, 0);
+ rc = test_create_race("orc");
+ set_study_speed(rc, SK_CATAPULT, -5);
+ f = test_create_faction_ex(rc, NULL);
+ u1 = test_create_unit(f, r);
+ set_number(u1, 1);
+ set_level(u1, SK_CATAPULT, 2);
+ u2 = test_create_unit(f, r);
+ set_number(u2, 10);
+ u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_CATAPULT]);
+ u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_CATAPULT]);
+ do_autostudy(r);
+ CuAssertIntEquals(tc, 2, get_level(u1, SK_CATAPULT));
+ CuAssertIntEquals(tc, 1, get_level(u2, SK_CATAPULT));
+ CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK + 5, u2->skills->days);
+ test_teardown();
+}
+
static void test_do_autostudy(CuTest *tc) {
unit *u1, *u2, *u3, *u4;
faction *f;
@@ -517,6 +545,7 @@ CuSuite *get_automate_suite(void)
SUITE_ADD_TEST(suite, test_autostudy_init_fallback);
SUITE_ADD_TEST(suite, test_autostudy_run);
SUITE_ADD_TEST(suite, test_do_autostudy);
+ SUITE_ADD_TEST(suite, test_do_autostudy_speed);
SUITE_ADD_TEST(suite, test_autostudy_batches);
SUITE_ADD_TEST(suite, test_autostudy_run_noteachers);
SUITE_ADD_TEST(suite, test_autostudy_run_can_teach);
diff --git a/src/battle.c b/src/battle.c
index 0b58ff8be..be364c33b 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -8,6 +8,7 @@
#include "laws.h"
#include "monsters.h"
#include "move.h"
+#include "randenc.h"
#include "study.h"
#include "spy.h"
@@ -40,6 +41,7 @@
#include "kernel/region.h"
#include "kernel/ship.h"
#include "kernel/skill.h"
+#include "kernel/skills.h"
#include "kernel/terrain.h"
#include "kernel/unit.h"
#include "kernel/spell.h"
@@ -57,7 +59,7 @@
#include "util/rand.h"
#include "util/rng.h"
-#include
+#include
#include
#include
@@ -936,7 +938,7 @@ void kill_troop(troop dt)
*/
void drain_exp(struct unit *u, int n)
{
- skill_t sk = (skill_t)(rng_int() % MAXSKILLS);
+ skill_t sk = (skill_t)(rng_uint() % MAXSKILLS);
skill_t ssk;
/* TODO (enno): we can use u->skill_size to find a random skill */
@@ -2636,6 +2638,24 @@ static void reorder_fleeing(region * r)
*udst = NULL;
}
+static void free_side(side *si)
+{
+ arrfree(si->leader.fighters);
+}
+
+static void free_fighter(fighter *fig)
+{
+ armor **ap = &fig->armors;
+ while (*ap) {
+ armor *a = *ap;
+ *ap = a->next;
+ free(a);
+ }
+ i_freeall(&fig->loot);
+ free(fig->person);
+ arrfree(fig->weapons);
+}
+
static void aftermath(battle * b)
{
region *r = b->region;
@@ -2645,26 +2665,37 @@ static void aftermath(battle * b)
for (si = 0; si != sl; ++si) {
side* s = b->sides[si];
- fighter *df;
+ fighter **fig = &s->fighters;
s->dead = 0;
- for (df = s->fighters; df; df = df->next) {
+ while (*fig) {
+ fighter *df = *fig;
unit *du = df->unit;
- int dead = dead_fighters(df);
- const race *rc = u_race(du);
-
- /* tote insgesamt: */
- s->dead += dead;
- /* Tote, die wiederbelebt werde koennen: */
- if (!undeadrace(rc)) {
- s->casualties += dead;
+ attrib *a = a = a_find(du->attribs, &at_unitdissolve);
+ if (a && a->data.ca[1] == 100) {
+ *fig = df->next;
+ s->alive -= df->alive;
+ free_fighter(df);
+ dissolve_unit(du, du->number, a);
}
- if (df->hits + df->kills) {
- struct message *m =
- msg_message("killsandhits", "unit hits kills", du, df->hits,
- df->kills);
- battle_message_faction(b, du->faction, m);
- msg_release(m);
+ else {
+ int dead = dead_fighters(df);
+ const race *rc = u_race(du);
+
+ /* tote insgesamt: */
+ s->dead += dead;
+ /* Tote, die wiederbelebt werde koennen: */
+ if (!undeadrace(rc)) {
+ s->casualties += dead;
+ }
+ if (df->hits + df->kills) {
+ struct message *m =
+ msg_message("killsandhits", "unit hits kills", du, df->hits,
+ df->kills);
+ battle_message_faction(b, du->faction, m);
+ msg_release(m);
+ }
+ fig = &df->next;
}
}
}
@@ -3515,25 +3546,6 @@ bool join_battle(battle * b, unit * u, bool attack, fighter ** cp)
return fc != NULL;
}
-static void free_side(side * si)
-{
- arrfree(si->leader.fighters);
-}
-
-static void free_fighter(fighter * fig)
-{
- armor **ap = &fig->armors;
- while (*ap) {
- armor *a = *ap;
- *ap = a->next;
- free(a);
- }
- i_freeall(&fig->loot);
- free(fig->person);
- arrfree(fig->weapons);
-
-}
-
static void battle_free(battle * b) {
size_t si, sl = arrlen(b->sides);
diff --git a/src/battle.h b/src/battle.h
index b1fad11eb..26dfa4f9f 100644
--- a/src/battle.h
+++ b/src/battle.h
@@ -193,6 +193,7 @@ enum { ALLY_SELF, ALLY_ANY };
int get_unitrow(const fighter* af, const side* vs);
+int count_enemies(const struct fighter *af, int minrow, int maxrow, int select);
bool escapes_tactics(const fighter *af, const troop dt);
troop select_enemy(struct fighter* af, int minrow, int maxrow,
int select);
@@ -200,7 +201,6 @@ troop select_ally(struct fighter* af, int minrow, int maxrow,
int allytype);
int get_tactics(const struct side* as, const struct side* ds);
-int count_enemies(const struct fighter* af, int minrow, int maxrow, int select);
int natural_armor(struct unit* u);
const struct armor_type* select_armor(struct troop t, bool shield);
const struct weapon* select_weapon(const struct troop t, bool attacking, bool ismissile);
diff --git a/src/battle.test.c b/src/battle.test.c
index d44fc2513..bbc2ba589 100644
--- a/src/battle.test.c
+++ b/src/battle.test.c
@@ -34,6 +34,7 @@
#include "util/rand.h"
#include "util/variant.h"
+#include
#include
#include
@@ -41,7 +42,6 @@
#include
#include
#include // for abort, calloc
-#include
#include "tests.h"
@@ -1520,6 +1520,7 @@ static void test_combat_rosthauch(CuTest *tc) {
const resource_type *rtype;
test_setup();
+ random_source_inject_constants(1.0, 0xdeadbeef);
init_resources();
rtype = rt_get_or_create("iron");
it_rust1 = create_weapon("sword", rtype);
diff --git a/src/bind_config.c b/src/bind_config.c
index 94fe643dc..c97b4875d 100644
--- a/src/bind_config.c
+++ b/src/bind_config.c
@@ -14,7 +14,7 @@
#include
#include
-#include
+#include
#include
@@ -39,7 +39,9 @@ int bind_config_parse(const char *json)
/* create default resources: */
init_resources();
if (conf) {
- json_config(conf);
+ if (!json_config(conf)) {
+ return 1;
+ }
cJSON_Delete(conf);
/* alte typen registrieren: */
init_oldpotions();
diff --git a/src/bind_region.c b/src/bind_region.c
index c80eb7efd..4750580f9 100644
--- a/src/bind_region.c
+++ b/src/bind_region.c
@@ -24,11 +24,11 @@
#include
#include
-#include
-
#include
#include
+#include
+
#include
#include
#include
diff --git a/src/bind_storage.c b/src/bind_storage.c
index b64beaa8a..b39f5f573 100644
--- a/src/bind_storage.c
+++ b/src/bind_storage.c
@@ -8,7 +8,7 @@
#include
#include
-#include
+#include
#include
#include
diff --git a/src/bind_unit.c b/src/bind_unit.c
index bc755b3e6..642160266 100644
--- a/src/bind_unit.c
+++ b/src/bind_unit.c
@@ -40,7 +40,7 @@
#include // for OP_Parser, OrderParserStruct
#include
-#include
+#include
#include
diff --git a/src/bindings.c b/src/bindings.c
index 0739557db..b4868ba5b 100755
--- a/src/bindings.c
+++ b/src/bindings.c
@@ -854,8 +854,9 @@ static void parse_inifile(lua_State * L, const dictionary * d, const char *secti
size_t len = strlen(section);
/* defaults */
- arg = config_get("config.install");
+ arg = (const char *) getenv("ERESSEA_INSTALL");
if (arg) {
+ config_set("config.install", arg);
lua_pushstring(L, "install");
lua_pushstring(L, arg);
lua_rawset(L, -3);
diff --git a/src/creport.c b/src/creport.c
index d4c4bd76d..48a0d0d19 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -69,10 +69,10 @@ static char g_bigbuf[BUFFERSIZE];
#include
#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
#include
diff --git a/src/crimport.c b/src/crimport.c
index 13c45d13a..3210be8a8 100644
--- a/src/crimport.c
+++ b/src/crimport.c
@@ -36,9 +36,9 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include
@@ -898,7 +898,7 @@ static enum CR_Error handle_skill(context *ctx, const char *value, const char *n
int level = atoi(val + 1) - rc_skillmod(rc, sk) - terrain_mod(rc, sk, u->region);
if (level > 0) {
struct skill *sv = add_skill(u, sk);
- sk_set_level(sv, level);
+ sk_set_level(u, sv, level);
sv->old = sv->level;
}
if (sk == SK_STAMINA) {
diff --git a/src/defaults.c b/src/defaults.c
index b4f64adc7..c66cec68a 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -63,10 +63,10 @@
#include
#include
-#include
-#include
+#include
#include
+#include
/* libc includes */
#include
@@ -79,7 +79,7 @@
#include
/**
- - Die alten Befehle nicht beim Befehle laden löschen, sondern in u->defaults befördern
+ - Die alten Befehle nicht beim Befehle laden l�schen, sondern in u->defaults bef�rdern
- Dann (hier) beim setzen des langen Befehls wegschmeissen, es sei denn es ist ein K_MOVE,
- im Falle von K_MOVE eine Variante von update_defaults machen, nur lange Befehle behalten.
*/
diff --git a/src/eressea.c b/src/eressea.c
index 405cf4091..9ae3988e5 100644
--- a/src/eressea.c
+++ b/src/eressea.c
@@ -52,7 +52,7 @@
#include "vortex.h"
#include "wormhole.h"
-#include
+#include
#include
#include
diff --git a/src/exparse.c b/src/exparse.c
index 9fca11990..bfb08de0a 100644
--- a/src/exparse.c
+++ b/src/exparse.c
@@ -22,7 +22,7 @@
#include "util/crmessage.h"
#include "util/nrmessage.h"
-#include
+#include
#include
#include
@@ -1091,7 +1091,7 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr
if (name) {
skill_t sk = find_skill(name);
if (sk != NOSKILL) {
- rc->bonus[sk] = (char)mod;
+ rc->bonus[sk] = (signed char)mod;
if (speed != INT_MAX) {
set_study_speed(rc, sk, speed);
}
diff --git a/src/gamedb.c b/src/gamedb.c
index 47059486b..8d332e84c 100644
--- a/src/gamedb.c
+++ b/src/gamedb.c
@@ -5,7 +5,7 @@
#include "kernel/faction.h"
#include "kernel/db/driver.h"
-#include
+#include
static int generate_factions(void *data, db_faction *results, int nresults)
{
diff --git a/src/give.test.c b/src/give.test.c
index 0968b3ee6..fd2f90221 100644
--- a/src/give.test.c
+++ b/src/give.test.c
@@ -104,12 +104,15 @@ static void test_give_unit(CuTest * tc) {
join_group(env.src, "group");
config_set("rules.give.max_men", "0");
+ usetprivate(env.src, "Hodor");
give_unit(env.src, env.dst, NULL);
+ CuAssertStrEquals(tc, "Hodor", uprivate(env.src));
CuAssertPtrEquals(tc, env.f1, env.src->faction);
CuAssertIntEquals(tc, 0, env.f2->newbies);
config_set("rules.give.max_men", "-1");
give_unit(env.src, env.dst, NULL);
+ CuAssertStrEquals(tc, NULL, uprivate(env.src));
CuAssertPtrEquals(tc, env.f2, env.src->faction);
CuAssertPtrEquals(tc, NULL, get_group(env.src));
CuAssertIntEquals(tc, 1, env.f2->newbies);
diff --git a/src/helpers.c b/src/helpers.c
index 3656290e1..ee8072bcf 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -20,7 +20,7 @@
#include
#include
-#include
+#include
#include
#include
diff --git a/src/items.c b/src/items.c
index 6fbdf5f92..22877cfc1 100644
--- a/src/items.c
+++ b/src/items.c
@@ -118,11 +118,12 @@ struct order *ord)
/* END speedsail */
+#define CRYSTAL_EFFECT 5
+#define CRYSTAL_FORCE (20.0 * (CRYSTAL_EFFECT))
/* ------------------------------------------------------------- */
/* Kann auch von Nichtmagiern benutzt werden, erzeugt eine
* Antimagiezone, die zwei Runden bestehen bleibt */
-static int
-use_antimagiccrystal(
+int use_antimagiccrystal(
unit * u,
const struct item_type *itype,
int amount,
@@ -143,12 +144,12 @@ use_antimagiccrystal(
UNUSED_ARG(ord);
/* Reduziert die Staerke jedes Spruchs um effect */
- effect = 5;
+ effect = CRYSTAL_EFFECT;
/* Haelt Sprueche bis zu einem summierten Gesamtlevel von power aus.
* Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone
* um seine Stufe */
- force = effect * 20.0; /* Stufe 5 =~ 100 */
+ force = CRYSTAL_FORCE; /* Stufe 5 =~ 100 */
/* Regionszauber aufloesen */
while (*ap && force > 0) {
@@ -170,9 +171,12 @@ use_antimagiccrystal(
continue;
}
- force = reduce_curse(c, effect, force, r);
- if (c->vigour <= 0) {
- a_remove(&r->attribs, a);
+ /* keine eigene Antimagie auflösen! */
+ if (c->type != &ct_antimagiczone || c->magician->faction != u->faction) {
+ force = reduce_curse(c, effect, force, r);
+ if (c->vigour <= 0) {
+ a_remove(&r->attribs, a);
+ }
}
if (*ap)
ap = &(*ap)->next;
@@ -180,7 +184,7 @@ use_antimagiccrystal(
if (force > 0) {
int duration = 2;
- create_curse(u, &r->attribs, &ct_antimagiczone, force, duration,
+ create_curse(NULL, &r->attribs, &ct_antimagiczone, force, duration,
effect, 0);
}
}
@@ -422,6 +426,37 @@ static int use_healing_potion(unit *u, const item_type *itype,
return potion_healing(u, amount);
}
+int use_foolpotion(unit *user, const item_type *itype, int amount,
+ struct order *ord)
+{
+ int targetno = read_unitid(user->faction, user->region);
+ unit *u = findunit(targetno);
+ int max_effects;
+ if (u == NULL || user->region != u->region) {
+ ADDMSG(&user->faction->msgs, msg_feedback(user, ord, "feedback_unit_not_found",
+ ""));
+ return ECUSTOM;
+ }
+ if (effskill(user, SK_STEALTH, NULL) <= effskill(u, SK_PERCEPTION, NULL)) {
+ cmistake(user, ord, 64, MSG_EVENT);
+ return ECUSTOM;
+ }
+ max_effects = u->number * 10 - get_effect(u, itype);
+ if (max_effects > 0) {
+ int use = (max_effects + 9) / 10;
+ int effects = max_effects;
+ if (use > amount) {
+ use = amount;
+ effects = use * 10;
+ }
+ change_effect(u, itype, effects);
+ ADDMSG(&user->faction->msgs, msg_message("givedumb",
+ "unit recipient amount", user, u, use));
+ return use;
+ }
+ return 0;
+}
+
static int potion_ointment(unit * u, int amount) {
int effect = amount * 400;
effect = heal(u, effect);
diff --git a/src/items.h b/src/items.h
index 439dc5632..2623d04ab 100644
--- a/src/items.h
+++ b/src/items.h
@@ -11,3 +11,8 @@ void register_itemfunctions(void);
int use_bloodpotion(struct unit* u, const struct item_type* itype,
int amount, struct order* ord);
+int use_foolpotion(struct unit *user, const struct item_type *itype,
+ int amount, struct order *ord);
+
+int use_antimagiccrystal(struct unit *u, const struct item_type *itype,
+ int amount, struct order *ord);
diff --git a/src/items.test.c b/src/items.test.c
new file mode 100644
index 000000000..07fb4394e
--- /dev/null
+++ b/src/items.test.c
@@ -0,0 +1,243 @@
+#include "items.h"
+#include "alchemy.h"
+
+#include
+#include
+
+#include "spells/regioncurse.h"
+
+#include "util/base36.h"
+#include "util/keyword.h"
+#include "util/variant.h" // IWYU pragma: keep
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include "tests.h"
+
+#include // for NULL
+/**
+ * Der Antimagiekristall erschafft eine Schw�chezone.
+ */
+static void test_crystal_creates_zone(CuTest *tc) {
+ unit *u;
+ curse *c;
+ struct item_type *itype;
+ test_setup();
+ itype = test_create_itemtype("antimagic");
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ CuAssertIntEquals(tc, 0, use_antimagiccrystal(u, itype, 1, NULL));
+ CuAssertPtrNotNull(tc, c = get_curse(u->region->attribs, &ct_antimagiczone));
+ CuAssertIntEquals(tc, 2, c->duration);
+ CuAssertDblEquals(tc, 5.0, c->effect, 0.01);
+ CuAssertDblEquals(tc, 100.0, c->vigour, 0.01);
+ CuAssertPtrEquals(tc, NULL, c->magician);
+ test_teardown();
+}
+
+/**
+ * TODO
+ *
+ * The antimagic zone xurse is immune to dissolution by
+ * antimagic crystals. The curse is updated, and not
+ * reduced in strength (full 100 vigour).
+ */
+static void test_crystal_immunity(CuTest *tc) {
+ unit *u;
+ region *r;
+ curse *c;
+ struct item_type *itype;
+ test_setup();
+ itype = test_create_itemtype("antimagic");
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ r = u->region;
+ c = create_curse(u, &r->attribs, &ct_antimagiczone, 50.0, 1, 1.0, 0);
+ CuAssertIntEquals(tc, 0, use_antimagiccrystal(u, itype, 1, NULL));
+ CuAssertPtrEquals(tc, c, get_curse(r->attribs, &ct_antimagiczone));
+ CuAssertPtrEquals(tc, NULL, c->magician);
+ CuAssertIntEquals(tc, 2, c->duration);
+ CuAssertDblEquals(tc, 100.0, c->vigour, 0.01);
+ test_teardown();
+}
+
+/**
+ * using a second AMK does not reduce the zone created by the first
+ */
+static void test_crystal_updates_zone(CuTest *tc) {
+ unit *u;
+ region *r;
+ curse *c;
+ struct item_type *itype;
+ test_setup();
+ itype = test_create_itemtype("antimagic");
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ r = u->region;
+ create_curse(u, &r->attribs, &ct_antimagiczone, 50.0, 1, 1.0, 0);
+
+ CuAssertIntEquals(tc, 0, use_antimagiccrystal(u, itype, 1, NULL));
+ CuAssertPtrNotNull(tc, c = get_curse(r->attribs, &ct_antimagiczone));
+ CuAssertPtrEquals(tc, NULL, c->magician);
+ CuAssertIntEquals(tc, 2, c->duration);
+ CuAssertDblEquals(tc, 100.0, c->vigour, 0.01);
+ test_teardown();
+}
+
+static void test_crystal_affects_curses(CuTest *tc) {
+ unit *u, *u2;
+ curse *c;
+ struct item_type *itype;
+ test_setup();
+ itype = test_create_itemtype("antimagic");
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ u2 = test_create_unit(test_create_faction(), u->region);
+ c = create_curse(u2, &u2->region->attribs, &ct_blessedharvest, 10.0, 2, 6.0, 0);
+ CuAssertPtrEquals(tc, u2, c->magician);
+ CuAssertIntEquals(tc, 0, use_antimagiccrystal(u, itype, 1, NULL));
+ CuAssertIntEquals(tc, 2, c->duration);
+ CuAssertDblEquals(tc, 6.0, c->effect, 0.01);
+ CuAssertDblEquals(tc, 7.0, c->vigour, 0.01);
+ //CuAssertPtrNotNull(tc, test_find_messagetype(u2->faction->msgs, "reduce_spell"));
+
+ CuAssertPtrNotNull(tc, c = get_curse(u->region->attribs, &ct_antimagiczone));
+ CuAssertPtrEquals(tc, NULL, c->magician);
+ CuAssertIntEquals(tc, 2, c->duration);
+ CuAssertDblEquals(tc, 5.0, c->effect, 0.01);
+ CuAssertDblEquals(tc, 90.0, c->vigour, 0.01);
+ test_teardown();
+}
+
+static void test_use_bloodpotion(CuTest *tc) {
+ unit *u;
+ struct item_type *itype;
+ struct race *rc_demon, *rc_toad;
+
+ test_setup();
+ itype = test_create_potiontype("bloodpotion", 1);
+ rc_demon = test_create_race("demon");
+ rc_toad = test_create_race("toad");
+ CuAssertPtrEquals(tc, rc_demon, (race *)get_race(RC_DAEMON));
+ CuAssertPtrEquals(tc, rc_toad, (race *)get_race(RC_TOAD));
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ u_setrace(u, rc_demon);
+ use_bloodpotion(u, itype, 5, NULL);
+ CuAssertPtrEquals(tc, NULL, get_timeout(u->attribs, "timer", &tt_changerace));
+ CuAssertIntEquals(tc, 500, get_effect(u, itype));
+ CuAssertPtrEquals(tc, rc_demon, (race *)u_race(u));
+
+ test_teardown();
+}
+
+static void test_bloodpotion_fail(CuTest *tc) {
+ unit *u;
+ struct item_type *itype;
+ struct race *rc_demon, *rc_smurf;
+ const struct race *rc;
+ trigger *t;
+ changerace_data *crd;
+
+ test_setup();
+ itype = test_create_potiontype("bloodpotion", 1);
+ rc_demon = test_create_race("demon");
+ rc_smurf = test_create_race("smurf");
+ CuAssertPtrEquals(tc, rc_demon, (race *)get_race(RC_DAEMON));
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ rc = u_race(u);
+ CuAssertTrue(tc, rc_demon != u_race(u));
+ use_bloodpotion(u, itype, 1, NULL);
+ CuAssertIntEquals(tc, 0, get_effect(u, itype));
+ CuAssertPtrEquals(tc, rc_smurf, (race *)u_race(u));
+ CuAssertPtrNotNull(tc, t = get_timeout(u->attribs, "timer", &tt_changerace));
+ CuAssertPtrNotNull(tc, crd = (changerace_data *)t->data.v);
+ CuAssertPtrEquals(tc, (race *)rc, (race *)crd->race);
+ CuAssertPtrEquals(tc, NULL, (race *)crd->irace);
+ test_teardown();
+}
+
+static void test_foolpotion_effect(CuTest *tc) {
+ unit *u;
+ const struct item_type *itype;
+
+ test_setup();
+ itype = oldpotiontype[P_FOOL] = test_create_potiontype("balm", 1);
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ test_set_skill(u, SK_MAGIC, 3, 1);
+ test_set_skill(u, SK_CROSSBOW, 2, 1);
+ change_effect(u, itype, 2);
+ CuAssertIntEquals(tc, 2, get_effect(u, itype));
+ potion_effects(u);
+ CuAssertIntEquals(tc, 1, get_effect(u, itype));
+ CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, unit_skill(u, SK_MAGIC)->days);
+ CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, unit_skill(u, SK_CROSSBOW)->days);
+ test_teardown();
+}
+
+static void test_use_foolpotion(CuTest *tc) {
+ unit *u, *u2;
+ const struct item_type *itype;
+
+ test_setup();
+ itype = oldpotiontype[P_FOOL] = test_create_potiontype("balm", 1);
+ u = test_create_unit(test_create_faction(), test_create_plain(0, 0));
+ u2 = test_create_unit(test_create_faction(), u->region);
+ u->thisorder = create_order(K_USE, u->faction->locale, itoa36(u2->no), NULL);
+
+ init_order(u->thisorder, u->faction->locale);
+ CuAssertIntEquals(tc, ECUSTOM, use_foolpotion(u, itype, 2, u->thisorder));
+
+ /* Maximal 10 Wirkungen pro Person: */
+ test_set_skill(u, SK_STEALTH, 1, 1);
+ init_order(u->thisorder, u->faction->locale);
+ CuAssertIntEquals(tc, 1, use_foolpotion(u, itype, 2, u->thisorder));
+ CuAssertIntEquals(tc, 0, get_effect(u, itype));
+ CuAssertIntEquals(tc, 10, get_effect(u2, itype));
+
+ a_removeall(&u2->attribs, &at_effect);
+ scale_number(u2, 2);
+ init_order(u->thisorder, u->faction->locale);
+ CuAssertIntEquals(tc, 2, use_foolpotion(u, itype, 3, u->thisorder));
+ CuAssertIntEquals(tc, 20, get_effect(u2, itype));
+
+ a_removeall(&u2->attribs, &at_effect);
+ scale_number(u2, 10);
+ init_order(u->thisorder, u->faction->locale);
+ CuAssertIntEquals(tc, 10, use_foolpotion(u, itype, 20, u->thisorder));
+ CuAssertIntEquals(tc, 100, get_effect(u2, itype));
+
+ /* limited use: */
+ a_removeall(&u2->attribs, &at_effect);
+ scale_number(u2, 2);
+ init_order(u->thisorder, u->faction->locale);
+ CuAssertIntEquals(tc, 1, use_foolpotion(u, itype, 1, u->thisorder));
+ CuAssertIntEquals(tc, 10, get_effect(u2, itype));
+
+ /* stacking only up to 10 effect/person: */
+ init_order(u->thisorder, u->faction->locale);
+ CuAssertIntEquals(tc, 1, use_foolpotion(u, itype, 2, u->thisorder));
+ CuAssertIntEquals(tc, 20, get_effect(u2, itype));
+
+ test_teardown();
+}
+
+CuSuite *get_items_suite(void)
+{
+ CuSuite *suite = CuSuiteNew();
+ SUITE_ADD_TEST(suite, test_use_bloodpotion);
+ SUITE_ADD_TEST(suite, test_bloodpotion_fail);
+ SUITE_ADD_TEST(suite, test_use_foolpotion);
+ SUITE_ADD_TEST(suite, test_foolpotion_effect);
+ SUITE_ADD_TEST(suite, test_crystal_creates_zone);
+ SUITE_ADD_TEST(suite, test_crystal_affects_curses);
+ SUITE_ADD_TEST(suite, test_crystal_immunity);
+ SUITE_ADD_TEST(suite, test_crystal_updates_zone);
+ return suite;
+}
diff --git a/src/jsonconf.c b/src/jsonconf.c
index 3137a79cb..b5dc0cd80 100644
--- a/src/jsonconf.c
+++ b/src/jsonconf.c
@@ -39,7 +39,7 @@
#include "util/path.h"
#include "util/pofile.h"
-#include
+#include
/* external libraries */
#include
@@ -259,11 +259,18 @@ static void json_terrain_production(cJSON *json, terrain_production *prod) {
}
}
-static void json_terrain(cJSON *json, terrain_type *ter) {
+static int popcnt8(unsigned char x) {
+ x = (x & 0x55) + (x >> 1 & 0x55);
+ x = (x & 0x33) + (x >> 2 & 0x33);
+ x = (x & 0x0f) + (x >> 4 & 0x0f);
+ return x;
+}
+
+static bool json_terrain(cJSON *json, terrain_type *ter) {
cJSON *child;
if (json->type != cJSON_Object) {
log_error("terrain %s is not a json object: %d", json->string, json->type);
- return;
+ return false;
}
for (child = json->child; child; child = child->next) {
switch (child->type) {
@@ -285,6 +292,7 @@ static void json_terrain(cJSON *json, terrain_type *ter) {
tp->chance = 1.0f;
if (entry->type != cJSON_Object) {
log_error("terrain %s contains invalid production %s", json->string, entry->string);
+ return false;
}
else {
json_terrain_production(entry, ter->production + n);
@@ -294,6 +302,7 @@ static void json_terrain(cJSON *json, terrain_type *ter) {
}
else {
log_error("terrain %s contains unknown attribute %s", json->string, child->string);
+ return false;
}
break;
case cJSON_String:
@@ -303,14 +312,21 @@ static void json_terrain(cJSON *json, terrain_type *ter) {
}
else {
log_error("terrain %s contains unknown attribute %s", json->string, child->string);
+ return false;
}
break;
case cJSON_Array:
if (strcmp(child->string, "flags") == 0) {
+ unsigned char type;
const char * flags[] = {
"land", "sea", "forest", "arctic", "cavalry", "forbidden", "sail", "fly", "swim", "walk", 0
};
ter->flags |= json_flags(child, flags);
+ type = ter->flags & (SEA_REGION|LAND_REGION|FORBIDDEN_REGION);
+ if (popcnt8(type) > 1) {
+ log_error("terrain %s contains more than one of sea, land, forbidden (%u)", json->string, type);
+ return false;
+ }
}
else if (strcmp(child->string, "herbs") == 0) {
cJSON *entry;
@@ -327,7 +343,8 @@ static void json_terrain(cJSON *json, terrain_type *ter) {
}
}
else {
- log_error("terrain %s contains unknown attribute %s", json->string, child->string);
+ log_error("terrain %s contains unknown attribute[] %s", json->string, child->string);
+ return false;
}
break;
case cJSON_Number:
@@ -342,12 +359,15 @@ static void json_terrain(cJSON *json, terrain_type *ter) {
}
else {
log_error("terrain %s contains unknown attribute %s", json->string, child->string);
+ return false;
}
break;
default:
log_error("terrain %s contains unknown attribute %s", json->string, child->string);
+ return false;
}
}
+ return true;
}
static void json_stage(cJSON *json, building_stage *stage) {
@@ -807,15 +827,18 @@ static void json_disable_features(cJSON *json) {
}
}
-static void json_terrains(cJSON *json) {
+static bool json_terrains(cJSON *json) {
cJSON *child;
if (json->type != cJSON_Object) {
log_error("terrains is not a json object: %d", json->type);
- return;
+ return false;
}
for (child = json->child; child; child = child->next) {
- json_terrain(child, get_or_create_terrain(child->string));
+ if (!json_terrain(child, get_or_create_terrain(child->string))) {
+ return false;
+ }
}
+ return true;
}
static void json_buildings(cJSON *json) {
@@ -1392,12 +1415,12 @@ static void json_include(cJSON *json) {
}
}
-void json_config(cJSON *json) {
+bool json_config(cJSON *json) {
cJSON *child;
assert(json);
if (json->type != cJSON_Object) {
log_error("config is not a json object: %d", json->type);
- return;
+ return false;
}
for (child = json->child; child; child = child->next) {
if (strcmp(child->string, "races") == 0) {
@@ -1449,13 +1472,17 @@ void json_config(cJSON *json) {
json_disable_features(child);
}
else if (strcmp(child->string, "terrains") == 0) {
- json_terrains(child);
+ if (!json_terrains(child)) {
+ return false;
+ }
init_terrains();
}
else {
log_error("config contains unknown attribute %s", child->string);
+ return false;
}
}
+ return true;
}
void jsonconf_done(void) {
diff --git a/src/jsonconf.h b/src/jsonconf.h
index 99db0cdb9..a4145b370 100644
--- a/src/jsonconf.h
+++ b/src/jsonconf.h
@@ -1,10 +1,12 @@
#pragma once
+#include
+
struct cJSON;
extern const char * json_relpath;
void add_authority(const char *key, const char *path);
-void json_config(struct cJSON *str);
+bool json_config(struct cJSON *str);
void jsonconf_done(void);
diff --git a/src/jsonconf.test.c b/src/jsonconf.test.c
index bdadd6d1b..268250af7 100644
--- a/src/jsonconf.test.c
+++ b/src/jsonconf.test.c
@@ -594,7 +594,15 @@ static void test_terrains(CuTest * tc)
"\"size\": 4000, "
"\"road\": 50, "
"\"seed\": 3, "
- "\"flags\" : [ \"forbidden\", \"arctic\", \"cavalry\", \"sea\", \"forest\", \"land\", \"fly\", \"swim\", \"walk\" ] } }}";
+ "\"flags\" : [ \"arctic\", \"cavalry\", \"forest\", \"land\", \"fly\", \"swim\", \"walk\" ] "
+ "},"
+ "\"ocean\" : {"
+ "\"flags\" : [ \"sea\" ] "
+ "}, "
+ "\"fog\" : { "
+ "\"flags\" : [ \"forbidden\" ] "
+ "}"
+ "}}";
const terrain_type *ter;
cJSON *json = cJSON_Parse(data);
@@ -603,10 +611,13 @@ static void test_terrains(CuTest * tc)
CuAssertPtrNotNull(tc, json);
CuAssertPtrEquals(tc, NULL, (void *)get_terrain("plain"));
- json_config(json);
- ter = get_terrain("plain");
- CuAssertPtrNotNull(tc, ter);
- CuAssertIntEquals(tc, ARCTIC_REGION | LAND_REGION | SEA_REGION | FOREST_REGION | CAVALRY_REGION | FORBIDDEN_REGION | FLY_INTO | WALK_INTO | SWIM_INTO , ter->flags);
+ CuAssertTrue(tc, json_config(json));
+ CuAssertPtrNotNull(tc, ter = get_terrain("fog"));
+ CuAssertIntEquals(tc, FORBIDDEN_REGION, ter->flags);
+ CuAssertPtrNotNull(tc, ter = get_terrain("ocean"));
+ CuAssertIntEquals(tc, SEA_REGION, ter->flags);
+ CuAssertPtrNotNull(tc, ter = get_terrain("plain"));
+ CuAssertIntEquals(tc, ARCTIC_REGION | LAND_REGION | FOREST_REGION | CAVALRY_REGION | FLY_INTO | WALK_INTO | SWIM_INTO , ter->flags);
CuAssertIntEquals(tc, 4000, ter->size);
CuAssertIntEquals(tc, 50, ter->max_road);
CuAssertIntEquals(tc, 3, ter->distribution);
diff --git a/src/kernel/alliance.c b/src/kernel/alliance.c
index 5b56410ae..e09a5eaa6 100644
--- a/src/kernel/alliance.c
+++ b/src/kernel/alliance.c
@@ -1,6 +1,7 @@
#include "alliance.h"
-/* kernel includes */
+/* other kernel includes */
+#include "attrib.h"
#include "calendar.h"
#include "config.h"
#include "building.h"
@@ -14,7 +15,6 @@
#include
/* util includes */
-#include
#include
#include
#include
@@ -22,8 +22,8 @@
#include
#include
-#include
-#include
+#include
+#include
/* libc includes */
#include
diff --git a/src/kernel/ally.c b/src/kernel/ally.c
index 8feeb2d13..10c229cc9 100644
--- a/src/kernel/ally.c
+++ b/src/kernel/ally.c
@@ -4,17 +4,18 @@
#include "config.h"
#include "ally.h"
+#include "attrib.h"
#include "unit.h"
#include "region.h"
+#include "gamedata.h"
#include "group.h"
#include "faction.h"
#include "objtypes.h"
-#include
-#include
-#include
-#include
+#include
+
+#include
#include
diff --git a/src/kernel/attrib.c b/src/kernel/attrib.c
index f199f3326..181f58692 100644
--- a/src/kernel/attrib.c
+++ b/src/kernel/attrib.c
@@ -4,9 +4,10 @@
#include
#include
-#include
-#include
-#include
+#include
+
+#include
+#include
#include
diff --git a/src/kernel/attrib.test.c b/src/kernel/attrib.test.c
index 0d4a7db2b..52a83fe43 100644
--- a/src/kernel/attrib.test.c
+++ b/src/kernel/attrib.test.c
@@ -3,10 +3,11 @@
#include
#include // for variant
-#include
-#include
-#include // for stream, stream_i
-#include
+#include
+#include
+#include // for stream, stream_i
+
+#include
#include
#include
diff --git a/src/kernel/build.c b/src/kernel/build.c
index e10ecd3f8..5762e6565 100644
--- a/src/kernel/build.c
+++ b/src/kernel/build.c
@@ -40,7 +40,7 @@
#include
#include
-#include
+#include
/* from libc */
#include