forked from github/server
Merge branch 'master' of github.com:eressea/server
This commit is contained in:
commit
f4f9e74e90
91 changed files with 2758 additions and 2532 deletions
.travis.yml
conf
res/core
s
scripts
src
CMakeLists.txtbattle.cbind_eressea.cbind_faction.cbuild.hdirection.cdirection.heconomy.ceressea.cgmtool.citems.cjson.c
kernel
build.cconfig.cconfig.hcurse.ccurse.hfaction.cfaction.hfaction.test.citem.citem.hjsonconf.cmessages.cplane.cplane.hrace.hregion.csave.cunit.cunit.hunit.test.cxmlreader.c
keyword.test.claws.claws.hlaws.test.cmodules
monsters.cmove.cmove.hmove.test.creport.csettings.hspells.cspy.cstudy.ctest_eressea.ctests.ctests.htriggers
upkeep.cupkeep.hupkeep.test.cutil
vortex.cvortex.hvortex.test.cwormhole.cwormhole.hwormhole.test.c
8
.travis.yml
Normal file
8
.travis.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
script: s/travis-build
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libtolua-dev liblua5.1-dev libncurses5-dev libsqlite3-dev libxml2-dev
|
|
@ -93,9 +93,16 @@
|
|||
<param name="GiveRestriction" value="3"/>
|
||||
<param name="hunger.long" value="1"/>
|
||||
<param name="init_spells" value="0"/>
|
||||
<param name="rules.reserve.twophase" value="1"/>
|
||||
<param name="rules.check_overload" value="0"/>
|
||||
<param name="rules.limit.faction" value="2500"/>
|
||||
<param name="rules.maxskills.magic" value="5"/>
|
||||
<param name="rules.guard.base_stop_prob" value="0.30"/>
|
||||
<param name="rules.guard.skill_stop_prob" value="0.05"/>
|
||||
<param name="rules.guard.amulet_stop_prob" value="0.10"/>
|
||||
<param name="rules.guard.guard_number_stop_prob" value="0.001"/>
|
||||
<param name="rules.guard.castle_stop_prob" value="0.05"/>
|
||||
<param name="rules.guard.region_type_stop_prob" value="0.05"/>
|
||||
<param name="game.id" value="2"/>
|
||||
<param name="game.name" value="Eressea"/>
|
||||
</game>
|
||||
|
@ -114,5 +121,9 @@
|
|||
<text locale="de">ERESSEA 2 BEFEHLE</text>
|
||||
<text locale="en">ERESSEA 2 ORDERS</text>
|
||||
</string>
|
||||
<string name="defaultorder">
|
||||
<text locale="de">ARBEITEN</text>
|
||||
<text locale="en">WORK</text>
|
||||
</string>
|
||||
</strings>
|
||||
</eressea>
|
||||
|
|
|
@ -114,9 +114,10 @@
|
|||
<param name="init_spells" value="0"/>
|
||||
<param name="recruit.allow_merge" value="1"/>
|
||||
<param name="study.expensivemigrants" value="1"/>
|
||||
<param name="study.speedup" value="0"/>
|
||||
<param name="study.speedup" value="2"/>
|
||||
<param name="world.era" value="3"/>
|
||||
<param name="rules.migrants" value="0"/>
|
||||
<param name="rules.reserve.twophase" value="1"/>
|
||||
<param name="rules.monsters.attack_chance" value="0.0"/>
|
||||
<param name="rules.transfermen" value="0"/>
|
||||
<param name="rules.stealth.faction" value="1"/>
|
||||
|
@ -128,6 +129,7 @@
|
|||
<param name="rules.combat.herospeed" value="3"/>
|
||||
<param name="rules.combat.demon_vampire" value="5"/> <!-- regen 1 hp per X points of damage done -->
|
||||
<param name="rules.combat.skill_bonus" value="0"/>
|
||||
<param name="rules.combat.nat_armor" value="1"/>
|
||||
<!--param name="rules.combat.loot" value="5"/--> <!-- only self + others - keeploot -->
|
||||
<param name="rules.items.loot_divisor" value="2"/> <!-- damage skims off 1/2 of goods transfers -->
|
||||
<param name="rules.items.give_divisor" value="2"/> <!-- corruption skims off 1/2 of goods transfers -->
|
||||
|
@ -148,6 +150,7 @@
|
|||
<param name="rules.economy.wages" value="1"/>
|
||||
<param name="rules.economy.roqf" value="5"/>
|
||||
<param name="rules.economy.herbrot" value="0"/>
|
||||
<param name="rules.region_owner_pay_building" value="market harbour lighthouse"/>
|
||||
<param name="rules.dwarf_castles" value="1"/>
|
||||
<!-- param name="rules.nmr.destroy" value="1"/ -->
|
||||
<param name="rules.limit.faction" value="250"/>
|
||||
|
@ -171,13 +174,17 @@
|
|||
</string>
|
||||
<string name="newbie_info_1">
|
||||
<text locale="de">Bitte denke daran, deine Befehle mit dem Betreff
|
||||
ERESSEA 3 BEFEHLE an eressea-server@eressea.de zu senden.</text>
|
||||
E3 BEFEHLE an eressea-server@eressea.de zu senden.</text>
|
||||
<text locale="en">Remember to send your orders to
|
||||
eressea-server@eressea.de with the subject ERESSEA 3 ORDERS.</text>
|
||||
eressea-server@eressea.de with the subject E3 ORDERS.</text>
|
||||
</string>
|
||||
<string name="mailcmd">
|
||||
<text locale="de">ERESSEA 3 BEFEHLE</text>
|
||||
<text locale="en">ERESSEA 3 ORDERS</text>
|
||||
<text locale="de">E3 BEFEHLE</text>
|
||||
<text locale="en">E3 ORDERS</text>
|
||||
</string>
|
||||
<string name="defaultorder">
|
||||
<text locale="de">ARBEITEN</text>
|
||||
<text locale="en">WORK</text>
|
||||
</string>
|
||||
</strings>
|
||||
</eressea>
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
<param name="init_spells" value="0"/>
|
||||
<param name="recruit.allow_merge" value="1"/>
|
||||
<param name="study.expensivemigrants" value="1"/>
|
||||
<param name="study.speedup" value="0"/>
|
||||
<param name="study.speedup" value="2"/>
|
||||
<param name="world.era" value="3"/>
|
||||
<param name="rules.migrants" value="0"/>
|
||||
<param name="rules.reserve.twophase" value="1"/>
|
||||
|
@ -128,6 +128,7 @@
|
|||
<param name="rules.combat.herospeed" value="3"/>
|
||||
<param name="rules.combat.demon_vampire" value="5"/> <!-- regen 1 hp per X points of damage done -->
|
||||
<param name="rules.combat.skill_bonus" value="0"/>
|
||||
<param name="rules.combat.nat_armor" value="1"/>
|
||||
<!--param name="rules.combat.loot" value="5"/--> <!-- only self + others - keeploot -->
|
||||
<param name="rules.items.loot_divisor" value="2"/> <!-- damage skims off 1/2 of goods transfers -->
|
||||
<param name="rules.items.give_divisor" value="2"/> <!-- corruption skims off 1/2 of goods transfers -->
|
||||
|
@ -148,6 +149,7 @@
|
|||
<param name="rules.economy.wages" value="1"/>
|
||||
<param name="rules.economy.roqf" value="5"/>
|
||||
<param name="rules.economy.herbrot" value="0"/>
|
||||
<param name="rules.region_owner_pay_building" value="market harbour lighthouse"/>
|
||||
<param name="rules.dwarf_castles" value="1"/>
|
||||
<!-- param name="rules.nmr.destroy" value="1"/ -->
|
||||
<param name="rules.limit.faction" value="250"/>
|
||||
|
@ -179,5 +181,9 @@
|
|||
<text locale="de">ERESSEA 4 BEFEHLE</text>
|
||||
<text locale="en">ERESSEA 4 ORDERS</text>
|
||||
</string>
|
||||
<string name="defaultorder">
|
||||
<text locale="de">ARBEITEN</text>
|
||||
<text locale="en">WORK</text>
|
||||
</string>
|
||||
</strings>
|
||||
</eressea>
|
||||
|
|
|
@ -4754,6 +4754,11 @@
|
|||
Der Landstrich kann über Jahre hinaus von den Folgen einer
|
||||
solchen Dürre betroffen sein.
|
||||
</text>
|
||||
<text locale="en">This powerful ritual opens a gate to the elemental plane of
|
||||
fire. A great drought comes over the land. Farmers, animals and
|
||||
plants of the region are fighting for survival, but only half of
|
||||
all living things will be able to survive a drought like this.
|
||||
The region will suffer the consequences of such a drought for years to come.</text>
|
||||
</string>
|
||||
<string name="magic_roots">
|
||||
<text locale="de">
|
||||
|
@ -4771,6 +4776,10 @@
|
|||
Strudel, einen Mahlstrom, welcher alle Schiffe, die ihn passieren,
|
||||
schwer beschädigen kann.
|
||||
</text>
|
||||
<text locale="en">
|
||||
This ritual summons a mighty water elemental from the depths of the ocean.
|
||||
The elemental creates an enormous maelstrom which damages any passing ships.
|
||||
</text>
|
||||
</string>
|
||||
<string name="wyrm_transformation">
|
||||
<text locale="de">
|
||||
|
|
|
@ -177,7 +177,7 @@
|
|||
<text locale="en">Wall</text>
|
||||
</string>
|
||||
<string name="wall1_trail">
|
||||
<text locale="de">a solid wall</text>
|
||||
<text locale="en">a solid wall</text>
|
||||
</string>
|
||||
<string name="hall1_trail">
|
||||
<text locale="en">the %s</text>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
[ -z $ERESSEA ] && ERESSEA=$HOME/eressea
|
||||
SRC=$ERESSEA/git
|
||||
$SRC/s/preview build master
|
||||
|
|
|
@ -22,8 +22,8 @@ exit $2 # otherwise
|
|||
function build() {
|
||||
assert_dir $SOURCE
|
||||
cd $SOURCE
|
||||
git pull || abort "failed to update source. do you have local changes?"
|
||||
[ -z $1 ] || git checkout $1
|
||||
git pull || abort "failed to update source. do you have local changes?"
|
||||
s/build || abort "build failed."
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,8 @@ while getopts :g:t:f:v: o; do
|
|||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
[ -d $ERESSEA ] || echo "invalid or missing env variable ERESSEA ($ERESSEA)"
|
||||
[ -z $ERESSEA ] && ERESSEA=$HOME/eressea
|
||||
[ -d $ERESSEA ] || abort "invalid or missing env variable ERESSEA ($ERESSEA)"
|
||||
[ -z $1 ] && usage
|
||||
[ -z $SOURCE ] && SOURCE=$ERESSEA/git
|
||||
[ -d $SOURCE ] || abort "missing source directory: $SOURCE"
|
||||
|
|
16
s/travis-build
Executable file
16
s/travis-build
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
|
||||
inifile() {
|
||||
if [ ! -e eressea.ini ]; then
|
||||
cp conf/eressea.ini .
|
||||
build/iniparser/inifile eressea.ini add lua:paths lunit:scripts
|
||||
fi
|
||||
}
|
||||
|
||||
[ -d build ] || mkdir build
|
||||
cd build && cmake .. \
|
||||
-DCMAKE_MODULE_PATH=$PWD/../cmake/Modules \
|
||||
-DCMAKE_BUILD_TYPE=Debug .. && \
|
||||
make && cd .. && inifile &&
|
||||
build/eressea/test_eressea &&
|
||||
build/eressea/eressea -v0 scripts/run-tests.lua
|
7
scripts/config.lua
Normal file
7
scripts/config.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
local path = 'scripts'
|
||||
if config.install then
|
||||
path = config.install .. '/' .. path
|
||||
end
|
||||
package.path = package.path .. ';' .. path .. '/?.lua;' .. path .. '/?/init.lua'
|
||||
require 'eressea'
|
||||
require 'eressea.xmlconf'
|
|
@ -1,193 +0,0 @@
|
|||
function size()
|
||||
return 16
|
||||
end
|
||||
|
||||
function make_island(pl, x, y, a, b)
|
||||
if b==nil then b = a/3 end
|
||||
local nx, ny = plane.normalize(pl, x, y)
|
||||
gmtool.make_island(nx, ny, a, b)
|
||||
end
|
||||
|
||||
function make_block(pl, x, y, r)
|
||||
local nx, ny = plane.normalize(pl, x, y)
|
||||
gmtool.make_block(nx, ny, r)
|
||||
end
|
||||
|
||||
function find(email)
|
||||
for f in factions() do if f.email==email then return f end end
|
||||
return nil
|
||||
end
|
||||
|
||||
function give_item(email, id, uname, iname)
|
||||
f = find(email)
|
||||
for u in f.units do
|
||||
u.id=atoi36(id)
|
||||
u.name=uname
|
||||
u:add_item(iname, 1)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
function give_items()
|
||||
give_item("hongeldongel@web.de", "boss", "Drollitz", "rpg_item_1")
|
||||
give_item("zangerl.helmut@chello.at", "holb", "Holbard", "rpg_item_2")
|
||||
give_item("r.lang@chello.at", "brtL", "Bertl", "rpg_item_2")
|
||||
give_item("schlaustauch@gmx.de", "bert", "Bertram", "rpg_item_3")
|
||||
end
|
||||
|
||||
function island(pl, x, y, r)
|
||||
make_block(pl, x, y, r)
|
||||
make_island(pl, x+r/2+2, y+r/2, size() * 3)
|
||||
make_island(pl, x-r-2, y+r/2, size() * 3)
|
||||
make_island(pl, x-r/2-2, y-r/2, size() * 3)
|
||||
make_island(pl, x+r+2, y-r/2, size() * 3)
|
||||
make_island(pl, x+r/2+2, y-r-2, size() * 3)
|
||||
make_island(pl, x-r/2-2, y+r+2, size() * 3)
|
||||
end
|
||||
|
||||
function cross(pl, x, y, r)
|
||||
make_block(pl, x-r, y+r*2, r)
|
||||
make_block(pl, x+r*4/3, y, r)
|
||||
make_block(pl, x-r*4/3, y, r)
|
||||
make_block(pl, x+r, y-r*2, r)
|
||||
|
||||
make_island(pl, x, y, size() * 3)
|
||||
make_island(pl, x, y-r*4/3, size() * 3)
|
||||
make_island(pl, x, y+r*4/3, size() * 3)
|
||||
make_island(pl, x+r*4/3, y-r*4/3, size() * 3)
|
||||
make_island(pl, x-r*4/3, y+r*4/3, size() * 3)
|
||||
end
|
||||
|
||||
function clean()
|
||||
for r in regions() do
|
||||
if r.terrain=="ocean" then
|
||||
region.destroy(r)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function count()
|
||||
local i = 0
|
||||
for f in factions() do i = i + 1 end
|
||||
print(i)
|
||||
end
|
||||
|
||||
function line(pl)
|
||||
local m = 0
|
||||
local i = 0
|
||||
local x, y = plane.normalize(pl, 0, i)
|
||||
local r = get_region(x, y)
|
||||
while true do
|
||||
if r==nil then
|
||||
if m==0 and (i>=0 or i<-10) then
|
||||
local s = size()
|
||||
gmtool.make_island(x, y, s*3, s)
|
||||
else
|
||||
gmtool.make_block(x, y, 6)
|
||||
end
|
||||
r = get_region(x, y)
|
||||
if r==nil then
|
||||
r = region.create(x, y, "ocean")
|
||||
end
|
||||
m = 1 - m
|
||||
end
|
||||
i = r.y + 1
|
||||
x, y = plane.normalize(pl, 0, i)
|
||||
r = get_region(x, y)
|
||||
if r~=nil and r.y==0 then break end
|
||||
end
|
||||
end
|
||||
|
||||
function build(pl)
|
||||
local d = 28
|
||||
local h = 20
|
||||
line(pl)
|
||||
island(pl, d+15, -6, 11)
|
||||
island(pl, -d, -h-10, 11)
|
||||
cross(pl, -d, h-10, 6)
|
||||
island(pl, d, 2*h, 11)
|
||||
end
|
||||
|
||||
function fill(pl, w, h)
|
||||
local x, y
|
||||
for x=0,w do
|
||||
for y=0,h do
|
||||
local nx, ny = plane.normalize(pl, x, y)
|
||||
local r = get_region(nx, ny)
|
||||
if r==nil then
|
||||
r = region.create(nx, ny, "ocean")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function seed()
|
||||
local input = io.open(config.basepath .. "/parteien.txt")
|
||||
for f in factions() do
|
||||
if f.race=="vampunicorn" then
|
||||
local str = input:read("*line")
|
||||
if str==nil then break end
|
||||
local race, lang, email = str:match("([^ ]*) ([^ ]*) ([^ ]*)")
|
||||
f.race = race:lower()
|
||||
f.options = f.options + 4096
|
||||
f.email = email
|
||||
f.locale = lang
|
||||
for u in f.units do
|
||||
u.race = race:lower()
|
||||
u.hp = u.hp_max
|
||||
local b = building.create(u.region, "castle")
|
||||
if lang=="de" then
|
||||
u.name = "Entdecker"
|
||||
b.name = "Heimat"
|
||||
else
|
||||
u.name = "Explorer"
|
||||
b.name = "Home"
|
||||
end
|
||||
b.size = 10
|
||||
u.building = b
|
||||
end
|
||||
end
|
||||
end
|
||||
for r in regions() do
|
||||
r:set_resource("sapling", r:get_resource("tree")/4)
|
||||
r:set_resource("seed", 0)
|
||||
end
|
||||
update_owners()
|
||||
end
|
||||
|
||||
function select()
|
||||
for f in factions() do
|
||||
if f.email=="enno@eressea.de" then
|
||||
for u in f.units do
|
||||
gmtool.select(u.region, true)
|
||||
u.number = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function justWords(str)
|
||||
local t = {}
|
||||
local function helper(word) table.insert(t, word) return "" end
|
||||
if not str:gsub("%w+", helper):find"%S" then return t end
|
||||
end
|
||||
|
||||
function rebuild()
|
||||
free_game()
|
||||
local w = 110
|
||||
local h = 80
|
||||
local pl = plane.create(0, -w/2, -h/2, w+1, h+1)
|
||||
build(pl)
|
||||
fill(pl, w, h)
|
||||
write_map("export.cr")
|
||||
end
|
||||
|
||||
function testwelt()
|
||||
free_game()
|
||||
local w = 10
|
||||
local h = 10
|
||||
local pl = plane.create(0, -w/2, -h/2, w+1, h+1)
|
||||
gmtool.make_island(0, 0, 30, 3)
|
||||
fill(pl, w, h)
|
||||
write_map("export.cr")
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
module('frost', package.seeall)
|
||||
|
||||
local function is_winter(turn)
|
||||
local season = get_season(turn)
|
||||
return season == "calendar::winter"
|
||||
end
|
||||
|
||||
local function is_spring(turn)
|
||||
local season = get_season(turn)
|
||||
return season == "calendar::spring"
|
||||
end
|
||||
|
||||
local function freeze(r, chance)
|
||||
for i, rn in ipairs(r.adj) do
|
||||
-- each region has a chance to freeze
|
||||
if rn.terrain=="ocean" and (chance>=100 or math.fmod(rng_int(), 100)<chance) then
|
||||
rn.terrain = "packice"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function thaw(r, chance)
|
||||
if chance>=100 or math.fmod(rng_int(), 100)<chance then
|
||||
r.terrain = "ocean"
|
||||
for s in r.ships do
|
||||
s.coast = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function update()
|
||||
local turn = get_turn()
|
||||
if is_winter(turn) then
|
||||
for r in regions() do
|
||||
if r.terrain=="glacier" then
|
||||
freeze(r, 20)
|
||||
end
|
||||
end
|
||||
elseif is_spring(turn) then
|
||||
for r in regions() do
|
||||
if r.terrain=="packice" then
|
||||
thaw(r, 20)
|
||||
end
|
||||
end
|
||||
elseif is_spring(turn-1) then
|
||||
for r in regions() do
|
||||
if r.terrain=="packice" then
|
||||
thaw(r, 100)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,64 +0,0 @@
|
|||
require "multis"
|
||||
require "e3a.frost"
|
||||
|
||||
function process(orders)
|
||||
local confirmed_multis = { }
|
||||
local suspected_multis = { }
|
||||
|
||||
if open_game(get_turn())~=0 then
|
||||
print("could not read game")
|
||||
return -1
|
||||
end
|
||||
init_summary()
|
||||
|
||||
-- kill multi-players (external script)
|
||||
kill_multis(confirmed_multis, false)
|
||||
mark_multis(suspected_multis, false)
|
||||
|
||||
-- run the turn:
|
||||
if read_orders(orders) ~= 0 then
|
||||
print("could not read " .. orders)
|
||||
return -1
|
||||
end
|
||||
|
||||
-- plan_monsters()
|
||||
local mon = get_faction(666)
|
||||
if mon ~= nil then
|
||||
mon.lastturn = get_turn()
|
||||
end
|
||||
|
||||
if nmr_check(config.maxnmrs or 30)~=0 then
|
||||
return -1
|
||||
end
|
||||
|
||||
process_orders()
|
||||
if xmas2009~=nil then
|
||||
xmas2009()
|
||||
end
|
||||
|
||||
-- create new monsters:
|
||||
spawn_dragons()
|
||||
spawn_undead()
|
||||
-- spawn_braineaters(0.25)
|
||||
-- spawn_ents()
|
||||
|
||||
-- post-turn updates:
|
||||
update_guards()
|
||||
update_scores()
|
||||
frost.update()
|
||||
|
||||
local localechange = { en = { "L46o" } }
|
||||
change_locales(localechange)
|
||||
|
||||
-- use newfactions file to place out new players
|
||||
-- autoseed(config.basepath .. "/newfactions", false)
|
||||
|
||||
write_files(config.locales)
|
||||
|
||||
file = "" .. get_turn() .. ".dat"
|
||||
if eressea.write_game(file)~=0 then
|
||||
print("could not write game")
|
||||
return -1
|
||||
end
|
||||
return 0
|
||||
end
|
|
@ -1,89 +0,0 @@
|
|||
function get_markets(r, result)
|
||||
local n = 0
|
||||
result = result or {}
|
||||
|
||||
for b in r.buildings do
|
||||
if b.type=="market" then
|
||||
u = b.owner
|
||||
if u~=nil then
|
||||
table.insert(result, u)
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return n, result
|
||||
end
|
||||
|
||||
function collect_markets(r, result)
|
||||
local result = result or {}
|
||||
local n = 0
|
||||
n, result = get_markets(r, result)
|
||||
for i, r in ipairs(r.adj) do
|
||||
if r then
|
||||
local x, result = get_markets(r, result)
|
||||
n = n + x
|
||||
end
|
||||
end
|
||||
return n, result
|
||||
end
|
||||
|
||||
function market_action(r)
|
||||
local f = r.owner
|
||||
local trade = 1000
|
||||
if f~=nil and f.race=="halfling" then
|
||||
trade = 600
|
||||
end
|
||||
|
||||
local p = r:get_resource("peasant")
|
||||
if p > 500 then
|
||||
local n, markets = collect_markets(r)
|
||||
|
||||
if n>0 then
|
||||
local give
|
||||
if r.luxury~=nil then
|
||||
give = {}
|
||||
local numlux = p / trade
|
||||
for x = 1, numlux do
|
||||
local m = 1+math.fmod(rng_int(), n)
|
||||
u = markets[m]
|
||||
if give[u] then
|
||||
give[u] = give[u] + 1
|
||||
else
|
||||
give[u] = 1
|
||||
end
|
||||
end
|
||||
|
||||
for u, v in pairs(give) do
|
||||
u:add_item(r.luxury, v)
|
||||
end
|
||||
end
|
||||
|
||||
if r.herb~=nil then
|
||||
give = {}
|
||||
local numherb = p / 500
|
||||
for x = 1, numherb do
|
||||
local m = 1+math.fmod(rng_int(), n)
|
||||
u = markets[m]
|
||||
if give[u] then
|
||||
give[u] = give[u] + 1
|
||||
else
|
||||
give[u] = 1
|
||||
end
|
||||
end
|
||||
|
||||
for u, v in pairs(give) do
|
||||
u:add_item(r.herb, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function markets()
|
||||
local r
|
||||
for r in regions() do
|
||||
market_action(r)
|
||||
end
|
||||
end
|
||||
|
||||
-- add_proc(markets, "Markets", "Bauernwanderung")
|
|
@ -1,17 +0,0 @@
|
|||
require "spells"
|
||||
require "e3a.xmas2009"
|
||||
require "e3a.rules"
|
||||
require "e3a.markets"
|
||||
|
||||
local srcpath = config.source_dir
|
||||
tests = {
|
||||
srcpath .. '/core/scripts/tests/common.lua',
|
||||
srcpath .. '/core/scripts/tests/spells.lua',
|
||||
-- srcpath .. '/eressea/scripts/tests/bson.lua',
|
||||
-- srcpath .. '/eressea/scripts/tests/attrib.lua',
|
||||
srcpath .. '/scripts/tests/spells.lua',
|
||||
srcpath .. '/scripts/tests/castles.lua',
|
||||
srcpath .. '/scripts/tests/morale.lua',
|
||||
srcpath .. '/scripts/tests/e3a.lua',
|
||||
srcpath .. '/scripts/tests/stealth.lua',
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
-- when appending to this, make sure the item has a canuse-function!
|
||||
local goblin_denied = " plate lance mallornlance greatbow axe greatsword halberd rustyaxe rustyhalberd towershield scale "
|
||||
function item_canuse(u, iname)
|
||||
local race = u.race
|
||||
if race=="goblin" then
|
||||
if string.find(goblin_denied, " " .. iname .. " ") then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if iname=="rep_crossbow" then
|
||||
-- only dwarves and halflings allowed to use repeating crossbow
|
||||
return race=="dwarf" or race=="halfling"
|
||||
end
|
||||
if iname=="scale" then
|
||||
-- only dwarves and halflings can use scale
|
||||
return race=="dwarf" or race=="halfling"
|
||||
end
|
||||
if iname=="towershield" then
|
||||
-- only dwarves allowed to use towershield
|
||||
return race=="dwarf"
|
||||
end
|
||||
if iname=="greatbow" then
|
||||
-- only elves use greatbow
|
||||
return race=="elf"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function building_protection(b, u)
|
||||
return 1
|
||||
end
|
||||
|
||||
function building_taxes(b, blevel)
|
||||
btype = b.type
|
||||
if btype=="castle" then
|
||||
return blevel * 0.01
|
||||
elseif btype=="watch" then
|
||||
return blevel * 0.005
|
||||
end
|
||||
return 0.0
|
||||
end
|
||||
|
||||
-- the "raindance" spell
|
||||
function raindance(r, mage, level, force)
|
||||
if (create_curse(mage, r, "blessedharvest", force, 1+force*2, 100 * force)) then
|
||||
-- slightly crooked way of reporting an action to everyone in the region
|
||||
local msg = message.create("raindance_effect")
|
||||
msg:set_unit("mage", mage)
|
||||
if (msg:report_action(r, mage, 3)) then
|
||||
local msg2 = message.create("raindance_effect")
|
||||
msg2:set_unit("mage", nil)
|
||||
msg2:report_action(r, mage, 4)
|
||||
end
|
||||
end
|
||||
return level
|
||||
end
|
||||
|
||||
-- the "blessed harvest" spell
|
||||
function blessedharvest(r, mage, level, force)
|
||||
if create_curse(mage, r, "blessedharvest", force, 1+force*2, 50 * force) then
|
||||
-- slightly crooked way of reporting an action to everyone in the region
|
||||
local msg = message.create("harvest_effect")
|
||||
msg:set_unit("mage", mage)
|
||||
if (msg:report_action(r, mage, 3)) then
|
||||
local msg2 = message.create("harvest_effect")
|
||||
msg2:set_unit("mage", nil)
|
||||
msg2:report_action(r, mage, 4)
|
||||
end
|
||||
for idx, rn in ipairs(r.adj) do
|
||||
-- nur landregionen haben moral>=0
|
||||
if r.morale>=0 then
|
||||
create_curse(mage, r, "blessedharvest", force, force*2, 50 * force)
|
||||
end
|
||||
end
|
||||
end
|
||||
return level
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
function xmas2009()
|
||||
if not get_key("xm09") then
|
||||
-- print("Es weihnachtet sehr (2009)")
|
||||
set_key("xm09", true)
|
||||
for f in factions() do
|
||||
f:add_item("xmastree", 1)
|
||||
local msg = message.create("msg_event")
|
||||
msg:set_string("string", translate("santa2006"))
|
||||
msg:send_faction(f)
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function use_xmastree(u, amount)
|
||||
if u.region.herb~=nil then
|
||||
local trees = u.region:get_resource("tree")
|
||||
u.region:set_resource("tree", 10+trees)
|
||||
u:use_pooled("xmastree", amount)
|
||||
local msg = message.create("usepotion")
|
||||
msg:set_unit("unit", u)
|
||||
msg:set_resource("potion", "xmastree")
|
||||
msg:send_region(u.region)
|
||||
return 0
|
||||
end
|
||||
end
|
|
@ -1,58 +0,0 @@
|
|||
require "multis"
|
||||
require "e3a.frost"
|
||||
|
||||
function process(orders)
|
||||
local confirmed_multis = { }
|
||||
local suspected_multis = { }
|
||||
|
||||
if open_game(get_turn())~=0 then
|
||||
print("could not read game")
|
||||
return -1
|
||||
end
|
||||
init_summary()
|
||||
|
||||
-- run the turn:
|
||||
if read_orders(orders) ~= 0 then
|
||||
print("could not read " .. orders)
|
||||
return -1
|
||||
end
|
||||
|
||||
-- plan_monsters()
|
||||
local mon = get_faction(666)
|
||||
if mon ~= nil then
|
||||
mon.lastturn = get_turn()
|
||||
end
|
||||
|
||||
if nmr_check(config.maxnmrs or 30)~=0 then
|
||||
return -1
|
||||
end
|
||||
|
||||
process_orders()
|
||||
if xmas2009~=nil then
|
||||
xmas2009()
|
||||
end
|
||||
|
||||
-- create new monsters:
|
||||
spawn_dragons()
|
||||
spawn_undead()
|
||||
|
||||
-- post-turn updates:
|
||||
update_guards()
|
||||
update_scores()
|
||||
frost.update()
|
||||
|
||||
local localechange = { en = { "L46o" } }
|
||||
change_locales(localechange)
|
||||
|
||||
-- use newfactions file to place out new players
|
||||
-- autoseed(config.basepath .. "/newfactions", false)
|
||||
|
||||
write_files(config.locales)
|
||||
|
||||
file = "" .. get_turn() .. ".dat"
|
||||
if eressea.write_game(file)~=0 then
|
||||
print("could not write game")
|
||||
return -1
|
||||
end
|
||||
return 0
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
require "spells"
|
||||
require "e3a.rules"
|
||||
require "e3a.markets"
|
||||
require "e3a.frost"
|
4
scripts/reports.lua
Normal file
4
scripts/reports.lua
Normal file
|
@ -0,0 +1,4 @@
|
|||
dofile('config.lua')
|
||||
eressea.read_game(get_turn() .. '.dat')
|
||||
init_reports()
|
||||
write_reports()
|
|
@ -3,7 +3,7 @@ function new_faction(email, race, lang, r)
|
|||
u = unit.create(f, r, 10)
|
||||
u:add_item("log", 5);
|
||||
u:add_item("horse", 2);
|
||||
u:add_item("silver", 1000);
|
||||
u:add_item("money", 1000);
|
||||
u:add_item("adamantium", 1);
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ function new_faction(email, race, lang, r)
|
|||
u = unit.create(f, r, 10)
|
||||
u:add_item("log", 5);
|
||||
u:add_item("horse", 2);
|
||||
u:add_item("silver", 1000);
|
||||
u:add_item("money", 1000);
|
||||
u:add_item("adamantium", 1);
|
||||
end
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ set (ERESSEA_SRC
|
|||
battle.c
|
||||
alchemy.c
|
||||
stealth.c
|
||||
upkeep.c
|
||||
vortex.c
|
||||
names.c
|
||||
reports.c
|
||||
|
@ -91,6 +92,7 @@ set (ERESSEA_SRC
|
|||
study.c
|
||||
summary.c
|
||||
monsters.c
|
||||
wormhole.c
|
||||
${SPELLS_SRC}
|
||||
${RACES_SRC}
|
||||
${ITEMS_SRC}
|
||||
|
@ -152,6 +154,7 @@ target_link_libraries(eressea
|
|||
)
|
||||
|
||||
set(TESTS_SRC
|
||||
wormhole.test.c
|
||||
test_eressea.c
|
||||
tests.c
|
||||
battle.test.c
|
||||
|
@ -159,15 +162,16 @@ set(TESTS_SRC
|
|||
tests.test.c
|
||||
reports.test.c
|
||||
stealth.test.c
|
||||
move.test.c
|
||||
callback.test.c
|
||||
direction.test.c
|
||||
keyword.test.c
|
||||
skill.test.c
|
||||
json.test.c
|
||||
economy.test.c
|
||||
market.test.c
|
||||
json.test.c
|
||||
keyword.test.c
|
||||
laws.test.c
|
||||
market.test.c
|
||||
move.test.c
|
||||
skill.test.c
|
||||
upkeep.test.c
|
||||
${UTIL_TESTS}
|
||||
${KERNEL_TESTS}
|
||||
${ERESSEA_SRC}
|
||||
|
|
|
@ -226,7 +226,7 @@ int armedmen(const unit * u, bool siege_weapons)
|
|||
const weapon_type *wtype = resource2weapon(itm->type->rtype);
|
||||
if (wtype == NULL || (!siege_weapons && (wtype->flags & WTF_SIEGE)))
|
||||
continue;
|
||||
if (effskill(u, wtype->skill) >= 1)
|
||||
if (effskill(u, wtype->skill) >= wtype->minskill)
|
||||
n += itm->number;
|
||||
/* if (effskill(u, wtype->skill) >= wtype->minskill) n += itm->number; */
|
||||
if (n > u->number)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "json.h"
|
||||
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/types.h>
|
||||
#include <kernel/config.h>
|
||||
#include <kernel/save.h>
|
||||
|
|
|
@ -11,15 +11,18 @@ without prior permission by the authors of Eressea.
|
|||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <kernel/types.h>
|
||||
#include "bind_faction.h"
|
||||
#include "bind_unit.h"
|
||||
#include "bindings.h"
|
||||
|
||||
#include <kernel/alliance.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/config.h>
|
||||
#include <kernel/unit.h>
|
||||
#include <kernel/item.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/messages.h>
|
||||
#include <kernel/plane.h>
|
||||
#include <kernel/race.h>
|
||||
#include <kernel/region.h>
|
||||
|
@ -209,6 +212,15 @@ static int tolua_faction_renumber(lua_State * L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tolua_faction_addnotice(lua_State * L)
|
||||
{
|
||||
faction *self = (faction *)tolua_tousertype(L, 1, 0);
|
||||
const char *str = tolua_tostring(L, 2, 0);
|
||||
|
||||
addmessage(NULL, self, str, MSG_MESSAGE, ML_IMPORTANT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tolua_faction_get_objects(lua_State * L)
|
||||
{
|
||||
faction *self = (faction *) tolua_tousertype(L, 1, 0);
|
||||
|
@ -452,8 +464,8 @@ static int tolua_faction_get_alliance(lua_State * L)
|
|||
|
||||
static int tolua_faction_set_alliance(lua_State * L)
|
||||
{
|
||||
faction *self = (faction *) tolua_tousertype(L, 1, 0);
|
||||
alliance *alli = (alliance *) tolua_tousertype(L, 2, 0);
|
||||
struct faction *self = (struct faction *)tolua_tousertype(L, 1, 0);
|
||||
struct alliance *alli = (struct alliance *) tolua_tousertype(L, 2, 0);
|
||||
|
||||
setalliance(self, alli);
|
||||
|
||||
|
@ -561,9 +573,10 @@ void tolua_faction_open(lua_State * L)
|
|||
.property("x", &faction_getorigin_x, &faction_setorigin_x)
|
||||
.property("y", &faction_getorigin_y, &faction_setorigin_y)
|
||||
|
||||
.def("add_notice", &faction_addnotice)
|
||||
#endif
|
||||
tolua_variable(L, TOLUA_CAST "objects", tolua_faction_get_objects,
|
||||
tolua_function(L, TOLUA_CAST "add_notice", &tolua_faction_addnotice);
|
||||
|
||||
tolua_variable(L, TOLUA_CAST "objects", tolua_faction_get_objects,
|
||||
NULL);
|
||||
}
|
||||
tolua_endmodule(L);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#define VERSION_MAJOR 3
|
||||
#define VERSION_MINOR 2
|
||||
#define VERSION_BUILD 684
|
||||
#define VERSION_MINOR 3
|
||||
#define VERSION_BUILD 687
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <platform.h>
|
||||
#include <kernel/config.h>
|
||||
#include "direction.h"
|
||||
#include "vortex.h"
|
||||
|
||||
#include "util/language.h"
|
||||
#include "util/umlaut.h"
|
||||
|
@ -14,7 +15,7 @@ void init_direction(const struct locale *lang, direction_t dir, const char *str)
|
|||
addtoken(tokens, str, token);
|
||||
}
|
||||
|
||||
void init_directions(const struct locale *lang) {
|
||||
void init_directions(struct locale *lang) {
|
||||
/* mit dieser routine kann man mehrere namen für eine direction geben,
|
||||
* das ist für die hexes ideal. */
|
||||
const struct {
|
||||
|
@ -39,6 +40,8 @@ void init_directions(const struct locale *lang) {
|
|||
int i;
|
||||
void **tokens = get_translations(lang, UT_DIRECTIONS);
|
||||
|
||||
register_special_direction(lang, "vortex");
|
||||
|
||||
for (i = 0; dirs[i].direction != NODIRECTION; ++i) {
|
||||
const char *str = locale_string(lang, dirs[i].name);
|
||||
if (str) {
|
||||
|
|
|
@ -22,7 +22,7 @@ typedef enum {
|
|||
} direction_t;
|
||||
|
||||
direction_t get_direction(const char *s, const struct locale *);
|
||||
void init_directions(const struct locale *lang);
|
||||
void init_directions(struct locale *lang);
|
||||
void init_direction(const struct locale *lang, direction_t dir, const char *str);
|
||||
|
||||
direction_t finddirection(const char *str);
|
||||
|
|
|
@ -57,9 +57,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <util/bsdstring.h>
|
||||
#include <util/event.h>
|
||||
#include <util/goodies.h>
|
||||
#include <util/lists.h>
|
||||
#include <util/language.h>
|
||||
#include <util/lists.h>
|
||||
#include <util/log.h>
|
||||
#include <util/parser.h>
|
||||
#include <util/rng.h>
|
||||
|
||||
|
@ -1039,12 +1039,10 @@ static bool maintain(building * b, bool first)
|
|||
u = building_owner(b);
|
||||
if (u == NULL)
|
||||
return false;
|
||||
/* If the owner is the region owner, check if biggest castle has the dontpay flag */
|
||||
/* If the owner is the region owner, check if dontpay flag is set for the building where he is in */
|
||||
if (check_param(global.parameters, "rules.region_owner_pay_building", b->type->_name)) {
|
||||
if (u == building_owner(largestbuilding(r, &cmp_taxes, false))) {
|
||||
if (fval(u->building, BLD_DONTPAY)) {
|
||||
return false;
|
||||
}
|
||||
if (fval(u->building, BLD_DONTPAY)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (c = 0; b->type->maintenance[c].number; ++c) {
|
||||
|
@ -1820,7 +1818,8 @@ int make_cmd(unit * u, struct order *ord)
|
|||
cmistake(u, ord, 275, MSG_PRODUCE);
|
||||
}
|
||||
else {
|
||||
direction_t d = get_direction(getstrtoken(), u->faction->locale);
|
||||
const char * s = getstrtoken();
|
||||
direction_t d = s ? get_direction(s, u->faction->locale) : NODIRECTION;
|
||||
if (d != NODIRECTION) {
|
||||
build_road(r, u, m, d);
|
||||
}
|
||||
|
@ -2058,6 +2057,7 @@ static void buy(unit * u, request ** buyorders, struct order *ord)
|
|||
const item_type *itype = NULL;
|
||||
const luxury_type *ltype = NULL;
|
||||
keyword_t kwd;
|
||||
const char *s;
|
||||
|
||||
if (u->ship && is_guarded(r, u, GUARD_CREWS)) {
|
||||
cmistake(u, ord, 69, MSG_INCOME);
|
||||
|
@ -2133,7 +2133,8 @@ static void buy(unit * u, request ** buyorders, struct order *ord)
|
|||
/* die Menge der verkauften Güter merken */
|
||||
a->data.i += n;
|
||||
|
||||
itype = finditemtype(getstrtoken(), u->faction->locale);
|
||||
s = getstrtoken();
|
||||
itype = s ? finditemtype(s, u->faction->locale) : 0;
|
||||
if (itype != NULL) {
|
||||
ltype = resource2luxury(itype->rtype);
|
||||
if (ltype == NULL) {
|
||||
|
@ -2154,6 +2155,13 @@ static void buy(unit * u, request ** buyorders, struct order *ord)
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
static void add_income(unit * u, int type, int want, int qty)
|
||||
{
|
||||
if (want == INT_MAX)
|
||||
want = qty;
|
||||
ADDMSG(&u->faction->msgs, msg_message("income",
|
||||
"unit region mode wanted amount", u, u->region, type, want, qty));
|
||||
}
|
||||
|
||||
/* Steuersätze in % bei Burggröße */
|
||||
static int tax_per_size[7] = { 0, 6, 12, 18, 24, 30, 36 };
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <kernel/xmlreader.h>
|
||||
#include <kernel/item.h>
|
||||
#include <kernel/building.h>
|
||||
#include <modules/wormhole.h>
|
||||
#include <modules/gmcmd.h>
|
||||
#include <modules/xmas.h>
|
||||
#include <items/itemtypes.h>
|
||||
|
@ -25,6 +24,7 @@
|
|||
#include "items.h"
|
||||
#include "creport.h"
|
||||
#include "names.h"
|
||||
#include "wormhole.h"
|
||||
|
||||
void game_done(void)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "gmtool_structs.h"
|
||||
#include "console.h"
|
||||
#include "listbox.h"
|
||||
#include "wormhole.h"
|
||||
|
||||
#include <modules/xmas.h>
|
||||
#include <modules/gmcmd.h>
|
||||
|
@ -26,7 +27,6 @@
|
|||
#if ARENA_MODULE
|
||||
#include <modules/arena.h>
|
||||
#endif
|
||||
#include <modules/wormhole.h>
|
||||
#include <modules/autoseed.h>
|
||||
#if DUNGEON_MODULE
|
||||
#include <modules/dungeon.h>
|
||||
|
@ -1108,7 +1108,7 @@ static void handlekey(state * st, int c)
|
|||
strcpy(kbuffer, "getch:");
|
||||
}
|
||||
sprintf(sbuffer, " 0x%x", c);
|
||||
strncat(kbuffer, sbuffer, sizeof(kbuffer));
|
||||
strncat(kbuffer, sbuffer, sizeof(kbuffer)-1);
|
||||
statusline(st->wnd_status->handle, kbuffer);
|
||||
if (strlen(kbuffer) > 70)
|
||||
kbuffer[0] = 0;
|
||||
|
|
16
src/items.c
16
src/items.c
|
@ -35,16 +35,19 @@ use_studypotion(struct unit *u, const struct item_type *itype, int amount,
|
|||
struct order *ord)
|
||||
{
|
||||
if (init_order(u->thisorder) == K_STUDY) {
|
||||
skill_t sk;
|
||||
skill *sv;
|
||||
skill_t sk = NOSKILL;
|
||||
skill *sv = 0;
|
||||
const char * s = getstrtoken();
|
||||
|
||||
sk = get_skill(getstrtoken(), u->faction->locale);
|
||||
sv = unit_skill(u, sk);
|
||||
if (s) {
|
||||
sk = get_skill(s, u->faction->locale);
|
||||
sv = unit_skill(u, sk);
|
||||
}
|
||||
|
||||
if (sv && sv->level > 2) {
|
||||
/* TODO: message */
|
||||
}
|
||||
else if (study_cost(u, sk) > 0) {
|
||||
else if (sk!=NOSKILL && study_cost(u, sk) > 0) {
|
||||
/* TODO: message */
|
||||
}
|
||||
else {
|
||||
|
@ -54,8 +57,9 @@ struct order *ord)
|
|||
a = a_add(&u->attribs, a_new(&at_learning));
|
||||
}
|
||||
teach = (teaching_info *)a->data.v;
|
||||
if (amount > MAXGAIN)
|
||||
if (amount > MAXGAIN) {
|
||||
amount = MAXGAIN;
|
||||
}
|
||||
teach->value += amount * 30;
|
||||
if (teach->value > MAXGAIN * 30) {
|
||||
teach->value = MAXGAIN * 30;
|
||||
|
|
|
@ -71,7 +71,7 @@ int json_export(stream * out, int flags) {
|
|||
cJSON_AddNumberToObject(data, "y", p->miny);
|
||||
cJSON_AddNumberToObject(data, "width", p->maxx-p->minx);
|
||||
cJSON_AddNumberToObject(data, "height", p->maxy-p->miny);
|
||||
cJSON_AddStringToObject(data, "name", p->name);
|
||||
if (p->name) cJSON_AddStringToObject(data, "name", p->name);
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root, "regions", json = cJSON_CreateObject());
|
||||
|
|
|
@ -90,47 +90,49 @@ ship *getship(const struct region * r)
|
|||
|
||||
static void destroy_road(unit * u, int nmax, struct order *ord)
|
||||
{
|
||||
direction_t d = get_direction(getstrtoken(), u->faction->locale);
|
||||
unit *u2;
|
||||
region *r = u->region;
|
||||
short n = (short)nmax;
|
||||
const char *s = getstrtoken();
|
||||
direction_t d = s ? get_direction(s, u->faction->locale) : NODIRECTION;
|
||||
if (d == NODIRECTION) {
|
||||
/* Die Richtung wurde nicht erkannt */
|
||||
cmistake(u, ord, 71, MSG_PRODUCE);
|
||||
} else {
|
||||
unit *u2;
|
||||
region *r = u->region;
|
||||
short road, n = (short)nmax;
|
||||
|
||||
if (nmax > SHRT_MAX) {
|
||||
n = SHRT_MAX;
|
||||
} else if (nmax < 0) {
|
||||
n = 0;
|
||||
}
|
||||
|
||||
if (nmax > SHRT_MAX)
|
||||
n = SHRT_MAX;
|
||||
else if (nmax < 0)
|
||||
n = 0;
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (u2->faction != u->faction && is_guard(u2, GUARD_TAX)
|
||||
&& cansee(u2->faction, u->region, u, 0)
|
||||
&& !alliedunit(u, u2->faction, HELP_GUARD)) {
|
||||
cmistake(u, ord, 70, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (u2->faction != u->faction && is_guard(u2, GUARD_TAX)
|
||||
&& cansee(u2->faction, u->region, u, 0)
|
||||
&& !alliedunit(u, u2->faction, HELP_GUARD)) {
|
||||
cmistake(u, ord, 70, MSG_EVENT);
|
||||
return;
|
||||
road = rroad(r, d);
|
||||
n = _min(n, road);
|
||||
if (n != 0) {
|
||||
region *r2 = rconnect(r, d);
|
||||
int willdo = eff_skill(u, SK_ROAD_BUILDING, r) * u->number;
|
||||
willdo = _min(willdo, n);
|
||||
if (willdo == 0) {
|
||||
/* TODO: error message */
|
||||
}
|
||||
if (willdo > SHRT_MAX)
|
||||
road = 0;
|
||||
else
|
||||
road = road - (short)willdo;
|
||||
rsetroad(r, d, road);
|
||||
ADDMSG(&u->faction->msgs, msg_message("destroy_road",
|
||||
"unit from to", u, r, r2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (d == NODIRECTION) {
|
||||
/* Die Richtung wurde nicht erkannt */
|
||||
cmistake(u, ord, 71, MSG_PRODUCE);
|
||||
} else {
|
||||
short road = rroad(r, d);
|
||||
n = _min(n, road);
|
||||
if (n != 0) {
|
||||
region *r2 = rconnect(r, d);
|
||||
int willdo = eff_skill(u, SK_ROAD_BUILDING, r) * u->number;
|
||||
willdo = _min(willdo, n);
|
||||
if (willdo == 0) {
|
||||
/* TODO: error message */
|
||||
}
|
||||
if (willdo > SHRT_MAX)
|
||||
road = 0;
|
||||
else
|
||||
road = road - (short)willdo;
|
||||
rsetroad(r, d, road);
|
||||
ADDMSG(&u->faction->msgs, msg_message("destroy_road",
|
||||
"unit from to", u, r, r2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int destroy_cmd(unit * u, struct order *ord)
|
||||
|
|
|
@ -102,7 +102,6 @@ struct settings global = {
|
|||
|
||||
bool lomem = false;
|
||||
FILE *logfile;
|
||||
FILE *updatelog;
|
||||
bool battledebug = false;
|
||||
int turn = -1;
|
||||
|
||||
|
@ -122,17 +121,6 @@ bool IsImmune(const faction * f)
|
|||
return !fval(f, FFL_NPC) && f->age < NewbieImmunity();
|
||||
}
|
||||
|
||||
static int MaxAge(void)
|
||||
{
|
||||
static int value = -1;
|
||||
static int gamecookie = -1;
|
||||
if (value < 0 || gamecookie != global.cookie) {
|
||||
gamecookie = global.cookie;
|
||||
value = get_param_int(global.parameters, "MaxAge", 0);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static int ally_flag(const char *s, int help_mask)
|
||||
{
|
||||
if ((help_mask & HELP_MONEY) && strcmp(s, "money") == 0)
|
||||
|
@ -572,7 +560,7 @@ void verify_data(void)
|
|||
int mage, alchemist;
|
||||
|
||||
if (verbosity >= 1)
|
||||
puts(" - Überprüfe Daten auf Korrektheit...");
|
||||
puts(" - checking data for correctness...");
|
||||
|
||||
for (f = factions; f; f = f->next) {
|
||||
mage = 0;
|
||||
|
@ -960,7 +948,7 @@ const char *strcheck(const char *s, size_t maxlen)
|
|||
static char buffer[16 * 1024]; // FIXME: static return value
|
||||
if (strlen(s) > maxlen) {
|
||||
assert(maxlen < 16 * 1024);
|
||||
log_warning("[strcheck] String wurde auf %d Zeichen verkürzt:\n%s\n", (int)maxlen, s);
|
||||
log_warning("[strcheck] string was shortened to %d bytes:\n%s\n", (int)maxlen, s);
|
||||
strlcpy(buffer, s, maxlen);
|
||||
return buffer;
|
||||
}
|
||||
|
@ -1127,7 +1115,8 @@ unsigned int getuint(void)
|
|||
|
||||
int getint(void)
|
||||
{
|
||||
return atoi(getstrtoken());
|
||||
const char * s = getstrtoken();
|
||||
return s ? atoi(s) : 0;
|
||||
}
|
||||
|
||||
const struct race *findrace(const char *s, const struct locale *lang)
|
||||
|
@ -1199,7 +1188,8 @@ bool isparam(const char *s, const struct locale * lang, param_t param)
|
|||
|
||||
param_t getparam(const struct locale * lang)
|
||||
{
|
||||
return findparam(getstrtoken(), lang);
|
||||
const char *s = getstrtoken();
|
||||
return s ? findparam(s, lang) : NOPARAM;
|
||||
}
|
||||
|
||||
faction *findfaction(int n)
|
||||
|
@ -1508,9 +1498,9 @@ bool idle(faction * f)
|
|||
|
||||
int maxworkingpeasants(const struct region *r)
|
||||
{
|
||||
int i = production(r) * MAXPEASANTS_PER_AREA
|
||||
- ((rtrees(r, 2) + rtrees(r, 1) / 2) * TREESIZE);
|
||||
return _max(i, 0);
|
||||
int size = production(r);
|
||||
int treespace = (rtrees(r, 2) + rtrees(r, 1) / 2) * TREESIZE;
|
||||
return _max(size-treespace, _min(size / 10 , 200));
|
||||
}
|
||||
|
||||
int lighthouse_range(const building * b, const faction * f)
|
||||
|
@ -1777,7 +1767,7 @@ void init_options_translation(const struct locale * lang) {
|
|||
}
|
||||
}
|
||||
|
||||
void init_locale(const struct locale *lang)
|
||||
void init_locale(struct locale *lang)
|
||||
{
|
||||
variant var;
|
||||
int i;
|
||||
|
@ -1980,7 +1970,7 @@ void init_locales(void)
|
|||
{
|
||||
int l;
|
||||
for (l = 0; localenames[l]; ++l) {
|
||||
const struct locale *lang = get_or_create_locale(localenames[l]);
|
||||
struct locale *lang = get_or_create_locale(localenames[l]);
|
||||
init_locale(lang);
|
||||
}
|
||||
}
|
||||
|
@ -2067,100 +2057,6 @@ char *_strdup(const char *s)
|
|||
}
|
||||
#endif
|
||||
|
||||
void remove_empty_factions(void)
|
||||
{
|
||||
faction **fp, *f3;
|
||||
|
||||
for (fp = &factions; *fp;) {
|
||||
faction *f = *fp;
|
||||
/* monster (0) werden nicht entfernt. alive kann beim readgame
|
||||
* () auf 0 gesetzt werden, wenn monsters keine einheiten mehr
|
||||
* haben. */
|
||||
if ((f->units == NULL || f->alive == 0) && !is_monsters(f)) {
|
||||
ursprung *ur = f->ursprung;
|
||||
while (ur && ur->id != 0)
|
||||
ur = ur->next;
|
||||
if (verbosity >= 2)
|
||||
log_printf(stdout, "\t%s\n", factionname(f));
|
||||
|
||||
/* Einfach in eine Datei schreiben und später vermailen */
|
||||
|
||||
if (updatelog)
|
||||
fprintf(updatelog, "dropout %s\n", itoa36(f->no));
|
||||
|
||||
for (f3 = factions; f3; f3 = f3->next) {
|
||||
ally *sf;
|
||||
group *g;
|
||||
ally **sfp = &f3->allies;
|
||||
while (*sfp) {
|
||||
sf = *sfp;
|
||||
if (sf->faction == f || sf->faction == NULL) {
|
||||
*sfp = sf->next;
|
||||
free(sf);
|
||||
}
|
||||
else
|
||||
sfp = &(*sfp)->next;
|
||||
}
|
||||
for (g = f3->groups; g; g = g->next) {
|
||||
sfp = &g->allies;
|
||||
while (*sfp) {
|
||||
sf = *sfp;
|
||||
if (sf->faction == f || sf->faction == NULL) {
|
||||
*sfp = sf->next;
|
||||
free(sf);
|
||||
}
|
||||
else
|
||||
sfp = &(*sfp)->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*fp = f->next;
|
||||
funhash(f);
|
||||
free_faction(f);
|
||||
free(f);
|
||||
}
|
||||
else
|
||||
fp = &(*fp)->next;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_empty_units_in_region(region * r)
|
||||
{
|
||||
unit **up = &r->units;
|
||||
|
||||
while (*up) {
|
||||
unit *u = *up;
|
||||
|
||||
if (u->number) {
|
||||
faction *f = u->faction;
|
||||
if (f == NULL || !f->alive) {
|
||||
set_number(u, 0);
|
||||
}
|
||||
if (MaxAge() > 0) {
|
||||
if ((!fval(f, FFL_NOTIMEOUT) && f->age > MaxAge())) {
|
||||
set_number(u, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((u->number == 0 && u_race(u) != get_race(RC_SPELL)) || (u->age <= 0
|
||||
&& u_race(u) == get_race(RC_SPELL))) {
|
||||
remove_unit(up, u);
|
||||
}
|
||||
if (*up == u)
|
||||
up = &u->next;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_empty_units(void)
|
||||
{
|
||||
region *r;
|
||||
|
||||
for (r = regions; r; r = r->next) {
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
||||
}
|
||||
|
||||
bool faction_id_is_unused(int id)
|
||||
{
|
||||
return findfaction(id) == NULL;
|
||||
|
@ -2240,27 +2136,6 @@ int besieged(const unit * u)
|
|||
&& u->building->besieged >= u->building->size * SIEGEFACTOR);
|
||||
}
|
||||
|
||||
int lifestyle(const unit * u)
|
||||
{
|
||||
int need;
|
||||
plane *pl;
|
||||
static int gamecookie = -1;
|
||||
if (gamecookie != global.cookie) {
|
||||
gamecookie = global.cookie;
|
||||
}
|
||||
|
||||
if (is_monsters(u->faction))
|
||||
return 0;
|
||||
|
||||
need = maintenance_cost(u);
|
||||
|
||||
pl = rplane(u->region);
|
||||
if (pl && fval(pl, PFL_NOFEED))
|
||||
return 0;
|
||||
|
||||
return need;
|
||||
}
|
||||
|
||||
bool has_horses(const struct unit * u)
|
||||
{
|
||||
item *itm = u->items;
|
||||
|
@ -2271,56 +2146,6 @@ bool has_horses(const struct unit * u)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool hunger(int number, unit * u)
|
||||
{
|
||||
region *r = u->region;
|
||||
int dead = 0, hpsub = 0;
|
||||
int hp = u->hp / u->number;
|
||||
static const char *damage = 0;
|
||||
static const char *rcdamage = 0;
|
||||
static const race *rc = 0;
|
||||
|
||||
if (!damage) {
|
||||
damage = get_param(global.parameters, "hunger.damage");
|
||||
if (damage == NULL)
|
||||
damage = "1d12+12";
|
||||
}
|
||||
if (rc != u_race(u)) {
|
||||
rcdamage = get_param(u_race(u)->parameters, "hunger.damage");
|
||||
rc = u_race(u);
|
||||
}
|
||||
|
||||
while (number--) {
|
||||
int dam = dice_rand(rcdamage ? rcdamage : damage);
|
||||
if (dam >= hp) {
|
||||
++dead;
|
||||
}
|
||||
else {
|
||||
hpsub += dam;
|
||||
}
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
/* Gestorbene aus der Einheit nehmen,
|
||||
* Sie bekommen keine Beerdingung. */
|
||||
ADDMSG(&u->faction->msgs, msg_message("starvation",
|
||||
"unit region dead live", u, r, dead, u->number - dead));
|
||||
|
||||
scale_number(u, u->number - dead);
|
||||
deathcounts(r, dead);
|
||||
}
|
||||
if (hpsub > 0) {
|
||||
/* Jetzt die Schäden der nicht gestorbenen abziehen. */
|
||||
u->hp -= hpsub;
|
||||
/* Meldung nur, wenn noch keine für Tote generiert. */
|
||||
if (dead == 0) {
|
||||
/* Durch unzureichende Ernährung wird %s geschwächt */
|
||||
ADDMSG(&u->faction->msgs, msg_message("malnourish", "unit region", u, r));
|
||||
}
|
||||
}
|
||||
return (dead || hpsub);
|
||||
}
|
||||
|
||||
void plagues(region * r, bool ismagic)
|
||||
{
|
||||
int peasants;
|
||||
|
@ -2653,51 +2478,6 @@ int maintenance_cost(const struct unit *u)
|
|||
return u_race(u)->maintenance * u->number;
|
||||
}
|
||||
|
||||
message *movement_error(unit * u, const char *token, order * ord,
|
||||
int error_code)
|
||||
{
|
||||
direction_t d;
|
||||
switch (error_code) {
|
||||
case E_MOVE_BLOCKED:
|
||||
d = get_direction(token, u->faction->locale);
|
||||
return msg_message("moveblocked", "unit direction", u, d);
|
||||
case E_MOVE_NOREGION:
|
||||
return msg_feedback(u, ord, "unknowndirection", "dirname", token);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool move_blocked(const unit * u, const region * r, const region * r2)
|
||||
{
|
||||
connection *b;
|
||||
curse *c;
|
||||
static const curse_type *fogtrap_ct = NULL;
|
||||
|
||||
if (r2 == NULL)
|
||||
return true;
|
||||
b = get_borders(r, r2);
|
||||
while (b) {
|
||||
if (b->type->block && b->type->block(b, u, r))
|
||||
return true;
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
if (fogtrap_ct == NULL)
|
||||
fogtrap_ct = ct_find("fogtrap");
|
||||
c = get_curse(r->attribs, fogtrap_ct);
|
||||
if (curse_active(c))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void add_income(unit * u, int type, int want, int qty)
|
||||
{
|
||||
if (want == INT_MAX)
|
||||
want = qty;
|
||||
ADDMSG(&u->faction->msgs, msg_message("income",
|
||||
"unit region mode wanted amount", u, u->region, type, want, qty));
|
||||
}
|
||||
|
||||
int produceexp(struct unit *u, skill_t sk, int n)
|
||||
{
|
||||
if (global.producexpchance > 0.0F) {
|
||||
|
@ -2829,20 +2609,12 @@ int entertainmoney(const region * r)
|
|||
|
||||
int rule_give(void)
|
||||
{
|
||||
static int value = -1;
|
||||
if (value < 0) {
|
||||
value = get_param_int(global.parameters, "rules.give", GIVE_DEFAULT);
|
||||
}
|
||||
return value;
|
||||
return get_param_int(global.parameters, "rules.give", GIVE_DEFAULT);
|
||||
}
|
||||
|
||||
int markets_module(void)
|
||||
{
|
||||
static int value = -1;
|
||||
if (value < 0) {
|
||||
value = get_param_int(global.parameters, "modules.markets", 0);
|
||||
}
|
||||
return value;
|
||||
return get_param_int(global.parameters, "modules.markets", 0);
|
||||
}
|
||||
|
||||
/** releases all memory associated with the game state.
|
||||
|
|
|
@ -46,8 +46,7 @@ extern "C" {
|
|||
# define MAXUNITS 1048573 /* must be prime for hashing. 524287 was >90% full */
|
||||
#endif
|
||||
|
||||
#define MAXPEASANTS_PER_AREA 10 /* number of peasants per region-size */
|
||||
#define TREESIZE (MAXPEASANTS_PER_AREA-2) /* space used by trees (in #peasants) */
|
||||
#define TREESIZE (8) /* space used by trees (in #peasants) */
|
||||
|
||||
#define PEASANTFORCE 0.75 /* Chance einer Vermehrung trotz 90% Auslastung */
|
||||
#define HERBROTCHANCE 5 /* Verrottchance für Kräuter (ifdef HERBS_ROT) */
|
||||
|
@ -107,10 +106,6 @@ extern "C" {
|
|||
|
||||
#define i2b(i) ((bool)((i)?(true):(false)))
|
||||
|
||||
void remove_empty_units_in_region(struct region *r);
|
||||
void remove_empty_units(void);
|
||||
void remove_empty_factions(void);
|
||||
|
||||
typedef struct strlist {
|
||||
struct strlist *next;
|
||||
char *s;
|
||||
|
@ -177,7 +172,7 @@ extern "C" {
|
|||
|
||||
int distribute(int old, int new_value, int n);
|
||||
void init_locales(void);
|
||||
void init_locale(const struct locale *lang);
|
||||
void init_locale(struct locale *lang);
|
||||
|
||||
int newunitid(void);
|
||||
int forbiddenid(int id);
|
||||
|
@ -324,8 +319,6 @@ extern "C" {
|
|||
*/
|
||||
unsigned int guard_flags(const struct unit *u);
|
||||
|
||||
bool hunger(int number, struct unit *u);
|
||||
int lifestyle(const struct unit *);
|
||||
int besieged(const struct unit *u);
|
||||
int maxworkingpeasants(const struct region *r);
|
||||
bool has_horses(const struct unit *u);
|
||||
|
@ -333,11 +326,6 @@ extern "C" {
|
|||
int wage(const struct region *r, const struct faction *f,
|
||||
const struct race *rc, int in_turn);
|
||||
int maintenance_cost(const struct unit *u);
|
||||
struct message *movement_error(struct unit *u, const char *token,
|
||||
struct order *ord, int error_code);
|
||||
bool move_blocked(const struct unit *u, const struct region *src,
|
||||
const struct region *dest);
|
||||
void add_income(struct unit *u, int type, int want, int qty);
|
||||
|
||||
const char *datapath(void);
|
||||
void set_datapath(const char *path);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2014, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
@ -251,7 +251,7 @@ extern "C" {
|
|||
bool is_cursed_internal(struct attrib *ap, const curse_type * ctype);
|
||||
/* ignoriert CURSE_ISNEW */
|
||||
|
||||
extern void remove_curse(struct attrib **ap, const struct curse *c);
|
||||
bool remove_curse(struct attrib **ap, const struct curse *c);
|
||||
/* löscht einen konkreten Spruch auf einem Objekt.
|
||||
*/
|
||||
|
||||
|
@ -280,11 +280,6 @@ extern "C" {
|
|||
* unterschiedlich gewünscht sein
|
||||
* */
|
||||
|
||||
extern struct curse *get_cursex(struct attrib *ap, const curse_type * ctype,
|
||||
variant data, bool(*compare) (const struct curse *, variant));
|
||||
/* gibt pointer auf die erste curse-struct zurück, deren Typ ctype ist,
|
||||
* und für die compare() true liefert, oder einen NULL-pointer.
|
||||
* */
|
||||
extern struct curse *get_curse(struct attrib *ap, const curse_type * ctype);
|
||||
/* gibt pointer auf die erste curse-struct zurück, deren Typ ctype ist,
|
||||
* oder einen NULL-pointer
|
||||
|
@ -303,9 +298,6 @@ extern "C" {
|
|||
extern void curse_done(struct attrib *a);
|
||||
extern int curse_age(struct attrib *a);
|
||||
|
||||
extern bool cmp_curse(const struct attrib *a, const void *data);
|
||||
extern bool cmp_cursetype(const struct attrib *a, const void *data);
|
||||
|
||||
extern float destr_curse(struct curse *c, int cast_level, float force);
|
||||
|
||||
extern int resolve_curse(variant data, void *address);
|
||||
|
|
|
@ -126,7 +126,13 @@ faction *get_or_create_monsters(void)
|
|||
if (!f) {
|
||||
const race *rc = rc_find("dragon");
|
||||
|
||||
f = addfaction("noreply@eressea.de", NULL, rc, NULL, 0);
|
||||
const char *email = get_param(global.parameters, "monster.email");
|
||||
if (email) {
|
||||
f = addfaction(email, NULL, rc, NULL, 0);
|
||||
}
|
||||
else {
|
||||
f = addfaction("noreply@eressea.de", NULL, rc, NULL, 0);
|
||||
}
|
||||
renumber_faction(f, 666);
|
||||
faction_setname(f, "Monster");
|
||||
f->options = 0;
|
||||
|
@ -340,7 +346,7 @@ void destroyfaction(faction * f)
|
|||
if (rc->ec_flags & ECF_REC_HORSES) { /* Zentauren an die Pferde */
|
||||
h += u->number;
|
||||
}
|
||||
else { /* Orks zählen nur zur Hälfte */
|
||||
else { /* Orks zählen nur zur Hälfte */
|
||||
p += (int)(u->number * rc->recruit_multi);
|
||||
}
|
||||
for (itm = u->items; itm; itm = itm->next) {
|
||||
|
@ -365,7 +371,7 @@ void destroyfaction(faction * f)
|
|||
group *g;
|
||||
ally *sf, *sfn;
|
||||
|
||||
/* Alle HELFE für die Partei löschen */
|
||||
/* Alle HELFE für die Partei löschen */
|
||||
for (sf = ff->allies; sf; sf = sf->next) {
|
||||
if (sf->faction == f) {
|
||||
removelist(&ff->allies, sf);
|
||||
|
@ -601,3 +607,57 @@ int skill_limit(faction * f, skill_t sk)
|
|||
return m;
|
||||
}
|
||||
|
||||
void remove_empty_factions(void)
|
||||
{
|
||||
faction **fp, *f3;
|
||||
|
||||
for (fp = &factions; *fp;) {
|
||||
faction *f = *fp;
|
||||
/* monster (0) werden nicht entfernt. alive kann beim readgame
|
||||
* () auf 0 gesetzt werden, wenn monsters keine einheiten mehr
|
||||
* haben. */
|
||||
if ((f->units == NULL || f->alive == 0) && !is_monsters(f)) {
|
||||
ursprung *ur = f->ursprung;
|
||||
while (ur && ur->id != 0)
|
||||
ur = ur->next;
|
||||
if (verbosity >= 2)
|
||||
log_printf(stdout, "\t%s\n", factionname(f));
|
||||
|
||||
/* Einfach in eine Datei schreiben und später vermailen */
|
||||
|
||||
for (f3 = factions; f3; f3 = f3->next) {
|
||||
ally *sf;
|
||||
group *g;
|
||||
ally **sfp = &f3->allies;
|
||||
while (*sfp) {
|
||||
sf = *sfp;
|
||||
if (sf->faction == f || sf->faction == NULL) {
|
||||
*sfp = sf->next;
|
||||
free(sf);
|
||||
}
|
||||
else
|
||||
sfp = &(*sfp)->next;
|
||||
}
|
||||
for (g = f3->groups; g; g = g->next) {
|
||||
sfp = &g->allies;
|
||||
while (*sfp) {
|
||||
sf = *sfp;
|
||||
if (sf->faction == f || sf->faction == NULL) {
|
||||
*sfp = sf->next;
|
||||
free(sf);
|
||||
}
|
||||
else
|
||||
sfp = &(*sfp)->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*fp = f->next;
|
||||
funhash(f);
|
||||
free_faction(f);
|
||||
free(f);
|
||||
}
|
||||
else
|
||||
fp = &(*fp)->next;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#define H_KRNL_FACTION
|
||||
|
||||
#include "skill.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -48,10 +49,10 @@ extern "C" {
|
|||
#define FFL_NOIDLEOUT (1<<24) /* Partei stirbt nicht an NMRs */
|
||||
#define FFL_NPC (1<<25) /* eine Partei mit Monstern */
|
||||
#define FFL_DBENTRY (1<<28) /* Partei ist in Datenbank eingetragen */
|
||||
#define FFL_NOTIMEOUT (1<<29) /* ignore MaxAge() */
|
||||
|
||||
#define FFL_GM (1<<30) /* eine Partei mit Sonderrechten */
|
||||
|
||||
#define FFL_SAVEMASK (FFL_DEFENDER|FFL_NEWID|FFL_GM|FFL_NPC|FFL_NOTIMEOUT|FFL_DBENTRY|FFL_NOIDLEOUT)
|
||||
#define FFL_SAVEMASK (FFL_DEFENDER|FFL_NEWID|FFL_GM|FFL_NPC|FFL_DBENTRY|FFL_NOIDLEOUT)
|
||||
|
||||
#define is_monsters(f) ((f)->flags&FFL_NPC)
|
||||
|
||||
|
@ -136,6 +137,7 @@ void destroyfaction(faction * f);
|
|||
|
||||
extern void renumber_faction(faction * f, int no);
|
||||
void free_faction(struct faction *f);
|
||||
void remove_empty_factions(void);
|
||||
|
||||
#ifdef SMART_INTERVALS
|
||||
extern void update_interval(struct faction *f, struct region *r);
|
||||
|
|
|
@ -1,12 +1,71 @@
|
|||
#include <platform.h>
|
||||
|
||||
#include <kernel/ally.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/types.h>
|
||||
#include <kernel/race.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/config.h>
|
||||
#include <util/language.h>
|
||||
#include "faction.h"
|
||||
|
||||
#include <CuTest.h>
|
||||
#include <tests.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void test_remove_empty_factions_allies(CuTest *tc) {
|
||||
faction *f1, *f2;
|
||||
region *r;
|
||||
|
||||
test_cleanup();
|
||||
r = test_create_region(0, 0, 0);
|
||||
f1 = test_create_faction(0);
|
||||
test_create_unit(f1, r);
|
||||
f2 = test_create_faction(0);
|
||||
ally_add(&f1->allies, f2);
|
||||
remove_empty_factions();
|
||||
CuAssertPtrEquals(tc, 0, f1->allies);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_remove_empty_factions(CuTest *tc) {
|
||||
faction *f, *fm;
|
||||
int fno;
|
||||
|
||||
test_cleanup();
|
||||
fm = get_or_create_monsters();
|
||||
assert(fm);
|
||||
f = test_create_faction(0);
|
||||
fno = f->no;
|
||||
remove_empty_factions();
|
||||
CuAssertPtrEquals(tc, 0, findfaction(fno));
|
||||
CuAssertPtrEquals(tc, fm, get_monsters());
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_remove_dead_factions(CuTest *tc) {
|
||||
faction *f, *fm;
|
||||
region *r;
|
||||
|
||||
test_cleanup();
|
||||
r = test_create_region(0, 0, 0);
|
||||
fm = get_or_create_monsters();
|
||||
f = test_create_faction(0);
|
||||
assert(fm && r && f);
|
||||
test_create_unit(f, r);
|
||||
test_create_unit(fm, r);
|
||||
remove_empty_factions();
|
||||
CuAssertPtrEquals(tc, f, findfaction(f->no));
|
||||
CuAssertPtrNotNull(tc, get_monsters());
|
||||
fm->alive = 0;
|
||||
f->alive = 0;
|
||||
remove_empty_factions();
|
||||
CuAssertPtrEquals(tc, 0, findfaction(f->no));
|
||||
CuAssertPtrEquals(tc, fm, get_monsters());
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_addfaction(CuTest *tc) {
|
||||
faction *f = 0;
|
||||
const struct race *rc = rc_get_or_create("human");
|
||||
|
@ -51,6 +110,9 @@ CuSuite *get_faction_suite(void)
|
|||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_addfaction);
|
||||
SUITE_ADD_TEST(suite, test_remove_empty_factions);
|
||||
SUITE_ADD_TEST(suite, test_remove_empty_factions_allies);
|
||||
SUITE_ADD_TEST(suite, test_remove_dead_factions);
|
||||
SUITE_ADD_TEST(suite, test_get_monsters);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -186,7 +186,7 @@ resource_type *rt_get_or_create(const char *name) {
|
|||
return rtype;
|
||||
}
|
||||
|
||||
void it_register(item_type * itype)
|
||||
static void it_register(item_type * itype)
|
||||
{
|
||||
char buffer[64];
|
||||
const char * name = itype->rtype->_name;
|
||||
|
@ -270,6 +270,7 @@ weapon_type *new_weapontype(item_type * itype,
|
|||
{
|
||||
weapon_type *wtype;
|
||||
|
||||
assert(minskill > 0);
|
||||
assert(resource2weapon(itype->rtype) == NULL);
|
||||
|
||||
wtype = calloc(sizeof(weapon_type), 1);
|
||||
|
|
|
@ -229,8 +229,6 @@ extern "C" {
|
|||
item_type *it_find(const char *name);
|
||||
|
||||
void it_set_appearance(item_type *itype, const char *appearance);
|
||||
void it_register(item_type * it);
|
||||
void wt_register(weapon_type * wt);
|
||||
|
||||
extern const item_type *resource2item(const resource_type * rtype);
|
||||
extern const resource_type *item2resource(const item_type * i);
|
||||
|
|
|
@ -496,7 +496,7 @@ static void json_strings(cJSON *json) {
|
|||
return;
|
||||
}
|
||||
for (child=json->child;child;child=child->next) {
|
||||
if ((child->type==cJSON_Object)) {
|
||||
if (child->type==cJSON_Object) {
|
||||
struct locale *lang = get_or_create_locale(child->string);
|
||||
json_locale(child, lang);
|
||||
} else {
|
||||
|
@ -558,7 +558,7 @@ static void json_skill(cJSON *json, struct locale *lang) {
|
|||
cJSON *entry;
|
||||
for (entry=child->child;entry;entry=entry->next) {
|
||||
init_skill(lang, sk, entry->valuestring);
|
||||
if ((entry==child->child)) {
|
||||
if (entry==child->child) {
|
||||
locale_setstring(lang, mkname("skill", skillnames[sk]), entry->valuestring);
|
||||
}
|
||||
}
|
||||
|
@ -588,7 +588,7 @@ static void json_keyword(cJSON *json, struct locale *lang) {
|
|||
cJSON *entry;
|
||||
for (entry=child->child;entry;entry=entry->next) {
|
||||
init_keyword(lang, kwd, entry->valuestring);
|
||||
if ((entry==child->child)) {
|
||||
if (entry==child->child) {
|
||||
locale_setstring(lang, mkname("keyword", keywords[kwd]), entry->valuestring);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ arg_set(variant args[], const message_type * mtype, const char *buffer,
|
|||
if (i != mtype->nparameters) {
|
||||
args[i] = v;
|
||||
} else {
|
||||
fprintf(stderr, "invalid parameter %s for message type %s\n", buffer,
|
||||
log_error("invalid parameter %s for message type %s\n", buffer,
|
||||
mtype->name);
|
||||
assert(!"program aborted.");
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ caddmessage(region * r, faction * f, const char *s, msg_t mtype, int level)
|
|||
m = add_message(&f->msgs, msg_message("msg_economy", "string", s));
|
||||
break;
|
||||
case MSG_BATTLE:
|
||||
assert(0 || !"battle-meldungen nicht über addmessage machen");
|
||||
assert(0 || !"battle messages must not use addmessage");
|
||||
break;
|
||||
case MSG_MOVE:
|
||||
assert(f);
|
||||
|
@ -240,7 +240,7 @@ caddmessage(region * r, faction * f, const char *s, msg_t mtype, int level)
|
|||
}
|
||||
break;
|
||||
default:
|
||||
assert(!"Ungültige Msg-Klasse!");
|
||||
assert(!"invalid message class");
|
||||
}
|
||||
if (m)
|
||||
msg_release(m);
|
||||
|
|
|
@ -277,7 +277,7 @@ rel_to_abs(const struct plane *pl, const struct faction *f, int rel,
|
|||
return (rel + ursprung_y(f, pl, NULL) + plane_center_y(pl));
|
||||
}
|
||||
|
||||
int resolve_plane(variant id, void *addr)
|
||||
static int resolve_plane(variant id, void *addr)
|
||||
{
|
||||
int result = 0;
|
||||
plane *pl = NULL;
|
||||
|
|
|
@ -74,7 +74,6 @@ extern "C" {
|
|||
extern int rel_to_abs(const struct plane *pl, const struct faction *f,
|
||||
int rel, unsigned char index);
|
||||
extern bool is_watcher(const struct plane *p, const struct faction *f);
|
||||
extern int resolve_plane(variant data, void *addr);
|
||||
extern void write_plane_reference(const plane * p, struct storage *store);
|
||||
extern int read_plane_reference(plane ** pp, struct storage *store);
|
||||
extern int plane_width(const plane * pl);
|
||||
|
|
|
@ -119,7 +119,7 @@ extern "C" {
|
|||
|
||||
typedef struct race {
|
||||
struct param *parameters;
|
||||
const char *_name; /* neu: name[4]völker */
|
||||
const char *_name;
|
||||
float magres;
|
||||
float maxaura; /* Faktor auf Maximale Aura */
|
||||
float regaura; /* Faktor auf Regeneration */
|
||||
|
|
|
@ -1187,7 +1187,7 @@ void terraform_region(region * r, const terrain_type * terrain)
|
|||
int production(const region * r)
|
||||
{
|
||||
/* muß rterrain(r) sein, nicht rterrain() wegen rekursion */
|
||||
int p = r->terrain->size / MAXPEASANTS_PER_AREA;
|
||||
int p = r->terrain->size;
|
||||
if (curse_active(get_curse(r->attribs, ct_find("drought"))))
|
||||
p /= 2;
|
||||
|
||||
|
|
|
@ -113,8 +113,6 @@ char *rns(FILE * f, char *c, size_t size)
|
|||
return c;
|
||||
}
|
||||
|
||||
extern unsigned int __at_hashkey(const char *s);
|
||||
|
||||
|
||||
static unit *unitorders(FILE * F, int enc, struct faction *f)
|
||||
{
|
||||
|
@ -1312,10 +1310,9 @@ faction *readfaction(struct gamedata * data)
|
|||
READ_INT(data->store, &f->flags);
|
||||
if (data->version < INTFLAGS_VERSION) {
|
||||
if (f->no == 0 || f->no == 666) {
|
||||
f->flags = FFL_NPC | FFL_NOTIMEOUT | FFL_NOIDLEOUT;
|
||||
f->flags = FFL_NPC | FFL_NOIDLEOUT;
|
||||
}
|
||||
}
|
||||
assert((f->flags&FFL_SAVEMASK) == f->flags);
|
||||
|
||||
a_read(data->store, &f->attribs, f);
|
||||
if (data->version >= CLAIM_VERSION) {
|
||||
|
|
|
@ -232,9 +232,8 @@ int gift_items(unit * u, int flags)
|
|||
int rule = rule_give();
|
||||
|
||||
assert(u->region);
|
||||
assert(u->faction);
|
||||
|
||||
if ((u->faction->flags & FFL_QUIT) == 0 || (rule & GIVE_ONDEATH) == 0) {
|
||||
if ((rule & GIVE_ONDEATH) == 0 || !u->faction || (u->faction->flags & FFL_QUIT) == 0) {
|
||||
if ((rule & GIVE_ALLITEMS) == 0 && (flags & GIFT_FRIENDS))
|
||||
flags -= GIFT_FRIENDS;
|
||||
if ((rule & GIVE_PEASANTS) == 0 && (flags & GIFT_PEASANTS))
|
||||
|
@ -1280,12 +1279,11 @@ static int att_modification(const unit * u, skill_t sk)
|
|||
return (int)result;
|
||||
}
|
||||
|
||||
int
|
||||
get_modifier(const unit * u, skill_t sk, int level, const region * r,
|
||||
bool noitem)
|
||||
int get_modifier(const unit * u, skill_t sk, int level, const region * r, bool noitem)
|
||||
{
|
||||
int bskill = level;
|
||||
int skill = bskill;
|
||||
int hunger_red_skill = -1;
|
||||
|
||||
if (r && sk == SK_STEALTH) {
|
||||
plane *pl = rplane(r);
|
||||
|
@ -1302,11 +1300,18 @@ bool noitem)
|
|||
}
|
||||
skill = skillmod(u->attribs, u, r, sk, skill, SMF_ALWAYS);
|
||||
|
||||
#ifdef HUNGER_REDUCES_SKILL
|
||||
if (fval(u, UFL_HUNGER)) {
|
||||
skill = skill / 2;
|
||||
if (hunger_red_skill == -1) {
|
||||
hunger_red_skill = get_param_int(global.parameters, "rules.hunger.reduces_skill", 2);
|
||||
}
|
||||
|
||||
if (fval(u, UFL_HUNGER) && hunger_red_skill) {
|
||||
if (sk == SK_SAILING && skill > 2 && hunger_red_skill == 2) {
|
||||
skill = skill - 1;
|
||||
}
|
||||
else {
|
||||
skill = skill / 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return skill - bskill;
|
||||
}
|
||||
|
||||
|
@ -1780,3 +1785,33 @@ int effskill(const unit * u, skill_t sk)
|
|||
return eff_skill(u, sk, u->region);
|
||||
}
|
||||
|
||||
void remove_empty_units_in_region(region * r)
|
||||
{
|
||||
unit **up = &r->units;
|
||||
|
||||
while (*up) {
|
||||
unit *u = *up;
|
||||
|
||||
if (u->number) {
|
||||
faction *f = u->faction;
|
||||
if (f == NULL || !f->alive) {
|
||||
set_number(u, 0);
|
||||
}
|
||||
}
|
||||
if ((u->number == 0 && u_race(u) != get_race(RC_SPELL)) || (u->age <= 0
|
||||
&& u_race(u) == get_race(RC_SPELL))) {
|
||||
remove_unit(up, u);
|
||||
}
|
||||
if (*up == u)
|
||||
up = &u->next;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_empty_units(void)
|
||||
{
|
||||
region *r;
|
||||
|
||||
for (r = regions; r; r = r->next) {
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,6 +238,8 @@ extern "C" {
|
|||
|
||||
struct spellbook * unit_get_spellbook(const struct unit * u);
|
||||
void unit_add_spell(struct unit * u, struct sc_mage * m, struct spell * sp, int level);
|
||||
void remove_empty_units_in_region(struct region * r);
|
||||
void remove_empty_units(void);
|
||||
|
||||
extern struct attrib_type at_creator;
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include <platform.h>
|
||||
#include <kernel/config.h>
|
||||
#include "alchemy.h"
|
||||
#include "faction.h"
|
||||
#include "unit.h"
|
||||
#include "item.h"
|
||||
#include "race.h"
|
||||
#include "region.h"
|
||||
|
||||
#include <CuTest.h>
|
||||
|
@ -11,6 +13,94 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static void test_remove_empty_units(CuTest *tc) {
|
||||
unit *u;
|
||||
int uid;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
|
||||
uid = u->no;
|
||||
remove_empty_units();
|
||||
CuAssertPtrNotNull(tc, findunit(uid));
|
||||
u->number = 0;
|
||||
remove_empty_units();
|
||||
CuAssertPtrEquals(tc, 0, findunit(uid));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_remove_empty_units_in_region(CuTest *tc) {
|
||||
unit *u;
|
||||
int uid;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
|
||||
uid = u->no;
|
||||
remove_empty_units_in_region(u->region);
|
||||
CuAssertPtrNotNull(tc, findunit(uid));
|
||||
u->number = 0;
|
||||
remove_empty_units_in_region(u->region);
|
||||
CuAssertPtrEquals(tc, 0, findunit(uid));
|
||||
CuAssertPtrEquals(tc, 0, u->region);
|
||||
CuAssertPtrEquals(tc, 0, u->faction);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_remove_units_without_faction(CuTest *tc) {
|
||||
unit *u;
|
||||
int uid;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
|
||||
uid = u->no;
|
||||
u_setfaction(u, 0);
|
||||
remove_empty_units_in_region(u->region);
|
||||
CuAssertPtrEquals(tc, 0, findunit(uid));
|
||||
CuAssertIntEquals(tc, 0, u->number);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_remove_units_with_dead_faction(CuTest *tc) {
|
||||
unit *u;
|
||||
int uid;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
|
||||
uid = u->no;
|
||||
u->faction->alive = false;
|
||||
remove_empty_units_in_region(u->region);
|
||||
CuAssertPtrEquals(tc, 0, findunit(uid));
|
||||
CuAssertIntEquals(tc, 0, u->number);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_remove_units_ignores_spells(CuTest *tc) {
|
||||
unit *u;
|
||||
int uid;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
u = create_unit(findregion(0, 0), test_create_faction(test_create_race("human")), 1, get_race(RC_SPELL), 0, 0, 0);
|
||||
uid = u->no;
|
||||
u->number = 0;
|
||||
u->age = 1;
|
||||
remove_empty_units_in_region(u->region);
|
||||
CuAssertPtrNotNull(tc, findunit(uid));
|
||||
CuAssertPtrNotNull(tc, u->region);
|
||||
u->age = 0;
|
||||
remove_empty_units_in_region(u->region);
|
||||
CuAssertPtrEquals(tc, 0, findunit(uid));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_scale_number(CuTest *tc) {
|
||||
unit *u;
|
||||
const struct potion_type *ptype;
|
||||
|
@ -37,5 +127,10 @@ CuSuite *get_unit_suite(void)
|
|||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_scale_number);
|
||||
SUITE_ADD_TEST(suite, test_remove_empty_units);
|
||||
SUITE_ADD_TEST(suite, test_remove_units_ignores_spells);
|
||||
SUITE_ADD_TEST(suite, test_remove_units_without_faction);
|
||||
SUITE_ADD_TEST(suite, test_remove_units_with_dead_faction);
|
||||
SUITE_ADD_TEST(suite, test_remove_empty_units_in_region);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -631,7 +631,7 @@ static weapon_type *xml_readweapon(xmlXPathContextPtr xpath, item_type * itype)
|
|||
xmlChar *propValue;
|
||||
int k;
|
||||
skill_t sk;
|
||||
int minskill = xml_ivalue(node, "minskill", 0);
|
||||
int minskill = xml_ivalue(node, "minskill", 1);
|
||||
int offmod = xml_ivalue(node, "offmod", 0);
|
||||
int defmod = xml_ivalue(node, "defmod", 0);
|
||||
int reload = xml_ivalue(node, "reload", 0);
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#include <platform.h>
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/config.h"
|
||||
#include "keyword.h"
|
||||
#include "util/language.h"
|
||||
#include "tests.h"
|
||||
|
||||
#include <critbit.h>
|
||||
#include <CuTest.h>
|
||||
|
||||
static void test_init_keywords(CuTest *tc) {
|
||||
|
@ -51,6 +53,24 @@ static void test_get_keyword_default(CuTest *tc) {
|
|||
CuAssertIntEquals(tc, K_STUDY, get_keyword("study", lang));
|
||||
}
|
||||
|
||||
static void test_get_shortest_match(CuTest *tc) {
|
||||
struct locale *lang;
|
||||
critbit_tree ** cb;
|
||||
|
||||
test_cleanup();
|
||||
lang = get_or_create_locale("en");
|
||||
|
||||
cb = (critbit_tree **)get_translations(lang, UT_KEYWORDS);
|
||||
/* note that the english order is FIGHT, not COMBAT, so this is a poor example */
|
||||
add_translation(cb, "COMBAT", K_STATUS);
|
||||
add_translation(cb, "COMBATSPELL", K_COMBATSPELL);
|
||||
|
||||
CuAssertIntEquals(tc, NOKEYWORD, get_keyword("", lang));
|
||||
CuAssertIntEquals(tc, K_STATUS, get_keyword("COM", lang));
|
||||
CuAssertIntEquals(tc, K_STATUS, get_keyword("COMBAT", lang));
|
||||
CuAssertIntEquals(tc, K_COMBATSPELL, get_keyword("COMBATS", lang));
|
||||
}
|
||||
|
||||
#define SUITE_DISABLE_TEST(suite, test) (void)test
|
||||
|
||||
CuSuite *get_keyword_suite(void)
|
||||
|
@ -59,6 +79,7 @@ CuSuite *get_keyword_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_init_keyword);
|
||||
SUITE_ADD_TEST(suite, test_init_keywords);
|
||||
SUITE_ADD_TEST(suite, test_findkeyword);
|
||||
SUITE_ADD_TEST(suite, test_get_shortest_match);
|
||||
SUITE_DISABLE_TEST(suite, test_get_keyword_default);
|
||||
return suite;
|
||||
}
|
||||
|
|
245
src/laws.c
245
src/laws.c
|
@ -22,18 +22,18 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "laws.h"
|
||||
|
||||
#include <modules/gmcmd.h>
|
||||
#include <modules/wormhole.h>
|
||||
|
||||
#include "alchemy.h"
|
||||
#include "battle.h"
|
||||
#include "economy.h"
|
||||
#include "keyword.h"
|
||||
#include "market.h"
|
||||
#include "monster.h"
|
||||
#include "move.h"
|
||||
#include "randenc.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
#include "market.h"
|
||||
#include "keyword.h"
|
||||
#include "move.h"
|
||||
#include "battle.h"
|
||||
#include "alchemy.h"
|
||||
#include "wormhole.h"
|
||||
|
||||
/* kernel includes */
|
||||
#include <kernel/alliance.h>
|
||||
|
@ -138,229 +138,6 @@ static void checkorders(void)
|
|||
ADDMSG(&f->msgs, msg_message("turnreminder", ""));
|
||||
}
|
||||
|
||||
static bool help_money(const unit * u)
|
||||
{
|
||||
if (u_race(u)->ec_flags & GIVEITEM)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void help_feed(unit * donor, unit * u, int *need_p)
|
||||
{
|
||||
int need = *need_p;
|
||||
int give = get_money(donor) - lifestyle(donor);
|
||||
give = _min(need, give);
|
||||
|
||||
if (give > 0) {
|
||||
change_money(donor, -give);
|
||||
change_money(u, give);
|
||||
need -= give;
|
||||
add_spende(donor->faction, u->faction, give, donor->region);
|
||||
}
|
||||
*need_p = need;
|
||||
}
|
||||
|
||||
enum {
|
||||
FOOD_FROM_PEASANTS = 1,
|
||||
FOOD_FROM_OWNER = 2,
|
||||
FOOD_IS_FREE = 4
|
||||
};
|
||||
|
||||
void get_food(region * r)
|
||||
{
|
||||
plane *pl = rplane(r);
|
||||
unit *u;
|
||||
int peasantfood = rpeasants(r) * 10;
|
||||
static int food_rules = -1;
|
||||
static int gamecookie = -1;
|
||||
|
||||
if (food_rules < 0 || gamecookie != global.cookie) {
|
||||
gamecookie = global.cookie;
|
||||
food_rules = get_param_int(global.parameters, "rules.economy.food", 0);
|
||||
}
|
||||
|
||||
if (food_rules & FOOD_IS_FREE) {
|
||||
return;
|
||||
}
|
||||
/* 1. Versorgung von eigenen Einheiten. Das vorhandene Silber
|
||||
* wird zunächst so auf die Einheiten aufgeteilt, dass idealerweise
|
||||
* jede Einheit genug Silber für ihren Unterhalt hat. */
|
||||
|
||||
for (u = r->units; u; u = u->next) {
|
||||
int need = lifestyle(u);
|
||||
|
||||
/* Erstmal zurücksetzen */
|
||||
freset(u, UFL_HUNGER);
|
||||
|
||||
if (u->ship && (u->ship->flags & SF_FISHING)) {
|
||||
unit *v;
|
||||
int c = 2;
|
||||
for (v = u; c > 0 && v; v = v->next) {
|
||||
if (v->ship == u->ship) {
|
||||
int get = 0;
|
||||
if (v->number <= c) {
|
||||
get = lifestyle(v);
|
||||
}
|
||||
else {
|
||||
get = lifestyle(v) * c / v->number;
|
||||
}
|
||||
if (get) {
|
||||
change_money(v, get);
|
||||
}
|
||||
}
|
||||
c -= v->number;
|
||||
}
|
||||
u->ship->flags -= SF_FISHING;
|
||||
}
|
||||
|
||||
if (food_rules & FOOD_FROM_PEASANTS) {
|
||||
faction *owner = region_get_owner(r);
|
||||
/* if the region is owned, and the owner is nice, then we'll get
|
||||
* food from the peasants - should not be used with WORK */
|
||||
if (owner != NULL && (get_alliance(owner, u->faction) & HELP_MONEY)) {
|
||||
int rm = rmoney(r);
|
||||
int use = _min(rm, need);
|
||||
rsetmoney(r, rm - use);
|
||||
need -= use;
|
||||
}
|
||||
}
|
||||
|
||||
need -= get_money(u);
|
||||
if (need > 0) {
|
||||
unit *v;
|
||||
|
||||
for (v = r->units; need && v; v = v->next) {
|
||||
if (v->faction == u->faction && help_money(v)) {
|
||||
int give = get_money(v) - lifestyle(v);
|
||||
give = _min(need, give);
|
||||
if (give > 0) {
|
||||
change_money(v, -give);
|
||||
change_money(u, give);
|
||||
need -= give;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Versorgung durch Fremde. Das Silber alliierter Einheiten wird
|
||||
* entsprechend verteilt. */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
int need = lifestyle(u);
|
||||
faction *f = u->faction;
|
||||
|
||||
need -= _max(0, get_money(u));
|
||||
|
||||
if (need > 0) {
|
||||
unit *v;
|
||||
|
||||
if (food_rules & FOOD_FROM_OWNER) {
|
||||
/* the owner of the region is the first faction to help out when you're hungry */
|
||||
faction *owner = region_get_owner(r);
|
||||
if (owner && owner != u->faction) {
|
||||
for (v = r->units; v; v = v->next) {
|
||||
if (v->faction == owner && alliedunit(v, f, HELP_MONEY)
|
||||
&& help_money(v)) {
|
||||
help_feed(v, u, &need);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (v = r->units; need && v; v = v->next) {
|
||||
if (v->faction != f && alliedunit(v, f, HELP_MONEY)
|
||||
&& help_money(v)) {
|
||||
help_feed(v, u, &need);
|
||||
}
|
||||
}
|
||||
|
||||
/* Die Einheit hat nicht genug Geld zusammengekratzt und
|
||||
* nimmt Schaden: */
|
||||
if (need > 0) {
|
||||
int lspp = lifestyle(u) / u->number;
|
||||
if (lspp > 0) {
|
||||
int number = (need + lspp - 1) / lspp;
|
||||
if (hunger(number, u))
|
||||
fset(u, UFL_HUNGER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. bestimmen, wie viele Bauern gefressen werden.
|
||||
* bei fehlenden Bauern den Dämon hungern lassen
|
||||
*/
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (u_race(u) == get_race(RC_DAEMON)) {
|
||||
int hungry = u->number;
|
||||
|
||||
/* use peasantblood before eating the peasants themselves */
|
||||
const struct potion_type *pt_blood = 0;
|
||||
const resource_type *rt_blood = rt_find("peasantblood");
|
||||
if (rt_blood) {
|
||||
pt_blood = rt_blood->ptype;
|
||||
}
|
||||
if (pt_blood) {
|
||||
/* always start with the unit itself, then the first known unit that may have some blood */
|
||||
unit *donor = u;
|
||||
while (donor != NULL && hungry > 0) {
|
||||
int blut = get_effect(donor, pt_blood);
|
||||
blut = _min(blut, hungry);
|
||||
if (blut) {
|
||||
change_effect(donor, pt_blood, -blut);
|
||||
hungry -= blut;
|
||||
}
|
||||
if (donor == u)
|
||||
donor = r->units;
|
||||
while (donor != NULL) {
|
||||
if (u_race(donor) == get_race(RC_DAEMON) && donor != u) {
|
||||
if (get_effect(donor, pt_blood)) {
|
||||
/* if he's in our faction, drain him: */
|
||||
if (donor->faction == u->faction)
|
||||
break;
|
||||
}
|
||||
}
|
||||
donor = donor->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* remaining demons feed on peasants */
|
||||
if (pl == NULL || !fval(pl, PFL_NOFEED)) {
|
||||
if (peasantfood >= hungry) {
|
||||
peasantfood -= hungry;
|
||||
hungry = 0;
|
||||
}
|
||||
else {
|
||||
hungry -= peasantfood;
|
||||
peasantfood = 0;
|
||||
}
|
||||
if (hungry > 0) {
|
||||
static int demon_hunger = -1;
|
||||
if (demon_hunger < 0) {
|
||||
demon_hunger = get_param_int(global.parameters, "hunger.demons", 0);
|
||||
}
|
||||
if (demon_hunger == 0) {
|
||||
/* demons who don't feed are hungry */
|
||||
if (hunger(hungry, u))
|
||||
fset(u, UFL_HUNGER);
|
||||
}
|
||||
else {
|
||||
/* no damage, but set the hungry-flag */
|
||||
fset(u, UFL_HUNGER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rsetpeasants(r, peasantfood / 10);
|
||||
|
||||
/* 3. Von den überlebenden das Geld abziehen: */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
int need = _min(get_money(u), lifestyle(u));
|
||||
change_money(u, -need);
|
||||
}
|
||||
}
|
||||
|
||||
static void age_unit(region * r, unit * u)
|
||||
{
|
||||
if (u_race(u) == get_race(RC_SPELL)) {
|
||||
|
@ -490,7 +267,7 @@ static void peasants(region * r)
|
|||
{
|
||||
int peasants = rpeasants(r);
|
||||
int money = rmoney(r);
|
||||
int maxp = production(r) * MAXPEASANTS_PER_AREA;
|
||||
int maxp = production(r);
|
||||
int n, satiated;
|
||||
int dead = 0;
|
||||
|
||||
|
@ -781,7 +558,7 @@ growing_trees(region * r, const int current_season, const int last_weeks_season)
|
|||
|
||||
/* Grundchance 1.0% */
|
||||
/* Jeder Elf in der Region erhöht die Chance marginal */
|
||||
elves = _min(elves, (production(r) * MAXPEASANTS_PER_AREA) / 8);
|
||||
elves = _min(elves, production(r) / 8);
|
||||
if (elves) {
|
||||
seedchance += 1.0 - pow(0.99999, elves * RESOURCE_QUANTITY);
|
||||
}
|
||||
|
@ -916,7 +693,6 @@ void demographics(void)
|
|||
for (r = regions; r; r = r->next) {
|
||||
++r->age; /* also oceans. no idea why we didn't always do that */
|
||||
live(r);
|
||||
/* check_split_dragons(); */
|
||||
|
||||
if (!fval(r->terrain, SEA_REGION)) {
|
||||
/* die Nachfrage nach Produkten steigt. */
|
||||
|
@ -2350,9 +2126,10 @@ int mail_cmd(unit * u, struct order *ord)
|
|||
int banner_cmd(unit * u, struct order *ord)
|
||||
{
|
||||
init_order(ord);
|
||||
const char * s = getstrtoken();
|
||||
|
||||
free(u->faction->banner);
|
||||
u->faction->banner = _strdup(getstrtoken());
|
||||
u->faction->banner = s ? _strdup(s) : 0;
|
||||
add_message(&u->faction->msgs, msg_message("changebanner", "value",
|
||||
u->faction->banner));
|
||||
|
||||
|
@ -2808,7 +2585,7 @@ int reshow_cmd(unit * u, struct order *ord)
|
|||
init_order(ord);
|
||||
s = getstrtoken();
|
||||
|
||||
if (isparam(s, u->faction->locale, P_ANY)) {
|
||||
if (s && isparam(s, u->faction->locale, P_ANY)) {
|
||||
p = getparam(u->faction->locale);
|
||||
s = NULL;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
extern int writepasswd(void);
|
||||
int getoption(void);
|
||||
int wanderoff(struct region *r, int p);
|
||||
void demographics(void);
|
||||
void last_orders(void);
|
||||
void find_address(void);
|
||||
|
|
269
src/laws.test.c
269
src/laws.test.c
|
@ -22,67 +22,67 @@
|
|||
|
||||
static void test_new_building_can_be_renamed(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
building *b;
|
||||
building_type *btype;
|
||||
region *r;
|
||||
building *b;
|
||||
building_type *btype;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
btype = bt_get_or_create("castle");
|
||||
r = findregion(-1, 0);
|
||||
btype = bt_get_or_create("castle");
|
||||
r = findregion(-1, 0);
|
||||
|
||||
b = new_building(btype, r, default_locale);
|
||||
CuAssertTrue(tc, !renamed_building(b));
|
||||
b = new_building(btype, r, default_locale);
|
||||
CuAssertTrue(tc, !renamed_building(b));
|
||||
}
|
||||
|
||||
static void test_rename_building(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
building *b;
|
||||
unit *u;
|
||||
faction *f;
|
||||
building_type *btype;
|
||||
region *r;
|
||||
building *b;
|
||||
unit *u;
|
||||
faction *f;
|
||||
building_type *btype;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
btype = bt_get_or_create("castle");
|
||||
btype = bt_get_or_create("castle");
|
||||
|
||||
r = findregion(-1, 0);
|
||||
b = new_building(btype, r, default_locale);
|
||||
f = test_create_faction(rc_find("human"));
|
||||
u = test_create_unit(f, r);
|
||||
u_set_building(u, b);
|
||||
r = findregion(-1, 0);
|
||||
b = new_building(btype, r, default_locale);
|
||||
f = test_create_faction(NULL);
|
||||
u = test_create_unit(f, r);
|
||||
u_set_building(u, b);
|
||||
|
||||
rename_building(u, NULL, b, "Villa Nagel");
|
||||
CuAssertStrEquals(tc, "Villa Nagel", b->name);
|
||||
rename_building(u, NULL, b, "Villa Nagel");
|
||||
CuAssertStrEquals(tc, "Villa Nagel", b->name);
|
||||
}
|
||||
|
||||
static void test_rename_building_twice(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
building *b;
|
||||
unit *u;
|
||||
faction *f;
|
||||
building_type *btype;
|
||||
region *r;
|
||||
building *b;
|
||||
unit *u;
|
||||
faction *f;
|
||||
building_type *btype;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
btype = bt_get_or_create("castle");
|
||||
btype = bt_get_or_create("castle");
|
||||
|
||||
r = findregion(-1, 0);
|
||||
b = new_building(btype, r, default_locale);
|
||||
f = test_create_faction(rc_find("human"));
|
||||
u = test_create_unit(f, r);
|
||||
u_set_building(u, b);
|
||||
r = findregion(-1, 0);
|
||||
b = new_building(btype, r, default_locale);
|
||||
f = test_create_faction(NULL);
|
||||
u = test_create_unit(f, r);
|
||||
u_set_building(u, b);
|
||||
|
||||
rename_building(u, NULL, b, "Villa Nagel");
|
||||
CuAssertStrEquals(tc, "Villa Nagel", b->name);
|
||||
rename_building(u, NULL, b, "Villa Nagel");
|
||||
CuAssertStrEquals(tc, "Villa Nagel", b->name);
|
||||
|
||||
rename_building(u, NULL, b, "Villa Kunterbunt");
|
||||
CuAssertStrEquals(tc, "Villa Kunterbunt", b->name);
|
||||
rename_building(u, NULL, b, "Villa Kunterbunt");
|
||||
CuAssertStrEquals(tc, "Villa Kunterbunt", b->name);
|
||||
}
|
||||
|
||||
static void test_fishing_feeds_2_people(CuTest * tc)
|
||||
|
@ -92,18 +92,18 @@ static void test_fishing_feeds_2_people(CuTest * tc)
|
|||
faction *f;
|
||||
unit *u;
|
||||
ship *sh;
|
||||
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
r = findregion(-1, 0);
|
||||
CuAssertStrEquals(tc, "ocean", r->terrain->_name); /* test_create_world needs coverage */
|
||||
f = test_create_faction(rc_find("human"));
|
||||
f = test_create_faction(NULL);
|
||||
u = test_create_unit(f, r);
|
||||
sh = new_ship(st_find("boat"), r, 0);
|
||||
u_set_ship(u, sh);
|
||||
rtype = get_resourcetype(R_SILVER);
|
||||
i_change(&u->items, rtype->itype, 42);
|
||||
|
||||
|
||||
scale_number(u, 1);
|
||||
sh->flags |= SF_FISHING;
|
||||
get_food(r);
|
||||
|
@ -122,7 +122,7 @@ static void test_fishing_feeds_2_people(CuTest * tc)
|
|||
|
||||
static int not_so_hungry(const unit * u)
|
||||
{
|
||||
return 6 * u->number;
|
||||
return 6 * u->number;
|
||||
}
|
||||
|
||||
static void test_fishing_does_not_give_goblins_money(CuTest * tc)
|
||||
|
@ -132,14 +132,14 @@ static void test_fishing_does_not_give_goblins_money(CuTest * tc)
|
|||
faction *f;
|
||||
unit *u;
|
||||
ship *sh;
|
||||
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
rtype = get_resourcetype(R_SILVER);
|
||||
|
||||
|
||||
r = findregion(-1, 0);
|
||||
CuAssertStrEquals(tc, "ocean", r->terrain->_name); /* test_create_world needs coverage */
|
||||
f = test_create_faction(rc_find("human"));
|
||||
f = test_create_faction(NULL);
|
||||
u = test_create_unit(f, r);
|
||||
sh = new_ship(st_find("boat"), r, 0);
|
||||
u_set_ship(u, sh);
|
||||
|
@ -159,23 +159,23 @@ static void test_fishing_gets_reset(CuTest * tc)
|
|||
faction *f;
|
||||
unit *u;
|
||||
ship *sh;
|
||||
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
rtype = get_resourcetype(R_SILVER);
|
||||
r = findregion(-1, 0);
|
||||
CuAssertStrEquals(tc, "ocean", r->terrain->_name); /* test_create_world needs coverage */
|
||||
f = test_create_faction(rc_find("human"));
|
||||
f = test_create_faction(NULL);
|
||||
u = test_create_unit(f, r);
|
||||
sh = new_ship(st_find("boat"), r, 0);
|
||||
u_set_ship(u, sh);
|
||||
i_change(&u->items, rtype->itype, 42);
|
||||
|
||||
|
||||
scale_number(u, 1);
|
||||
sh->flags |= SF_FISHING;
|
||||
get_food(r);
|
||||
CuAssertIntEquals(tc, 42, i_get(u->items, rtype->itype));
|
||||
|
||||
|
||||
scale_number(u, 1);
|
||||
get_food(r);
|
||||
CuAssertIntEquals(tc, 32, i_get(u->items, rtype->itype));
|
||||
|
@ -183,33 +183,33 @@ static void test_fishing_gets_reset(CuTest * tc)
|
|||
|
||||
static void test_unit_limit(CuTest * tc)
|
||||
{
|
||||
set_param(&global.parameters, "rules.limit.faction", "250");
|
||||
CuAssertIntEquals(tc, 250, rule_faction_limit());
|
||||
set_param(&global.parameters, "rules.limit.faction", "250");
|
||||
CuAssertIntEquals(tc, 250, rule_faction_limit());
|
||||
|
||||
set_param(&global.parameters, "rules.limit.faction", "200");
|
||||
CuAssertIntEquals(tc, 200, rule_faction_limit());
|
||||
set_param(&global.parameters, "rules.limit.faction", "200");
|
||||
CuAssertIntEquals(tc, 200, rule_faction_limit());
|
||||
|
||||
set_param(&global.parameters, "rules.limit.alliance", "250");
|
||||
CuAssertIntEquals(tc, 250, rule_alliance_limit());
|
||||
set_param(&global.parameters, "rules.limit.alliance", "250");
|
||||
CuAssertIntEquals(tc, 250, rule_alliance_limit());
|
||||
|
||||
}
|
||||
|
||||
extern int checkunitnumber(const faction * f, int add);
|
||||
static void test_cannot_create_unit_above_limit(CuTest * tc)
|
||||
{
|
||||
faction *f;
|
||||
faction *f;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
f = test_create_faction(rc_find("human"));
|
||||
set_param(&global.parameters, "rules.limit.faction", "4");
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
f = test_create_faction(NULL);
|
||||
set_param(&global.parameters, "rules.limit.faction", "4");
|
||||
|
||||
CuAssertIntEquals(tc, 0, checkunitnumber(f, 4));
|
||||
CuAssertIntEquals(tc, 2, checkunitnumber(f, 5));
|
||||
CuAssertIntEquals(tc, 0, checkunitnumber(f, 4));
|
||||
CuAssertIntEquals(tc, 2, checkunitnumber(f, 5));
|
||||
|
||||
set_param(&global.parameters, "rules.limit.alliance", "3");
|
||||
CuAssertIntEquals(tc, 0, checkunitnumber(f, 3));
|
||||
CuAssertIntEquals(tc, 1, checkunitnumber(f, 4));
|
||||
set_param(&global.parameters, "rules.limit.alliance", "3");
|
||||
CuAssertIntEquals(tc, 0, checkunitnumber(f, 3));
|
||||
CuAssertIntEquals(tc, 1, checkunitnumber(f, 4));
|
||||
}
|
||||
|
||||
static void test_reserve_cmd(CuTest *tc) {
|
||||
|
@ -225,7 +225,7 @@ static void test_reserve_cmd(CuTest *tc) {
|
|||
|
||||
rtype = get_resourcetype(R_SILVER);
|
||||
assert(rtype && rtype->itype);
|
||||
f = test_create_faction(rc_find("human"));
|
||||
f = test_create_faction(NULL);
|
||||
r = findregion(0, 0);
|
||||
assert(r && f);
|
||||
u1 = test_create_unit(f, r);
|
||||
|
@ -251,7 +251,7 @@ static void test_new_units(CuTest *tc) {
|
|||
const struct locale *loc;
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
f = test_create_faction(rc_find("human"));
|
||||
f = test_create_faction(NULL);
|
||||
r = findregion(0, 0);
|
||||
assert(r && f);
|
||||
u = test_create_unit(f, r);
|
||||
|
@ -266,6 +266,101 @@ static void test_new_units(CuTest *tc) {
|
|||
test_cleanup();
|
||||
}
|
||||
|
||||
typedef struct guard_fixture {
|
||||
unit * u;
|
||||
} guard_fixture;
|
||||
|
||||
void setup_guard(guard_fixture *fix, bool armed) {
|
||||
region *r;
|
||||
faction *f;
|
||||
unit * u;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
f = test_create_faction(NULL);
|
||||
r = findregion(0, 0);
|
||||
assert(r && f);
|
||||
u = test_create_unit(f, r);
|
||||
fset(u, UFL_GUARD);
|
||||
u->status = ST_FIGHT;
|
||||
|
||||
if (armed) {
|
||||
item_type *itype;
|
||||
itype = it_get_or_create(rt_get_or_create("sword"));
|
||||
new_weapontype(itype, 0, 0.0, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
i_change(&u->items, itype, 1);
|
||||
set_level(u, SK_MELEE, 2);
|
||||
}
|
||||
fix->u = u;
|
||||
}
|
||||
|
||||
static void test_update_guards(CuTest *tc) {
|
||||
guard_fixture fix;
|
||||
|
||||
setup_guard(&fix, true);
|
||||
|
||||
update_guards();
|
||||
CuAssertTrue(tc, fval(fix.u, UFL_GUARD));
|
||||
freset(fix.u, UFL_GUARD);
|
||||
update_guards();
|
||||
CuAssertTrue(tc, !fval(fix.u, UFL_GUARD));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_newbie_cannot_guard(CuTest *tc) {
|
||||
guard_fixture fix;
|
||||
|
||||
setup_guard(&fix, true);
|
||||
set_param(&global.parameters, "NewbieImmunity", "4");
|
||||
CuAssertTrue(tc, IsImmune(fix.u->faction));
|
||||
update_guards();
|
||||
CuAssertTrue(tc, !fval(fix.u, UFL_GUARD));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_unarmed_cannot_guard(CuTest *tc) {
|
||||
guard_fixture fix;
|
||||
|
||||
setup_guard(&fix, false);
|
||||
update_guards();
|
||||
CuAssertTrue(tc, !fval(fix.u, UFL_GUARD));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_unarmed_races_can_guard(CuTest *tc) {
|
||||
guard_fixture fix;
|
||||
race * rc;
|
||||
|
||||
setup_guard(&fix, false);
|
||||
rc = rc_get_or_create(fix.u->race_->_name);
|
||||
rc->flags |= RCF_UNARMEDGUARD;
|
||||
update_guards();
|
||||
CuAssertTrue(tc, fval(fix.u, UFL_GUARD));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_low_skill_cannot_guard(CuTest *tc) {
|
||||
guard_fixture fix;
|
||||
|
||||
setup_guard(&fix, true);
|
||||
set_level(fix.u, SK_MELEE, 1);
|
||||
fix.u->status = ST_FLEE;
|
||||
update_guards();
|
||||
CuAssertTrue(tc, !fval(fix.u, UFL_GUARD));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_fleeing_cannot_guard(CuTest *tc) {
|
||||
guard_fixture fix;
|
||||
|
||||
setup_guard(&fix, true);
|
||||
fix.u->status = ST_FLEE;
|
||||
update_guards();
|
||||
CuAssertTrue(tc, !fval(fix.u, UFL_GUARD));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_reserve_self(CuTest *tc) {
|
||||
unit *u1, *u2;
|
||||
faction *f;
|
||||
|
@ -279,7 +374,7 @@ static void test_reserve_self(CuTest *tc) {
|
|||
|
||||
rtype = get_resourcetype(R_SILVER);
|
||||
assert(rtype && rtype->itype);
|
||||
f = test_create_faction(rc_find("human"));
|
||||
f = test_create_faction(NULL);
|
||||
r = findregion(0, 0);
|
||||
assert(r && f);
|
||||
u1 = test_create_unit(f, r);
|
||||
|
@ -299,17 +394,23 @@ static void test_reserve_self(CuTest *tc) {
|
|||
|
||||
CuSuite *get_laws_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_new_building_can_be_renamed);
|
||||
SUITE_ADD_TEST(suite, test_rename_building);
|
||||
SUITE_ADD_TEST(suite, test_rename_building_twice);
|
||||
SUITE_ADD_TEST(suite, test_fishing_feeds_2_people);
|
||||
SUITE_ADD_TEST(suite, test_fishing_does_not_give_goblins_money);
|
||||
SUITE_ADD_TEST(suite, test_fishing_gets_reset);
|
||||
SUITE_ADD_TEST(suite, test_unit_limit);
|
||||
SUITE_ADD_TEST(suite, test_reserve_self);
|
||||
SUITE_ADD_TEST(suite, test_reserve_cmd);
|
||||
SUITE_ADD_TEST(suite, test_new_units);
|
||||
SUITE_ADD_TEST(suite, test_cannot_create_unit_above_limit);
|
||||
return suite;
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_new_building_can_be_renamed);
|
||||
SUITE_ADD_TEST(suite, test_rename_building);
|
||||
SUITE_ADD_TEST(suite, test_rename_building_twice);
|
||||
SUITE_ADD_TEST(suite, test_fishing_feeds_2_people);
|
||||
SUITE_ADD_TEST(suite, test_fishing_does_not_give_goblins_money);
|
||||
SUITE_ADD_TEST(suite, test_fishing_gets_reset);
|
||||
SUITE_ADD_TEST(suite, test_unit_limit);
|
||||
SUITE_ADD_TEST(suite, test_update_guards);
|
||||
SUITE_ADD_TEST(suite, test_newbie_cannot_guard);
|
||||
SUITE_ADD_TEST(suite, test_unarmed_cannot_guard);
|
||||
SUITE_ADD_TEST(suite, test_unarmed_races_can_guard);
|
||||
SUITE_ADD_TEST(suite, test_fleeing_cannot_guard);
|
||||
SUITE_ADD_TEST(suite, test_low_skill_cannot_guard);
|
||||
SUITE_ADD_TEST(suite, test_reserve_self);
|
||||
SUITE_ADD_TEST(suite, test_reserve_cmd);
|
||||
SUITE_ADD_TEST(suite, test_new_units);
|
||||
SUITE_ADD_TEST(suite, test_cannot_create_unit_above_limit);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ gmcmd.c
|
|||
museum.c
|
||||
score.c
|
||||
weather.c
|
||||
wormhole.c
|
||||
xmas.c
|
||||
)
|
||||
FOREACH(_FILE ${_FILES})
|
||||
|
|
|
@ -35,16 +35,6 @@
|
|||
|
||||
/* util includes */
|
||||
#include <util/attrib.h>
|
||||
#include <util/base36.h>
|
||||
#include <util/event.h>
|
||||
#include <util/goodies.h>
|
||||
#include <util/language.h>
|
||||
#include <util/lists.h>
|
||||
#include <util/log.h>
|
||||
#include <util/umlaut.h>
|
||||
#include <util/parser.h>
|
||||
#include <util/rng.h>
|
||||
|
||||
#include <storage.h>
|
||||
|
||||
/* libc includes */
|
||||
|
@ -56,18 +46,11 @@
|
|||
static int read_permissions(attrib * a, void *owner, struct storage *store)
|
||||
{
|
||||
attrib *attr = NULL;
|
||||
a_read(store, &attr, NULL);
|
||||
a_free(attr);
|
||||
a_read(store, &attr, owner);
|
||||
a_remove(&attr, a);
|
||||
return AT_READ_OK;
|
||||
}
|
||||
|
||||
struct attrib_type at_permissions = {
|
||||
"GM:permissions",
|
||||
NULL, NULL, NULL,
|
||||
NULL, read_permissions,
|
||||
ATF_UNIQUE
|
||||
};
|
||||
|
||||
static int read_gmcreate(attrib * a, void *owner, struct storage *store)
|
||||
{
|
||||
char zText[32];
|
||||
|
@ -75,15 +58,8 @@ static int read_gmcreate(attrib * a, void *owner, struct storage *store)
|
|||
return AT_READ_OK;
|
||||
}
|
||||
|
||||
/* at_gmcreate specifies that the owner can create items of a particular type */
|
||||
attrib_type at_gmcreate = {
|
||||
"GM:create",
|
||||
NULL, NULL, NULL,
|
||||
NULL, read_gmcreate
|
||||
};
|
||||
|
||||
void register_gmcmd(void)
|
||||
{
|
||||
at_register(&at_gmcreate);
|
||||
at_register(&at_permissions);
|
||||
at_deprecate("GM:create", read_gmcreate);
|
||||
at_deprecate("GM:permissions", read_permissions);
|
||||
}
|
||||
|
|
|
@ -1,225 +0,0 @@
|
|||
/* vi: set ts=2:
|
||||
+-------------------+
|
||||
| | Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
| Eressea PBEM host | Enno Rehling <enno@eressea.de>
|
||||
| (c) 1998 - 2004 | Katja Zedel <katze@felidae.kn-bremen.de>
|
||||
| |
|
||||
+-------------------+
|
||||
|
||||
This program may not be used, modified or distributed
|
||||
without prior permission by the authors of Eressea.
|
||||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <kernel/config.h>
|
||||
#include "settings.h"
|
||||
|
||||
#include "wormhole.h"
|
||||
|
||||
/* kernel includes */
|
||||
#include <kernel/building.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/messages.h>
|
||||
#include <kernel/plane.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/unit.h>
|
||||
#include <kernel/version.h>
|
||||
|
||||
/* util includes */
|
||||
#include <util/attrib.h>
|
||||
#include <util/language.h>
|
||||
#include <util/resolve.h>
|
||||
#include <util/rng.h>
|
||||
#include <quicklist.h>
|
||||
|
||||
#include <storage.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static bool good_region(const region * r)
|
||||
{
|
||||
return (!fval(r, RF_CHAOTIC) && r->age > 30 && rplane(r) == NULL
|
||||
&& r->units != NULL && r->land != NULL);
|
||||
}
|
||||
|
||||
static int cmp_age(const void *v1, const void *v2)
|
||||
{
|
||||
const region *r1 = (const region *)v1;
|
||||
const region *r2 = (const region *)v2;
|
||||
if (r1->age < r2->age)
|
||||
return -1;
|
||||
if (r1->age > r2->age)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct wormhole_data {
|
||||
building *entry;
|
||||
region *exit;
|
||||
} wormhole_data;
|
||||
|
||||
static void wormhole_init(struct attrib *a)
|
||||
{
|
||||
a->data.v = calloc(1, sizeof(wormhole_data));
|
||||
}
|
||||
|
||||
static void wormhole_done(struct attrib *a)
|
||||
{
|
||||
free(a->data.v);
|
||||
}
|
||||
|
||||
static int wormhole_age(struct attrib *a)
|
||||
{
|
||||
wormhole_data *data = (wormhole_data *) a->data.v;
|
||||
int maxtransport = data->entry->size;
|
||||
region *r = data->entry->region;
|
||||
unit *u = r->units;
|
||||
|
||||
for (; u != NULL && maxtransport != 0; u = u->next) {
|
||||
if (u->building == data->entry) {
|
||||
message *m = NULL;
|
||||
if (u->number > maxtransport || has_limited_skills(u)) {
|
||||
m = msg_message("wormhole_requirements", "unit region", u, u->region);
|
||||
} else if (data->exit != NULL) {
|
||||
move_unit(u, data->exit, NULL);
|
||||
maxtransport -= u->number;
|
||||
m = msg_message("wormhole_exit", "unit region", u, data->exit);
|
||||
add_message(&data->exit->msgs, m);
|
||||
}
|
||||
if (m != NULL) {
|
||||
add_message(&u->faction->msgs, m);
|
||||
msg_release(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_building(&r->buildings, data->entry);
|
||||
ADDMSG(&r->msgs, msg_message("wormhole_dissolve", "region", r));
|
||||
|
||||
/* age returns 0 if the attribute needs to be removed, !=0 otherwise */
|
||||
return AT_AGE_KEEP;
|
||||
}
|
||||
|
||||
static void
|
||||
wormhole_write(const struct attrib *a, const void *owner, struct storage *store)
|
||||
{
|
||||
wormhole_data *data = (wormhole_data *) a->data.v;
|
||||
write_building_reference(data->entry, store);
|
||||
write_region_reference(data->exit, store);
|
||||
}
|
||||
|
||||
/** conversion code, turn 573, 2008-05-23 */
|
||||
static int resolve_exit(variant id, void *address)
|
||||
{
|
||||
building *b = findbuilding(id.i);
|
||||
region **rp = address;
|
||||
if (b) {
|
||||
*rp = b->region;
|
||||
return 0;
|
||||
}
|
||||
*rp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int wormhole_read(struct attrib *a, void *owner, struct storage *store)
|
||||
{
|
||||
wormhole_data *data = (wormhole_data *) a->data.v;
|
||||
resolve_fun resolver = (global.data_version < UIDHASH_VERSION)
|
||||
? resolve_exit : resolve_region_id;
|
||||
read_fun reader = (global.data_version < UIDHASH_VERSION)
|
||||
? read_building_reference : read_region_reference;
|
||||
|
||||
int rb =
|
||||
read_reference(&data->entry, store, read_building_reference,
|
||||
resolve_building);
|
||||
int rr = read_reference(&data->exit, store, reader, resolver);
|
||||
if (rb == 0 && rr == 0) {
|
||||
if (!data->exit || !data->entry) {
|
||||
return AT_READ_FAIL;
|
||||
}
|
||||
}
|
||||
return AT_READ_OK;
|
||||
}
|
||||
|
||||
static attrib_type at_wormhole = {
|
||||
"wormhole",
|
||||
wormhole_init,
|
||||
wormhole_done,
|
||||
wormhole_age,
|
||||
wormhole_write,
|
||||
wormhole_read,
|
||||
ATF_UNIQUE
|
||||
};
|
||||
|
||||
static void
|
||||
make_wormhole(const building_type * bt_wormhole, region * r1, region * r2)
|
||||
{
|
||||
building *b1 = new_building(bt_wormhole, r1, default_locale);
|
||||
building *b2 = new_building(bt_wormhole, r2, default_locale);
|
||||
attrib *a1 = a_add(&b1->attribs, a_new(&at_wormhole));
|
||||
attrib *a2 = a_add(&b2->attribs, a_new(&at_wormhole));
|
||||
wormhole_data *d1 = (wormhole_data *) a1->data.v;
|
||||
wormhole_data *d2 = (wormhole_data *) a2->data.v;
|
||||
d1->entry = b1;
|
||||
d2->entry = b2;
|
||||
d1->exit = b2->region;
|
||||
d2->exit = b1->region;
|
||||
b1->size = bt_wormhole->maxsize;
|
||||
b2->size = bt_wormhole->maxsize;
|
||||
ADDMSG(&r1->msgs, msg_message("wormhole_appear", "region", r1));
|
||||
ADDMSG(&r2->msgs, msg_message("wormhole_appear", "region", r2));
|
||||
}
|
||||
|
||||
void create_wormholes(void)
|
||||
{
|
||||
#define WORMHOLE_CHANCE 10000
|
||||
const building_type *bt_wormhole = bt_find("wormhole");
|
||||
quicklist *ql, *rlist = 0;
|
||||
region *r = regions;
|
||||
int qi, i = 0, count = 0;
|
||||
region **match;
|
||||
|
||||
if (bt_wormhole == NULL)
|
||||
return;
|
||||
/*
|
||||
* select a list of regions. we'll sort them by age later.
|
||||
*/
|
||||
while (r != NULL) {
|
||||
int next = rng_int() % (2 * WORMHOLE_CHANCE);
|
||||
while (r != NULL && (next != 0 || !good_region(r))) {
|
||||
if (good_region(r)) {
|
||||
--next;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (r == NULL)
|
||||
break;
|
||||
ql_push(&rlist, r);
|
||||
++count;
|
||||
r = r->next;
|
||||
}
|
||||
|
||||
if (count < 2)
|
||||
return;
|
||||
|
||||
match = (region **) malloc(sizeof(region *) * count);
|
||||
|
||||
for (ql = rlist,qi = 0; i != count; ql_advance(&ql, &qi, 1)) {
|
||||
match[i++] = (region *)ql_get(ql, qi);
|
||||
}
|
||||
qsort(match, count, sizeof(region *), cmp_age);
|
||||
ql_free(rlist);
|
||||
|
||||
count /= 2;
|
||||
for (i = 0; i != count; ++i) {
|
||||
make_wormhole(bt_wormhole, match[i], match[i + count]);
|
||||
}
|
||||
free(match);
|
||||
}
|
||||
|
||||
void register_wormholes(void)
|
||||
{
|
||||
at_register(&at_wormhole);
|
||||
}
|
|
@ -151,51 +151,56 @@ static order *monster_attack(unit * u, const unit * target)
|
|||
|
||||
static order *get_money_for_dragon(region * r, unit * u, int wanted)
|
||||
{
|
||||
unit *u2;
|
||||
int n;
|
||||
|
||||
/* attackiere bewachende einheiten */
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (u2 != u && is_guard(u2, GUARD_TAX)) {
|
||||
order *ord = monster_attack(u, u2);
|
||||
if (ord)
|
||||
addlist(&u->orders, ord);
|
||||
}
|
||||
}
|
||||
|
||||
/* falls genug geld in der region ist, treiben wir steuern ein. */
|
||||
if (rmoney(r) >= wanted) {
|
||||
/* 5% chance, dass der drache aus einer laune raus attackiert */
|
||||
if (chance(1.0 - u_race(u)->aggression)) {
|
||||
return create_order(K_TAX, default_locale, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* falls der drache launisch ist, oder das regionssilber knapp, greift er alle an */
|
||||
n = 0;
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (inside_building(u2)!=u->building && u2->faction != u->faction && cansee(u->faction, r, u2, 0)) {
|
||||
int m = get_money(u2);
|
||||
if (m == 0 || is_guard(u2, GUARD_TAX))
|
||||
continue;
|
||||
else {
|
||||
order *ord = monster_attack(u, u2);
|
||||
if (ord) {
|
||||
addlist(&u->orders, ord);
|
||||
n += m;
|
||||
unit *u2;
|
||||
int n;
|
||||
double attack_chance = monster_attack_chance();
|
||||
|
||||
if (attack_chance > 0.0 && is_guard(u, GUARD_TAX)) {
|
||||
/* attackiere bewachende Einheiten nur wenn wir selbst schon bewachen */
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (u2 != u && is_guard(u2, GUARD_TAX)) {
|
||||
/*In E3 + E4 etwas problematisch, da der Regionsbesitzer immer bewacht. Der Drache greift also immer die Burg an!*/
|
||||
order *ord = monster_attack(u, u2);
|
||||
if (ord)
|
||||
addlist(&u->orders, ord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* falls die einnahmen erreicht werden, bleibt das monster noch eine
|
||||
* runde hier. */
|
||||
if (n + rmoney(r) >= wanted) {
|
||||
return create_order(K_TAX, default_locale, NULL);
|
||||
}
|
||||
/* falls genug geld in der region ist, treiben wir steuern ein. */
|
||||
if (rmoney(r) >= wanted) {
|
||||
/* 5% chance, dass der drache aus einer laune raus attackiert */
|
||||
if (attack_chance <= 0.0 || chance(1.0 - u_race(u)->aggression)) {
|
||||
/* Drachen haben in E3 und E4 keine Einnahmen. Neuer Befehl Pluendern erstmal nur fuer Monster?*/
|
||||
return create_order(K_TAX, default_locale, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* wenn wir NULL zurückliefern, macht der drache was anderes, z.b. weggehen */
|
||||
return NULL;
|
||||
/* falls der drache launisch ist, oder das regionssilber knapp, greift er alle an */
|
||||
n = 0;
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (inside_building(u2) != u->building && is_guard(u, GUARD_TAX) && u2->faction != u->faction && cansee(u->faction, r, u2, 0)) {
|
||||
int m = get_money(u2);
|
||||
if (m == 0 || is_guard(u2, GUARD_TAX) || attack_chance <= 0.0)
|
||||
continue;
|
||||
else {
|
||||
order *ord = monster_attack(u, u2);
|
||||
if (ord) {
|
||||
addlist(&u->orders, ord);
|
||||
n += m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* falls die einnahmen erreicht werden, bleibt das monster noch eine */
|
||||
/* runde hier. */
|
||||
if (n + rmoney(r) >= wanted) {
|
||||
return create_order(K_TAX, default_locale, NULL);
|
||||
}
|
||||
|
||||
/* wenn wir NULL zurueckliefern, macht der drache was anderes, z.b. weggehen */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int all_money(region * r, faction * f)
|
||||
|
@ -780,7 +785,7 @@ void plan_monsters(faction * f)
|
|||
/* Befehle müssen jede Runde neu gegeben werden: */
|
||||
free_orders(&u->orders);
|
||||
|
||||
if (attacking) {
|
||||
if (attacking && is_guard(u, GUARD_TAX)) {
|
||||
monster_attacks(u);
|
||||
}
|
||||
/* units with a plan to kill get ATTACK orders: */
|
||||
|
|
66
src/move.c
66
src/move.c
|
@ -696,7 +696,14 @@ int check_ship_allowed(struct ship *sh, const region * r)
|
|||
}
|
||||
|
||||
if (bt_harbour && buildingtype_exists(r, bt_harbour, true)) {
|
||||
return SA_HARBOUR;
|
||||
unit* harbourmaster = NULL;
|
||||
harbourmaster = owner_buildingtyp(r, bt_harbour);
|
||||
if (!harbourmaster || !sh->_owner) {
|
||||
return SA_HARBOUR;
|
||||
}
|
||||
else if ((sh->_owner->faction == harbourmaster->faction) || (ucontact(harbourmaster, sh->_owner)) || (alliedunit(harbourmaster, sh->_owner->faction, HELP_GUARD))) {
|
||||
return SA_HARBOUR;
|
||||
}
|
||||
}
|
||||
if (fval(r->terrain, SEA_REGION)) {
|
||||
return SA_COAST;
|
||||
|
@ -910,7 +917,7 @@ static unit *bewegung_blockiert_von(unit * reisender, region * r)
|
|||
if ((u->faction == reisender->faction) || (ucontact(u, reisender)) || (alliedunit(u, reisender->faction, HELP_GUARD)))
|
||||
guard_count = guard_count - u->number;
|
||||
else if (sk >= stealth) {
|
||||
guard_count =+ u->number;
|
||||
guard_count += u->number;
|
||||
double prob_u = (sk - stealth) * skill_prob;
|
||||
/* amulet counts at most once */
|
||||
prob_u += _min (1, _min(u->number, i_get(u->items, ramulet->itype))) * amulet_prob;
|
||||
|
@ -1069,6 +1076,29 @@ unit *is_guarded(region * r, unit * u, unsigned int mask)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool move_blocked(const unit * u, const region * r, const region * r2)
|
||||
{
|
||||
connection *b;
|
||||
curse *c;
|
||||
static const curse_type *fogtrap_ct = NULL;
|
||||
|
||||
if (r2 == NULL)
|
||||
return true;
|
||||
b = get_borders(r, r2);
|
||||
while (b) {
|
||||
if (b->type->block && b->type->block(b, u, r))
|
||||
return true;
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
if (fogtrap_ct == NULL)
|
||||
fogtrap_ct = ct_find("fogtrap");
|
||||
c = get_curse(r->attribs, fogtrap_ct);
|
||||
if (curse_active(c))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int movewhere(const unit * u, const char *token, region * r, region ** resultp)
|
||||
{
|
||||
region *r2;
|
||||
|
@ -1389,6 +1419,20 @@ static const region_list *reroute(unit * u, const region_list * route,
|
|||
return route;
|
||||
}
|
||||
|
||||
static message *movement_error(unit * u, const char *token, order * ord,
|
||||
int error_code)
|
||||
{
|
||||
direction_t d;
|
||||
switch (error_code) {
|
||||
case E_MOVE_BLOCKED:
|
||||
d = get_direction(token, u->faction->locale);
|
||||
return msg_message("moveblocked", "unit direction", u, d);
|
||||
case E_MOVE_NOREGION:
|
||||
return msg_feedback(u, ord, "unknowndirection", "dirname", token);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void make_route(unit * u, order * ord, region_list ** routep)
|
||||
{
|
||||
region_list **iroute = routep;
|
||||
|
@ -2020,7 +2064,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep)
|
|||
* Inland zu segeln versuchte */
|
||||
|
||||
if (sh != NULL && fval(sh, SF_MOVED)) {
|
||||
unit *hafenmeister;
|
||||
unit *harbourmaster;
|
||||
/* nachdem alle Richtungen abgearbeitet wurden, und alle Einheiten
|
||||
* transferiert wurden, kann der aktuelle Befehl gelöscht werden. */
|
||||
cycle_route(ord, u, step);
|
||||
|
@ -2049,25 +2093,25 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep)
|
|||
|
||||
/* Hafengebühren ? */
|
||||
|
||||
hafenmeister = owner_buildingtyp(current_point, bt_find("harbour"));
|
||||
if (sh && hafenmeister != NULL) {
|
||||
harbourmaster = owner_buildingtyp(current_point, bt_find("harbour"));
|
||||
if (sh && harbourmaster != NULL) {
|
||||
item *itm;
|
||||
unit *u2;
|
||||
item *trans = NULL;
|
||||
|
||||
for (u2 = current_point->units; u2; u2 = u2->next) {
|
||||
if (u2->ship == sh && !alliedunit(hafenmeister, u->faction, HELP_GUARD)) {
|
||||
if (u2->ship == sh && !alliedunit(harbourmaster, u->faction, HELP_GUARD)) {
|
||||
|
||||
if (effskill(hafenmeister, SK_PERCEPTION) > effskill(u2, SK_STEALTH)) {
|
||||
if (effskill(harbourmaster, SK_PERCEPTION) > effskill(u2, SK_STEALTH)) {
|
||||
for (itm = u2->items; itm; itm = itm->next) {
|
||||
const luxury_type *ltype = resource2luxury(itm->type->rtype);
|
||||
if (ltype != NULL && itm->number > 0) {
|
||||
int st = itm->number * effskill(hafenmeister, SK_TRADE) / 50;
|
||||
int st = itm->number * effskill(harbourmaster, SK_TRADE) / 50;
|
||||
st = _min(itm->number, st);
|
||||
|
||||
if (st > 0) {
|
||||
i_change(&u2->items, itm->type, -st);
|
||||
i_change(&hafenmeister->items, itm->type, st);
|
||||
i_change(&harbourmaster->items, itm->type, st);
|
||||
i_add(&trans, i_new(itm->type, st));
|
||||
}
|
||||
}
|
||||
|
@ -2077,10 +2121,10 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep)
|
|||
}
|
||||
if (trans) {
|
||||
message *msg =
|
||||
msg_message("harbor_trade", "unit items ship", hafenmeister, trans,
|
||||
msg_message("harbor_trade", "unit items ship", harbourmaster, trans,
|
||||
u->ship);
|
||||
add_message(&u->faction->msgs, msg);
|
||||
add_message(&hafenmeister->faction->msgs, msg);
|
||||
add_message(&harbourmaster->faction->msgs, msg);
|
||||
msg_release(msg);
|
||||
while (trans)
|
||||
i_remove(&trans, trans);
|
||||
|
|
|
@ -79,6 +79,8 @@ extern "C" {
|
|||
const struct building_type *bt, bool working);
|
||||
struct unit *owner_buildingtyp(const struct region *r,
|
||||
const struct building_type *bt);
|
||||
bool move_blocked(const struct unit *u, const struct region *src,
|
||||
const struct region *dest);
|
||||
|
||||
#define SA_HARBOUR 2
|
||||
#define SA_COAST 1
|
||||
|
|
191
src/move.test.c
191
src/move.test.c
|
@ -3,10 +3,13 @@
|
|||
#include <stdlib.h>
|
||||
#include "move.h"
|
||||
|
||||
#include <kernel/ally.h>
|
||||
#include <kernel/building.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/ship.h>
|
||||
#include <kernel/terrain.h>
|
||||
#include <kernel/unit.h>
|
||||
|
||||
#include <util/language.h>
|
||||
|
||||
|
@ -15,77 +18,165 @@
|
|||
|
||||
static void test_ship_not_allowed_in_coast(CuTest * tc)
|
||||
{
|
||||
region *r1, *r2;
|
||||
ship * sh;
|
||||
terrain_type *ttype, *otype;
|
||||
ship_type *stype;
|
||||
region *r1, *r2;
|
||||
ship * sh;
|
||||
terrain_type *ttype, *otype;
|
||||
ship_type *stype;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
ttype = test_create_terrain("glacier", LAND_REGION|ARCTIC_REGION|WALK_INTO|SAIL_INTO);
|
||||
otype = test_create_terrain("ocean", SEA_REGION|SAIL_INTO);
|
||||
stype = test_create_shiptype("derp");
|
||||
stype->coasts = (const struct terrain_type **)calloc(2, sizeof(const struct terrain_type *));
|
||||
ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO);
|
||||
otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO);
|
||||
stype = test_create_shiptype("derp");
|
||||
stype->coasts = (const struct terrain_type **)calloc(2, sizeof(const struct terrain_type *));
|
||||
|
||||
r1 = test_create_region(0, 0, ttype);
|
||||
r2 = test_create_region(1, 0, otype);
|
||||
sh = test_create_ship(0, stype);
|
||||
r1 = test_create_region(0, 0, ttype);
|
||||
r2 = test_create_region(1, 0, otype);
|
||||
sh = test_create_ship(0, stype);
|
||||
|
||||
CuAssertIntEquals(tc, SA_COAST, check_ship_allowed(sh, r2));
|
||||
CuAssertIntEquals(tc, SA_NO_COAST, check_ship_allowed(sh, r1));
|
||||
stype->coasts[0] = ttype;
|
||||
CuAssertIntEquals(tc, SA_COAST, check_ship_allowed(sh, r1));
|
||||
CuAssertIntEquals(tc, SA_COAST, check_ship_allowed(sh, r2));
|
||||
CuAssertIntEquals(tc, SA_NO_COAST, check_ship_allowed(sh, r1));
|
||||
stype->coasts[0] = ttype;
|
||||
CuAssertIntEquals(tc, SA_COAST, check_ship_allowed(sh, r1));
|
||||
}
|
||||
|
||||
static void test_ship_allowed_with_harbor(CuTest * tc)
|
||||
typedef struct move_fixture {
|
||||
region *r;
|
||||
ship *sh;
|
||||
building * b;
|
||||
unit *u;
|
||||
} move_fixture;
|
||||
|
||||
static void setup_harbor(move_fixture *mf) {
|
||||
region *r;
|
||||
ship * sh;
|
||||
terrain_type * ttype;
|
||||
building_type * btype;
|
||||
building * b;
|
||||
unit *u;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO);
|
||||
btype = test_create_buildingtype("harbour");
|
||||
|
||||
sh = test_create_ship(0, 0);
|
||||
r = test_create_region(0, 0, ttype);
|
||||
|
||||
b = test_create_building(r, btype);
|
||||
b->flags |= BLD_WORKING;
|
||||
|
||||
u = test_create_unit(test_create_faction(0), r);
|
||||
u->ship = sh;
|
||||
ship_set_owner(u);
|
||||
|
||||
mf->r = r;
|
||||
mf->u = u;
|
||||
mf->sh = sh;
|
||||
mf->b = b;
|
||||
}
|
||||
|
||||
static void test_ship_allowed_without_harbormaster(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
ship * sh;
|
||||
terrain_type * ttype;
|
||||
building_type * btype;
|
||||
building * b;
|
||||
move_fixture mf;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
setup_harbor(&mf);
|
||||
|
||||
ttype = test_create_terrain("glacier", LAND_REGION|ARCTIC_REGION|WALK_INTO|SAIL_INTO);
|
||||
btype = test_create_buildingtype("harbour");
|
||||
CuAssertIntEquals(tc, SA_HARBOUR, check_ship_allowed(mf.sh, mf.r));
|
||||
}
|
||||
|
||||
r = test_create_region(0, 0, ttype);
|
||||
sh = test_create_ship(0, 0);
|
||||
static void test_ship_blocked_by_harbormaster(CuTest * tc) {
|
||||
unit *u;
|
||||
move_fixture mf;
|
||||
|
||||
b = test_create_building(r, btype);
|
||||
b->flags |= BLD_WORKING;
|
||||
CuAssertIntEquals(tc, SA_HARBOUR, check_ship_allowed(sh, r));
|
||||
setup_harbor(&mf);
|
||||
|
||||
u = test_create_unit(test_create_faction(0), mf.r);
|
||||
u->building = mf.b;
|
||||
building_set_owner(u);
|
||||
|
||||
CuAssertIntEquals_Msg(tc, "harbor master must contact ship", SA_NO_COAST, check_ship_allowed(mf.sh, mf.r));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_ship_has_harbormaster_contact(CuTest * tc) {
|
||||
unit *u;
|
||||
move_fixture mf;
|
||||
|
||||
setup_harbor(&mf);
|
||||
|
||||
u = test_create_unit(test_create_faction(0), mf.r);
|
||||
u->building = mf.b;
|
||||
building_set_owner(u);
|
||||
usetcontact(mf.b->_owner, mf.sh->_owner);
|
||||
|
||||
CuAssertIntEquals(tc, SA_HARBOUR, check_ship_allowed(mf.sh, mf.r));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_ship_has_harbormaster_same_faction(CuTest * tc) {
|
||||
unit *u;
|
||||
move_fixture mf;
|
||||
|
||||
setup_harbor(&mf);
|
||||
|
||||
u = test_create_unit(mf.u->faction, mf.r);
|
||||
u->building = mf.b;
|
||||
building_set_owner(u);
|
||||
|
||||
CuAssertIntEquals(tc, SA_HARBOUR, check_ship_allowed(mf.sh, mf.r));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_ship_has_harbormaster_ally(CuTest * tc) {
|
||||
unit *u;
|
||||
move_fixture mf;
|
||||
ally *al;
|
||||
|
||||
setup_harbor(&mf);
|
||||
|
||||
u = test_create_unit(test_create_faction(0), mf.r);
|
||||
u->building = mf.b;
|
||||
building_set_owner(u);
|
||||
al = ally_add(&u->faction->allies, mf.u->faction);
|
||||
al->status = HELP_GUARD;
|
||||
|
||||
CuAssertIntEquals(tc, SA_HARBOUR, check_ship_allowed(mf.sh, mf.r));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_building_type_exists(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
building *b;
|
||||
building_type *btype, *btype2;
|
||||
region *r;
|
||||
building *b;
|
||||
building_type *btype, *btype2;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
btype2 = bt_get_or_create("lighthouse");
|
||||
btype = bt_get_or_create("castle");
|
||||
btype2 = bt_get_or_create("lighthouse");
|
||||
btype = bt_get_or_create("castle");
|
||||
|
||||
r = findregion(-1, 0);
|
||||
b = new_building(btype, r, default_locale);
|
||||
r = findregion(-1, 0);
|
||||
b = new_building(btype, r, default_locale);
|
||||
|
||||
CuAssertPtrNotNull(tc, b);
|
||||
CuAssertTrue(tc, !buildingtype_exists(r, NULL, false));
|
||||
CuAssertTrue(tc, buildingtype_exists(r, btype, false));
|
||||
CuAssertTrue(tc, !buildingtype_exists(r, btype2, false));
|
||||
CuAssertPtrNotNull(tc, b);
|
||||
CuAssertTrue(tc, !buildingtype_exists(r, NULL, false));
|
||||
CuAssertTrue(tc, buildingtype_exists(r, btype, false));
|
||||
CuAssertTrue(tc, !buildingtype_exists(r, btype2, false));
|
||||
}
|
||||
|
||||
CuSuite *get_move_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_building_type_exists);
|
||||
SUITE_ADD_TEST(suite, test_ship_not_allowed_in_coast);
|
||||
SUITE_ADD_TEST(suite, test_ship_allowed_with_harbor);
|
||||
return suite;
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_building_type_exists);
|
||||
SUITE_ADD_TEST(suite, test_ship_not_allowed_in_coast);
|
||||
SUITE_ADD_TEST(suite, test_ship_allowed_without_harbormaster);
|
||||
SUITE_ADD_TEST(suite, test_ship_blocked_by_harbormaster);
|
||||
SUITE_ADD_TEST(suite, test_ship_has_harbormaster_contact);
|
||||
SUITE_ADD_TEST(suite, test_ship_has_harbormaster_ally);
|
||||
SUITE_ADD_TEST(suite, test_ship_has_harbormaster_same_faction);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -32,12 +32,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <attributes/reduceproduction.h>
|
||||
|
||||
/* gamecode includes */
|
||||
#include "creport.h"
|
||||
#include "economy.h"
|
||||
#include "monster.h"
|
||||
#include "laws.h"
|
||||
#include "move.h"
|
||||
#include "alchemy.h"
|
||||
#include "economy.h"
|
||||
#include "move.h"
|
||||
#include "upkeep.h"
|
||||
#include "vortex.h"
|
||||
|
||||
/* kernel includes */
|
||||
|
|
|
@ -29,9 +29,6 @@
|
|||
#define BATTLE_KILLS_PEASANTS 20
|
||||
#define PEASANTLUCK 10
|
||||
|
||||
#define HUNGER_REDUCES_SKILL /* Hunger reduziert den Talentwert
|
||||
auf die Hälfte */
|
||||
|
||||
#define ASTRAL_ITEM_RESTRICTIONS /* keine grossen dinge im astralraum */
|
||||
#define NEW_DAEMONHUNGER_RULE
|
||||
#define NEW_COMBATSKILLS_RULE
|
||||
|
|
|
@ -6868,7 +6868,6 @@ void register_spells(void)
|
|||
register_function((pf_generic)sp_kampfzauber, "combat_spell");
|
||||
|
||||
register_spelldata();
|
||||
register_special_direction("vortex");
|
||||
|
||||
register_unitcurse();
|
||||
register_regioncurse();
|
||||
|
|
|
@ -93,13 +93,13 @@ void spy_message(int spy, const unit * u, const unit * target)
|
|||
if (first == 1) {
|
||||
first = 0;
|
||||
} else {
|
||||
strncat(buf, ", ", sizeof(buf));
|
||||
strncat(buf, ", ", sizeof(buf)-1);
|
||||
}
|
||||
strncat(buf, (const char *)skillname((skill_t)sv->id, u->faction->locale),
|
||||
sizeof(buf));
|
||||
strncat(buf, " ", sizeof(buf));
|
||||
sizeof(buf)-1);
|
||||
strncat(buf, " ", sizeof(buf)-1);
|
||||
strncat(buf, itoa10(eff_skill(target, (skill_t)sv->id, target->region)),
|
||||
sizeof(buf));
|
||||
sizeof(buf)-1);
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
|
|
47
src/study.c
47
src/study.c
|
@ -59,7 +59,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
static skill_t getskill(const struct locale *lang)
|
||||
{
|
||||
return get_skill(getstrtoken(), lang);
|
||||
const char * s = getstrtoken();
|
||||
return s ? get_skill(s, lang) : NOSKILL;
|
||||
}
|
||||
|
||||
magic_t getmagicskill(const struct locale * lang)
|
||||
|
@ -492,25 +493,37 @@ int teach_cmd(unit * u, struct order *ord)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static double study_speedup(unit * u)
|
||||
typedef enum study_rule_t {
|
||||
STUDY_DEFAULT = 0,
|
||||
STUDY_FASTER = 1,
|
||||
STUDY_AUTOTEACH = 2
|
||||
} study_rule_t;
|
||||
|
||||
static double study_speedup(unit * u, skill_t s, study_rule_t rule)
|
||||
{
|
||||
#define MINTURN 5 /* 5 */
|
||||
#define OFSTURN 2 /* 2 */
|
||||
#define MINTURN 16
|
||||
double learnweeks = 0;
|
||||
int i;
|
||||
if (turn > MINTURN) {
|
||||
static int speed_rule = -1;
|
||||
if (speed_rule < 0) {
|
||||
speed_rule = get_param_int(global.parameters, "study.speedup", 0);
|
||||
}
|
||||
if (speed_rule == 1) {
|
||||
double learn_age = OFSTURN;
|
||||
int i;
|
||||
if (rule == STUDY_FASTER) {
|
||||
for (i = 0; i != u->skill_size; ++i) {
|
||||
skill *sv = u->skills + i;
|
||||
double learn_time = sv->level * (sv->level + 1) / 2.0;
|
||||
learn_age += learn_time;
|
||||
if (sv->id == s){
|
||||
learnweeks = sv->level * (sv->level + 1) / 2.0;
|
||||
if (learnweeks < turn / 3) {
|
||||
return 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (learn_age < turn) {
|
||||
return 2.0 - learn_age / turn;
|
||||
return 2.0; /* If the skill was not found it is the first study. */
|
||||
}
|
||||
if (rule == STUDY_AUTOTEACH) {
|
||||
for (i = 0; i != u->skill_size; ++i) {
|
||||
skill *sv = u->skills + i;
|
||||
learnweeks = +(sv->level * (sv->level + 1) / 2.0);
|
||||
}
|
||||
if (learnweeks < turn / 2) {
|
||||
return 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,6 +543,7 @@ int learn_cmd(unit * u, order * ord)
|
|||
int money = 0;
|
||||
skill_t sk;
|
||||
int maxalchemy = 0;
|
||||
int speed_rule = (study_rule_t)get_param_int(global.parameters, "study.speedup", 0);
|
||||
static int learn_newskills = -1;
|
||||
if (learn_newskills < 0) {
|
||||
const char *str = get_param(global.parameters, "study.newskills");
|
||||
|
@ -538,7 +552,6 @@ int learn_cmd(unit * u, order * ord)
|
|||
else
|
||||
learn_newskills = 1;
|
||||
}
|
||||
|
||||
if ((u_race(u)->flags & RCF_NOLEARN) || fval(u, UFL_WERE)) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_race_nolearn", "race",
|
||||
u_race(u)));
|
||||
|
@ -731,7 +744,7 @@ int learn_cmd(unit * u, order * ord)
|
|||
teach->value -= u->number * 10;
|
||||
}
|
||||
|
||||
multi *= study_speedup(u);
|
||||
multi *= study_speedup(u, sk, speed_rule);
|
||||
days = study_days(u, sk);
|
||||
days = (int)((days + teach->value) * multi);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ int RunAllTests(void)
|
|||
ADD_TESTS(suite, race);
|
||||
/* util */
|
||||
ADD_TESTS(suite, config);
|
||||
ADD_TESTS(suite, attrib);
|
||||
ADD_TESTS(suite, base36);
|
||||
ADD_TESTS(suite, bsdstring);
|
||||
ADD_TESTS(suite, functions);
|
||||
|
@ -58,7 +59,9 @@ int RunAllTests(void)
|
|||
ADD_TESTS(suite, market);
|
||||
ADD_TESTS(suite, move);
|
||||
ADD_TESTS(suite, stealth);
|
||||
ADD_TESTS(suite, upkeep);
|
||||
ADD_TESTS(suite, vortex);
|
||||
ADD_TESTS(suite, wormhole);
|
||||
|
||||
CuSuiteRun(suite);
|
||||
CuSuiteSummary(suite, output);
|
||||
|
|
15
src/tests.c
15
src/tests.c
|
@ -34,25 +34,26 @@ struct race *test_create_race(const char *name)
|
|||
struct region *test_create_region(int x, int y, const terrain_type *terrain)
|
||||
{
|
||||
region *r = new_region(x, y, NULL, 0);
|
||||
terraform_region(r, terrain);
|
||||
terraform_region(r, terrain ? terrain : get_or_create_terrain("plain"));
|
||||
rsettrees(r, 0, 0);
|
||||
rsettrees(r, 1, 0);
|
||||
rsettrees(r, 2, 0);
|
||||
rsethorses(r, 0);
|
||||
rsetpeasants(r, terrain->size);
|
||||
rsetpeasants(r, r->terrain->size);
|
||||
return r;
|
||||
}
|
||||
|
||||
struct faction *test_create_faction(const struct race *rc)
|
||||
{
|
||||
faction *f = addfaction("nobody@eressea.de", NULL, rc?rc:rc_find("human"), default_locale, 0);
|
||||
faction *f = addfaction("nobody@eressea.de", NULL, rc?rc:rc_get_or_create("human"), default_locale, 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
struct unit *test_create_unit(struct faction *f, struct region *r)
|
||||
{
|
||||
unit *u = create_unit(r, f, 1, f?f->race:rc_find("human"), 0, 0, 0);
|
||||
return u;
|
||||
const struct race * rc = f ? f->race : 0;
|
||||
assert(f || !r);
|
||||
return create_unit(r, f, 1, rc ? rc : rc_get_or_create("human"), 0, 0, 0);
|
||||
}
|
||||
|
||||
void test_cleanup(void)
|
||||
|
@ -115,7 +116,9 @@ building_type * test_create_buildingtype(const char * name)
|
|||
btype->construction->materials[1].number = 0;
|
||||
btype->construction->materials[0].number = 1;
|
||||
btype->construction->materials[0].rtype = get_resourcetype(R_STONE);
|
||||
locale_setstring(default_locale, name, name);
|
||||
if (default_locale) {
|
||||
locale_setstring(default_locale, name, name);
|
||||
}
|
||||
bt_register(btype);
|
||||
return btype;
|
||||
}
|
||||
|
|
41
src/tests.h
41
src/tests.h
|
@ -1,25 +1,38 @@
|
|||
#ifndef ERESSEA_TESTS_H
|
||||
#define ERESSEA_TESTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void test_cleanup(void);
|
||||
struct region;
|
||||
struct unit;
|
||||
struct faction;
|
||||
struct building;
|
||||
struct ship;
|
||||
struct item_type;
|
||||
struct building_type;
|
||||
struct ship_type;
|
||||
struct terrain_type;
|
||||
|
||||
struct terrain_type * test_create_terrain(const char * name, unsigned int flags);
|
||||
struct race *test_create_race(const char *name);
|
||||
struct region *test_create_region(int x, int y,
|
||||
const struct terrain_type *terrain);
|
||||
struct faction *test_create_faction(const struct race *rc);
|
||||
struct unit *test_create_unit(struct faction *f, struct region *r);
|
||||
void test_create_world(void);
|
||||
struct building * test_create_building(struct region * r, const struct building_type * btype);
|
||||
struct ship * test_create_ship(struct region * r, const struct ship_type * stype);
|
||||
struct item_type * test_create_itemtype(const char * name);
|
||||
struct ship_type *test_create_shiptype(const char * name);
|
||||
struct building_type *test_create_buildingtype(const char *name);
|
||||
void test_cleanup(void);
|
||||
|
||||
int RunAllTests(void);
|
||||
struct terrain_type * test_create_terrain(const char * name, unsigned int flags);
|
||||
struct race *test_create_race(const char *name);
|
||||
struct region *test_create_region(int x, int y,
|
||||
const struct terrain_type *terrain);
|
||||
struct faction *test_create_faction(const struct race *rc);
|
||||
struct unit *test_create_unit(struct faction *f, struct region *r);
|
||||
void test_create_world(void);
|
||||
struct building * test_create_building(struct region * r, const struct building_type * btype);
|
||||
struct ship * test_create_ship(struct region * r, const struct ship_type * stype);
|
||||
struct item_type * test_create_itemtype(const char * name);
|
||||
struct ship_type *test_create_shiptype(const char * name);
|
||||
struct building_type *test_create_buildingtype(const char *name);
|
||||
|
||||
int RunAllTests(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
Copyright (c) 1998-2014, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -41,68 +41,66 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <stdio.h>
|
||||
|
||||
typedef struct removecurse_data {
|
||||
curse *curse;
|
||||
unit *target;
|
||||
curse *curse;
|
||||
unit *target;
|
||||
} removecurse_data;
|
||||
|
||||
static void removecurse_init(trigger * t)
|
||||
{
|
||||
t->data.v = calloc(sizeof(removecurse_data), 1);
|
||||
t->data.v = calloc(sizeof(removecurse_data), 1);
|
||||
}
|
||||
|
||||
static void removecurse_free(trigger * t)
|
||||
{
|
||||
free(t->data.v);
|
||||
free(t->data.v);
|
||||
}
|
||||
|
||||
static int removecurse_handle(trigger * t, void *data)
|
||||
{
|
||||
/* call an event handler on removecurse.
|
||||
* data.v -> ( variant event, int timer )
|
||||
*/
|
||||
removecurse_data *td = (removecurse_data *) t->data.v;
|
||||
if (td->curse && td->target) {
|
||||
attrib *a = a_select(td->target->attribs, td->curse, cmp_curse);
|
||||
if (a) {
|
||||
a_remove(&td->target->attribs, a);
|
||||
} else
|
||||
log_error("could not perform removecurse::handle()\n");
|
||||
}
|
||||
unused_arg(data);
|
||||
return 0;
|
||||
/* call an event handler on removecurse.
|
||||
* data.v -> ( variant event, int timer )
|
||||
*/
|
||||
removecurse_data *td = (removecurse_data *)t->data.v;
|
||||
if (td->curse && td->target) {
|
||||
if (!remove_curse(&td->target->attribs, td->curse)) {
|
||||
log_error("could not perform removecurse::handle()\n");
|
||||
}
|
||||
}
|
||||
unused_arg(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void removecurse_write(const trigger * t, struct storage *store)
|
||||
{
|
||||
removecurse_data *td = (removecurse_data *) t->data.v;
|
||||
WRITE_TOK(store, td->target ? itoa36(td->target->no) : 0);
|
||||
WRITE_INT(store, td->curse ? td->curse->no : 0);
|
||||
removecurse_data *td = (removecurse_data *)t->data.v;
|
||||
WRITE_TOK(store, td->target ? itoa36(td->target->no) : 0);
|
||||
WRITE_INT(store, td->curse ? td->curse->no : 0);
|
||||
}
|
||||
|
||||
static int removecurse_read(trigger * t, struct storage *store)
|
||||
{
|
||||
removecurse_data *td = (removecurse_data *) t->data.v;
|
||||
removecurse_data *td = (removecurse_data *)t->data.v;
|
||||
|
||||
read_reference(&td->target, store, read_unit_reference, resolve_unit);
|
||||
read_reference(&td->curse, store, read_int, resolve_curse);
|
||||
read_reference(&td->target, store, read_unit_reference, resolve_unit);
|
||||
read_reference(&td->curse, store, read_int, resolve_curse);
|
||||
|
||||
return AT_READ_OK;
|
||||
return AT_READ_OK;
|
||||
}
|
||||
|
||||
trigger_type tt_removecurse = {
|
||||
"removecurse",
|
||||
removecurse_init,
|
||||
removecurse_free,
|
||||
removecurse_handle,
|
||||
removecurse_write,
|
||||
removecurse_read
|
||||
"removecurse",
|
||||
removecurse_init,
|
||||
removecurse_free,
|
||||
removecurse_handle,
|
||||
removecurse_write,
|
||||
removecurse_read
|
||||
};
|
||||
|
||||
trigger *trigger_removecurse(curse * c, unit * target)
|
||||
{
|
||||
trigger *t = t_new(&tt_removecurse);
|
||||
removecurse_data *td = (removecurse_data *) t->data.v;
|
||||
td->curse = c;
|
||||
td->target = target;
|
||||
return t;
|
||||
trigger *t = t_new(&tt_removecurse);
|
||||
removecurse_data *td = (removecurse_data *)t->data.v;
|
||||
td->curse = c;
|
||||
td->target = target;
|
||||
return t;
|
||||
}
|
||||
|
|
308
src/upkeep.c
Normal file
308
src/upkeep.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
#include <platform.h>
|
||||
#include "upkeep.h"
|
||||
|
||||
#include <kernel/types.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/config.h>
|
||||
#include <kernel/item.h>
|
||||
#include <kernel/messages.h>
|
||||
#include <kernel/plane.h>
|
||||
#include <kernel/race.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/ship.h>
|
||||
#include <kernel/unit.h>
|
||||
|
||||
#include <util/rand.h>
|
||||
|
||||
#include "alchemy.h"
|
||||
#include "economy.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int lifestyle(const unit * u)
|
||||
{
|
||||
int need;
|
||||
plane *pl;
|
||||
static int gamecookie = -1;
|
||||
if (gamecookie != global.cookie) {
|
||||
gamecookie = global.cookie;
|
||||
}
|
||||
|
||||
if (is_monsters(u->faction))
|
||||
return 0;
|
||||
|
||||
need = maintenance_cost(u);
|
||||
|
||||
pl = rplane(u->region);
|
||||
if (pl && fval(pl, PFL_NOFEED))
|
||||
return 0;
|
||||
|
||||
return need;
|
||||
}
|
||||
|
||||
static bool help_money(const unit * u)
|
||||
{
|
||||
if (u_race(u)->ec_flags & GIVEITEM)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void help_feed(unit * donor, unit * u, int *need_p)
|
||||
{
|
||||
int need = *need_p;
|
||||
int give = get_money(donor) - lifestyle(donor);
|
||||
give = _min(need, give);
|
||||
|
||||
if (give > 0) {
|
||||
change_money(donor, -give);
|
||||
change_money(u, give);
|
||||
need -= give;
|
||||
add_spende(donor->faction, u->faction, give, donor->region);
|
||||
}
|
||||
*need_p = need;
|
||||
}
|
||||
|
||||
static bool hunger(int number, unit * u)
|
||||
{
|
||||
region *r = u->region;
|
||||
int dead = 0, hpsub = 0;
|
||||
int hp = u->hp / u->number;
|
||||
static const char *damage = 0;
|
||||
static const char *rcdamage = 0;
|
||||
static const race *rc = 0;
|
||||
|
||||
if (!damage) {
|
||||
damage = get_param(global.parameters, "hunger.damage");
|
||||
if (damage == NULL)
|
||||
damage = "1d12+12";
|
||||
}
|
||||
if (rc != u_race(u)) {
|
||||
rcdamage = get_param(u_race(u)->parameters, "hunger.damage");
|
||||
rc = u_race(u);
|
||||
}
|
||||
|
||||
while (number--) {
|
||||
int dam = dice_rand(rcdamage ? rcdamage : damage);
|
||||
if (dam >= hp) {
|
||||
++dead;
|
||||
}
|
||||
else {
|
||||
hpsub += dam;
|
||||
}
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
/* Gestorbene aus der Einheit nehmen,
|
||||
* Sie bekommen keine Beerdingung. */
|
||||
ADDMSG(&u->faction->msgs, msg_message("starvation",
|
||||
"unit region dead live", u, r, dead, u->number - dead));
|
||||
|
||||
scale_number(u, u->number - dead);
|
||||
deathcounts(r, dead);
|
||||
}
|
||||
if (hpsub > 0) {
|
||||
/* Jetzt die Schäden der nicht gestorbenen abziehen. */
|
||||
u->hp -= hpsub;
|
||||
/* Meldung nur, wenn noch keine für Tote generiert. */
|
||||
if (dead == 0) {
|
||||
/* Durch unzureichende Ernährung wird %s geschwächt */
|
||||
ADDMSG(&u->faction->msgs, msg_message("malnourish", "unit region", u, r));
|
||||
}
|
||||
}
|
||||
return (dead || hpsub);
|
||||
}
|
||||
|
||||
void get_food(region * r)
|
||||
{
|
||||
plane *pl = rplane(r);
|
||||
unit *u;
|
||||
int peasantfood = rpeasants(r) * 10;
|
||||
static int food_rules = -1;
|
||||
static int gamecookie = -1;
|
||||
|
||||
if (food_rules < 0 || gamecookie != global.cookie) {
|
||||
gamecookie = global.cookie;
|
||||
food_rules = get_param_int(global.parameters, "rules.economy.food", 0);
|
||||
}
|
||||
|
||||
if (food_rules & FOOD_IS_FREE) {
|
||||
return;
|
||||
}
|
||||
/* 1. Versorgung von eigenen Einheiten. Das vorhandene Silber
|
||||
* wird zunächst so auf die Einheiten aufgeteilt, dass idealerweise
|
||||
* jede Einheit genug Silber für ihren Unterhalt hat. */
|
||||
|
||||
for (u = r->units; u; u = u->next) {
|
||||
int need = lifestyle(u);
|
||||
|
||||
/* Erstmal zurücksetzen */
|
||||
freset(u, UFL_HUNGER);
|
||||
|
||||
if (u->ship && (u->ship->flags & SF_FISHING)) {
|
||||
unit *v;
|
||||
int c = 2;
|
||||
for (v = u; c > 0 && v; v = v->next) {
|
||||
if (v->ship == u->ship) {
|
||||
int get = 0;
|
||||
if (v->number <= c) {
|
||||
get = lifestyle(v);
|
||||
}
|
||||
else {
|
||||
get = lifestyle(v) * c / v->number;
|
||||
}
|
||||
if (get) {
|
||||
change_money(v, get);
|
||||
}
|
||||
}
|
||||
c -= v->number;
|
||||
}
|
||||
u->ship->flags -= SF_FISHING;
|
||||
}
|
||||
|
||||
if (food_rules & FOOD_FROM_PEASANTS) {
|
||||
struct faction *owner = region_get_owner(r);
|
||||
/* if the region is owned, and the owner is nice, then we'll get
|
||||
* food from the peasants - should not be used with WORK */
|
||||
if (owner != NULL && (get_alliance(owner, u->faction) & HELP_MONEY)) {
|
||||
int rm = rmoney(r);
|
||||
int use = _min(rm, need);
|
||||
rsetmoney(r, rm - use);
|
||||
need -= use;
|
||||
}
|
||||
}
|
||||
|
||||
need -= get_money(u);
|
||||
if (need > 0) {
|
||||
unit *v;
|
||||
|
||||
for (v = r->units; need && v; v = v->next) {
|
||||
if (v->faction == u->faction && help_money(v)) {
|
||||
int give = get_money(v) - lifestyle(v);
|
||||
give = _min(need, give);
|
||||
if (give > 0) {
|
||||
change_money(v, -give);
|
||||
change_money(u, give);
|
||||
need -= give;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Versorgung durch Fremde. Das Silber alliierter Einheiten wird
|
||||
* entsprechend verteilt. */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
int need = lifestyle(u);
|
||||
faction *f = u->faction;
|
||||
|
||||
need -= _max(0, get_money(u));
|
||||
|
||||
if (need > 0) {
|
||||
unit *v;
|
||||
|
||||
if (food_rules & FOOD_FROM_OWNER) {
|
||||
/* the owner of the region is the first faction to help out when you're hungry */
|
||||
faction *owner = region_get_owner(r);
|
||||
if (owner && owner != u->faction) {
|
||||
for (v = r->units; v; v = v->next) {
|
||||
if (v->faction == owner && alliedunit(v, f, HELP_MONEY)
|
||||
&& help_money(v)) {
|
||||
help_feed(v, u, &need);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (v = r->units; need && v; v = v->next) {
|
||||
if (v->faction != f && alliedunit(v, f, HELP_MONEY)
|
||||
&& help_money(v)) {
|
||||
help_feed(v, u, &need);
|
||||
}
|
||||
}
|
||||
|
||||
/* Die Einheit hat nicht genug Geld zusammengekratzt und
|
||||
* nimmt Schaden: */
|
||||
if (need > 0) {
|
||||
int lspp = lifestyle(u) / u->number;
|
||||
if (lspp > 0) {
|
||||
int number = (need + lspp - 1) / lspp;
|
||||
if (hunger(number, u))
|
||||
fset(u, UFL_HUNGER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. bestimmen, wie viele Bauern gefressen werden.
|
||||
* bei fehlenden Bauern den Dämon hungern lassen
|
||||
*/
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (u_race(u) == get_race(RC_DAEMON)) {
|
||||
int hungry = u->number;
|
||||
|
||||
/* use peasantblood before eating the peasants themselves */
|
||||
const struct potion_type *pt_blood = 0;
|
||||
const resource_type *rt_blood = rt_find("peasantblood");
|
||||
if (rt_blood) {
|
||||
pt_blood = rt_blood->ptype;
|
||||
}
|
||||
if (pt_blood) {
|
||||
/* always start with the unit itself, then the first known unit that may have some blood */
|
||||
unit *donor = u;
|
||||
while (donor != NULL && hungry > 0) {
|
||||
int blut = get_effect(donor, pt_blood);
|
||||
blut = _min(blut, hungry);
|
||||
if (blut) {
|
||||
change_effect(donor, pt_blood, -blut);
|
||||
hungry -= blut;
|
||||
}
|
||||
if (donor == u)
|
||||
donor = r->units;
|
||||
while (donor != NULL) {
|
||||
if (u_race(donor) == get_race(RC_DAEMON) && donor != u) {
|
||||
if (get_effect(donor, pt_blood)) {
|
||||
/* if he's in our faction, drain him: */
|
||||
if (donor->faction == u->faction)
|
||||
break;
|
||||
}
|
||||
}
|
||||
donor = donor->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* remaining demons feed on peasants */
|
||||
if (pl == NULL || !fval(pl, PFL_NOFEED)) {
|
||||
if (peasantfood >= hungry) {
|
||||
peasantfood -= hungry;
|
||||
hungry = 0;
|
||||
}
|
||||
else {
|
||||
hungry -= peasantfood;
|
||||
peasantfood = 0;
|
||||
}
|
||||
if (hungry > 0) {
|
||||
static int demon_hunger = -1;
|
||||
if (demon_hunger < 0) {
|
||||
demon_hunger = get_param_int(global.parameters, "hunger.demons", 0);
|
||||
}
|
||||
if (demon_hunger == 0) {
|
||||
/* demons who don't feed are hungry */
|
||||
if (hunger(hungry, u))
|
||||
fset(u, UFL_HUNGER);
|
||||
}
|
||||
else {
|
||||
/* no damage, but set the hungry-flag */
|
||||
fset(u, UFL_HUNGER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rsetpeasants(r, peasantfood / 10);
|
||||
|
||||
/* 3. Von den überlebenden das Geld abziehen: */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
int need = _min(get_money(u), lifestyle(u));
|
||||
change_money(u, -need);
|
||||
}
|
||||
}
|
22
src/upkeep.h
Normal file
22
src/upkeep.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef UPKEEP_H
|
||||
#define UPKEEP_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct region;
|
||||
struct unit;
|
||||
void get_food(struct region * r);
|
||||
int lifestyle(const struct unit * u);
|
||||
|
||||
enum {
|
||||
FOOD_FROM_PEASANTS = 1,
|
||||
FOOD_FROM_OWNER = 2,
|
||||
FOOD_IS_FREE = 4
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
170
src/upkeep.test.c
Normal file
170
src/upkeep.test.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
#include <platform.h>
|
||||
#include "upkeep.h"
|
||||
|
||||
#include <kernel/config.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/unit.h>
|
||||
#include <kernel/item.h>
|
||||
|
||||
#include <CuTest.h>
|
||||
#include <tests.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void test_upkeep_default(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
unit *u1, *u2;
|
||||
faction *f1, *f2;
|
||||
const item_type *i_silver;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
i_silver = it_find("money");
|
||||
assert(i_silver);
|
||||
r = findregion(0, 0);
|
||||
f1 = test_create_faction(test_create_race("human"));
|
||||
f2 = test_create_faction(test_create_race("human"));
|
||||
assert(f1 && f2);
|
||||
u1 = test_create_unit(f1, r);
|
||||
u2 = test_create_unit(f2, r);
|
||||
assert(r && u1 && u2);
|
||||
|
||||
set_param(&global.parameters, "rules.economy.food", "0");
|
||||
i_change(&u1->items, i_silver, 20);
|
||||
get_food(r);
|
||||
// since u1 and u2 are not allied, u1 should not help u2 with upkeep
|
||||
CuAssertIntEquals(tc, 10, i_get(u1->items, i_silver));
|
||||
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
|
||||
CuAssertIntEquals(tc, UFL_HUNGER, fval(u2, UFL_HUNGER));
|
||||
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
void test_upkeep_hunger_damage(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
unit *u1;
|
||||
faction *f1;
|
||||
const item_type *i_silver;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
i_silver = it_find("money");
|
||||
assert(i_silver);
|
||||
r = findregion(0, 0);
|
||||
f1 = test_create_faction(test_create_race("human"));
|
||||
u1 = test_create_unit(f1, r);
|
||||
assert(r && u1);
|
||||
|
||||
set_param(&global.parameters, "rules.economy.food", "0");
|
||||
u1->hp = 100;
|
||||
get_food(r);
|
||||
// since u1 and u2 are not allied, u1 should not help u2 with upkeep
|
||||
CuAssertTrue(tc, u1->hp<100);
|
||||
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
void test_upkeep_from_pool(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
unit *u1, *u2;
|
||||
const item_type *i_silver;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
i_silver = it_find("money");
|
||||
assert(i_silver);
|
||||
r = findregion(0, 0);
|
||||
u1 = test_create_unit(test_create_faction(test_create_race("human")), r);
|
||||
u2 = test_create_unit(u1->faction, r);
|
||||
assert(r && u1 && u2);
|
||||
|
||||
set_param(&global.parameters, "rules.economy.food", "0");
|
||||
i_change(&u1->items, i_silver, 30);
|
||||
get_food(r);
|
||||
CuAssertIntEquals(tc, 10, i_get(u1->items, i_silver));
|
||||
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
|
||||
CuAssertIntEquals(tc, 0, fval(u2, UFL_HUNGER));
|
||||
get_food(r);
|
||||
CuAssertIntEquals(tc, 0, i_get(u1->items, i_silver));
|
||||
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
|
||||
CuAssertIntEquals(tc, UFL_HUNGER, fval(u2, UFL_HUNGER));
|
||||
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
|
||||
void test_upkeep_from_friend(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
unit *u1, *u2;
|
||||
faction *f1, *f2;
|
||||
const item_type *i_silver;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
i_silver = it_find("money");
|
||||
assert(i_silver);
|
||||
r = findregion(0, 0);
|
||||
f1 = test_create_faction(test_create_race("human"));
|
||||
f2 = test_create_faction(test_create_race("human"));
|
||||
assert(f1 && f2);
|
||||
set_alliance(f1, f2, HELP_MONEY);
|
||||
u1 = test_create_unit(f1, r);
|
||||
u2 = test_create_unit(f2, r);
|
||||
assert(r && u1 && u2);
|
||||
|
||||
set_param(&global.parameters, "rules.economy.food", "0");
|
||||
i_change(&u1->items, i_silver, 30);
|
||||
get_food(r);
|
||||
CuAssertIntEquals(tc, 10, i_get(u1->items, i_silver));
|
||||
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
|
||||
CuAssertIntEquals(tc, 0, fval(u2, UFL_HUNGER));
|
||||
get_food(r);
|
||||
CuAssertIntEquals(tc, 0, i_get(u1->items, i_silver));
|
||||
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
|
||||
CuAssertIntEquals(tc, UFL_HUNGER, fval(u2, UFL_HUNGER));
|
||||
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
void test_upkeep_free(CuTest * tc)
|
||||
{
|
||||
region *r;
|
||||
unit *u;
|
||||
const item_type *i_silver;
|
||||
|
||||
test_cleanup();
|
||||
test_create_world();
|
||||
|
||||
i_silver = it_find("money");
|
||||
assert(i_silver);
|
||||
r = findregion(0, 0);
|
||||
u = test_create_unit(test_create_faction(test_create_race("human")), r);
|
||||
assert(r && u);
|
||||
|
||||
set_param(&global.parameters, "rules.economy.food", "4"); // FOOD_IS_FREE
|
||||
get_food(r);
|
||||
CuAssertIntEquals(tc, 0, i_get(u->items, i_silver));
|
||||
CuAssertIntEquals(tc, 0, fval(u, UFL_HUNGER));
|
||||
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
CuSuite *get_upkeep_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_upkeep_default);
|
||||
SUITE_ADD_TEST(suite, test_upkeep_from_pool);
|
||||
SUITE_ADD_TEST(suite, test_upkeep_from_friend);
|
||||
SUITE_ADD_TEST(suite, test_upkeep_hunger_damage);
|
||||
SUITE_ADD_TEST(suite, test_upkeep_free);
|
||||
return suite;
|
||||
}
|
|
@ -2,6 +2,7 @@ project(util C)
|
|||
|
||||
SET(_TEST_FILES
|
||||
base36.test.c
|
||||
attrib.test.c
|
||||
strings.test.c
|
||||
bsdstring.test.c
|
||||
functions.test.c
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -29,322 +29,329 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <stdlib.h>
|
||||
|
||||
#define MAXATHASH 61
|
||||
attrib_type *at_hash[MAXATHASH];
|
||||
static attrib_type *at_hash[MAXATHASH];
|
||||
|
||||
static unsigned int __at_hashkey(const char *s)
|
||||
{
|
||||
int key = 0;
|
||||
size_t i = strlen(s);
|
||||
int key = 0;
|
||||
size_t i = strlen(s);
|
||||
|
||||
while (i > 0) {
|
||||
key = (s[--i] + key * 37);
|
||||
}
|
||||
return key & 0x7fffffff;
|
||||
while (i > 0) {
|
||||
key = (s[--i] + key * 37);
|
||||
}
|
||||
return key & 0x7fffffff;
|
||||
}
|
||||
|
||||
void at_register(attrib_type * at)
|
||||
{
|
||||
attrib_type *find;
|
||||
attrib_type *find;
|
||||
|
||||
if (at->read == NULL) {
|
||||
log_warning("registering non-persistent attribute %s.\n", at->name);
|
||||
}
|
||||
at->hashkey = __at_hashkey(at->name);
|
||||
find = at_hash[at->hashkey % MAXATHASH];
|
||||
while (find && at->hashkey != find->hashkey)
|
||||
find = find->nexthash;
|
||||
if (find && find == at) {
|
||||
log_warning("attribute '%s' was registered more than once\n", at->name);
|
||||
return;
|
||||
} else {
|
||||
assert(!find || !"hashkey is already in use");
|
||||
}
|
||||
at->nexthash = at_hash[at->hashkey % MAXATHASH];
|
||||
at_hash[at->hashkey % MAXATHASH] = at;
|
||||
if (at->read == NULL) {
|
||||
log_warning("registering non-persistent attribute %s.\n", at->name);
|
||||
}
|
||||
at->hashkey = __at_hashkey(at->name);
|
||||
find = at_hash[at->hashkey % MAXATHASH];
|
||||
while (find && at->hashkey != find->hashkey) {
|
||||
find = find->nexthash;
|
||||
}
|
||||
if (find && find == at) {
|
||||
log_warning("attribute '%s' was registered more than once\n", at->name);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
assert(!find || !"hashkey is already in use");
|
||||
}
|
||||
at->nexthash = at_hash[at->hashkey % MAXATHASH];
|
||||
at_hash[at->hashkey % MAXATHASH] = at;
|
||||
}
|
||||
|
||||
static attrib_type *at_find(unsigned int hk)
|
||||
{
|
||||
const char *translate[3][2] = {
|
||||
{"zielregion", "targetregion"}, /* remapping: from 'zielregion, heute targetregion */
|
||||
{"verzaubert", "curse"}, /* remapping: früher verzaubert, jetzt curse */
|
||||
{NULL, NULL}
|
||||
};
|
||||
attrib_type *find = at_hash[hk % MAXATHASH];
|
||||
while (find && hk != find->hashkey)
|
||||
find = find->nexthash;
|
||||
if (!find) {
|
||||
int i = 0;
|
||||
while (translate[i][0]) {
|
||||
if (__at_hashkey(translate[i][0]) == hk)
|
||||
return at_find(__at_hashkey(translate[i][1]));
|
||||
++i;
|
||||
const char *translate[3][2] = {
|
||||
{ "zielregion", "targetregion" }, /* remapping: from 'zielregion, heute targetregion */
|
||||
{ "verzaubert", "curse" }, /* remapping: früher verzaubert, jetzt curse */
|
||||
{ NULL, NULL }
|
||||
};
|
||||
attrib_type *find = at_hash[hk % MAXATHASH];
|
||||
while (find && hk != find->hashkey)
|
||||
find = find->nexthash;
|
||||
if (!find) {
|
||||
int i = 0;
|
||||
while (translate[i][0]) {
|
||||
if (__at_hashkey(translate[i][0]) == hk)
|
||||
return at_find(__at_hashkey(translate[i][1]));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return find;
|
||||
return find;
|
||||
}
|
||||
|
||||
attrib *a_select(attrib * a, const void *data,
|
||||
bool(*compare) (const attrib *, const void *))
|
||||
bool(*compare) (const attrib *, const void *))
|
||||
{
|
||||
while (a && !compare(a, data))
|
||||
a = a->next;
|
||||
return a;
|
||||
while (a && !compare(a, data))
|
||||
a = a->next;
|
||||
return a;
|
||||
}
|
||||
|
||||
attrib *a_find(attrib * a, const attrib_type * at)
|
||||
{
|
||||
while (a && a->type != at)
|
||||
a = a->nexttype;
|
||||
return a;
|
||||
while (a && a->type != at)
|
||||
a = a->nexttype;
|
||||
return a;
|
||||
}
|
||||
|
||||
const attrib *a_findc(const attrib * a, const attrib_type * at)
|
||||
{
|
||||
while (a && a->type != at)
|
||||
a = a->nexttype;
|
||||
return a;
|
||||
while (a && a->type != at)
|
||||
a = a->nexttype;
|
||||
return a;
|
||||
}
|
||||
|
||||
static attrib *a_insert(attrib * head, attrib * a)
|
||||
{
|
||||
attrib **pa = &head->next;
|
||||
attrib **pa = &head->next;
|
||||
|
||||
assert(!(a->type->flags & ATF_UNIQUE));
|
||||
assert(head && head->type == a->type);
|
||||
assert(!(a->type->flags & ATF_UNIQUE));
|
||||
assert(head && head->type == a->type);
|
||||
|
||||
while (*pa && (*pa)->type == a->type) {
|
||||
pa = &(*pa)->next;
|
||||
}
|
||||
a->next = *pa;
|
||||
return *pa = a;
|
||||
while (*pa && (*pa)->type == a->type) {
|
||||
pa = &(*pa)->next;
|
||||
}
|
||||
a->next = *pa;
|
||||
return *pa = a;
|
||||
}
|
||||
|
||||
attrib *a_add(attrib ** pa, attrib * a)
|
||||
{
|
||||
attrib *first = *pa;
|
||||
assert(a->next == NULL && a->nexttype == NULL);
|
||||
attrib *first = *pa;
|
||||
assert(a->next == NULL && a->nexttype == NULL);
|
||||
|
||||
if (first == NULL)
|
||||
return *pa = a;
|
||||
if (first->type == a->type) {
|
||||
return a_insert(first, a);
|
||||
}
|
||||
for (;;) {
|
||||
attrib *next = first->nexttype;
|
||||
if (next == NULL) {
|
||||
/* the type is not in the list, append it behind the last type */
|
||||
attrib **insert = &first->next;
|
||||
first->nexttype = a;
|
||||
while (*insert)
|
||||
insert = &(*insert)->next;
|
||||
*insert = a;
|
||||
break;
|
||||
if (first == NULL)
|
||||
return *pa = a;
|
||||
if (first->type == a->type) {
|
||||
return a_insert(first, a);
|
||||
}
|
||||
if (next->type == a->type) {
|
||||
return a_insert(next, a);
|
||||
for (;;) {
|
||||
attrib *next = first->nexttype;
|
||||
if (next == NULL) {
|
||||
/* the type is not in the list, append it behind the last type */
|
||||
attrib **insert = &first->next;
|
||||
first->nexttype = a;
|
||||
while (*insert)
|
||||
insert = &(*insert)->next;
|
||||
*insert = a;
|
||||
break;
|
||||
}
|
||||
if (next->type == a->type) {
|
||||
return a_insert(next, a);
|
||||
}
|
||||
first = next;
|
||||
}
|
||||
first = next;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void a_free(attrib * a)
|
||||
{
|
||||
const attrib_type *at = a->type;
|
||||
if (at->finalize)
|
||||
at->finalize(a);
|
||||
free(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
static int a_unlink(attrib ** pa, attrib * a)
|
||||
{
|
||||
attrib **pnexttype = pa;
|
||||
attrib **pnext = NULL;
|
||||
attrib **pnexttype = pa;
|
||||
attrib **pnext = NULL;
|
||||
|
||||
assert(a != NULL);
|
||||
while (*pnexttype) {
|
||||
attrib *next = *pnexttype;
|
||||
if (next->type == a->type)
|
||||
break;
|
||||
pnexttype = &next->nexttype;
|
||||
pnext = &next->next;
|
||||
}
|
||||
if (*pnexttype && (*pnexttype)->type == a->type) {
|
||||
if (*pnexttype == a) {
|
||||
*pnexttype = a->next;
|
||||
if (a->next != a->nexttype) {
|
||||
a->next->nexttype = a->nexttype;
|
||||
}
|
||||
if (pnext == NULL)
|
||||
return 1;
|
||||
while (*pnext && (*pnext)->type != a->type)
|
||||
pnext = &(*pnext)->next;
|
||||
} else {
|
||||
pnext = &(*pnexttype)->next;
|
||||
assert(a != NULL);
|
||||
while (*pnexttype) {
|
||||
attrib *next = *pnexttype;
|
||||
if (next->type == a->type)
|
||||
break;
|
||||
pnexttype = &next->nexttype;
|
||||
pnext = &next->next;
|
||||
}
|
||||
while (*pnext && (*pnext)->type == a->type) {
|
||||
if (*pnext == a) {
|
||||
*pnext = a->next;
|
||||
return 1;
|
||||
}
|
||||
pnext = &(*pnext)->next;
|
||||
if (*pnexttype && (*pnexttype)->type == a->type) {
|
||||
if (*pnexttype == a) {
|
||||
*pnexttype = a->next;
|
||||
if (a->next != a->nexttype) {
|
||||
a->next->nexttype = a->nexttype;
|
||||
}
|
||||
if (pnext == NULL)
|
||||
return 1;
|
||||
while (*pnext && (*pnext)->type != a->type)
|
||||
pnext = &(*pnext)->next;
|
||||
}
|
||||
else {
|
||||
pnext = &(*pnexttype)->next;
|
||||
}
|
||||
while (*pnext && (*pnext)->type == a->type) {
|
||||
if (*pnext == a) {
|
||||
*pnext = a->next;
|
||||
return 1;
|
||||
}
|
||||
pnext = &(*pnext)->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a_free(attrib * a)
|
||||
{
|
||||
const attrib_type *at = a->type;
|
||||
if (at->finalize)
|
||||
at->finalize(a);
|
||||
free(a);
|
||||
}
|
||||
|
||||
int a_remove(attrib ** pa, attrib * a)
|
||||
{
|
||||
int ok;
|
||||
assert(a != NULL);
|
||||
ok = a_unlink(pa, a);
|
||||
if (ok)
|
||||
a_free(a);
|
||||
return ok;
|
||||
int ok;
|
||||
assert(a != NULL);
|
||||
ok = a_unlink(pa, a);
|
||||
if (ok)
|
||||
a_free(a);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void a_removeall(attrib ** pa, const attrib_type * at)
|
||||
{
|
||||
attrib **pnexttype = pa;
|
||||
attrib **pnext = NULL;
|
||||
attrib **pnexttype = pa;
|
||||
attrib **pnext = NULL;
|
||||
|
||||
while (*pnexttype) {
|
||||
attrib *next = *pnexttype;
|
||||
if (next->type == at)
|
||||
break;
|
||||
pnexttype = &next->nexttype;
|
||||
pnext = &next->next;
|
||||
}
|
||||
if (*pnexttype && (*pnexttype)->type == at) {
|
||||
attrib *a = *pnexttype;
|
||||
while (*pnexttype) {
|
||||
attrib *next = *pnexttype;
|
||||
if (next->type == at)
|
||||
break;
|
||||
pnexttype = &next->nexttype;
|
||||
pnext = &next->next;
|
||||
}
|
||||
if (*pnexttype && (*pnexttype)->type == at) {
|
||||
attrib *a = *pnexttype;
|
||||
|
||||
*pnexttype = a->nexttype;
|
||||
if (pnext) {
|
||||
while (*pnext && (*pnext)->type != at)
|
||||
pnext = &(*pnext)->next;
|
||||
*pnext = a->nexttype;
|
||||
*pnexttype = a->nexttype;
|
||||
if (pnext) {
|
||||
while (*pnext && (*pnext)->type != at)
|
||||
pnext = &(*pnext)->next;
|
||||
*pnext = a->nexttype;
|
||||
}
|
||||
while (a && a->type == at) {
|
||||
attrib *ra = a;
|
||||
a = a->next;
|
||||
a_free(ra);
|
||||
}
|
||||
}
|
||||
while (a && a->type == at) {
|
||||
attrib *ra = a;
|
||||
a = a->next;
|
||||
a_free(ra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attrib *a_new(const attrib_type * at)
|
||||
{
|
||||
attrib *a = (attrib *) calloc(1, sizeof(attrib));
|
||||
assert(at != NULL);
|
||||
a->type = at;
|
||||
if (at->initialize)
|
||||
at->initialize(a);
|
||||
return a;
|
||||
attrib *a = (attrib *)calloc(1, sizeof(attrib));
|
||||
assert(at != NULL);
|
||||
a->type = at;
|
||||
if (at->initialize)
|
||||
at->initialize(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
int a_age(attrib ** p)
|
||||
{
|
||||
attrib **ap = p;
|
||||
/* Attribute altern, und die Entfernung (age()==0) eines Attributs
|
||||
* hat Einfluß auf den Besitzer */
|
||||
while (*ap) {
|
||||
attrib *a = *ap;
|
||||
if (a->type->age) {
|
||||
int result = a->type->age(a);
|
||||
assert(result >= 0 || !"age() returned a negative value");
|
||||
if (result == 0) {
|
||||
a_remove(p, a);
|
||||
continue;
|
||||
}
|
||||
attrib **ap = p;
|
||||
/* Attribute altern, und die Entfernung (age()==0) eines Attributs
|
||||
* hat Einfluß auf den Besitzer */
|
||||
while (*ap) {
|
||||
attrib *a = *ap;
|
||||
if (a->type->age) {
|
||||
int result = a->type->age(a);
|
||||
assert(result >= 0 || !"age() returned a negative value");
|
||||
if (result == 0) {
|
||||
a_remove(p, a);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ap = &a->next;
|
||||
}
|
||||
ap = &a->next;
|
||||
}
|
||||
return (*p != NULL);
|
||||
return (*p != NULL);
|
||||
}
|
||||
|
||||
static critbit_tree cb_deprecated = { 0 };
|
||||
|
||||
void at_deprecate(const char * name, int (*reader)(attrib *, void *, struct storage *))
|
||||
void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *))
|
||||
{
|
||||
char buffer[64];
|
||||
size_t len = strlen(name);
|
||||
len = cb_new_kv(name, len, &reader, sizeof(reader), buffer);
|
||||
cb_insert(&cb_deprecated, buffer, len);
|
||||
char buffer[64];
|
||||
size_t len = strlen(name);
|
||||
len = cb_new_kv(name, len, &reader, sizeof(reader), buffer);
|
||||
cb_insert(&cb_deprecated, buffer, len);
|
||||
}
|
||||
|
||||
int a_read(struct storage *store, attrib ** attribs, void *owner)
|
||||
{
|
||||
int key, retval = AT_READ_OK;
|
||||
char zText[128];
|
||||
|
||||
zText[0] = 0;
|
||||
key = -1;
|
||||
READ_TOK(store, zText, sizeof(zText));
|
||||
if (strcmp(zText, "end") == 0)
|
||||
return retval;
|
||||
else
|
||||
key = __at_hashkey(zText);
|
||||
|
||||
while (key != -1) {
|
||||
int (*reader)(attrib *, void *, struct storage *) = 0;
|
||||
attrib_type *at = at_find(key);
|
||||
attrib * na = 0;
|
||||
|
||||
if (at) {
|
||||
reader = at->read;
|
||||
na = a_new(at);
|
||||
} else {
|
||||
const void * kv;
|
||||
cb_find_prefix(&cb_deprecated, zText, strlen(zText)+1, &kv, 1, 0);
|
||||
if (kv) {
|
||||
cb_get_kv(kv, &reader, sizeof(reader));
|
||||
} else {
|
||||
fprintf(stderr, "attribute hash: %d (%s)\n", key, zText);
|
||||
assert(at || !"attribute not registered");
|
||||
}
|
||||
}
|
||||
if (reader) {
|
||||
int i = reader(na, owner, store);
|
||||
if (na) {
|
||||
switch (i) {
|
||||
case AT_READ_OK:
|
||||
a_add(attribs, na);
|
||||
break;
|
||||
case AT_READ_FAIL:
|
||||
retval = AT_READ_FAIL;
|
||||
a_free(na);
|
||||
break;
|
||||
default:
|
||||
assert(!"invalid return value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(!"error: no registered callback can read attribute");
|
||||
}
|
||||
int key, retval = AT_READ_OK;
|
||||
char zText[128];
|
||||
|
||||
zText[0] = 0;
|
||||
key = -1;
|
||||
READ_TOK(store, zText, sizeof(zText));
|
||||
if (!strcmp(zText, "end"))
|
||||
break;
|
||||
key = __at_hashkey(zText);
|
||||
}
|
||||
return retval;
|
||||
if (strcmp(zText, "end") == 0)
|
||||
return retval;
|
||||
else
|
||||
key = __at_hashkey(zText);
|
||||
|
||||
while (key != -1) {
|
||||
int(*reader)(attrib *, void *, struct storage *) = 0;
|
||||
attrib_type *at = at_find(key);
|
||||
attrib * na = 0;
|
||||
|
||||
if (at) {
|
||||
reader = at->read;
|
||||
na = a_new(at);
|
||||
}
|
||||
else {
|
||||
const void * kv;
|
||||
cb_find_prefix(&cb_deprecated, zText, strlen(zText) + 1, &kv, 1, 0);
|
||||
if (kv) {
|
||||
cb_get_kv(kv, &reader, sizeof(reader));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "attribute hash: %d (%s)\n", key, zText);
|
||||
assert(at || !"attribute not registered");
|
||||
}
|
||||
}
|
||||
if (reader) {
|
||||
int i = reader(na, owner, store);
|
||||
if (na) {
|
||||
switch (i) {
|
||||
case AT_READ_OK:
|
||||
a_add(attribs, na);
|
||||
break;
|
||||
case AT_READ_FAIL:
|
||||
retval = AT_READ_FAIL;
|
||||
a_free(na);
|
||||
break;
|
||||
default:
|
||||
assert(!"invalid return value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(!"error: no registered callback can read attribute");
|
||||
}
|
||||
|
||||
READ_TOK(store, zText, sizeof(zText));
|
||||
if (!strcmp(zText, "end"))
|
||||
break;
|
||||
key = __at_hashkey(zText);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void a_write(struct storage *store, const attrib * attribs, const void *owner)
|
||||
{
|
||||
const attrib *na = attribs;
|
||||
const attrib *na = attribs;
|
||||
|
||||
while (na) {
|
||||
if (na->type->write) {
|
||||
assert(na->type->hashkey || !"attribute not registered");
|
||||
WRITE_TOK(store, na->type->name);
|
||||
na->type->write(na, owner, store);
|
||||
na = na->next;
|
||||
} else {
|
||||
na = na->nexttype;
|
||||
while (na) {
|
||||
if (na->type->write) {
|
||||
assert(na->type->hashkey || !"attribute not registered");
|
||||
WRITE_TOK(store, na->type->name);
|
||||
na->type->write(na, owner, store);
|
||||
na = na->next;
|
||||
}
|
||||
else {
|
||||
na = na->nexttype;
|
||||
}
|
||||
}
|
||||
}
|
||||
WRITE_TOK(store, "end");
|
||||
WRITE_TOK(store, "end");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -18,66 +18,66 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
#ifndef ATTRIB_H
|
||||
#define ATTRIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct gamedata;
|
||||
struct storage;
|
||||
typedef void (*afun) (void);
|
||||
struct gamedata;
|
||||
struct storage;
|
||||
typedef void(*afun) (void);
|
||||
|
||||
typedef struct attrib {
|
||||
const struct attrib_type *type;
|
||||
union {
|
||||
afun f;
|
||||
void *v;
|
||||
int i;
|
||||
float flt;
|
||||
char c;
|
||||
short s;
|
||||
short sa[2];
|
||||
char ca[4];
|
||||
} data;
|
||||
/* internal data, do not modify: */
|
||||
struct attrib *next; /* next attribute in the list */
|
||||
struct attrib *nexttype; /* skip to attribute of a different type */
|
||||
} attrib;
|
||||
typedef struct attrib {
|
||||
const struct attrib_type *type;
|
||||
union {
|
||||
afun f;
|
||||
void *v;
|
||||
int i;
|
||||
float flt;
|
||||
char c;
|
||||
short s;
|
||||
short sa[2];
|
||||
char ca[4];
|
||||
} data;
|
||||
/* internal data, do not modify: */
|
||||
struct attrib *next; /* next attribute in the list */
|
||||
struct attrib *nexttype; /* skip to attribute of a different type */
|
||||
} attrib;
|
||||
|
||||
#define ATF_UNIQUE (1<<0) /* only one per attribute-list */
|
||||
#define ATF_PRESERVE (1<<1) /* preserve order in list. append to back */
|
||||
#define ATF_USER_DEFINED (1<<2) /* use this to make udf */
|
||||
|
||||
typedef struct attrib_type {
|
||||
const char *name;
|
||||
void (*initialize) (struct attrib *);
|
||||
void (*finalize) (struct attrib *);
|
||||
int (*age) (struct attrib *);
|
||||
/* age returns 0 if the attribute needs to be removed, !=0 otherwise */
|
||||
void(*write) (const struct attrib *, const void *owner, struct storage *);
|
||||
int(*read) (struct attrib *, void *owner, struct storage *); /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */
|
||||
unsigned int flags;
|
||||
/* ---- internal data, do not modify: ---- */
|
||||
struct attrib_type *nexthash;
|
||||
unsigned int hashkey;
|
||||
} attrib_type;
|
||||
typedef struct attrib_type {
|
||||
const char *name;
|
||||
void(*initialize) (struct attrib *);
|
||||
void(*finalize) (struct attrib *);
|
||||
int(*age) (struct attrib *);
|
||||
/* age returns 0 if the attribute needs to be removed, !=0 otherwise */
|
||||
void(*write) (const struct attrib *, const void *owner, struct storage *);
|
||||
int(*read) (struct attrib *, void *owner, struct storage *); /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */
|
||||
unsigned int flags;
|
||||
/* ---- internal data, do not modify: ---- */
|
||||
struct attrib_type *nexthash;
|
||||
unsigned int hashkey;
|
||||
} attrib_type;
|
||||
|
||||
extern void at_register(attrib_type * at);
|
||||
extern void at_deprecate(const char * name, int (*reader)(attrib *, void *, struct storage *));
|
||||
extern void at_register(attrib_type * at);
|
||||
extern void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *));
|
||||
|
||||
extern attrib *a_select(attrib * a, const void *data,
|
||||
bool(*compare) (const attrib *, const void *));
|
||||
extern attrib *a_find(attrib * a, const attrib_type * at);
|
||||
extern const attrib *a_findc(const attrib * a, const attrib_type * at);
|
||||
extern attrib *a_add(attrib ** pa, attrib * at);
|
||||
extern int a_remove(attrib ** pa, attrib * at);
|
||||
extern void a_removeall(attrib ** a, const attrib_type * at);
|
||||
extern attrib *a_new(const attrib_type * at);
|
||||
extern void a_free(attrib * a);
|
||||
extern attrib *a_select(attrib * a, const void *data,
|
||||
bool(*compare) (const attrib *, const void *));
|
||||
extern attrib *a_find(attrib * a, const attrib_type * at);
|
||||
extern const attrib *a_findc(const attrib * a, const attrib_type * at);
|
||||
extern attrib *a_add(attrib ** pa, attrib * at);
|
||||
extern int a_remove(attrib ** pa, attrib * at);
|
||||
extern void a_removeall(attrib ** a, const attrib_type * at);
|
||||
extern attrib *a_new(const attrib_type * at);
|
||||
|
||||
extern int a_age(attrib ** attribs);
|
||||
extern int a_read(struct storage *store, attrib ** attribs, void *owner);
|
||||
extern void a_write(struct storage *store, const attrib * attribs,
|
||||
const void *owner);
|
||||
extern int a_age(attrib ** attribs);
|
||||
extern int a_read(struct storage *store, attrib ** attribs, void *owner);
|
||||
extern void a_write(struct storage *store, const attrib * attribs,
|
||||
const void *owner);
|
||||
|
||||
#define DEFAULT_AGE NULL
|
||||
#define DEFAULT_INIT NULL
|
||||
|
|
84
src/util/attrib.test.c
Normal file
84
src/util/attrib.test.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include <platform.h>
|
||||
#include "attrib.h"
|
||||
|
||||
#include <CuTest.h>
|
||||
#include <tests.h>
|
||||
|
||||
static void test_attrib_new(CuTest * tc)
|
||||
{
|
||||
attrib_type at_test = { "test" };
|
||||
attrib * a;
|
||||
CuAssertPtrNotNull(tc, (a = a_new(&at_test)));
|
||||
CuAssertPtrEquals(tc, 0, a->next);
|
||||
CuAssertPtrEquals(tc, 0, a->nexttype);
|
||||
CuAssertPtrEquals(tc, (void *)a->type, (void *)&at_test);
|
||||
a_remove(&a, a);
|
||||
CuAssertPtrEquals(tc, 0, a);
|
||||
}
|
||||
|
||||
|
||||
static void test_attrib_add(CuTest * tc)
|
||||
{
|
||||
attrib_type at_foo = { "foo" };
|
||||
attrib_type at_bar = { "bar" };
|
||||
attrib *a, *alist = 0;
|
||||
|
||||
CuAssertPtrNotNull(tc, (a = a_new(&at_foo)));
|
||||
CuAssertPtrEquals(tc, a, a_add(&alist, a));
|
||||
CuAssertPtrEquals(tc, a, alist);
|
||||
|
||||
CuAssertPtrNotNull(tc, (a = a_add(&alist, a_new(&at_foo))));
|
||||
CuAssertPtrEquals_Msg(tc, "new attribute not added after existing", alist->next, a);
|
||||
|
||||
CuAssertPtrNotNull(tc, (a = a_add(&alist, a_new(&at_bar))));
|
||||
CuAssertPtrEquals_Msg(tc, "new atribute not added at end of list", alist->next->next, a);
|
||||
|
||||
CuAssertPtrNotNull(tc, (a = a_add(&alist, a_new(&at_foo))));
|
||||
CuAssertPtrEquals_Msg(tc, "messages not sorted by type", alist->next->next, a);
|
||||
a_removeall(&alist, &at_foo);
|
||||
a_removeall(&alist, &at_bar);
|
||||
}
|
||||
|
||||
static void test_attrib_remove(CuTest * tc)
|
||||
{
|
||||
attrib_type at_foo = { "foo" };
|
||||
attrib *a, *alist = 0;
|
||||
|
||||
CuAssertPtrNotNull(tc, a_add(&alist, a_new(&at_foo)));
|
||||
CuAssertPtrNotNull(tc, a = a_add(&alist, a_new(&at_foo)));
|
||||
CuAssertIntEquals(tc, 1, a_remove(&alist, a));
|
||||
CuAssertPtrNotNull(tc, alist);
|
||||
CuAssertIntEquals(tc, 1, a_remove(&alist, alist));
|
||||
CuAssertPtrEquals(tc, 0, alist);
|
||||
}
|
||||
|
||||
static void test_attrib_nexttype(CuTest * tc)
|
||||
{
|
||||
attrib_type at_foo = { "foo" };
|
||||
attrib_type at_bar = { "bar" };
|
||||
attrib *a, *alist = 0;
|
||||
CuAssertPtrNotNull(tc, (a = a_new(&at_foo)));
|
||||
CuAssertPtrEquals(tc, 0, a->nexttype);
|
||||
CuAssertPtrEquals(tc, a, a_add(&alist, a));
|
||||
CuAssertPtrEquals(tc, 0, alist->nexttype);
|
||||
|
||||
CuAssertPtrNotNull(tc, a_add(&alist, a_new(&at_foo)));
|
||||
CuAssertPtrEquals(tc, 0, alist->nexttype);
|
||||
|
||||
CuAssertPtrNotNull(tc, (a = a_add(&alist, a_new(&at_bar))));
|
||||
CuAssertPtrEquals(tc, a, alist->nexttype);
|
||||
CuAssertPtrEquals(tc, 0, a->nexttype);
|
||||
|
||||
a_removeall(&alist, &at_foo);
|
||||
a_removeall(&alist, &at_bar);
|
||||
}
|
||||
|
||||
CuSuite *get_attrib_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_attrib_new);
|
||||
SUITE_ADD_TEST(suite, test_attrib_add);
|
||||
SUITE_ADD_TEST(suite, test_attrib_remove);
|
||||
SUITE_ADD_TEST(suite, test_attrib_nexttype);
|
||||
return suite;
|
||||
}
|
|
@ -18,6 +18,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
#include <platform.h>
|
||||
#include "base36.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
@ -56,6 +57,7 @@ int atoi36(const char *str)
|
|||
|
||||
const char *itoab(int i, int base)
|
||||
{
|
||||
const int maxlen = 20;
|
||||
static char **as = NULL; // FIXME: static return value
|
||||
char *s, *dst;
|
||||
static int index = 0; /* STATIC_XCALL: used across calls */
|
||||
|
@ -63,34 +65,45 @@ const char *itoab(int i, int base)
|
|||
|
||||
if (!as) {
|
||||
int j;
|
||||
char *x = (char *)calloc(sizeof(char), 8 * 4); /* STATIC_LEAK: malloc in static variable */
|
||||
char *x = (char *)calloc(sizeof(char), maxlen * 4); /* STATIC_LEAK: malloc in static variable */
|
||||
as = (char **)calloc(sizeof(char *), 4);
|
||||
for (j = 0; j != 4; ++j)
|
||||
as[j] = x + j * 8;
|
||||
as[j] = x + j * maxlen;
|
||||
}
|
||||
s = as[index];
|
||||
index = (index + 1) & 3; /* quick for % 4 */
|
||||
dst = s + 7;
|
||||
dst = s + maxlen - 1;
|
||||
(*dst--) = 0;
|
||||
if (i != 0) {
|
||||
if (i < 0) {
|
||||
i = -i;
|
||||
neg = 1;
|
||||
}
|
||||
while (i) {
|
||||
while (i && dst>=s) {
|
||||
int x = i % base;
|
||||
i = i / base;
|
||||
if (x < 10)
|
||||
if (x < 10) {
|
||||
*(dst--) = (char)('0' + x);
|
||||
else if ('a' + x - 10 == 'l')
|
||||
}
|
||||
else if ('a' + x - 10 == 'l') {
|
||||
*(dst--) = 'L';
|
||||
else
|
||||
}
|
||||
else {
|
||||
*(dst--) = (char)('a' + (x - 10));
|
||||
}
|
||||
}
|
||||
if (dst > s) {
|
||||
if (neg) {
|
||||
*(dst) = '-';
|
||||
}
|
||||
else {
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_error("static buffer exhauset, itoab(%d, %d)", i, base);
|
||||
assert(i == 0 || !"itoab: static buffer exhausted");
|
||||
}
|
||||
if (neg)
|
||||
*(dst) = '-';
|
||||
else
|
||||
++dst;
|
||||
}
|
||||
else
|
||||
*dst = '0';
|
||||
|
|
|
@ -17,6 +17,8 @@ static void test_itoa36(CuTest * tc)
|
|||
{
|
||||
CuAssertStrEquals(tc, itoa36(0), "0");
|
||||
CuAssertStrEquals(tc, itoa10(INT_MAX), "2147483647");
|
||||
CuAssertStrEquals(tc, itoab(INT_MAX, 8), "17777777777");
|
||||
CuAssertStrEquals(tc, itoab(INT_MAX, 4), "1333333333333333");
|
||||
CuAssertStrEquals(tc, itoab(-1, 5), "-1");
|
||||
CuAssertStrEquals(tc, itoa36(-1), "-1");
|
||||
CuAssertStrEquals(tc, itoa36(-10), "-a");
|
||||
|
|
|
@ -116,22 +116,8 @@ void addtoken(void ** root, const char *str, variant id)
|
|||
const char str[3];
|
||||
} replace[] = {
|
||||
/* match lower-case (!) umlauts and others to transcriptions */
|
||||
{
|
||||
228, "AE" }, /* auml */
|
||||
{
|
||||
246, "OE" }, /* ouml */
|
||||
{
|
||||
252, "UE" }, /* uuml */
|
||||
{
|
||||
223, "SS" }, /* szlig */
|
||||
{
|
||||
230, "AE" }, /* norsk */
|
||||
{
|
||||
248, "OE" }, /* norsk */
|
||||
{
|
||||
229, "AA" }, /* norsk */
|
||||
{
|
||||
0, "" }
|
||||
{ 228, "AE" }, { 246, "OE" }, { 252, "UE" }, { 223, "SS" },
|
||||
{ 230, "AE" }, { 248, "OE" }, { 229, "AA" }, { 0, "" }
|
||||
};
|
||||
|
||||
assert(root && str);
|
||||
|
|
36
src/vortex.c
36
src/vortex.c
|
@ -26,33 +26,29 @@ typedef struct dir_lookup {
|
|||
|
||||
static dir_lookup *dir_name_lookup;
|
||||
|
||||
void register_special_direction(const char *name)
|
||||
void register_special_direction(struct locale *lang, const char *name)
|
||||
{
|
||||
struct locale *lang;
|
||||
char *str = _strdup(name);
|
||||
const char *token = LOC(lang, name);
|
||||
|
||||
for (lang = locales; lang; lang = nextlocale(lang)) {
|
||||
if (token) {
|
||||
void **tokens = get_translations(lang, UT_SPECDIR);
|
||||
const char *token = LOC(lang, name);
|
||||
variant var;
|
||||
char *str = _strdup(name);
|
||||
|
||||
if (token) {
|
||||
variant var;
|
||||
var.v = str;
|
||||
addtoken(tokens, token, var);
|
||||
|
||||
var.v = str;
|
||||
addtoken(tokens, token, var);
|
||||
|
||||
if (lang == locales) {
|
||||
dir_lookup *dl = malloc(sizeof(dir_lookup));
|
||||
dl->name = str;
|
||||
dl->oldname = token;
|
||||
dl->next = dir_name_lookup;
|
||||
dir_name_lookup = dl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_error("no translation for spec_direction '%s' in locale '%s'\n", name, locale_name(lang));
|
||||
if (lang == locales) {
|
||||
dir_lookup *dl = malloc(sizeof(dir_lookup));
|
||||
dl->name = str;
|
||||
dl->oldname = token;
|
||||
dl->next = dir_name_lookup;
|
||||
dir_name_lookup = dl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_error("no translation for spec_direction '%s' in locale '%s'\n", name, locale_name(lang));
|
||||
}
|
||||
}
|
||||
|
||||
/********************/
|
||||
|
|
|
@ -6,6 +6,7 @@ extern "C" {
|
|||
|
||||
struct region;
|
||||
struct attrib;
|
||||
struct locale;
|
||||
|
||||
typedef struct spec_direction {
|
||||
int x, y;
|
||||
|
@ -19,7 +20,7 @@ extern "C" {
|
|||
|
||||
struct region *find_special_direction(const struct region *r,
|
||||
const char *token);
|
||||
void register_special_direction(const char *name);
|
||||
void register_special_direction(struct locale *lang, const char *name);
|
||||
struct spec_direction *special_direction(const struct region * from,
|
||||
const struct region * to);
|
||||
struct attrib *create_special_direction(struct region *r, struct region *rt,
|
||||
|
@ -28,4 +29,4 @@ extern "C" {
|
|||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -25,7 +25,6 @@ static void test_move_to_vortex(CuTest *tc) {
|
|||
lang = get_or_create_locale("en");
|
||||
locale_setstring(lang, "vortex", "wirbel");
|
||||
init_locale(lang);
|
||||
register_special_direction("vortex");
|
||||
t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION);
|
||||
r1 = test_create_region(0, 0, t_plain);
|
||||
r2 = test_create_region(5, 0, t_plain);
|
||||
|
@ -36,13 +35,9 @@ static void test_move_to_vortex(CuTest *tc) {
|
|||
CuAssertPtrEquals(tc, r2, r);
|
||||
}
|
||||
|
||||
static void test_vortex(CuTest *tc) {
|
||||
}
|
||||
|
||||
CuSuite *get_vortex_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_vortex);
|
||||
SUITE_ADD_TEST(suite, test_move_to_vortex);
|
||||
return suite;
|
||||
}
|
||||
|
|
245
src/wormhole.c
Normal file
245
src/wormhole.c
Normal file
|
@ -0,0 +1,245 @@
|
|||
/* vi: set ts=2:
|
||||
+-------------------+
|
||||
| | Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
| Eressea PBEM host | Enno Rehling <enno@eressea.de>
|
||||
| (c) 1998 - 2004 | Katja Zedel <katze@felidae.kn-bremen.de>
|
||||
| |
|
||||
+-------------------+
|
||||
|
||||
This program may not be used, modified or distributed
|
||||
without prior permission by the authors of Eressea.
|
||||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <kernel/config.h>
|
||||
#include "settings.h"
|
||||
|
||||
#include "wormhole.h"
|
||||
|
||||
/* kernel includes */
|
||||
#include <kernel/building.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/messages.h>
|
||||
#include <kernel/plane.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/unit.h>
|
||||
#include <kernel/version.h>
|
||||
|
||||
/* util includes */
|
||||
#include <util/attrib.h>
|
||||
#include <util/language.h>
|
||||
#include <util/resolve.h>
|
||||
#include <util/rng.h>
|
||||
#include <quicklist.h>
|
||||
|
||||
#include <storage.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static bool good_region(const region * r)
|
||||
{
|
||||
return (!fval(r, RF_CHAOTIC) && r->age > 30 && rplane(r) == NULL
|
||||
&& r->units != NULL && r->land != NULL);
|
||||
}
|
||||
|
||||
static int cmp_age(const void *v1, const void *v2)
|
||||
{
|
||||
const region **r1 = (const region **)v1;
|
||||
const region **r2 = (const region **)v2;
|
||||
if ((*r1)->age < (*r2)->age)
|
||||
return -1;
|
||||
if ((*r1)->age > (*r2)->age)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct wormhole_data {
|
||||
building *entry;
|
||||
region *exit;
|
||||
} wormhole_data;
|
||||
|
||||
static void wormhole_init(struct attrib *a)
|
||||
{
|
||||
a->data.v = calloc(1, sizeof(wormhole_data));
|
||||
}
|
||||
|
||||
static void wormhole_done(struct attrib *a)
|
||||
{
|
||||
free(a->data.v);
|
||||
}
|
||||
|
||||
static int wormhole_age(struct attrib *a)
|
||||
{
|
||||
wormhole_data *data = (wormhole_data *)a->data.v;
|
||||
int maxtransport = data->entry->size;
|
||||
region *r = data->entry->region;
|
||||
unit *u = r->units;
|
||||
|
||||
for (; u != NULL && maxtransport != 0; u = u->next) {
|
||||
if (u->building == data->entry) {
|
||||
message *m = NULL;
|
||||
if (u->number > maxtransport || has_limited_skills(u)) {
|
||||
m = msg_message("wormhole_requirements", "unit region", u, u->region);
|
||||
}
|
||||
else if (data->exit != NULL) {
|
||||
move_unit(u, data->exit, NULL);
|
||||
maxtransport -= u->number;
|
||||
m = msg_message("wormhole_exit", "unit region", u, data->exit);
|
||||
add_message(&data->exit->msgs, m);
|
||||
}
|
||||
if (m != NULL) {
|
||||
add_message(&u->faction->msgs, m);
|
||||
msg_release(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_building(&r->buildings, data->entry);
|
||||
ADDMSG(&r->msgs, msg_message("wormhole_dissolve", "region", r));
|
||||
|
||||
/* age returns 0 if the attribute needs to be removed, !=0 otherwise */
|
||||
return AT_AGE_KEEP;
|
||||
}
|
||||
|
||||
static void
|
||||
wormhole_write(const struct attrib *a, const void *owner, struct storage *store)
|
||||
{
|
||||
wormhole_data *data = (wormhole_data *)a->data.v;
|
||||
write_building_reference(data->entry, store);
|
||||
write_region_reference(data->exit, store);
|
||||
}
|
||||
|
||||
/** conversion code, turn 573, 2008-05-23 */
|
||||
static int resolve_exit(variant id, void *address)
|
||||
{
|
||||
building *b = findbuilding(id.i);
|
||||
region **rp = address;
|
||||
if (b) {
|
||||
*rp = b->region;
|
||||
return 0;
|
||||
}
|
||||
*rp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int wormhole_read(struct attrib *a, void *owner, struct storage *store)
|
||||
{
|
||||
wormhole_data *data = (wormhole_data *)a->data.v;
|
||||
resolve_fun resolver = (global.data_version < UIDHASH_VERSION)
|
||||
? resolve_exit : resolve_region_id;
|
||||
read_fun reader = (global.data_version < UIDHASH_VERSION)
|
||||
? read_building_reference : read_region_reference;
|
||||
|
||||
int rb =
|
||||
read_reference(&data->entry, store, read_building_reference,
|
||||
resolve_building);
|
||||
int rr = read_reference(&data->exit, store, reader, resolver);
|
||||
if (rb == 0 && rr == 0) {
|
||||
if (!data->exit || !data->entry) {
|
||||
return AT_READ_FAIL;
|
||||
}
|
||||
}
|
||||
return AT_READ_OK;
|
||||
}
|
||||
|
||||
static attrib_type at_wormhole = {
|
||||
"wormhole",
|
||||
wormhole_init,
|
||||
wormhole_done,
|
||||
wormhole_age,
|
||||
wormhole_write,
|
||||
wormhole_read,
|
||||
ATF_UNIQUE
|
||||
};
|
||||
|
||||
static void
|
||||
make_wormhole(const building_type * bt_wormhole, region * r1, region * r2)
|
||||
{
|
||||
building *b1 = new_building(bt_wormhole, r1, default_locale);
|
||||
building *b2 = new_building(bt_wormhole, r2, default_locale);
|
||||
attrib *a1 = a_add(&b1->attribs, a_new(&at_wormhole));
|
||||
attrib *a2 = a_add(&b2->attribs, a_new(&at_wormhole));
|
||||
wormhole_data *d1 = (wormhole_data *)a1->data.v;
|
||||
wormhole_data *d2 = (wormhole_data *)a2->data.v;
|
||||
d1->entry = b1;
|
||||
d2->entry = b2;
|
||||
d1->exit = b2->region;
|
||||
d2->exit = b1->region;
|
||||
b1->size = bt_wormhole->maxsize;
|
||||
b2->size = bt_wormhole->maxsize;
|
||||
ADDMSG(&r1->msgs, msg_message("wormhole_appear", "region", r1));
|
||||
ADDMSG(&r2->msgs, msg_message("wormhole_appear", "region", r2));
|
||||
}
|
||||
|
||||
#define WORMHOLE_CHANCE 10000
|
||||
|
||||
void select_wormhole_regions(quicklist **rlistp, int *countp) {
|
||||
quicklist *rlist = 0;
|
||||
region *r = regions;
|
||||
int count = 0;
|
||||
|
||||
while (r != NULL) {
|
||||
int next = rng_int() % (2 * WORMHOLE_CHANCE);
|
||||
while (r != NULL && (next != 0 || !good_region(r))) {
|
||||
if (good_region(r)) {
|
||||
--next;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (r == NULL)
|
||||
break;
|
||||
ql_push(&rlist, r);
|
||||
++count;
|
||||
r = r->next;
|
||||
}
|
||||
|
||||
*countp = count;
|
||||
*rlistp = rlist;
|
||||
}
|
||||
|
||||
void sort_wormhole_regions(quicklist *rlist, region **match, int count) {
|
||||
quicklist *ql;
|
||||
int qi, i = 0;
|
||||
|
||||
for (ql = rlist, qi = 0; i != count; ql_advance(&ql, &qi, 1)) {
|
||||
match[i++] = (region *)ql_get(ql, qi);
|
||||
}
|
||||
qsort(match, count, sizeof(region *), cmp_age);
|
||||
}
|
||||
|
||||
void make_wormholes(region **match, int count, const building_type *bt_wormhole) {
|
||||
int i;
|
||||
count /= 2;
|
||||
for (i = 0; i != count; ++i) {
|
||||
make_wormhole(bt_wormhole, match[i], match[i + count]);
|
||||
}
|
||||
}
|
||||
|
||||
void create_wormholes(void)
|
||||
{
|
||||
const building_type *bt_wormhole = bt_find("wormhole");
|
||||
quicklist *rlist = 0;
|
||||
int count = 0;
|
||||
region **match;
|
||||
|
||||
if (bt_wormhole == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
select_wormhole_regions(&rlist, &count);
|
||||
if (count < 2) {
|
||||
return;
|
||||
}
|
||||
match = (region **)malloc(sizeof(region *) * count);
|
||||
sort_wormhole_regions(rlist, match, count);
|
||||
ql_free(rlist);
|
||||
make_wormholes(match, count, bt_wormhole);
|
||||
free(match);
|
||||
}
|
||||
|
||||
void register_wormholes(void)
|
||||
{
|
||||
at_register(&at_wormhole);
|
||||
}
|
68
src/wormhole.test.c
Normal file
68
src/wormhole.test.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include <platform.h>
|
||||
#include <kernel/types.h>
|
||||
|
||||
#include "wormhole.h"
|
||||
#include "tests.h"
|
||||
|
||||
#include <kernel/building.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/terrain.h>
|
||||
|
||||
#include <util/attrib.h>
|
||||
|
||||
#include <quicklist.h>
|
||||
|
||||
#include <CuTest.h>
|
||||
|
||||
void sort_wormhole_regions(quicklist *rlist, region **match, int count);
|
||||
void make_wormholes(region **match, int count, const building_type *bt_wormhole);
|
||||
|
||||
static void test_make_wormholes(CuTest *tc) {
|
||||
region *r1, *r2, *match[2];
|
||||
terrain_type *t_plain;
|
||||
building_type *btype;
|
||||
|
||||
test_cleanup();
|
||||
t_plain = test_create_terrain("plain", LAND_REGION);
|
||||
btype = test_create_buildingtype("wormhole");
|
||||
match[0] = r1 = test_create_region(0, 0, t_plain);
|
||||
match[1] = r2 = test_create_region(1, 0, t_plain);
|
||||
make_wormholes(match, 2, btype);
|
||||
CuAssertPtrNotNull(tc, r1->buildings);
|
||||
CuAssertPtrNotNull(tc, r1->buildings->attribs);
|
||||
CuAssertPtrEquals(tc, (void *)r1->buildings->type, (void *)btype);
|
||||
CuAssertPtrNotNull(tc, r2->buildings);
|
||||
CuAssertPtrNotNull(tc, r2->buildings->attribs);
|
||||
CuAssertPtrEquals(tc, (void *)r2->buildings->type, (void *)btype);
|
||||
CuAssertPtrEquals(tc, (void *)r1->buildings->attribs->type, (void *)r2->buildings->attribs->type);
|
||||
CuAssertStrEquals(tc, r1->buildings->attribs->type->name, "wormhole");
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
static void test_sort_wormhole_regions(CuTest *tc) {
|
||||
region *r1, *r2, *match[2];
|
||||
terrain_type *t_plain;
|
||||
quicklist *rlist = 0;
|
||||
|
||||
test_cleanup();
|
||||
t_plain = test_create_terrain("plain", LAND_REGION);
|
||||
r1 = test_create_region(0, 0, t_plain);
|
||||
r2 = test_create_region(1, 0, t_plain);
|
||||
r1->age = 4;
|
||||
r2->age = 2;
|
||||
ql_push(&rlist, r1);
|
||||
ql_push(&rlist, r2);
|
||||
sort_wormhole_regions(rlist, match, 2);
|
||||
CuAssertPtrEquals(tc, r2, match[0]);
|
||||
CuAssertPtrEquals(tc, r1, match[1]);
|
||||
ql_free(rlist);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
CuSuite *get_wormhole_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_sort_wormhole_regions);
|
||||
SUITE_ADD_TEST(suite, test_make_wormholes);
|
||||
return suite;
|
||||
}
|
Loading…
Add table
Reference in a new issue