Merge branch 'master' of github.com:eressea/server

This commit is contained in:
Enno Rehling 2014-10-19 05:56:41 +02:00
commit f4f9e74e90
91 changed files with 2758 additions and 2532 deletions

8
.travis.yml Normal file
View 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

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
[ -z $ERESSEA ] && ERESSEA=$HOME/eressea
SRC=$ERESSEA/git
$SRC/s/preview build master

View file

@ -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
View 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
View 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'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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',
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,4 +0,0 @@
require "spells"
require "e3a.rules"
require "e3a.markets"
require "e3a.frost"

4
scripts/reports.lua Normal file
View file

@ -0,0 +1,4 @@
dofile('config.lua')
eressea.read_game(get_turn() .. '.dat')
init_reports()
write_reports()

View file

@ -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

View file

@ -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

View file

@ -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}

View file

@ -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)

View file

@ -4,6 +4,7 @@
#include "json.h"
#include <kernel/faction.h>
#include <kernel/types.h>
#include <kernel/config.h>
#include <kernel/save.h>

View file

@ -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);

View file

@ -1,3 +1,3 @@
#define VERSION_MAJOR 3
#define VERSION_MINOR 2
#define VERSION_BUILD 684
#define VERSION_MINOR 3
#define VERSION_BUILD 687

View file

@ -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) {

View file

@ -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);

View file

@ -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 };

View file

@ -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)
{

View file

@ -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;

View file

@ -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;

View file

@ -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());

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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 */

View file

@ -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;

View file

@ -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) {

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -7,7 +7,6 @@ gmcmd.c
museum.c
score.c
weather.c
wormhole.c
xmas.c
)
FOREACH(_FILE ${_FILES})

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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: */

View file

@ -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);

View file

@ -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

View file

@ -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;
}

View file

@ -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 */

View file

@ -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

View file

@ -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();

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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
}

View file

@ -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
View 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
View 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
View 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;
}

View file

@ -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

View file

@ -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");
}

View file

@ -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
View 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;
}

View file

@ -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';

View file

@ -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");

View file

@ -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);

View file

@ -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));
}
}
/********************/

View file

@ -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

View file

@ -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
View 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
View 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;
}