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 diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c index 05366fa66..4c4765c1f 100644 --- a/src/kernel/build.test.c +++ b/src/kernel/build.test.c @@ -307,6 +307,7 @@ static void test_build_with_potion_and_ring(CuTest *tc) set_level(u, bf.cons.skill, bf.cons.minskill); CuAssertIntEquals(tc, 1, build(u, 1, &bf.cons, 0, 200, 0)); + set_level(u, bf.cons.skill, bf.cons.minskill); i_change(&u->items, ring, 1); change_effect(u, ptype, 4); CuAssertIntEquals(tc, 11, build(u, 1, &bf.cons, 0, 200, 0)); diff --git a/src/kernel/building.c b/src/kernel/building.c index c66adbe56..9e1671648 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -1,27 +1,27 @@ -#include #include "building.h" #include #include /* kernel includes */ +#include "attrib.h" +#include "config.h" #include "curse.h" -#include "item.h" -#include "unit.h" +#include "event.h" #include "faction.h" +#include "gamedata.h" +#include "item.h" +#include "lighthouse.h" +#include "messages.h" +#include "order.h" #include "race.h" #include "region.h" #include "skill.h" #include "study.h" #include "terrain.h" -#include "lighthouse.h" +#include "unit.h" /* util includes */ -#include -#include -#include -#include -#include #include #include @@ -32,10 +32,12 @@ #include #include #include +// #include "util/variant.h" // IWYU pragma: keep + +#include -#include -#include -#include +#include +#include #include @@ -1025,7 +1027,7 @@ default_wage(const region * r, const race * rc) else { wage = wagetable[esize][2]; if (rule_blessed_harvest() & HARVEST_WORK) { - /* Geändert in E3 */ + /* Ge�ndert in E3 */ wage += harvest_effect(r); } } diff --git a/src/kernel/building.test.c b/src/kernel/building.test.c index ee3423f22..d2aa90a59 100644 --- a/src/kernel/building.test.c +++ b/src/kernel/building.test.c @@ -8,9 +8,9 @@ #include "region.h" #include "unit.h" -#include "util/variant.h" // for variant +#include // IWYU pragma: keep -#include +#include #include diff --git a/src/kernel/calendar.c b/src/kernel/calendar.c index 2a16b2370..b3c3aed62 100644 --- a/src/kernel/calendar.c +++ b/src/kernel/calendar.c @@ -8,6 +8,7 @@ #include #include +int *storms; int turn = 0; int first_month = 0; int weeks_per_month = 3; @@ -44,6 +45,20 @@ int first_turn(void) return config_get_int("game.start", 0); } +int storm_factor(int month) +{ + static int config; + static int fixed; + + if (config_changed(&config)) { + fixed = config_get_int("rules.calendar.stormchance", 0); + } + if (fixed > 0) { + return fixed; + } + return storms ? storms[month] : 0; +} + const gamedate *get_gamedate(int turn_now, gamedate * gd) { int weeks_per_year = months_per_year * weeks_per_month; diff --git a/src/kernel/calendar.h b/src/kernel/calendar.h index 80cd79c5d..012f1a8df 100644 --- a/src/kernel/calendar.h +++ b/src/kernel/calendar.h @@ -34,5 +34,8 @@ void calendar_cleanup(void); const char* calendar_month(int index); const char* calendar_era(void); int first_turn(void); +int storm_factor(int month); + +extern int *storms; #endif diff --git a/src/kernel/calendar.test.c b/src/kernel/calendar.test.c index 2f2af7b05..10dc4f3bb 100644 --- a/src/kernel/calendar.test.c +++ b/src/kernel/calendar.test.c @@ -65,6 +65,18 @@ static void test_calendar(CuTest * tc) test_teardown(); } +static void test_storm_chance(CuTest * tc) +{ + test_setup(); + CuAssertIntEquals(tc, 0, storm_factor(0)); + storms = malloc(12 * sizeof(int)); + storms[0] = 40; + CuAssertIntEquals(tc, 40, storm_factor(0)); + config_set_int("rules.calendar.stormchance", 100); + CuAssertIntEquals(tc, 100, storm_factor(0)); + test_teardown(); +} + static void test_calendar_season(CuTest * tc) { test_setup(); @@ -118,6 +130,7 @@ CuSuite *get_calendar_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_calendar_config); SUITE_ADD_TEST(suite, test_calendar); + SUITE_ADD_TEST(suite, test_storm_chance); SUITE_ADD_TEST(suite, test_calendar_season); SUITE_ADD_TEST(suite, test_gamedate); return suite; diff --git a/src/kernel/config.c b/src/kernel/config.c index e1485fae4..b0bb6ee45 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -49,7 +49,7 @@ /* external libraries */ #include #include -#include +#include /* libc includes */ #include diff --git a/src/kernel/connection.c b/src/kernel/connection.c index 8a6740c78..71bd78490 100644 --- a/src/kernel/connection.c +++ b/src/kernel/connection.c @@ -17,8 +17,8 @@ #include #include -#include -#include +#include +#include /* libc includes */ #include diff --git a/src/kernel/curse.c b/src/kernel/curse.c index a2c113a8e..12c632d1e 100644 --- a/src/kernel/curse.c +++ b/src/kernel/curse.c @@ -28,8 +28,8 @@ #include #include -#include -#include +#include +#include #include @@ -427,17 +427,6 @@ int curse_geteffect_int(const curse * c) return (int)effect; } -/* ------------------------------------------------------------- */ -static void -set_curseingmagician(struct unit *magician, struct attrib *ap_target, - const curse_type * ct) -{ - curse *c = get_curse(ap_target, ct); - if (c) { - c->magician = magician; - } -} - /* ------------------------------------------------------------- */ /* gibt bei Personenbeschraenkten Verzauberungen die Anzahl der * betroffenen Personen zurueck. Ansonsten wird 0 zurueckgegeben. */ @@ -562,7 +551,7 @@ curse *create_curse(unit * magician, attrib ** ap, const curse_type * ct, if (ct->mergeflags & M_MEN && ct->typ == CURSETYP_UNIT) { c->data.i += men; } - set_curseingmagician(magician, *ap, ct); + c->magician = magician; } else if (ap) { c = make_curse(magician, ap, ct, vigour, duration, effect, men); @@ -639,7 +628,7 @@ void transfer_curse(const unit * u, unit * u2, int n) /* ------------------------------------------------------------- */ int curse_cansee(const curse *c, const faction *viewer, objtype_t typ, const void *obj, int self) { - if (self < 3 && c->magician && c->magician->faction == viewer) { + if (self < 3 && (!c->magician || c->magician->faction == viewer)) { /* magicians can see their own curses better than anybody, no exceptions */ self = 3; } diff --git a/src/kernel/curse.test.c b/src/kernel/curse.test.c index c97331b46..baba489aa 100644 --- a/src/kernel/curse.test.c +++ b/src/kernel/curse.test.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/src/kernel/equipment.c b/src/kernel/equipment.c index f3f094b7f..669e5b101 100644 --- a/src/kernel/equipment.c +++ b/src/kernel/equipment.c @@ -15,7 +15,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/kernel/event.c b/src/kernel/event.c index 051c6ac10..bf5f5ede9 100644 --- a/src/kernel/event.c +++ b/src/kernel/event.c @@ -5,8 +5,8 @@ #include #include -#include -#include +#include +#include /* libc includes */ #include diff --git a/src/kernel/faction.c b/src/kernel/faction.c index 4a5a49de0..7e7a521b3 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -44,8 +44,8 @@ #include #include -#include -#include +#include +#include /* libc includes */ #include diff --git a/src/kernel/gamedata.c b/src/kernel/gamedata.c index 1079dc716..1f3f34204 100644 --- a/src/kernel/gamedata.c +++ b/src/kernel/gamedata.c @@ -5,7 +5,7 @@ #include -#include +#include #include #include #include diff --git a/src/kernel/group.c b/src/kernel/group.c index 4e0feb16e..896fa1ef0 100755 --- a/src/kernel/group.c +++ b/src/kernel/group.c @@ -19,8 +19,8 @@ #include #include -#include -#include +#include +#include /* libc includes */ #include diff --git a/src/kernel/group.test.c b/src/kernel/group.test.c index 77afa2cd6..2cd8f4fd8 100644 --- a/src/kernel/group.test.c +++ b/src/kernel/group.test.c @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/src/kernel/item.c b/src/kernel/item.c index c4bcf8e04..8aa2ef749 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -31,8 +31,8 @@ #include #include -#include -#include +#include +#include /* libc includes */ #include diff --git a/src/kernel/messages.c b/src/kernel/messages.c index c3ded0b16..ac1ae8915 100644 --- a/src/kernel/messages.c +++ b/src/kernel/messages.c @@ -155,7 +155,7 @@ struct message *msg_feedback(const struct unit *u, struct order *ord, msg = msg_create(mtype, args); } if (ord && is_silent(ord)) { - msg->is_silent = -1; + msg->is_silent = ~0; } return msg; diff --git a/src/kernel/messages.test.c b/src/kernel/messages.test.c index 247d5c74d..3ebc20d7e 100644 --- a/src/kernel/messages.test.c +++ b/src/kernel/messages.test.c @@ -6,7 +6,7 @@ #include "util/message.h" #include "util/keyword.h" // for K_ENTERTAIN, K_MOVE -#include "util/variant.h" +#include "util/variant.h" // IWYU pragma: keep #include #include diff --git a/src/kernel/order.c b/src/kernel/order.c index 2ab526247..db4e50960 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -16,7 +16,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/kernel/plane.c b/src/kernel/plane.c index 441288e6f..6197b6378 100644 --- a/src/kernel/plane.c +++ b/src/kernel/plane.c @@ -10,8 +10,8 @@ #include #include -#include -#include +#include +#include /* libc includes */ #include diff --git a/src/kernel/race.c b/src/kernel/race.c index bda9bceff..ce40ad235 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -29,8 +29,8 @@ #include #include -#include -#include +#include +#include /* attrib includes */ #include @@ -455,7 +455,7 @@ void set_study_speed(race *rc, skill_t sk, int modifier) { rc->study_speed = calloc(1, MAXSKILLS); if (!rc->study_speed) abort(); } - rc->study_speed[sk] = (char)modifier; + rc->study_speed[sk] = (signed char)modifier; } const race *rc_otherrace(const race *rc) diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c index 56ecb8b51..6ca2f6fba 100644 --- a/src/kernel/race.test.c +++ b/src/kernel/race.test.c @@ -8,7 +8,7 @@ #include #include -#include +#include // IWYU pragma: keep #include #include diff --git a/src/kernel/region.c b/src/kernel/region.c index 022c7e3e4..acc2f5783 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -34,8 +34,8 @@ #include -#include -#include +#include +#include #include @@ -1097,8 +1097,8 @@ void init_region(region *r) t_plain = get_terrain(terrainnames[T_PLAIN]); } if (terrain->size>0) { - horses = rng_int() % (terrain->size / 50); - trees = terrain->size * (30 + rng_int() % 40) / 1000; + horses = rng_uint() % (terrain->size / 50); + trees = terrain->size * (30 + rng_uint() % 40) / 1000; } if (t_plain && terrain == t_plain) { rsethorses(r, horses); diff --git a/src/kernel/save.c b/src/kernel/save.c index 2b54afaba..f931d68b7 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -68,9 +68,9 @@ #include #include #include -#include +#include #include -#include +#include #include @@ -383,7 +383,7 @@ static void write_skills(gamedata *data, const unit *u) { #ifndef NDEBUG assert(SK_SKILL(sv) > sk); sk = SK_SKILL(sv); - assert(sv->days <= MAX_DAYS_TO_NEXT_LEVEL(sv->level)); + ASSERT_VALID_SKILL(sv, u_race(u)); #endif WRITE_INT(data->store, sv->id); WRITE_INT(data->store, sv->level); @@ -1045,7 +1045,7 @@ static char * getpasswd(int fno) { return NULL; } -static void read_password(gamedata *data, faction *f) { +void read_password(gamedata *data, faction *f) { char name[128]; READ_STR(data->store, name, sizeof(name)); if (name[0] == '$' && data->version == BADCRYPT_VERSION) { @@ -1063,18 +1063,10 @@ static void read_password(gamedata *data, faction *f) { } } -void _test_read_password(gamedata *data, faction *f) { - read_password(data, f); -} - -static void write_password(gamedata *data, const faction *f) { +void write_password(gamedata *data, const faction *f) { WRITE_TOK(data->store, faction_getpassword(f)); } -void _test_write_password(gamedata *data, const faction *f) { - write_password(data, f); -} - faction *read_faction(gamedata * data) { int planes, n; diff --git a/src/kernel/save.h b/src/kernel/save.h index b9a5bda79..8b5f2633e 100644 --- a/src/kernel/save.h +++ b/src/kernel/save.h @@ -42,8 +42,7 @@ struct ship *read_ship(struct gamedata *data); int write_game(struct gamedata *data); int read_game(struct gamedata *data); -/* test-only functions that give access to internal implementation details (BAD) */ -void _test_write_password(struct gamedata *data, const struct faction *f); -void _test_read_password(struct gamedata *data, struct faction *f); +void write_password(struct gamedata *data, const struct faction *f); +void read_password(struct gamedata *data, struct faction *f); void fix_shadows(void); diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index b839317f0..201474ff2 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -29,9 +29,9 @@ #include #include -#include +#include #include -#include +#include #include #include @@ -383,9 +383,9 @@ static void test_read_password(CuTest *tc) { faction_setpassword(f, password_hash("secret", PASSWORD_DEFAULT)); mstream_init(&data.strm); gamedata_init(&data, &store, RELEASE_VERSION); - _test_write_password(&data, f); + write_password(&data, f); data.strm.api->rewind(data.strm.handle); - _test_read_password(&data, f); + read_password(&data, f); mstream_done(&data.strm); gamedata_done(&data); CuAssertTrue(tc, checkpasswd(f, "secret")); @@ -413,10 +413,10 @@ static void test_read_password_external(CuTest *tc) { WRITE_TOK(data.store, "$brokenhash"); data.strm.api->rewind(data.strm.handle); data.version = NOCRYPT_VERSION; - _test_read_password(&data, f); + read_password(&data, f); CuAssertTrue(tc, checkpasswd(f, "newpassword")); data.version = BADCRYPT_VERSION; - _test_read_password(&data, f); + read_password(&data, f); CuAssertTrue(tc, checkpasswd(f, "secret")); F = fopen(pwfile, "wt"); if (F) { @@ -424,7 +424,7 @@ static void test_read_password_external(CuTest *tc) { fclose(F); } CuAssertTrue(tc, checkpasswd(f, "secret")); - _test_read_password(&data, f); + read_password(&data, f); CuAssertTrue(tc, checkpasswd(f, "pwfile")); mstream_done(&data.strm); gamedata_done(&data); diff --git a/src/kernel/ship.c b/src/kernel/ship.c index 5f732aafb..bf41e7b16 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -33,8 +33,8 @@ #include #include -#include -#include +#include +#include #include @@ -613,8 +613,8 @@ const char *ship_getname(const ship * sh) } int ship_damage_percent(const ship *sh) { - /* Schaden muss granularer sein als Größe, deshalb ist er skaliert - * DAMAGE_SCALE ist der Faktor zwischen 1 Schadenspunkt und 1 Größenpunkt. + /* Schaden muss granularer sein als Gr��e, deshalb ist er skaliert + * DAMAGE_SCALE ist der Faktor zwischen 1 Schadenspunkt und 1 Gr��enpunkt. */ if (sh->damage > 0) { double d = (sh->damage * 100.) / ((double)DAMAGE_SCALE * sh->size); diff --git a/src/kernel/skills.c b/src/kernel/skills.c index 43e75d809..0f6acda52 100644 --- a/src/kernel/skills.c +++ b/src/kernel/skills.c @@ -5,6 +5,7 @@ #include "unit.h" #include +#include #include #include @@ -80,62 +81,96 @@ static bool rule_random_progress(void) return rule != 0; } -static int progress_weeks(unsigned int level) +static unsigned int progress_weeks(unsigned int level) /* how many weeks must i study to get from level-1 to level */ { unsigned int coins = MAX_WEEKS_TO_NEXT_LEVEL(level - 1) - 1; - int heads = 1; + unsigned int heads = 1; while (coins--) { - heads += rng_int() % 2; + heads += rng_uint() % 2; } return heads; } static void skill_set(skill *sv, unsigned int level, unsigned int days) { - assert(days <= MAX_DAYS_TO_NEXT_LEVEL(level)); sv->level = level; sv->days = days; assert(sv->days == days && sv->level == level); } -void sk_set_level(skill *sv, int level) +int study_speed(const struct race *rc, enum skill_t sk) { - int weeks = rule_random_progress() ? progress_weeks(level + 1) : (level + 1); - skill_set(sv, level, weeks * SKILL_DAYS_PER_WEEK); + int mod = (rc && rc->study_speed) ? rc->study_speed[sk] : 0; + return SKILL_DAYS_PER_WEEK - mod; } -void increase_skill_weeks(unit * u, enum skill_t sk, const unsigned int weeks) +void sk_set_level(const struct unit *u, skill *sv, unsigned int level) { - skill *sv = unit_skill(u, sk); - unsigned int days = weeks * SKILL_DAYS_PER_WEEK; - if (!sv) { - sv = add_skill(u, sk); - } - while (sv->days <= days) { - days -= sv->days; - sk_set_level(sv, sv->level + 1); + unsigned int weeks = rule_random_progress() ? progress_weeks(level + 1) : (level + 1); + const struct race *rc = u ? u_race(u) : NULL; + int speed = rc ? study_speed(rc, sv->id) : SKILL_DAYS_PER_WEEK; + unsigned int days = SKILL_DAYS_PER_WEEK + (weeks - 1) * speed; + skill_set(sv, level, days); + ASSERT_VALID_SKILL(sv, rc); +} + +static void increase_skill(unit *u, skill *sv, unsigned int days) { + if (days > 0) { + unsigned int leveldays = sv->days; + while (leveldays <= days) { + sk_set_level(u, sv, sv->level + 1); + days -= leveldays; + leveldays = sv->days; + } + sv->days = leveldays - days; + ASSERT_VALID_SKILL(sv, u_race(u)); } - sv->days -= days; - assert(sv->days <= MAX_DAYS_TO_NEXT_LEVEL(sv->level)); } -void reduce_skill_weeks(unit * u, skill * sv, const unsigned int weeks) +void reduce_skill(unit *u, skill *sv, unsigned int days) { - unsigned int days = weeks * SKILL_DAYS_PER_WEEK; - unsigned int max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level); + if (sv) { + // first, strip full levels off the skill: + // max_days = maximum days I can have "to do" at current level + unsigned int max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level); + days += sv->days; + while (sv->level > 0 && max_days < days) { + // level_days = expected time to complete current level. + unsigned int level_days = SKILL_DAYS_PER_WEEK * (1 + sv->level); + days -= level_days; + --sv->level; + max_days = MAX_DAYS_TO_NEXT_LEVEL(sv->level); + } + // store the remaining days + sv->days = days; + } +} - sv->days += days; - while (sv->level > 0 && sv->days > max_days) { - sv->days -= sv->level * SKILL_DAYS_PER_WEEK; - --sv->level; - max_days -= 2 * SKILL_DAYS_PER_WEEK; +void change_skill(unit *u, skill *sv, int days) +{ + assert(sv); + if (days < 0) { + reduce_skill(u, sv, -days); + if (sv->level == 0 && sv->days >= SKILL_DAYS_PER_WEEK) { + remove_skill(u, (skill_t)sv->id); + } + } + else { + increase_skill(u, sv, days); } - if (sv->level == 0) { - /* reroll */ - sk_set_level(sv, sv->level + 1); +} + +void change_skill_days(struct unit *u, enum skill_t sk, int days) +{ + assert(sk >= 0 && sk < MAXSKILLS); + if (days != 0) { + skill *sv = unit_skill(u, sk); + if (!sv && days > 0) { + sv = add_skill(u, sk); + } + change_skill(u, sv, days); } - assert(sv->days <= MAX_DAYS_TO_NEXT_LEVEL(sv->level)); } int skill_compare(const skill * sk, const skill * sc) @@ -232,3 +267,4 @@ int skill_days(unit *u, enum skill_t sk) const skill *sv = unit_skill(u, sk); return sv ? sv->days : 1; } + diff --git a/src/kernel/skills.h b/src/kernel/skills.h index a8c26581e..4702e45bc 100644 --- a/src/kernel/skills.h +++ b/src/kernel/skills.h @@ -4,7 +4,9 @@ #define MAX_WEEKS_TO_NEXT_LEVEL(level) ((level) * 2 + 1) #define MAX_DAYS_TO_NEXT_LEVEL(level) (SKILL_DAYS_PER_WEEK * MAX_WEEKS_TO_NEXT_LEVEL(level)) - +#define MAX_DAYS_TO_NEXT_LEVEL_EX(level, speed) (SKILL_DAYS_PER_WEEK + (speed) * (MAX_WEEKS_TO_NEXT_LEVEL(level) - 1)) +#define ASSERT_VALID_SKILL(sv, rc) \ + assert((sv)->days <= MAX_DAYS_TO_NEXT_LEVEL_EX((sv)->level, study_speed((rc), (sv)->id))) typedef struct skill { unsigned int id : 5; unsigned int level : 7; @@ -35,14 +37,17 @@ int skillmod(const struct unit *u, const struct region *r, struct attrib *make_skillmod(enum skill_t sk, skillmod_fun special, double multiplier, int bonus); -void increase_skill_weeks(struct unit * u, enum skill_t sk, const unsigned int weeks); -void reduce_skill_weeks(struct unit *u, skill * sv, const unsigned int weeks); +void change_skill_days(struct unit *u, enum skill_t sk, int days); +void reduce_skill(struct unit *u, skill *sv, unsigned int days); +void change_skill(struct unit *u, skill *sv, int days); int merge_skill(const skill* sv, const skill* sn, skill* result, int n, int add); -void sk_set_level(skill * sv, int level); +void sk_set_level(const struct unit *u, skill * sv, unsigned int level); int skill_compare(const skill* sk, const skill* sc); int skill_level(struct unit *u, enum skill_t sk); /** number of days-equivalent the unit must STUDY to reach the next level: */ int skill_days(struct unit *u, enum skill_t sk); +/** number of days in a week for this learner */ +int study_speed(const struct race *rc, enum skill_t sk); #define SK_SKILL(sv) ((skill_t) (sv->id)) diff --git a/src/kernel/skills.test.c b/src/kernel/skills.test.c index d7752c304..654706851 100644 --- a/src/kernel/skills.test.c +++ b/src/kernel/skills.test.c @@ -17,12 +17,19 @@ static void test_skill_set(CuTest *tc) test_setup(); config_set_int("study.random_progress", 0); - sk_set_level(&value, 2); + sk_set_level(NULL, &value, 2); CuAssertIntEquals(tc, 1, value.old); CuAssertIntEquals(tc, 2, value.level); CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, value.days); } +static int days_effort(const skill *sv) +{ + int n = sv->level + 1; + int weeks = (n + 1) * n / 2; + return weeks * SKILL_DAYS_PER_WEEK - sv->days; +} + static void test_skill_change(CuTest *tc) { unit *u; @@ -31,54 +38,88 @@ static void test_skill_change(CuTest *tc) test_setup(); config_set_int("study.random_progress", 0); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); - increase_skill_weeks(u, SK_CROSSBOW, 1); - sv = unit_skill(u, SK_CROSSBOW); + set_number(u, 2); // number should have no effect on skill values + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 1, sv->level); + /* no random progress, so it will take 2 weeks of learning to next level: */ CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, sv->days); - increase_skill_weeks(u, SK_CROSSBOW, 1); + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, sv->days); - increase_skill_weeks(u, SK_CROSSBOW, 1); + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 2, sv->level); CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, sv->days); - increase_skill_weeks(u, SK_CROSSBOW, 4); + change_skill_days(u, SK_CROSSBOW, 4 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, sv->days); - - reduce_skill_weeks(u, sv, 1); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 6 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, sv->days); - reduce_skill_weeks(u, sv, 1); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, sv->days); - reduce_skill_weeks(u, sv, 2); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK * 2); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 3, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 3, sv->level); CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK, sv->days); - reduce_skill_weeks(u, sv, 1); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, days_effort(sv)); CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 2, sv->level); - CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, sv->days); + CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + sv->level = 10; - reduce_skill_weeks(u, sv, 25); - CuAssertIntEquals(tc, 8, skill_level(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 8, sv->level); - CuAssertIntEquals(tc, 11 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); - CuAssertIntEquals(tc, 11 * SKILL_DAYS_PER_WEEK, sv->days); + sv->days = SKILL_DAYS_PER_WEEK; + CuAssertIntEquals(tc, 65 * SKILL_DAYS_PER_WEEK, days_effort(sv)); + + change_skill_days(u, SK_CROSSBOW, -25 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 40 * SKILL_DAYS_PER_WEEK, days_effort(sv)); + // 40 = (8 * 9) / 2 + 4 + CuAssertIntEquals(tc, 9, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 15 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); +} + +static void test_reduce_skill(CuTest *tc) +{ + unit *u; + + test_setup(); + config_set_int("study.random_progress", 0); + u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); + set_number(u, 2); // number should have no effect on skill values + change_skill_days(u, SK_CROSSBOW, SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertPtrEquals(tc, NULL, u->skills); + + change_skill_days(u, SK_CROSSBOW, 3 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, - 2 * SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, days_effort(u->skills)); + CuAssertIntEquals(tc, 2, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK - 1, days_effort(u->skills)); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK + 1, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, 2 - SKILL_DAYS_PER_WEEK); + CuAssertIntEquals(tc, 1, days_effort(u->skills)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); + change_skill_days(u, SK_CROSSBOW, -1); + CuAssertPtrEquals(tc, NULL, u->skills); } static void test_set_level(CuTest * tc) @@ -211,6 +252,7 @@ static void test_skills_merge(CuTest* tc) CuSuite *get_skills_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_reduce_skill); SUITE_ADD_TEST(suite, test_skill_set); SUITE_ADD_TEST(suite, test_skill_change); SUITE_ADD_TEST(suite, test_set_level); diff --git a/src/kernel/spell.c b/src/kernel/spell.c index c35d34d5a..a6f4a8f2e 100644 --- a/src/kernel/spell.c +++ b/src/kernel/spell.c @@ -7,7 +7,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/kernel/spellbook.c b/src/kernel/spellbook.c index 99f2a0637..2fabcde9e 100644 --- a/src/kernel/spellbook.c +++ b/src/kernel/spellbook.c @@ -10,8 +10,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/src/kernel/terrain.c b/src/kernel/terrain.c index 68d2d778d..2e49b82d3 100644 --- a/src/kernel/terrain.c +++ b/src/kernel/terrain.c @@ -14,7 +14,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/kernel/terrain.h b/src/kernel/terrain.h index 31c54c158..0a91affb0 100644 --- a/src/kernel/terrain.h +++ b/src/kernel/terrain.h @@ -1,65 +1,62 @@ -#ifndef TERRAIN_H -#define TERRAIN_H +#pragma once struct region; - /* diverse Flags */ - /* Strassen und Gebaeude koennen gebaut werden, wenn max_road > 0 */ +/* diverse Flags */ +/* Strassen und Gebaeude koennen gebaut werden, wenn max_road > 0 */ #define LAND_REGION (1<<0) /* Standard-Land-struct region */ #define SEA_REGION (1<<1) /* hier braucht man ein Boot */ #define FOREST_REGION (1<<2) /* Elfen- und Kampfvorteil durch Baeume */ #define ARCTIC_REGION (1<<3) /* Gletscher & co = Keine Insekten! */ #define CAVALRY_REGION (1<<4) /* riding in combat is possible */ - /* Achtung: SEA_REGION ist nicht das Gegenteil von LAND_REGION. Die zwei schliessen sich nichtmal aus! */ +/* Achtung: SEA_REGION ist nicht das Gegenteil von LAND_REGION. Die zwei schliessen sich nichtmal aus! */ #define FORBIDDEN_REGION (1<<5) /* unpassierbare Blockade-struct region */ #define FLY_INTO (1<<7) /* man darf hierhin fliegen */ #define SWIM_INTO (1<<8) /* man darf hierhin schwimmen */ #define WALK_INTO (1<<9) /* man darf hierhin laufen */ - extern const char *terrainnames[]; - - typedef struct production_rule { - char *name; - const struct resource_type *rtype; - - void(*terraform) (struct production_rule *, const struct region *); - void(*update) (struct production_rule *, const struct region *); - void(*use) (struct production_rule *, const struct region *, int amount); - int(*visible) (const struct production_rule *, int skilllevel); - - /* no initialization required */ - struct production_rule *next; - } production_rule; - - typedef struct terrain_production { - const struct resource_type *type; - char *startlevel; - char *base; - char *divisor; - float chance; - } terrain_production; - - typedef struct terrain_type { - char *_name; - char *alias; - int size; /* how many peasants can work? */ - int flags; - short max_road; /* this many stones make a full road */ - short distribution; /* multiplier used for seeding */ - struct terrain_production *production; /* stb_ds array */ - struct item_type **herbs; /* zero-terminated array of herbs */ - const char *(*name) (const struct region * r); - struct terrain_type *next; - } terrain_type; - - terrain_type *get_or_create_terrain(const char *name); - const terrain_type *terrains(void); - const terrain_type *get_terrain(const char *name); - const char *terrain_name(const struct region *r); - const char *terrain_alias(const struct region *r); - bool terrain_changed(int *cache); - - void init_terrains(void); - void free_terrains(void); - -#endif /* TERRAIN_H */ +extern const char *terrainnames[]; + +typedef struct production_rule { + char *name; + const struct resource_type *rtype; + + void(*terraform) (struct production_rule *, const struct region *); + void(*update) (struct production_rule *, const struct region *); + void(*use) (struct production_rule *, const struct region *, int amount); + int(*visible) (const struct production_rule *, int skilllevel); + + /* no initialization required */ + struct production_rule *next; +} production_rule; + +typedef struct terrain_production { + const struct resource_type *type; + char *startlevel; + char *base; + char *divisor; + float chance; +} terrain_production; + +typedef struct terrain_type { + char *_name; + char *alias; + int size; /* how many peasants can work? */ + int flags; + short max_road; /* this many stones make a full road */ + short distribution; /* multiplier used for seeding */ + struct terrain_production *production; /* stb_ds array */ + struct item_type **herbs; /* zero-terminated array of herbs */ + const char *(*name) (const struct region * r); + struct terrain_type *next; +} terrain_type; + +terrain_type *get_or_create_terrain(const char *name); +const terrain_type *terrains(void); +const terrain_type *get_terrain(const char *name); +const char *terrain_name(const struct region *r); +const char *terrain_alias(const struct region *r); +bool terrain_changed(int *cache); + +void init_terrains(void); +void free_terrains(void); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 2288dffb3..5815a4000 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -46,8 +46,8 @@ #include #include -#include -#include +#include +#include #include @@ -581,13 +581,13 @@ void set_level(struct unit * u, enum skill_t sk, unsigned int value) for (len = arrlen(u->skills), s = 0; s != len; ++s) { skill* sv = u->skills + s; if (sv->id == sk) { - sk_set_level(sv, value); + sk_set_level(u, sv, value); sv->old = sv->level; return; } ++sv; } - sk_set_level(add_skill(u, sk), value); + sk_set_level(u, add_skill(u, sk), value); } static int leftship_age(struct attrib *a, void *owner) @@ -886,6 +886,7 @@ void u_setfaction(unit * u, faction * f) u->faction->num_people -= u->number; } set_group(u, NULL); + usetprivate(u, NULL); if (u->nextF) { u->nextF->prevF = u->prevF; } @@ -1231,7 +1232,7 @@ static int newunitid(void) { int random_unit_no; int start_random_no; - random_unit_no = 1 + (rng_int() % MAX_UNIT_NR); + random_unit_no = 1 + (rng_uint() % MAX_UNIT_NR); start_random_no = random_unit_no; while (ufindhash(random_unit_no) || dfindhash(random_unit_no) diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index d35a05f29..346fde8c8 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -9,6 +9,7 @@ #include "building.h" #include "config.h" #include "faction.h" +#include "group.h" #include "item.h" #include "magic.h" #include "order.h" @@ -76,7 +77,7 @@ static void test_remove_units_without_faction(CuTest *tc) { u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); uid = u->no; - u_setfaction(u, 0); + u_setfaction(u, NULL); remove_empty_units_in_region(u->region); CuAssertPtrEquals(tc, NULL, findunit(uid)); CuAssertIntEquals(tc, 0, u->number); @@ -634,6 +635,21 @@ static void test_unlimited_units(CuTest *tc) { test_teardown(); } +static void test_change_faction_clears_private(CuTest *tc) { + unit *u; + faction *f; + + test_setup(); + f = test_create_faction(); + u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); + usetprivate(u, "Hodor"); + join_group(u, "Groupies"); + u_setfaction(u, f); + CuAssertPtrEquals(tc, NULL, get_group(u)); + CuAssertStrEquals(tc, NULL, uprivate(u)); + test_teardown(); +} + static void test_transfermen_bug_2386(CuTest *tc) { unit *u1, *u2; region *r; @@ -1102,6 +1118,7 @@ CuSuite *get_unit_suite(void) SUITE_ADD_TEST(suite, test_transfer_hitpoints); SUITE_ADD_TEST(suite, test_transfer_skills); SUITE_ADD_TEST(suite, test_transfer_skills_merge); + SUITE_ADD_TEST(suite, test_change_faction_clears_private); SUITE_ADD_TEST(suite, test_transfermen_bug_2386); SUITE_ADD_TEST(suite, test_transfermen_bug_2886); SUITE_ADD_TEST(suite, test_remove_unit); diff --git a/src/kernel/version.c b/src/kernel/version.c index b017fdb12..9d860fc51 100644 --- a/src/kernel/version.c +++ b/src/kernel/version.c @@ -8,7 +8,7 @@ #ifndef ERESSEA_VERSION /* the version number, if it was not passed to make with -D */ -#define ERESSEA_VERSION "31.1.0" +#define ERESSEA_VERSION "31.2.0" #endif const char *eressea_version(void) { diff --git a/src/laws.c b/src/laws.c index 967835d1a..19c5dce2c 100644 --- a/src/laws.c +++ b/src/laws.c @@ -89,10 +89,10 @@ #include #include -#include -#include -#include +#include +#include +#include #include /* libc includes */ @@ -173,30 +173,6 @@ static bool RemoveNMRNewbie(void) return value != 0; } -static void potion_effects(unit *u) { - 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; - } - /* bestes Talent raussuchen */ - if (sb != NULL) { - reduce_skill_weeks(u, sb, 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); - } -} - static void astral_crumble(unit *u) { item **itemp = &u->items; while (*itemp) { @@ -794,14 +770,16 @@ void immigration(void) } } +#define HELP_NMR (HELP_GUARD|HELP_MONEY) + void nmr_warnings(void) { faction *f, *fa; -#define HELP_NMR (HELP_GUARD|HELP_MONEY) + int timeout = NMRTimeout(); for (f = factions; f; f = f->next) { if (!fval(f, FFL_NOIDLEOUT|FFL_PAUSED) && turn > f->lastorders) { ADDMSG(&f->msgs, msg_message("nmr_warning", "")); - if (turn - f->lastorders == NMRTimeout() - 1) { + if (turn - f->lastorders == timeout - 1) { ADDMSG(&f->msgs, msg_message("nmr_warning_final", "")); } if ((turn - f->lastorders) >= 2) { @@ -816,7 +794,7 @@ void nmr_warnings(void) else if (alliedfaction(f, fa, HELP_NMR) && alliedfaction(fa, f, HELP_NMR)) { warn = 1; } - if (warn) { + if (warn && turn - f->lastorders < timeout) { if (msg == NULL) { msg = msg_message("warn_dropout", "faction turns", f, turn - f->lastorders); @@ -2490,6 +2468,7 @@ int status_cmd(unit * u, struct order *ord) break; case P_FLEE: unit_setstatus(u, ST_FLEE); + setguard(u, false); break; case P_CHICKEN: unit_setstatus(u, ST_CHICKEN); diff --git a/src/laws.test.c b/src/laws.test.c index eaf9f2538..6d31d8862 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1577,7 +1577,24 @@ static void test_status_cmd(CuTest* tc) { test_teardown(); } -static void test_ally_cmd(CuTest* tc) { +static void test_status_flee_stops_guard(CuTest *tc) { + unit *u; + faction *f; + + guard_fixture fix; + + setup_guard(&fix, true); + u = fix.u; + f = fix.u->faction; + u->thisorder = create_order(K_STATUS, f->locale, param_name(P_FLEE, f->locale)); + status_cmd(u, u->thisorder); + CuAssertIntEquals(tc, ST_FLEE, u->status); + CuAssertTrue(tc, !is_guard(u)); + + test_teardown(); +} + +static void test_ally_cmd(CuTest *tc) { unit *u; faction * f; order *ord; @@ -1627,13 +1644,24 @@ static void test_nmr_warnings(CuTest *tc) { faction_set_age(f2, 2); CuAssertIntEquals(tc, 2, faction_age(f2)); f2->lastorders = 1; + ally_set(&f1->allies, f2, HELP_GUARD); + ally_set(&f2->allies, f1, HELP_GUARD); nmr_warnings(); CuAssertPtrNotNull(tc, f1->msgs); CuAssertPtrNotNull(tc, test_find_messagetype(f1->msgs, "nmr_warning")); + CuAssertPtrNotNull(tc, test_find_messagetype(f1->msgs, "warn_dropout")); CuAssertPtrNotNull(tc, f2->msgs); CuAssertPtrNotNull(tc, f2->msgs->begin); CuAssertPtrNotNull(tc, test_find_messagetype(f2->msgs, "nmr_warning")); CuAssertPtrNotNull(tc, test_find_messagetype(f2->msgs, "nmr_warning_final")); + test_clear_messages(f1); + test_clear_messages(f2); + f2->lastorders = 0; + nmr_warnings(); + CuAssertPtrNotNull(tc, f1->msgs); + CuAssertPtrNotNull(tc, test_find_messagetype(f1->msgs, "nmr_warning")); + CuAssertPtrEquals(tc, NULL, test_find_messagetype(f1->msgs, "warn_dropout")); + test_teardown(); } @@ -3027,6 +3055,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_findparam_ex); SUITE_ADD_TEST(suite, test_nmr_warnings); SUITE_ADD_TEST(suite, test_status_cmd); + SUITE_ADD_TEST(suite, test_status_flee_stops_guard); SUITE_ADD_TEST(suite, test_ally_cmd); SUITE_ADD_TEST(suite, test_name_cmd); SUITE_ADD_TEST(suite, test_name_foreign_cmd); diff --git a/src/listbox.c b/src/listbox.c index fd67af1e4..bf9aaf658 100644 --- a/src/listbox.c +++ b/src/listbox.c @@ -1,6 +1,6 @@ #include "listbox.h" -#include +#include #include diff --git a/src/magic.c b/src/magic.c index 697d8c11e..a5e139686 100644 --- a/src/magic.c +++ b/src/magic.c @@ -62,10 +62,11 @@ #include #include -#include -#include -#include -#include +#include + +#include +#include +#include #include @@ -932,18 +933,23 @@ spellpower(region * r, unit * u, const spell * sp, int cast_level) unit *mage = c->magician; force -= curse_geteffect(c); curse_changevigour(&r->attribs, c, -cast_level); - if (mage != NULL && mage->faction != NULL) { - if (force > 0) { - ADDMSG(&mage->faction->msgs, msg_message("reduce_spell", - "self mage region", mage, u, r)); + if (force > 0) { + ADDMSG(&u->faction->msgs, msg_message("spell_reduced", + "target region", u, r)); + if (mage != NULL && mage->faction != NULL) { + ADDMSG(&mage->faction->msgs, msg_message("spell_reduced_by", + "mage target region", mage, u, r)); } - else { - ADDMSG(&mage->faction->msgs, msg_message("block_spell", - "self mage region", mage, u, r)); + } + else { + ADDMSG(&u->faction->msgs, msg_message("spell_blocked", + "target region", u, r)); + if (mage != NULL && mage->faction != NULL) { + ADDMSG(&mage->faction->msgs, msg_message("spell_blocked_by", + "mage target region", mage, u, r)); } } } - /* Patzerfluch-Effekt: */ c = get_curse(r->attribs, &ct_fumble); if (curse_active(c)) { @@ -2447,6 +2453,9 @@ static bool is_moving_ship(ship * sh) const unit *u = ship_owner(sh); if (u) { + if (u->flags & UFL_FOLLOWING) { + return true; + } switch (getkeyword(u->thisorder)) { case K_ROUTE: case K_MOVE: diff --git a/src/magic.test.c b/src/magic.test.c index 83e9948bd..a5eaf8f90 100644 --- a/src/magic.test.c +++ b/src/magic.test.c @@ -3,6 +3,8 @@ #include "contact.h" #include "teleport.h" +#include + #include #include @@ -10,6 +12,8 @@ #include // for frac_make, frac_sub, frac_equal, variant #include +#include "kernel/config.h" +#include "kernel/curse.h" #include "kernel/skill.h" // for SK_MAGIC, enable_skill, SK_STAMINA #include "kernel/types.h" // for M_TYBIED, M_GWYRRD, M_CERDDOR, M_GRAY #include @@ -26,12 +30,14 @@ #include #include #include +#include "kernel/terrain.h" #include #include #include -#include -#include +#include +#include + #include #include @@ -869,6 +875,61 @@ static void test_fumble_toad(CuTest *tc) { test_teardown(); } +static void test_spellpower(CuTest *tc) { + unit *u, *um; + region *r; + race *rc_elf; + spell *sp; + item_type *it_ring; + building_type *bt_magic; + curse *c; + + test_setup(); + bt_magic = test_create_buildingtype("stonecircle"); + bt_magic->flags |= BTF_MAGIC; + it_ring = test_create_itemtype("rop"); + rc_elf = test_create_race("elf"); + sp = create_spell("herpes"); + u = test_create_unit(test_create_faction(), r = test_create_plain(0, 0)); + um = test_create_unit(test_create_faction(), r); + rsettrees(r, 2, r->terrain->size); + CuAssertTrue(tc, r_isforest(r)); + CuAssertDblEquals(tc, 0.0, spellpower(r, u, NULL, 10), 0.01); + CuAssertDblEquals(tc, 10.0, spellpower(r, u, sp, 10), 0.01); + + /* elf in forest : +1 power */ + u_setrace(u, rc_elf); + config_set("rules.magic.elfpower", "0"); + CuAssertDblEquals(tc, 10.0, spellpower(r, u, sp, 10), 0.01); + config_set("rules.magic.elfpower", "1"); + CuAssertDblEquals(tc, 11.0, spellpower(r, u, sp, 10), 0.01); + + /* ring of power : +1 power */ + i_change(&u->items, it_ring, 1); + CuAssertDblEquals(tc, 12.0, spellpower(r, u, sp, 10), 0.01); + + /* magical buildings : +1 power */ + u_set_building(u, test_create_building(r, bt_magic)); + CuAssertDblEquals(tc, 13.0, spellpower(r, u, sp, 10), 0.01); + + /* antimagic zone: drains power, sends messages, reduces c->vigour */ + c = create_curse(um, &r->attribs, &ct_antimagiczone, 100.0, 2, 5, 0); + CuAssertDblEquals(tc, 8.0, spellpower(r, u, sp, 10), 0.01); + CuAssertPtrNotNull(tc, test_find_faction_message(um->faction, "spell_reduced_by")); + CuAssertPtrNotNull(tc, test_find_faction_message(u->faction, "spell_reduced")); + CuAssertDblEquals(tc, 90.0, c->vigour, 0.01); + test_clear_messages(um->faction); + test_clear_messages(u->faction); + + /* TODO: antimagic without caster sends message */ + c->magician = NULL; + CuAssertDblEquals(tc, 8.0, spellpower(r, u, sp, 10), 0.01); + CuAssertPtrNotNull(tc, test_find_faction_message(u->faction, "spell_reduced")); + CuAssertDblEquals(tc, 80.0, c->vigour, 0.01); + + test_teardown(); +} + CuSuite *get_magic_suite(void) { CuSuite* suite = CuSuiteNew(); @@ -896,6 +957,7 @@ CuSuite *get_magic_suite(void) SUITE_ADD_TEST(suite, test_fix_fam_spells); SUITE_ADD_TEST(suite, test_fix_fam_migrants); SUITE_ADD_TEST(suite, test_fumble_toad); + SUITE_ADD_TEST(suite, test_spellpower); return suite; } diff --git a/src/main.c b/src/main.c index 7376bd8c4..b803861c0 100644 --- a/src/main.c +++ b/src/main.c @@ -88,8 +88,6 @@ static const char * valid_keys[] = { "game.dbswap", "game.dbbatch", "editor.color", - "editor.codepage", - "editor.population.", "lua.", NULL }; @@ -209,14 +207,6 @@ static int parse_args(int argc, char **argv) case 'D': config_set("config.debug", "1"); break; - case 'i': - i = get_arg(argc, argv, 2, i, &arg, NULL); - if (arg) { - config_set("config.install", arg); - } else { - return usage(argv[0], NULL); - } - break; case 'c': i = get_arg(argc, argv, 2, i, &arg, NULL); if (arg) { diff --git a/src/modules/autoseed.c b/src/modules/autoseed.c index 0dd37df5c..4fd846da5 100644 --- a/src/modules/autoseed.c +++ b/src/modules/autoseed.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c index df564e41e..bb3b6aa34 100644 --- a/src/modules/gmcmd.c +++ b/src/modules/gmcmd.c @@ -23,7 +23,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/modules/museum.c b/src/modules/museum.c index a392dcbba..97ec5b5c2 100644 --- a/src/modules/museum.c +++ b/src/modules/museum.c @@ -26,8 +26,8 @@ #include #include -#include -#include +#include +#include /* libc includes */ #include diff --git a/src/modules/xmas.c b/src/modules/xmas.c index d9e80c18e..4abbd7da0 100644 --- a/src/modules/xmas.c +++ b/src/modules/xmas.c @@ -19,7 +19,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/monsters.c b/src/monsters.c index c04b198de..dc5bbed25 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -50,7 +50,7 @@ #include "spells/regioncurse.h" -#include +#include #include diff --git a/src/move.c b/src/move.c index 0f2e7a1c2..f5b5dcd73 100644 --- a/src/move.c +++ b/src/move.c @@ -60,8 +60,9 @@ #include #include -#include -#include +#include +#include + #include /* libc includes */ @@ -73,8 +74,6 @@ #include #include -int *storms; - typedef struct traveldir { int no; direction_t dir; @@ -516,7 +515,7 @@ static int walk_speed(const unit * u, const capacities *cap) return BP_WALKING; } -static bool cansail(const region * r, ship * sh) +static bool cansail(const region * r, const ship * sh) { UNUSED_ARG(r); @@ -755,7 +754,7 @@ static building *get_harbour(const region *r) { return NULL; } -int check_ship_allowed(struct ship *sh, const region * r, const building *harbour) +int check_ship_allowed(const struct ship *sh, const region * r, const building *harbour) { if (fval(r->terrain, SEA_REGION)) { return SA_ALLOWED; @@ -874,7 +873,7 @@ static void msg_to_passengers(ship *sh, unit **firstu, unit **lastu, message *ms msg_release(msg); } -direction_t drift_target(ship *sh) { +direction_t drift_target(const ship *sh) { direction_t d, dir = rng_int() % MAXDIRECTIONS; direction_t result = NODIRECTION; for (d = 0; d != MAXDIRECTIONS; ++d) { @@ -892,7 +891,18 @@ direction_t drift_target(ship *sh) { return result; } -static void drifting_ships(region * r) +const char *drift_reason(const struct region *r, const struct ship *sh) +{ + if (!ship_crewed(sh, ship_owner(sh))) { + return "ship_drift_nocrew"; + } + else if (!cansail(r, sh)) { + return "ship_drift_overload"; + } + return NULL; +} + +void drifting_ships(region * r) { static int config; static bool drift; @@ -928,13 +938,8 @@ static void drifting_ships(region * r) * Genuegend leicht? Dann ist alles OK. */ if (ship_finished(sh)) { - if (!ship_crewed(sh, ship_owner(sh))) { - reason = "ship_drift_nocrew"; - } - else if (!cansail(r, sh)) { - reason = "ship_drift_overload"; - } - else { + reason = drift_reason(r, sh); + if (!reason) { /* no problems, don't drift */ shp = &sh->next; continue; @@ -1837,21 +1842,44 @@ bool can_takeoff(const ship * sh, const region * from, const region * to) { if (!fval(from->terrain, SEA_REGION) && sh->coast != NODIRECTION) { direction_t coast = sh->coast; + const building_type *btype; direction_t dir = reldirection(from, to); direction_t coastr = (direction_t)((coast + 1) % MAXDIRECTIONS); direction_t coastl = (direction_t)((coast + MAXDIRECTIONS - 1) % MAXDIRECTIONS); - if (dir != coast && dir != coastl && dir != coastr - && !buildingtype_exists(from, bt_find("harbour"), true)) { + if (dir == coast || dir == coastl || dir == coastr) { + return true; + } + if (NULL != (btype = bt_find("harbour"))) { + building *b = get_building_of_type(from, btype, true); + if (b) { + unit *bo = building_owner(b); + if (bo) { + unit *so = ship_owner(sh); + if (so) { + if (ucontact(bo, so)) { + return true; + } + if (alliedunit(bo, so->faction, HELP_GUARD)) { + return true; + } + } + return false; + } + return true; + } return false; } + else { + } + return false; } return true; } -void harbour_taxes(region *r, unit *captain, unit *harbourmaster) +static void harbour_taxes(region *r, unit *captain, unit *harbourmaster) { item *itm; item *trans = NULL; @@ -1890,7 +1918,33 @@ void harbour_taxes(region *r, unit *captain, unit *harbourmaster) } } -static void sail(unit * u, order * ord, bool drifting) +region * storm_redirect(const region *current_point, const region *next_point) +{ + int d_offset = rng_int() % MAXDIRECTIONS; + region *rnext = NULL; + /* Sturm nur, wenn naechste Region Hochsee ist. */ + for (direction_t d = 0; d != MAXDIRECTIONS; ++d) { + direction_t dnext = (direction_t)((d + d_offset) % MAXDIRECTIONS); + region *rn = rconnect(current_point, dnext); + + if (rn != NULL) { + if (!fval(rn->terrain, SEA_REGION)) { + rnext = NULL; + break; + } + if (fval(rn->terrain, FORBIDDEN_REGION)) { + // do we even *have* forbidden sea regions? + continue; + } + if (rn != next_point) { + rnext = rn; + } + } + } + return rnext; +} + +void sail(unit * u, order * ord, bool drifting) { region_list *route = NULL; region *starting_point = u->region; @@ -1955,7 +2009,7 @@ static void sail(unit * u, order * ord, bool drifting) int stormyness; gamedate date; get_gamedate(turn, &date); - stormyness = storms ? storms[date.month] * 5 : 0; + stormyness = storm_factor(date.month) * 5; /* storms should be the first thing we do. */ stormchance = stormyness / shipspeed(sh, u); @@ -1970,27 +2024,8 @@ static void sail(unit * u, order * ord, bool drifting) if (stormchance && rng_int() % 10000 < stormchance * sh->type->storm && fval(current_point->terrain, SEA_REGION)) { if (!is_cursed(sh->attribs, &ct_nodrift)) { - region *rnext = NULL; - bool storm = true; - int d_offset = rng_int() % MAXDIRECTIONS; - direction_t d; - /* Sturm nur, wenn naechste Region Hochsee ist. */ - for (d = 0; d != MAXDIRECTIONS; ++d) { - direction_t dnext = (direction_t)((d + d_offset) % MAXDIRECTIONS); - region *rn = rconnect(current_point, dnext); - - if (rn != NULL) { - if (fval(rn->terrain, FORBIDDEN_REGION)) - continue; - if (!fval(rn->terrain, SEA_REGION)) { - storm = false; - break; - } - if (rn != next_point) - rnext = rn; - } - } - if (storm && rnext != NULL) { + region *rnext = storm_redirect(current_point, next_point); + if (rnext != NULL) { ADDMSG(&f->msgs, msg_message("storm", "ship region sink", sh, next_point, ship_damage_percent(sh) >= 100)); diff --git a/src/move.h b/src/move.h index f037c04ef..b6b078049 100644 --- a/src/move.h +++ b/src/move.h @@ -14,7 +14,6 @@ struct unit; struct item; extern struct attrib_type at_shiptrail; -extern int *storms; /* Bewegungsweiten: */ #define BP_WALKING 4 @@ -57,9 +56,11 @@ struct unit *owner_buildingtyp(const struct region *r, bool move_blocked(const struct unit *u, const struct region *src, const struct region *dest); bool can_takeoff(const struct ship * sh, const struct region * from, const struct region * to); +void sail(struct unit * u, struct order * ord, bool drifting); void move_cmd(struct unit * u, struct order * ord); void move_cmd_ex(struct unit * u, struct order * ord, const char *directions); int follow_ship(struct unit * u, struct order * ord); +struct region * storm_redirect(const struct region *src, const struct region *dst); #define SA_HARBOUR_ALLOWED 1 #define SA_ALLOWED 0 @@ -68,8 +69,10 @@ int follow_ship(struct unit * u, struct order * ord); #define SA_HARBOUR_DENIED -3 #define SA_HARBOUR_DISABLED -4 -int check_ship_allowed(struct ship *sh, const struct region * r, const struct building *harbour); -direction_t drift_target(struct ship *sh); +int check_ship_allowed(const struct ship *sh, const struct region * r, const struct building *harbour); +direction_t drift_target(const struct ship *sh); +const char *drift_reason(const struct region *r, const struct ship *sh); +void drifting_ships(struct region * r); struct order * cycle_route(struct order * ord, const struct locale *lang, int gereist); struct order * make_movement_order(const struct locale *lang, direction_t steps[], int length); diff --git a/src/move.test.c b/src/move.test.c index c5f54ffac..d2f9908f2 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -22,6 +22,7 @@ #include "util/keyword.h" #include "util/language.h" +#include "util/macros.h" #include "util/message.h" #include "util/base36.h" #include "util/param.h" @@ -44,6 +45,53 @@ static void setup_move(void) { "unit:unit", "region:region", MT_NEW_END); } +static void test_ship_takeoff_from_harbor(CuTest *tc) +{ + region *r1, *r2; + ship *sh; + unit *u1, *u2; + building *b; + + test_setup(); + r1 = test_create_ocean(0, 0); + r2 = test_create_plain(1, 0); + u1 = test_create_unit(test_create_faction(), r2); + u1->ship = sh = test_create_ship(r2, NULL); + sh->coast = NODIRECTION; + CuAssertTrue(tc, can_takeoff(sh, r2, r1)); + sh->coast = D_WEST; + CuAssertTrue(tc, can_takeoff(sh, r2, r1)); + sh->coast = D_EAST; + CuAssertTrue(tc, !can_takeoff(sh, r2, r1)); + + // harbor is maintained, not owned by anyone: + b = test_create_building(r2, test_create_buildingtype("harbour")); + CuAssertTrue(tc, can_takeoff(sh, r2, r1)); + // harbor is unmaintained: + b->flags |= BLD_UNMAINTAINED; + CuAssertTrue(tc, !can_takeoff(sh, r2, r1)); + + // harbor is maintained, owner is allied: + b->flags &= ~BLD_UNMAINTAINED; + u2 = test_create_unit(u1->faction, r2); + u2->building = b; + building_set_owner(u2); + CuAssertTrue(tc, can_takeoff(sh, r2, r1)); + + // harbor owner is an allied faction: + u_setfaction(u2, test_create_faction()); + ally_set(&u2->faction->allies, u1->faction, HELP_GUARD); + CuAssertTrue(tc, can_takeoff(sh, r2, r1)); + // harbor owner doesn't set HELP GUARD: + u2->faction->allies->status = 0; + CuAssertTrue(tc, !can_takeoff(sh, r2, r1)); + + contact_unit(u2, u1); + CuAssertTrue(tc, can_takeoff(sh, r2, r1)); + + test_teardown(); +} + static void test_ship_not_allowed_in_coast(CuTest * tc) { region *r1, *r2; @@ -121,7 +169,7 @@ static void test_ship_allowed_coast_ignores_harbor(CuTest* tc) u->building = mf.b; building_set_owner(u); - /* ship cannot sail in becasue of the harbor: */ + /* ship cannot sail in because of the harbor: */ CuAssertIntEquals(tc, SA_HARBOUR_DENIED, check_ship_allowed(mf.sh, mf.r, mf.b)); /* make it so our ship can enter glaciers: */ @@ -833,21 +881,152 @@ static void test_follow_ship_msg(CuTest * tc) { test_teardown(); } +static void test_storms(CuTest *tc) { + ship *sh; + unit *u; + faction *f; + region *r, *r2; + const char *dir; + ship_type * stype; + + test_setup(); + stype = test_create_shiptype("bucket"); + stype->storm = 100; + stype->cargo = 100000; + u = test_create_unit(f = test_create_faction(), r = test_create_ocean(0,0)); + test_set_skill(u, SK_SAILING, stype->sumskill, 0); + test_create_ocean(1,0); + r2 = test_create_ocean(2,0); + u_set_ship(u, sh = test_create_ship(r, stype)); + dir = LOC(f->locale, directions[D_EAST]); + u->thisorder = create_order(K_MOVE, f->locale, "%s %s", dir, dir); + + config_set_int("rules.ship.storms", 0); + init_order(u->thisorder, f->locale); + sail(u, u->thisorder, true); + CuAssertPtrEquals(tc, r2, sh->region); + CuAssertPtrEquals(tc, r2, u->region); + + move_ship(sh, r2, r, NULL); + config_set_int("rules.ship.storms", 1); + config_set_int("rules.calendar.stormchance", 100); + init_order(u->thisorder, f->locale); + sail(u, u->thisorder, true); + CuAssertPtrEquals(tc, sh->region, u->region); + CuAssertPtrNotNull(tc, test_find_faction_message(f, "storm")); + CuAssertTrue(tc, sh->damage != 0); + + test_teardown(); +} + +static void test_storm_redirect(CuTest *tc) { + region *r, *r1, *r2; + terrain_type *t_fire; + + /* drift into an adjacent ocean, but not the original destination */ + test_setup(); + r = test_create_ocean(0, 0); + r1 = test_create_ocean(0, 1); + r2 = test_create_ocean(1, 0); + + CuAssertPtrEquals(tc, r2, storm_redirect(r, r1)); + + /* do not drift if there is an adjacent non-ocean */ + r = test_create_ocean(10, 0); + r1 = test_create_ocean(10, 1); + r2 = test_create_ocean(11, 0); + test_create_plain(10, -1); // protects from storm + + CuAssertPtrEquals(tc, NULL, storm_redirect(r, r1)); + + /* bug 3119: do not drift if there is an adjacent firewall */ + t_fire = test_create_terrain("firewall", FORBIDDEN_REGION); + r = test_create_ocean(20, 0); + r1 = test_create_ocean(20, 1); + r2 = test_create_ocean(21, 0); + test_create_region(20, -1, t_fire); + + CuAssertPtrEquals(tc, NULL, storm_redirect(r, r1)); + + // we do not ave forbidden oceans, do we? but if we did, + // they would surely not count as coasts. + t_fire = test_create_terrain("deepsea", FORBIDDEN_REGION|SEA_REGION); + r = test_create_ocean(20, 0); + r1 = test_create_ocean(20, 1); + r2 = test_create_ocean(21, 0); + test_create_region(20, -1, t_fire); + + CuAssertPtrEquals(tc, r2, storm_redirect(r, r1)); + + test_teardown(); +} + static void test_drifting_ships(CuTest *tc) { + ship *sh; + ship_type * stype; + region * r, *r2; + unit * u; + faction *f; + + test_setup(); + config_set("rules.ship.drifting", "1"); + r = test_create_ocean(0, 0); + r2 = test_create_ocean(0, 1); + stype = test_create_shiptype("fisher"); + stype->fishing = 1; + u = test_create_unit(f = test_create_faction(), r); + sh = u->ship = test_create_ship(r, stype); + CuAssertIntEquals(tc, 0, sh->flags); + drifting_ships(r); + CuAssertPtrEquals(tc, r2, sh->region); + CuAssertIntEquals(tc, SF_DRIFTED|SF_FISHING, sh->flags); + CuAssertPtrNotNull(tc, test_find_faction_message(f, "ship_drift_nocrew")); + test_teardown(); +} + +static void test_drift_reason(CuTest *tc) { ship *sh; region *r; - terrain_type *t_ocean, *t_plain; - ship_type *st_boat; + unit *u; + item_type * itype; + ship_type *stype; test_setup(); - t_ocean = test_create_terrain("ocean", SEA_REGION); - t_plain = test_create_terrain("plain", LAND_REGION); - r = test_create_region(0, 0, t_ocean); - test_create_region(1, 0, t_ocean); - st_boat = test_create_shiptype("boat"); - sh = test_create_ship(r, st_boat); + itype = test_create_silver(); + stype = test_create_shiptype("clipper"); + stype->cabins = 0; + stype->cargo = 10000; + r = test_create_ocean(0, 0); + sh = test_create_ship(r, stype); + CuAssertStrEquals(tc, "ship_drift_nocrew", drift_reason(r, sh)); + u = test_create_unit(test_create_faction(), r); + u_set_ship(u, sh); + CuAssertStrEquals(tc, "ship_drift_nocrew", drift_reason(r, sh)); + test_set_skill(u, SK_SAILING, sh->type->sumskill, 0); + CuAssertStrEquals(tc, NULL, drift_reason(r, sh)); + + scale_number(u, 4); + test_set_skill(u, SK_SAILING, MAX(sh->type->cptskill, sh->type->sumskill/3), 0); + CuAssertStrEquals(tc, NULL, drift_reason(r, sh)); + + stype->cabins = 3; + CuAssertStrEquals(tc, "ship_drift_overload", drift_reason(r, sh)); + stype->cabins = 0; + + i_change(&u->items, itype, sh->type->cargo); + CuAssertStrEquals(tc, "ship_drift_overload", drift_reason(r, sh)); +} + +static void test_drift_target(CuTest *tc) { + ship *sh; + region *r; + + test_setup(); + r = test_create_ocean(0, 0); + test_create_ocean(1, 0); + sh = test_create_ship(r, NULL); CuAssertIntEquals(tc, D_EAST, drift_target(sh)); - test_create_region(-1, 0, t_plain); + test_create_plain(-1, 0); CuAssertIntEquals(tc, D_WEST, drift_target(sh)); test_teardown(); } @@ -1476,6 +1655,7 @@ CuSuite *get_move_suite(void) SUITE_ADD_TEST(suite, test_sailing_sets_coast); SUITE_ADD_TEST(suite, test_sail_into_harbour); SUITE_ADD_TEST(suite, test_harbour_in_plain); + SUITE_ADD_TEST(suite, test_ship_takeoff_from_harbor); SUITE_ADD_TEST(suite, test_ship_not_allowed_in_coast); SUITE_ADD_TEST(suite, test_ship_leave_trail); SUITE_ADD_TEST(suite, test_ship_allowed_coast_ignores_harbor); @@ -1503,7 +1683,12 @@ CuSuite *get_move_suite(void) SUITE_ADD_TEST(suite, test_follow_bad_target); SUITE_ADD_TEST(suite, test_follow_unit_self); SUITE_ADD_TEST(suite, test_follow_ship_msg); + + SUITE_ADD_TEST(suite, test_storms); + SUITE_ADD_TEST(suite, test_storm_redirect); SUITE_ADD_TEST(suite, test_drifting_ships); + SUITE_ADD_TEST(suite, test_drift_target); + SUITE_ADD_TEST(suite, test_drift_reason); SUITE_ADD_TEST(suite, test_route_cycle); SUITE_ADD_TEST(suite, test_cycle_route); SUITE_ADD_TEST(suite, test_route_pause); diff --git a/src/names.c b/src/names.c index 3cabc3440..6410b8c9a 100644 --- a/src/names.c +++ b/src/names.c @@ -21,7 +21,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/orderfile.test.c b/src/orderfile.test.c index 3e5dd68bf..ebd32fe73 100644 --- a/src/orderfile.test.c +++ b/src/orderfile.test.c @@ -15,9 +15,10 @@ #include #include +#include + #include #include -#include #include #include diff --git a/src/prefix.c b/src/prefix.c index 88e67a4db..c89ab3044 100644 --- a/src/prefix.c +++ b/src/prefix.c @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/src/randenc.c b/src/randenc.c index 71904e39d..90f10933a 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -52,6 +52,51 @@ #include extern struct attrib_type at_unitdissolve; +void dissolve_unit(unit *u, int n, const attrib *a) +{ + message *msg; + region *r = u->region; + scale_number(u, u->number - n); + + switch (a->data.ca[0]) { + case 1: + rsetpeasants(r, rpeasants(r) + n); + msg = + msg_message("dissolve_units_1", "unit region number race", u, r, + n, u_race(u)); + break; + case 2: + if (r->land && !fval(r, RF_MALLORN)) { + rsettrees(r, 2, rtrees(r, 2) + n); + msg = + msg_message("dissolve_units_2", "unit region number race", u, r, + n, u_race(u)); + } + else { + msg = + msg_message("dissolve_units_3", "unit region number race", u, r, + n, u_race(u)); + } + break; + default: + if (u_race(u) == get_race(RC_STONEGOLEM) + || u_race(u) == get_race(RC_IRONGOLEM)) { + msg = + msg_message("dissolve_units_4", "unit region number race", u, r, + n, u_race(u)); + } + else { + msg = + msg_message("dissolve_units_5", "unit region number race", u, r, + n, u_race(u)); + } + break; + } + + add_message(&u->faction->msgs, msg); + msg_release(msg); + +} /* In a->data.ca[1] steht der Prozentsatz mit dem sich die Einheit * aufloest, in a->data.ca[0] kann angegeben werden, wohin die Personen * verschwinden. Passiert bereits in der ersten Runde! */ @@ -66,8 +111,6 @@ static void dissolve_units(void) for (u = r->units; u; u = u->next) { attrib *a = a_find(u->attribs, &at_unitdissolve); if (a) { - message *msg; - if (u->age == 0 && a->data.ca[1] < 100) continue; @@ -88,45 +131,7 @@ static void dissolve_units(void) continue; } - scale_number(u, u->number - n); - - switch (a->data.ca[0]) { - case 1: - rsetpeasants(r, rpeasants(r) + n); - msg = - msg_message("dissolve_units_1", "unit region number race", u, r, - n, u_race(u)); - break; - case 2: - if (r->land && !fval(r, RF_MALLORN)) { - rsettrees(r, 2, rtrees(r, 2) + n); - msg = - msg_message("dissolve_units_2", "unit region number race", u, r, - n, u_race(u)); - } - else { - msg = - msg_message("dissolve_units_3", "unit region number race", u, r, - n, u_race(u)); - } - break; - default: - if (u_race(u) == get_race(RC_STONEGOLEM) - || u_race(u) == get_race(RC_IRONGOLEM)) { - msg = - msg_message("dissolve_units_4", "unit region number race", u, r, - n, u_race(u)); - } - else { - msg = - msg_message("dissolve_units_5", "unit region number race", u, r, - n, u_race(u)); - } - break; - } - - add_message(&u->faction->msgs, msg); - msg_release(msg); + dissolve_unit(u, n, a); } } } diff --git a/src/randenc.h b/src/randenc.h index 14f957f6c..e43a8534e 100644 --- a/src/randenc.h +++ b/src/randenc.h @@ -1,20 +1,14 @@ -#ifndef H_GC_RANDENC -#define H_GC_RANDENC -#ifdef __cplusplus -extern "C" { -#endif +#pragma once - struct region; +struct attrib; +struct region; +struct unit; - /** Plagues **/ +/** Plagues **/ #define PLAGUE_CHANCE 0.1 /* Seuchenwahrscheinlichkeit (siehe plagues()) */ #define PLAGUE_VICTIMS 0.2 /* % Betroffene */ #define PLAGUE_HEALCHANCE 0.25 /* Wahrscheinlichkeit Heilung */ #define PLAGUE_HEALCOST 30 /* Heilkosten */ - void plagues(struct region *r); - void randomevents(void); - -#ifdef __cplusplus -} -#endif -#endif +void plagues(struct region *r); +void randomevents(void); +void dissolve_unit(struct unit *u, int n, const struct attrib *a); diff --git a/src/recruit.test.c b/src/recruit.test.c index 7c7f8fa8a..d5c18d62a 100644 --- a/src/recruit.test.c +++ b/src/recruit.test.c @@ -12,7 +12,7 @@ #include "util/keyword.h" // for K_RECRUIT #include "util/message.h" -#include "util/variant.h" +#include "util/variant.h" // IWYU pragma: keep #include "stb_ds.h" diff --git a/src/report.c b/src/report.c index 38babddb2..ee7b8d069 100644 --- a/src/report.c +++ b/src/report.c @@ -70,11 +70,12 @@ #include "util/param.h" #include -#include -#include -#include -#include -#include +#include +#include + +#include +#include +#include #include diff --git a/src/report.test.c b/src/report.test.c index ff956a625..33c069745 100644 --- a/src/report.test.c +++ b/src/report.test.c @@ -18,9 +18,10 @@ #include "util/param.h" #include -#include -#include -#include +#include +#include + +#include #include #include diff --git a/src/reports.c b/src/reports.c index 619e9dfad..ced8cf748 100644 --- a/src/reports.c +++ b/src/reports.c @@ -61,9 +61,10 @@ #include "util/password.h" #include "util/translation.h" -#include -#include -#include +#include + +#include +#include #include #include diff --git a/src/spells.c b/src/spells.c index 22778666a..c950eb605 100644 --- a/src/spells.c +++ b/src/spells.c @@ -75,9 +75,11 @@ #include #include +#include + +#include + #include -#include -#include /* libc includes */ #include @@ -4051,7 +4053,7 @@ static int sp_headache(castorder * co) int change = target->number; if (change > 10) change = 10; change *= (rng_uint() % 2 + 1) / target->number; - reduce_skill_weeks(target, smax, change); + change_skill(target, smax, -SKILL_DAYS_PER_WEEK * change); } set_order(&target->thisorder, NULL); diff --git a/src/spells.test.c b/src/spells.test.c index 4e860d9b2..7cac9a2e2 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -29,7 +29,7 @@ #include "util/language.h" #include "util/message.h" #include "util/rand.h" -#include "util/variant.h" // for variant +#include "util/variant.h" // IWYU pragma: keep #include #include @@ -44,7 +44,8 @@ #include #include -#include +#include + #include #include @@ -925,7 +926,7 @@ static void test_summonent(CuTest *tc) { u = test_create_unit(test_create_faction(), r = test_create_plain(0, 0)); test_create_castorder(&co, u, 3, 4.0, 0, NULL); - /* keine Bäume, keine Kosten */ + /* keine B�ume, keine Kosten */ rsettrees(r, 2, 0); CuAssertIntEquals(tc, 0, sp_summonent(&co)); CuAssertPtrNotNull(tc, test_find_faction_message(u->faction, "error204")); diff --git a/src/spells/borders.c b/src/spells/borders.c index 93062dbbf..c5605dd7b 100644 --- a/src/spells/borders.c +++ b/src/spells/borders.c @@ -20,7 +20,7 @@ #include "util/rng.h" #include "util/resolve.h" -#include +#include #include #include diff --git a/src/spells/charming.c b/src/spells/charming.c index 2e93f442c..02674c2b4 100644 --- a/src/spells/charming.c +++ b/src/spells/charming.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include diff --git a/src/spells/charming.test.c b/src/spells/charming.test.c index 6639cacbf..e3a339fd0 100644 --- a/src/spells/charming.test.c +++ b/src/spells/charming.test.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -32,9 +32,11 @@ static void test_charm_unit(CuTest * tc) u = test_create_unit(f = test_create_faction(), test_create_plain(0, 0)); mage = test_create_unit(test_create_faction(), u->region); u->thisorder = create_order(K_WORK, u->faction->locale, NULL); - unit_addorder(u,copy_order(u->thisorder)); + unit_addorder(u, copy_order(u->thisorder)); + usetprivate(u, "Sikrit"); CuAssertTrue(tc, !unit_is_slaved(u)); charm_unit(u, mage, 3.0, 2); + CuAssertStrEquals(tc, NULL, uprivate(u)); CuAssertPtrEquals(tc, NULL, u->orders); CuAssertPtrEquals(tc, NULL, u->thisorder); CuAssertPtrNotNull(tc, u->attribs); diff --git a/src/spells/combatspells.c b/src/spells/combatspells.c index 647ccdead..6ccb7b991 100644 --- a/src/spells/combatspells.c +++ b/src/spells/combatspells.c @@ -223,13 +223,10 @@ int sp_stun(struct castorder * co) stunned = 0; while (force-- && stunned < enemies) { troop dt = select_enemy(fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); - fighter *df = dt.fighter; - if (df) { - unit *du = df->unit; - - --force; + if (dt.fighter) { + unit *du = dt.fighter->unit; if (!is_magic_resistant(mage, du, 0)) { - df->person[dt.index].flags |= FL_STUNNED; + dt.fighter->person[dt.index].flags |= FL_STUNNED; ++stunned; } } diff --git a/src/spells/firewall.c b/src/spells/firewall.c index da621bdaa..c021e26a3 100644 --- a/src/spells/firewall.c +++ b/src/spells/firewall.c @@ -14,7 +14,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/spells/flyingship.c b/src/spells/flyingship.c index b576cee94..919187b3a 100644 --- a/src/spells/flyingship.c +++ b/src/spells/flyingship.c @@ -15,7 +15,7 @@ #include -#include +#include /* libc includes */ #include diff --git a/src/spells/regioncurse.c b/src/spells/regioncurse.c index 91cd0a713..ca0f84e85 100644 --- a/src/spells/regioncurse.c +++ b/src/spells/regioncurse.c @@ -119,7 +119,7 @@ const curse * c, int self) const struct curse_type ct_antimagiczone = { "antimagiczone", - CURSETYP_NORM, 0, (M_DURATION | M_VIGOUR), + CURSETYP_NORM, CURSE_IMMUNE, (M_DURATION | M_VIGOUR), cinfo_antimagiczone, NULL, NULL, NULL, cansee_antimagiczone }; diff --git a/src/spells/regioncurse.h b/src/spells/regioncurse.h index acda153c2..8705f413f 100644 --- a/src/spells/regioncurse.h +++ b/src/spells/regioncurse.h @@ -18,6 +18,7 @@ extern const struct curse_type ct_maelstrom; extern const struct curse_type ct_riotzone; extern const struct curse_type ct_generous; extern const struct curse_type ct_badlearn; +extern const struct curse_type ct_antimagiczone; #ifdef ENABLE_FOGTRAP_CURSE extern const struct curse_type ct_fogtrap; #endif diff --git a/src/spells/shipcurse.c b/src/spells/shipcurse.c index 15863cb71..0905b82cd 100644 --- a/src/spells/shipcurse.c +++ b/src/spells/shipcurse.c @@ -15,7 +15,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/spells/unitcurse.c b/src/spells/unitcurse.c index ed70c770b..61543aa84 100644 --- a/src/spells/unitcurse.c +++ b/src/spells/unitcurse.c @@ -19,7 +19,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/spy.c b/src/spy.c index 2aa06adc0..93d2124e2 100644 --- a/src/spy.c +++ b/src/spy.c @@ -33,7 +33,7 @@ #include #include -#include +#include #include diff --git a/src/steal.test.c b/src/steal.test.c index c0b7d01ef..1b4234a39 100644 --- a/src/steal.test.c +++ b/src/steal.test.c @@ -9,10 +9,10 @@ #include "kernel/skill.h" // for SK_STEALTH, SK_PERCEPTION #include -#include -#include -#include -#include +#include "util/base36.h" +#include "util/keyword.h" +#include "util/message.h" +#include "util/variant.h" // IWYU pragma: keep #include diff --git a/src/study.c b/src/study.c index 6560e7add..8e768aa85 100644 --- a/src/study.c +++ b/src/study.c @@ -39,7 +39,7 @@ #include #include -#include +#include #include @@ -164,21 +164,6 @@ static int produceexp_days(void) { return rule; } -static int study_days(unit * u, skill_t sk) -{ - int speed = SKILL_DAYS_PER_WEEK; - if (u_race(u)->study_speed) { - speed += u_race(u)->study_speed[sk]; - if (speed < SKILL_DAYS_PER_WEEK) { - skill *sv = unit_skill(u, sk); - if (sv == NULL) { - speed = SKILL_DAYS_PER_WEEK; - } - } - } - return u->number * speed; -} - static int teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, bool report, int *academy_students) @@ -215,7 +200,7 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, const struct building_type *btype = bt_find("academy"); if (active_building(student, btype)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ - teach->days += students * produceexp_days(); /* learning erhoehen */ + teach->days += students * SKILL_DAYS_PER_WEEK/3; /* learning erhoehen */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */ if (academy_students) { *academy_students += students; @@ -429,7 +414,8 @@ int teach_cmd(unit * teacher, struct order *ord) free_order(new_order); /* parse_order & set_order have each increased the refcount */ } if (academy_students > 0 && sk_academy != NOSKILL) { - change_skill_days(teacher, sk_academy, academy_students); + // TODO: rounding errors here. + change_skill_days(teacher, sk_academy, academy_students / teacher->number); } reset_order(); return 0; @@ -636,7 +622,7 @@ int study_cmd(unit * u, order * ord) } days = teach ? teach->days : 0; - days += study_days(u, sk); + days += SKILL_DAYS_PER_WEEK * u->number; if (studycost) { int cost = studycost * u->number; @@ -687,64 +673,18 @@ bool can_teach(const unit* u) return !(fval(u, UFL_WERE) || fval(u_race(u), RCF_NOTEACH)); } -static learn_fun inject_learn_fun = 0; - -void inject_learn(learn_fun fun) { - inject_learn_fun = fun; -} - -static void increase_skill_days(unit *u, skill_t sk, int days) { - assert(sk >= 0 && sk < MAXSKILLS && days >= 0); - if (days > 0) { - int leveldays = SKILL_DAYS_PER_WEEK * u->number; - int weeks = 0; - if (inject_learn_fun) { - inject_learn_fun(u, sk, days); - } - while (days >= leveldays) { - ++weeks; - days -= leveldays; - } - if (days > 0 && rng_int() % leveldays >= leveldays - days) { - ++weeks; - } - if (weeks > 0) { - increase_skill_weeks(u, sk, weeks); - } - } -} - void produceexp(struct unit *u, enum skill_t sk) { assert(u); if (u->number > 0) { const struct race *rc = u_race(u); if ((rc->flags & RCF_NOLEARN) == 0 && rc_can_learn(rc, sk)) { - increase_skill_days(u, sk, produceexp_days() * u->number); - } - } -} - -static void reduce_skill_days(unit *u, skill_t sk, int days) { - if (days > 0) { - skill *sv = unit_skill(u, sk); - if (sv) { - while (days > 0) { - if (days >= SKILL_DAYS_PER_WEEK * u->number) { - reduce_skill_weeks(u, sv, 1); - days -= SKILL_DAYS_PER_WEEK; - } - else { - if (chance(days / ((double)SKILL_DAYS_PER_WEEK * u->number))) /* (rng_int() % (30 * u->number) < days)*/ - reduce_skill_weeks(u, sv, 1); - days = 0; - } - } + change_skill_days(u, sk, produceexp_days()); } } } -/** +/** * days should be scaled by u->number; SKILL_DAYS_PER_WEEK * u->number is one week worth of learning * @return int * The additional spend, i.e. from an academy. @@ -774,7 +714,7 @@ int learn_skill(unit *u, enum skill_t sk, int days, int studycost) { n = n * avail / cost; cost = n * studycost; } - days += produceexp_days() * n; + days += SKILL_DAYS_PER_WEEK * n / 3; } /* the artacademy currently improves the learning of entertainment @@ -809,19 +749,10 @@ int learn_skill(unit *u, enum skill_t sk, int days, int studycost) { if (fval(u, UFL_HUNGER)) { days /= 2; } - change_skill_days(u, sk, days); + change_skill_days(u, sk, days / u->number); return cost; } -void change_skill_days(unit *u, enum skill_t sk, int days) { - if (days < 0) { - reduce_skill_days(u, sk, -days); - } - else { - increase_skill_days(u, sk, days); - } -} - /** * Talente von Daemonen verschieben sich. */ @@ -864,14 +795,14 @@ void demon_skillchange(unit *u) } if (roll < downchance) { - reduce_skill_weeks(u, sv, weeks); + reduce_skill(u, sv, SKILL_DAYS_PER_WEEK * weeks); if (sv->level < 1) { /* demons should never forget below 1 */ set_level(u, sv->id, 1); } } else { - change_skill_days(u, sv->id, SKILL_DAYS_PER_WEEK * u->number * weeks); + change_skill_days(u, sv->id, SKILL_DAYS_PER_WEEK * weeks); } } ++sv; diff --git a/src/study.h b/src/study.h index e0ff4d93a..06619e320 100644 --- a/src/study.h +++ b/src/study.h @@ -36,13 +36,9 @@ bool check_student(const struct unit *u, struct order *ord, int learn_skill(struct unit *u, enum skill_t sk, int days, int studycost); -void change_skill_days(struct unit *u, enum skill_t sk, int days); void produceexp(struct unit *u, enum skill_t sk); void demon_skillchange(struct unit *u); -typedef void(*learn_fun)(struct unit *u, enum skill_t sk, int days); -void inject_learn(learn_fun fun); - #endif diff --git a/src/study.test.c b/src/study.test.c index 3d06bb248..ac3843dcc 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -33,34 +33,6 @@ struct locale; -#define MAXLOG 4 -typedef struct log_entry { - unit *u; - skill_t sk; - int days; -} log_entry; - -static log_entry log_learners[MAXLOG]; -static int log_size; - -static void log_learn(unit *u, skill_t sk, int days) { - if (log_size < MAXLOG) { - log_entry * entry = &log_learners[log_size++]; - entry->u = u; - entry->sk = sk; - entry->days = days; - } -} - -void learn_inject(void) { - log_size = 0; - inject_learn(log_learn); -} - -void learn_reset(void) { - inject_learn(0); -} - typedef struct { unit *u; unit *teachers[2]; @@ -68,6 +40,7 @@ typedef struct { static void setup_study(void) { test_setup(); + config_set("study.random_progress", "0"); mt_create_error(77); mt_create_error(771); mt_create_error(178); @@ -99,7 +72,6 @@ static void setup_teacher(study_fixture *fix, skill_t sk) { assert(fix); setup_study(); - config_set("study.random_progress", "0"); r = test_create_plain(0, 0); f = test_create_faction(); f->locale = lang = test_create_locale(); @@ -185,42 +157,73 @@ static void test_study_speed(CuTest *tc) { race *rc; skill *sv; - test_setup(); - learn_inject(); + setup_study(); rc = test_create_race("orc"); - u = test_create_unit(test_create_faction_ex(rc, NULL), test_create_plain(0, 0)); - set_level(u, SK_BUILDING, 1); - set_level(u, SK_CATAPULT, 1); set_study_speed(rc, SK_BUILDING, -5); - sv = unit_skill(u, SK_BUILDING); - sv->days = 1 * SKILL_DAYS_PER_WEEK; CuAssertIntEquals(tc, -5, rc->study_speed[SK_BUILDING]); - u->thisorder = create_order(K_STUDY, u->faction->locale, skillnames[SK_BUILDING]); - random_source_inject_constants(0.0, 0); - study_cmd(u, u->thisorder); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_BUILDING, log_learners[0].sk); - CuAssertIntEquals(tc, 25, log_learners[0].days); + u = test_create_unit(test_create_faction_ex(rc, NULL), test_create_plain(0, 0)); + + sv = add_skill(u, SK_CATAPULT); + set_level(u, SK_CATAPULT, 3); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK, sv->days); + + sv = add_skill(u, SK_BUILDING); + set_level(u, SK_BUILDING, 1); CuAssertIntEquals(tc, 1, sv->level); - CuAssertIntEquals(tc, 1 * SKILL_DAYS_PER_WEEK, sv->days); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); + set_level(u, SK_BUILDING, 3); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 4 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); - random_source_inject_constants(0.0, 5); - u->flags &= ~UFL_LONGACTION; + // learning at 30 days/week, no study_cost effect here: + u->thisorder = create_order(K_STUDY, u->faction->locale, skillnames[SK_BUILDING]); study_cmd(u, u->thisorder); - CuAssertPtrEquals(tc, u, log_learners[1].u); - CuAssertIntEquals(tc, SK_BUILDING, log_learners[1].sk); - CuAssertIntEquals(tc, 25, log_learners[1].days); - CuAssertIntEquals(tc, 2, sv->level); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); + // learning at 30 days/week, no study_cost effect here: free_order(u->thisorder); u->thisorder = create_order(K_STUDY, u->faction->locale, skillnames[SK_CATAPULT]); u->flags &= ~UFL_LONGACTION; study_cmd(u, u->thisorder); - CuAssertPtrEquals(tc, u, log_learners[2].u); - CuAssertIntEquals(tc, SK_CATAPULT, log_learners[2].sk); - CuAssertIntEquals(tc, 30, log_learners[2].days); + CuAssertIntEquals(tc, 3, sv->level); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK + sv->level * 5, sv->days); + + test_teardown(); +} + +static void test_study_cmd_with_speed(CuTest *tc) { + unit *u; + race *rc; + skill *sv; + + setup_study(); + rc = test_create_race("orc"); + set_study_speed(rc, SK_BUILDING, -SKILL_DAYS_PER_WEEK / 2); + CuAssertIntEquals(tc, -15, rc->study_speed[SK_BUILDING]); + u = test_create_unit(test_create_faction_ex(rc, NULL), test_create_plain(0, 0)); + + // learning at 30 days/week, no study_cost effect here: + u->thisorder = create_order(K_STUDY, u->faction->locale, skillnames[SK_BUILDING]); + study_cmd(u, u->thisorder); + CuAssertPtrNotNull(tc, sv = u->skills); + CuAssertIntEquals(tc, SK_BUILDING, sv->id); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 5 * SKILL_DAYS_PER_WEEK / 2, sv->days); + + study_cmd(u, u->thisorder); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK / 2, sv->days); + + study_cmd(u, u->thisorder); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK / 2, sv->days); + + study_cmd(u, u->thisorder); + CuAssertIntEquals(tc, 2, sv->level); + CuAssertIntEquals(tc, 7 * SKILL_DAYS_PER_WEEK / 2, sv->days); - learn_reset(); test_teardown(); } @@ -254,6 +257,7 @@ static void test_study_bug_2194(CuTest *tc) { unit *u, *u1, *u2; struct locale * loc; building * b; + skill *sv; setup_study(); random_source_inject_constant(0.0); @@ -278,20 +282,16 @@ static void test_study_bug_2194(CuTest *tc) { u_set_building(u2, b); i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); - learn_inject(); teach_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, 1, log_size); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "teach_asgood")); + CuAssertPtrEquals(tc, NULL, unit_skill(u, SK_MAGIC)); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 3 - (u1->number / u->number), sv->days); free_order(u->thisorder); u->thisorder = create_order(K_TEACH, loc, itoa36(u2->no)); - learn_inject(); teach_cmd(u, u->thisorder); - learn_reset(); - CuAssertIntEquals(tc, 0, log_size); + CuAssertPtrEquals(tc, NULL, unit_skill(u, SK_MAGIC)); test_teardown(); } @@ -300,13 +300,17 @@ static void test_academy_building(CuTest *tc) { struct locale * loc; building * b; message * msg; + skill *sv; + const attrib *a; + const teaching_info *ti; + const struct item_type *it_silver; setup_study(); mt_create_va(mt_new("teach_asgood", NULL), "unit:unit", "region:region", "command:order", "student:unit", MT_NEW_END); - random_source_inject_constant(0.0); init_resources(); + it_silver = test_create_silver(); loc = test_create_locale(); setup_locale(loc); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); @@ -326,18 +330,42 @@ static void test_academy_building(CuTest *tc) { u_set_building(u, b); u_set_building(u1, b); u_set_building(u2, b); - i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); - i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); - learn_inject(); + i_change(&u1->items, it_silver, 50 * u1->number + 10); + i_change(&u2->items, it_silver, 50 * u2->number + 10); + + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK, sv ? sv->days : 0); teach_cmd(u, u->thisorder); - learn_reset(); + // cannot teach u2, because skill too high: CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "teach_asgood")); CuAssertPtrEquals(tc, u, (unit *)msg->parameters[0].v); CuAssertPtrEquals(tc, u2, (unit *)msg->parameters[3].v); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, u1->number, log_learners[0].days); + // teacher teaches 15 students, gets 15/2 XP: + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK - (u1->number / u->number), sv->days); + + // u1 gets taught, gets teacher-in-academy bonus: + CuAssertPtrNotNull(tc, a = a_find(u1->attribs, &at_learning)); + ti = (const teaching_info *)a->data.v; + CuAssertIntEquals(tc, u1->number, ti->students); + CuAssertIntEquals(tc, u1->number * 20 * 2, ti->days); + + // u1 is learning with a teacher, gains 60*4/3 = 80 XP: + study_cmd(u1, u1->thisorder); + CuAssertIntEquals(tc, 10, i_get(u1->items, it_silver)); + CuAssertPtrNotNull(tc, sv = unit_skill(u1, SK_CROSSBOW)); + // uses 30 to reach L1, 50 towards L2 (10 remain): + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 10, sv->days); + + // u2 is learning without a teacher, gets to level 1, +10 XP for academy: + study_cmd(u2, u2->thisorder); + CuAssertIntEquals(tc, 10, i_get(u2->items, it_silver)); + CuAssertPtrNotNull(tc, sv = unit_skill(u2, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 20, sv->days); + test_teardown(); } @@ -350,10 +378,9 @@ static void test_academy_bonus(CuTest *tc) { unit *u, *u0, *u1, *u3; struct locale * loc; building * b; + skill *sv; setup_study(); - - random_source_inject_constant(0.0); init_resources(); loc = test_create_locale(); setup_locale(loc); @@ -380,26 +407,35 @@ static void test_academy_bonus(CuTest *tc) { scale_number(u, 2); scale_number(u1, 9); - scale_number(u3, 2); + scale_number(u3, 3); i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 5000); - learn_inject(); + // u0 teaches 10 people, gains +10 XP + sv = unit_skill(u0, SK_CROSSBOW); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK, sv->days); teach_cmd(u0, u0->thisorder); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK - SKILL_DAYS_PER_WEEK / 3, sv->days); + + // u teaches remaining 2 person, gains +1 XP + sv = unit_skill(u, SK_CROSSBOW); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK, sv->days); teach_cmd(u, u->thisorder); + CuAssertIntEquals(tc, TEACHDIFFERENCE, sv->level); + CuAssertIntEquals(tc, (sv->level + 1) * SKILL_DAYS_PER_WEEK - SKILL_DAYS_PER_WEEK / 30, sv->days); + + // u1 lernt in einer Akademie (+40 XP), mit Lehrer (+20): + sv = add_skill(u1, SK_CROSSBOW); study_cmd(u1, u1->thisorder); - study_cmd(u3, u3->thisorder); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK / 3, sv->days); - CuAssertIntEquals(tc, 4, log_size); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertPtrEquals(tc, u0, log_learners[0].u); - CuAssertIntEquals(tc, 10, log_learners[0].days); - CuAssertPtrEquals(tc, u, log_learners[1].u); - CuAssertIntEquals(tc, 1, log_learners[1].days); - CuAssertPtrEquals(tc, u1, log_learners[2].u); - CuAssertIntEquals(tc, 720, log_learners[2].days); - CuAssertPtrEquals(tc, u3, log_learners[3].u); - CuAssertIntEquals(tc, 160, log_learners[3].days); - learn_reset(); + sv = add_skill(u3, SK_CROSSBOW); + study_cmd(u3, u3->thisorder); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK / 3, sv->days); test_teardown(); } @@ -408,7 +444,6 @@ void test_learn_skill_single(CuTest *tc) { skill *sv; setup_study(); - config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, SKILL_DAYS_PER_WEEK, 0)); CuAssertPtrNotNull(tc, sv = u->skills); @@ -428,7 +463,6 @@ void test_learn_skill_multi(CuTest *tc) { skill *sv; setup_study(); - config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); scale_number(u, 10); CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, SKILL_DAYS_PER_WEEK * u->number, 0)); @@ -530,19 +564,33 @@ static void test_demon_skillchange_hungry(CuTest *tc) { test_teardown(); } +static void test_produceexp(CuTest *tc) { + unit *u; + skill *sv; + + test_setup(); + + u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); + test_set_skill(u, SK_CROSSBOW, 1, 1); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + produceexp(u, SK_CROSSBOW); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 / 3, sv->days); + test_teardown(); +} + static void test_study_cmd(CuTest *tc) { unit *u; + skill *sv; setup_study(); init_resources(); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); u->thisorder = create_order(K_STUDY, u->faction->locale, "CROSSBOW"); - learn_inject(); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, log_learners[0].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } @@ -606,6 +654,7 @@ static void test_study_cost_magic(CuTest *tc) { static void test_study_cost(CuTest *tc) { unit *u; const struct item_type *itype; + skill *sv; setup_study(); @@ -619,13 +668,11 @@ static void test_study_cost(CuTest *tc) { CuAssertIntEquals(tc, 50, study_cost(u, SK_ALCHEMY)); i_change(&u->items, itype, u->number * study_cost(u, SK_ALCHEMY)); - learn_inject(); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_ALCHEMY, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * u->number, log_learners[0].days); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_ALCHEMY)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } @@ -633,6 +680,7 @@ static void test_teach_magic(CuTest *tc) { unit *u, *ut; faction *f; const struct item_type *itype; + skill *sv; setup_study(); init_resources(); @@ -646,20 +694,18 @@ static void test_teach_magic(CuTest *tc) { set_level(ut, SK_MAGIC, TEACHDIFFERENCE); create_mage(ut, M_GWYRRD); ut->thisorder = create_order(K_TEACH, f->locale, itoa36(u->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_MAGIC, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2, log_learners[0].days); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_MAGIC)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } static void test_teach_cmd(CuTest *tc) { unit *u, *ut; - + setup_study(); init_resources(); u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); @@ -668,13 +714,11 @@ static void test_teach_cmd(CuTest *tc) { ut = test_create_unit(u->faction, u->region); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); - learn_inject(); + teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u->number, log_learners[0].days); + CuAssertIntEquals(tc, 1, skill_level(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, skill_days(u, SK_CROSSBOW)); test_teardown(); } @@ -700,7 +744,8 @@ static void test_teach_not_found(CuTest *tc) { static void test_teach_two(CuTest *tc) { unit *u1, *u2, *ut; - + skill *sv; + setup_study(); init_resources(); u1 = test_create_unit(test_create_faction(), test_create_plain(0, 0)); @@ -712,17 +757,17 @@ static void test_teach_two(CuTest *tc) { ut = test_create_unit(u1->faction, u1->region); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, ut->faction->locale, "%s %s", itoa36(u1->no), itoa36(u2->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u1, u1->thisorder); study_cmd(u2, u2->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u1, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u1->number, log_learners[0].days); - CuAssertPtrEquals(tc, u2, log_learners[1].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[1].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u2->number, log_learners[1].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u1, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); + + CuAssertPtrNotNull(tc, sv = unit_skill(u2, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); + test_teardown(); } @@ -730,6 +775,7 @@ static void test_teach_two_skills(CuTest *tc) { unit *u1, *u2, *ut; faction *f; region *r; + skill *sv; setup_study(); init_resources(); @@ -745,22 +791,22 @@ static void test_teach_two_skills(CuTest *tc) { set_level(ut, SK_ENTERTAINMENT, TEACHDIFFERENCE); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, f->locale, "%s %s", itoa36(u1->no), itoa36(u2->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u1, u1->thisorder); study_cmd(u2, u2->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u1, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u1->number, log_learners[0].days); - CuAssertPtrEquals(tc, u2, log_learners[1].u); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, log_learners[1].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 2 * u2->number, log_learners[1].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u1, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); + + CuAssertPtrNotNull(tc, sv = unit_skill(u2, SK_ENTERTAINMENT)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } static void test_teach_one_to_many(CuTest *tc) { unit *u, *ut; + const skill *sv; setup_study(); init_resources(); @@ -770,18 +816,17 @@ static void test_teach_one_to_many(CuTest *tc) { ut = test_create_unit(u->faction, u->region); set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); ut->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); - learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK * 10 + SKILL_DAYS_PER_WEEK * u->number, log_learners[0].days); + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 3 * SKILL_DAYS_PER_WEEK / 2, sv->days); test_teardown(); } static void test_teach_many_to_one(CuTest *tc) { unit *u, *u1, *u2; + const skill *sv; setup_study(); init_resources(); @@ -794,14 +839,14 @@ static void test_teach_many_to_one(CuTest *tc) { u2 = test_create_unit(u->faction, u->region); set_level(u2, SK_CROSSBOW, TEACHDIFFERENCE); u2->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); - learn_inject(); + teach_cmd(u1, u1->thisorder); teach_cmd(u2, u2->thisorder); study_cmd(u, u->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, u, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK * u->number, log_learners[0].days); + + CuAssertPtrNotNull(tc, sv = unit_skill(u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, sv->days); test_teardown(); } @@ -867,24 +912,24 @@ static void test_teach_many_to_many(CuTest *tc) { scale_number(t2, 2); set_level(t2, SK_CROSSBOW, TEACHDIFFERENCE); t2->thisorder = create_order(K_TEACH, f->locale, "%s %s", itoa36(s1->no), itoa36(s2->no)); - learn_inject(); + teach_cmd(t1, t1->thisorder); teach_cmd(t2, t2->thisorder); study_cmd(s1, s1->thisorder); study_cmd(s2, s2->thisorder); - learn_reset(); - CuAssertPtrEquals(tc, s1, log_learners[0].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK * s1->number, log_learners[0].days); - CuAssertPtrEquals(tc, s2, log_learners[1].u); - CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[1].sk); - CuAssertIntEquals(tc, 2 * SKILL_DAYS_PER_WEEK * s2->number, log_learners[1].days); + + CuAssertIntEquals(tc, 1, skill_level(s1, SK_CROSSBOW)); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, skill_days(s1, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, skill_level(s2, SK_CROSSBOW)); + CuAssertIntEquals(tc, SKILL_DAYS_PER_WEEK, skill_days(s2, SK_CROSSBOW)); + test_teardown(); } CuSuite *get_study_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_produceexp); SUITE_ADD_TEST(suite, test_study_cmd); SUITE_ADD_TEST(suite, test_study_cost); SUITE_ADD_TEST(suite, test_study_cost_magic); @@ -905,6 +950,7 @@ CuSuite *get_study_suite(void) SUITE_ADD_TEST(suite, test_study_with_bad_teacher); SUITE_ADD_TEST(suite, test_study_race_noteach); SUITE_ADD_TEST(suite, test_study_speed); + SUITE_ADD_TEST(suite, test_study_cmd_with_speed); SUITE_ADD_TEST(suite, test_academy_building); SUITE_ADD_TEST(suite, test_academy_bonus); SUITE_ADD_TEST(suite, test_demon_skillchange); diff --git a/src/summary.c b/src/summary.c index a98f74a5e..8db2b03b7 100644 --- a/src/summary.c +++ b/src/summary.c @@ -86,7 +86,7 @@ int update_nmrs(int since) if (faction_age(f)<=1) { ++newplayers; } - else if (!fval(f, FFL_NOIDLEOUT | FFL_CURSED)) { + else if (!fval(f, FFL_NOIDLEOUT | FFL_CURSED | FFL_PAUSED)) { int nmr = since - f->lastorders; if (timeout>0) { if (nmr < 0 || nmr > timeout) { diff --git a/src/test_eressea.c b/src/test_eressea.c index 73402a77a..22d8c9c10 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -4,7 +4,7 @@ #include "util/stats.h" #include "util/password.h" -#include +#include #include @@ -108,6 +108,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(equipment); ADD_SUITE(familiar); ADD_SUITE(item); + ADD_SUITE(items); ADD_SUITE(magic); ADD_SUITE(magicresistance); ADD_SUITE(regioncurse); diff --git a/src/tests.c b/src/tests.c index 87be6ba4d..03737e38d 100644 --- a/src/tests.c +++ b/src/tests.c @@ -42,7 +42,7 @@ #include "util/stats.h" #include "util/variant.h" // for variant, VAR_VOIDPTR, VAR_INT -#include +#include #include #include @@ -271,6 +271,7 @@ void test_reset(void) if (month_season == test_months) { month_season = NULL; } + random_source_reset(); free_gamedata(); } diff --git a/src/tests.h b/src/tests.h index bcb3d097e..1b875805b 100644 --- a/src/tests.h +++ b/src/tests.h @@ -3,8 +3,8 @@ #define ASSERT_DBL_DELTA 0.001 -enum param_t; -enum skill_t; +#include "kernel/skill.h" // for skill_t +#include "util/param.h" // for param_t struct region; struct unit; diff --git a/src/triggers/changerace.c b/src/triggers/changerace.c index 03e122fef..abee22c48 100644 --- a/src/triggers/changerace.c +++ b/src/triggers/changerace.c @@ -15,7 +15,7 @@ #include #include -#include +#include /* ansi includes */ #include diff --git a/src/triggers/clonedied.c b/src/triggers/clonedied.c index f28c03db9..5a6bd1e36 100644 --- a/src/triggers/clonedied.c +++ b/src/triggers/clonedied.c @@ -15,7 +15,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/triggers/createcurse.c b/src/triggers/createcurse.c index 4a41ff94c..3dc161e2d 100644 --- a/src/triggers/createcurse.c +++ b/src/triggers/createcurse.c @@ -13,7 +13,7 @@ #include #include -#include +#include /* ansi includes */ #include diff --git a/src/triggers/createunit.c b/src/triggers/createunit.c index ba72ba83b..64fdae449 100644 --- a/src/triggers/createunit.c +++ b/src/triggers/createunit.c @@ -15,7 +15,7 @@ #include #include -#include +#include /* ansi includes */ #include diff --git a/src/triggers/gate.c b/src/triggers/gate.c index fa1b07800..29055051c 100644 --- a/src/triggers/gate.c +++ b/src/triggers/gate.c @@ -13,7 +13,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/triggers/giveitem.c b/src/triggers/giveitem.c index a0173c636..062890b57 100644 --- a/src/triggers/giveitem.c +++ b/src/triggers/giveitem.c @@ -13,7 +13,7 @@ #include #include -#include +#include /* ansi includes */ #include diff --git a/src/triggers/killunit.c b/src/triggers/killunit.c index 262110ee3..63a5600fe 100644 --- a/src/triggers/killunit.c +++ b/src/triggers/killunit.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/src/triggers/shock.c b/src/triggers/shock.c index 3af2122b7..460886a1c 100644 --- a/src/triggers/shock.c +++ b/src/triggers/shock.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include /* libc includes */ @@ -64,7 +64,7 @@ static void do_shock(unit * u, const char *reason) skill* sv = u->skills + s; int weeks = (sv->level * sv->level - sv->level) / 2; int change = (weeks + 9) / 10; - reduce_skill_weeks(u, sv, change); + change_skill(u, sv, -SKILL_DAYS_PER_WEEK * change); } } /* Dies ist ein Hack, um das skillmod und familiar-Attribut beim Mage diff --git a/src/triggers/timeout.c b/src/triggers/timeout.c index 3c5273916..163f37ae1 100644 --- a/src/triggers/timeout.c +++ b/src/triggers/timeout.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/src/triggers/triggers.c b/src/triggers/triggers.c index 7e972c704..c193f60b0 100644 --- a/src/triggers/triggers.c +++ b/src/triggers/triggers.c @@ -16,7 +16,7 @@ #include #include -#include +#include /* libc includes */ #include diff --git a/src/util/aliases.c b/src/util/aliases.c index bec4dabcd..331afbd4e 100644 --- a/src/util/aliases.c +++ b/src/util/aliases.c @@ -1,6 +1,6 @@ #include "aliases.h" -#include +#include #include #include diff --git a/src/util/macros.h b/src/util/macros.h index 76f1826e0..eb32a98bc 100644 --- a/src/util/macros.h +++ b/src/util/macros.h @@ -1 +1,7 @@ +#pragma once + #define UNUSED_ARG(x) (void)(x) + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + diff --git a/src/util/message.test.c b/src/util/message.test.c index ff06f2ab2..72a1ffdae 100644 --- a/src/util/message.test.c +++ b/src/util/message.test.c @@ -1,5 +1,5 @@ #include "message.h" - +#include "variant.h" // IWYU pragma: keep #include #include diff --git a/src/util/param.c b/src/util/param.c index 245832a48..04304f365 100644 --- a/src/util/param.c +++ b/src/util/param.c @@ -8,7 +8,7 @@ #include "umlaut.h" #include -#include +#include #include #include diff --git a/src/util/rand.c b/src/util/rand.c index cac84bc2a..8a70566fe 100644 --- a/src/util/rand.c +++ b/src/util/rand.c @@ -13,7 +13,7 @@ int lovar(double xpct_x2) int n = (int)(xpct_x2 * 500) + 1; if (n == 0) return 0; - return (rng_int() % n + rng_int() % n) / 1000; + return (rng_uint() % n + rng_uint() % n) / 1000; } /* gaussian distribution diff --git a/src/vortex.c b/src/vortex.c index 385cddae3..5d9fc832d 100644 --- a/src/vortex.c +++ b/src/vortex.c @@ -10,8 +10,8 @@ #include "util/umlaut.h" #include "util/variant.h" -#include -#include +#include +#include #include #include diff --git a/src/wormhole.c b/src/wormhole.c index c266430d8..36ae0d4a4 100644 --- a/src/wormhole.c +++ b/src/wormhole.c @@ -19,8 +19,9 @@ #include #include -#include -#include +#include + +#include /* libc includes */ #include diff --git a/tests/data/184.dat b/tests/data/184.dat index a1ee8802a..6217e7a3d 100644 Binary files a/tests/data/184.dat and b/tests/data/184.dat differ diff --git a/tests/run-turn.sh b/tests/run-turn.sh index 082eaf309..45edb8ad5 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -1,4 +1,4 @@ -NEWFILES="data/185.dat datum parteien parteien.full passwd htpasswd score turn" +NEWFILES="data/185.dat datum parteien parteien.full passwd htpasswd scores turn" cleanup () { rm -rf reports $NEWFILES diff --git a/tests/test-turn.lua b/tests/test-turn.lua index 826ffc00f..d394c58f6 100644 --- a/tests/test-turn.lua +++ b/tests/test-turn.lua @@ -1,6 +1,6 @@ dofile('../scripts/run-turn.lua') turn = get_turn() eressea.free_game() -print("trying to read data from " .. turn) +print("trying to re-read data from " .. turn) eressea.read_game(turn .. ".dat") eressea.free_game()