Merge pull request from ennorehling/master

pre-release 3.14 merge
This commit is contained in:
Enno Rehling 2017-10-28 20:00:09 +02:00 committed by GitHub
commit c3992c6a6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
140 changed files with 2712 additions and 1953 deletions

2
clibs

@ -1 +1 @@
Subproject commit d286006a28c8aa7cd70ed7fd4cd172b50ade9727
Subproject commit 147584ad70b220abf6a4e97ca76e785729b9ac32

View file

@ -27,7 +27,7 @@
"rules.reserve.twophase": true,
"rules.give.max_men": -1,
"rules.check_overload": false,
"rules.limit.faction": 3000,
"rules.limit.faction": 2500,
"rules.maxskills.magic": 5,
"rules.guard.base_stop_prob": 0.30,
"rules.guard.skill_stop_prob": 0.05,

View file

@ -25,7 +25,7 @@
<item name="log" amount="10"/>
<item name="stone" amount="4"/>
</set>
<set name="autoseed_unit">
<set name="seed_unit">
<item name="log" amount="50"/>
<item name="stone" amount="50"/>
<item name="iron" amount="50"/>
@ -38,7 +38,7 @@
<skill name="perception" level="30"/>
<skill name="melee" level="1"/>
</set>
<set name="autoseed_faction">
<set name="seed_faction">
<item name="adamantium" amount="1"/>
</set>
</equipment>

View file

@ -88,7 +88,7 @@
"rules.economy.herbrot": 0,
"rules.region_owner_pay_building": "market harbour lighthouse",
"rules.dwarf_castles": true,
"rules.limit.faction": 500,
"rules.limit.faction": 250,
"rules.grow.formula": 1,
"rules.tactics.formula": 1,
"rules.help.mask": "fight guard money give",

View file

@ -1,3 +1,5 @@
#!/bin/bash
GAME=$1
TURN=$2
@ -7,6 +9,7 @@ if [ ! -d $ERESSEA/game-$GAME ] ; then
fi
cd $ERESSEA/game-$GAME
if [ -d orders.dir.$TURN ]; then
echo "orders.dir.$TURN already exists"
else
@ -14,3 +17,12 @@ else
mkdir -p orders.dir
fi
ls -1rt orders.dir.$TURN/turn-* | xargs cat > orders.$TURN
lockfile -r3 -l120 orders.queue.lock
if [ -e orders.queue ] ; then
mv orders.queue orders.dir.$TURN/orders.queue
fi
rm -f orders.queue.lock
fi

View file

@ -4990,12 +4990,12 @@
<string name="sacrifice_strength">
<text locale="de">Mit Hilfe dieses Zaubers kann der Magier einen Teil
seiner magischen Kraft permanent auf einen anderen Magier übertragen.
Auf einen Tybied-Magier kann er die Hälfte der eingesetzten Kraft
übertragen, auf einen Magier eines anderen Gebietes ein Drittel.</text>
Auf einen Magier des selben Magiegebietes kann er die Hälfte der
eingesetzten Kraft übertragen, auf andere Magier ein Drittel.</text>
<text locale="en">This spell allows the magician to transfer part of
his magical powers to another magician. Tybied magicians will receive
half the power invested, magicians of another school will receive one
third.</text>
his magical powers to another magician. Magicians of the seam school
will receive half the power invested, magicians of other schoolsreceive
receive one third.</text>
</string>
<string name="eternal_walls">
<text locale="de">Mit dieser Formel bindet der Magier auf ewig die

View file

@ -7154,7 +7154,7 @@
<text locale="de">"Das Wurmloch in $region($region) schließt sich."</text>
<text locale="en">"The wormhole in $region($region) disappears."</text>
</message>
<message name="battle::useflamingsword" section="battle">
<message name="useflamingsword" section="battle">
<type>
<arg name="unit" type="unit"/>
<arg name="amount" type="int"/>
@ -7162,7 +7162,7 @@
<text locale="de">"$int($amount) Krieger von $unit($unit) benutzen ihre Flammenschwerter."</text>
<text locale="en">"$int($amount) fighters of $unit($unit) are using their flaming sword."</text>
</message>
<message name="battle::usecatapult" section="battle">
<message name="usecatapult" section="battle">
<type>
<arg name="unit" type="unit"/>
<arg name="amount" type="int"/>
@ -7170,35 +7170,35 @@
<text locale="de">"$int($amount) Krieger von $unit($unit) feuern ihre Katapulte ab."</text>
<text locale="en">"$int($amount) fighters of $unit($unit) launch their catapults."</text>
</message>
<message name="battle::starters" section="battle">
<message name="start_battle" section="battle">
<type>
<arg name="factions" type="string"/>
</type>
<text locale="de">"Der Kampf wurde ausgelöst von ${factions}."</text>
<text locale="en">"The battle was initiated by ${factions}."</text>
</message>
<message name="battle::potionsave" section="battle">
<message name="potionsave" section="battle">
<type>
<arg name="unit" type="unit"/>
</type>
<text locale="de">"Eine Person von $unit($unit) konnte durch einen Heiltrank überleben."</text>
<text locale="en">"A fighter of $unit($unit) was saved by a healing potion."</text>
</message>
<message name="battle::tactics_lost" section="battle">
<message name="tactics_lost" section="battle">
<type>
<arg name="unit" type="unit"/>
</type>
<text locale="de">"$unit($unit) konnte dem Gegner eine Falle stellen."</text>
<text locale="en">"$unit($unit) lured the enemy into an ambush."</text>
</message>
<message name="battle::tactics_won" section="battle">
<message name="tactics_won" section="battle">
<type>
<arg name="unit" type="unit"/>
</type>
<text locale="de">"$unit($unit) überrascht den Gegner."</text>
<text locale="en">"$unit($unit) surprises the enemies."</text>
</message>
<message name="battle::spell_failed" section="battle">
<message name="spell_failed" section="battle">
<type>
<arg name="unit" type="unit"/>
<arg name="spell" type="spell"/>
@ -7206,20 +7206,20 @@
<text locale="de">"$unit($unit) versucht $spell($spell) zu zaubern, doch der Zauber schlägt fehl!"</text>
<text locale="en">"$unit($unit) tries to cast $spell($spell), but the spell fails!"</text>
</message>
<message name="battle::aborted" section="battle">
<message name="aborted_battle" section="battle">
<type>
</type>
<text locale="de">"Der Kampf wurde abgebrochen, da alle Verteidiger flohen."</text>
<text locale="en">"The battle was aborted because all enemies escaped."</text>
</message>
<message name="battle::row_header" section="battle">
<message name="battle_row" section="battle">
<type>
<arg name="row" type="int"/>
</type>
<text locale="de">"... in der $int($row). Kampflinie:"</text>
<text locale="en">"... in combat rank $int($row):"</text>
</message>
<message name="battle::out_of_range" section="battle">
<message name="spell_out_of_range" section="battle">
<type>
<arg name="mage" type="unit"/>
<arg name="spell" type="spell"/>
@ -7228,14 +7228,14 @@
<text locale="en">"$unit($mage) casts $spell($spell), but nobody was in range."</text>
</message>
<message name="battle::after" section="battle">
<message name="after_battle" section="battle">
<type>
</type>
<text locale="de">"Einheiten nach dem Kampf:"</text>
<text locale="en">"Units after the battle:"</text>
</message>
<message name="battle::section" section="battle">
<message name="section_battle" section="battle">
<type>
</type>
<text locale="de">""</text>
@ -7301,21 +7301,21 @@
<text locale="en">"$unit($mage) causes the walls of $building($building) to glow in an eerie magic light."</text>
</message>
<message name="battle::lineup" section="battle">
<message name="lineup_battle" section="battle">
<type>
<arg name="turn" type="int"/>
</type>
<text locale="de">"Einheiten vor der $int($turn). Runde:"</text>
<text locale="en">"Units before turn $int($turn):"</text>
</message>
<message name="battle::header" section="battle">
<message name="header_battle" section="battle">
<type>
<arg name="region" type="region"/>
</type>
<text locale="de">"In $region($region) findet ein Kampf statt."</text>
<text locale="en">"There is a battle in $region($region)."</text>
</message>
<message name="battle::combatspell" section="battle">
<message name="cast_combatspell" section="battle">
<type>
<arg name="mage" type="unit"/>
<arg name="spell" type="spell"/>
@ -7516,7 +7516,7 @@
<text locale="en">"$unit($mage) casts $spell($spell). $int($amount) fighters are temporarily losing some of their memories."</text>
</message>
<message name="battle::killed" section="battle">
<message name="killed_battle" section="battle">
<type>
<arg name="unit" type="unit"/>
<arg name="dead" type="int"/>
@ -7524,7 +7524,7 @@
<text locale="de">"$unit($unit) tötete $int($dead) Krieger."</text>
<text locale="en">"$unit($unit) killed $int($dead) opponents."</text>
</message>
<message name="battle::army_report" section="battle">
<message name="army_report" section="battle">
<type>
<arg name="index" type="int"/>
<arg name="abbrev" type="string"/>

View file

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<equipment>
<set name="seaserpent_spoils">
<set name="spo_seaserpent">
<item name="dragonblood" amount="2"/>
<item name="seaserpenthead" amount="1"/>
</set>

View file

@ -2,27 +2,27 @@
<equipment>
<!-- equipment given to familiars -->
<set name="lynx_familiar">
<set name="fam_lynx">
<skill name="magic" level="1"/>
</set>
<set name="tunnelworm_familiar">
<set name="fam_tunnelworm">
<skill name="magic" level="1"/>
<skill name="mining" level="1"/>
<skill name="forestry" level="1"/>
<skill name="stamina" level="1"/>
</set>
<set name="eagle_familiar">
<set name="fam_eagle">
<skill name="magic" level="1"/>
</set>
<set name="rat_familiar">
<set name="fam_rat">
<skill name="magic" level="1"/>
<skill name="stamina" level="6"/>
</set>
<set name="songdragon_familiar">
<set name="fam_songdragon">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="flee" level="2"/>
@ -30,7 +30,7 @@
<spell name="frighten" level="8"/>
</set>
<set name="nymph_familiar">
<set name="fam_nymph">
<skill name="magic" level="1"/>
<skill name="bow" level="1"/>
<skill name="training" level="1"/>
@ -42,7 +42,7 @@
<spell name="appeasement" level="1"/>
</set>
<set name="unicorn_familiar">
<set name="fam_unicorn">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="resist_magic" level="3"/>
@ -53,11 +53,11 @@
<spell name="appeasement" level="1"/>
</set>
<set name="direwolf_familiar">
<set name="fam_direwolf">
<skill name="magic" level="1"/>
</set>
<set name="ghost_familiar">
<set name="fam_ghost">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="steal_aura" level="6"/>
@ -65,7 +65,7 @@
<spell name="summonundead" level="6"/>
</set>
<set name="imp_familiar">
<set name="fam_imp">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="steal_aura" level="6"/>
@ -73,14 +73,14 @@
<spell name="seduction" level="6"/>
</set>
<set name="dreamcat_familiar">
<set name="fam_dreamcat">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="shapeshift" level="3"/>
<spell name="transferauratraum" level="3"/>
</set>
<set name="fairy_familiar">
<set name="fam_fairy">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="appeasement" level="1"/>
@ -88,15 +88,15 @@
<spell name="seduction" level="6"/>
</set>
<set name="owl_familiar">
<set name="fam_owl">
<skill name="magic" level="1"/>
</set>
<set name="hellcat_familiar">
<set name="fam_hellcat">
<skill name="magic" level="1"/>
</set>
<set name="tiger_familiar">
<set name="fam_tiger">
<skill name="magic" level="1"/>
</set>

View file

@ -830,7 +830,7 @@
<attack type="1" damage="1d7"/>
</race>
<race name="shadowknight" maxaura="0.000000" regaura="0.000000" weight="1000" capacity="540" speed="1.0" hp="1" damage="1d1" unarmedattack="0" unarmeddefense="0" attackmodifier="1" defensemodifier="1" scarepeasants="yes" walk="yes" canlearn="no" teach="no" noblock="yes">
<race name="shadowknight" maxaura="0.000000" regaura="0.000000" weight="0" capacity="540" speed="1.0" hp="1" damage="1d1" unarmedattack="0" unarmeddefense="0" attackmodifier="1" defensemodifier="1" scarepeasants="yes" walk="yes" canlearn="no" teach="no" noblock="yes">
<ai splitsize="20000" moverandom="yes"/>
<attack type="1" damage="1d1"/>
</race>

View file

@ -2,18 +2,18 @@
<equipment>
<!-- one equipment-set per player-race for the first unit in a faction -->
<set name="autoseed_dwarf">
<set name="seed_dwarf">
<item name="axe" amount="1"/>
<item name="chainmail" amount="1"/>
<skill name="melee" level="1"/>
</set>
<set name="autoseed_elf">
<set name="seed_elf">
<item name="fairyboot" amount="1"/>
<callback name="equip_newunits"/>
</set>
<set name="autoseed_orc">
<set name="seed_orc">
<skill name="polearm" level="4"/>
<skill name="melee" level="4"/>
<skill name="crossbow" level="4"/>
@ -21,30 +21,30 @@
<skill name="bow" level="4"/>
</set>
<set name="autoseed_goblin">
<set name="seed_goblin">
<item name="roi" amount="1"/>
<callback name="equip_newunits"/>
</set>
<set name="autoseed_human">
<set name="seed_human">
<callback name="equip_newunits"/>
</set>
<set name="autoseed_troll">
<set name="seed_troll">
<skill name="building" level="1"/>
<skill name="perception" level="3"/>
<item name="stone" amount="50"/>
</set>
<set name="autoseed_demon">
<set name="seed_demon">
<skill name="stamina" level="15"/>
</set>
<set name="autoseed_insect">
<set name="seed_insect">
<item name="nestwarmth" amount="9"/>
</set>
<set name="autoseed_halfling">
<set name="seed_halfling">
<skill name="trade" level="1"/>
<skill name="riding" level="2"/>
<item name="horse" amount="2"/>
@ -58,18 +58,18 @@
<item name="incense" amount="5"/>
</set>
<set name="autoseed_cat">
<set name="seed_cat">
<item name="roi" amount="1"/>
<callback name="equip_newunits"/>
</set>
<set name="autoseed_aquarian">
<set name="seed_aquarian">
<skill name="sailing" level="1"/>
<callback name="equip_newunits"/>
</set>
<!-- equipment-sets for random encounters -->
<set name="random_desert">
<set name="rand_desert">
<skill name="melee" level="d2"/>
<skill name="trade" level="d3"/>
<skill name="riding" level="d2+1"/>
@ -79,53 +79,53 @@
<item name="money" amount="d30+19"/>
</set>
<set name="random_swamp">
<set name="rand_swamp">
<skill name="stealth" level="d3+1"/>
<skill name="polearm" level="d3+1"/>
<item name="spear" amount="1"/>
<item name="money" amount="d20+9"/>
</set>
<set name="random_glacier">
<set name="rand_glacier">
<skill name="armorer" level="d2+1"/>
<skill name="melee" level="d2+1"/>
<item name="sword" amount="1"/>
<item name="money" amount="d20+19"/>
</set>
<set name="random_mountain">
<set name="rand_mountain">
<skill name="armorer" level="d2+1"/>
<skill name="melee" level="d2+1"/>
<skill name="trade" level="d3"/>
<item name="sword" amount="1"/>
<item name="money" amount="d40+59"/>
<subset chance="0.6">
<set name="item_plate"/>
<set name="i_plate"/>
</subset>
</set>
<set name="random_highland">
<set name="rand_highland">
<skill name="melee" level="d2"/>
<item name="sword" amount="1"/>
<item name="money" amount="d10+19"/>
</set>
<set name="random_forest">
<set name="rand_forest">
<skill name="stealth" level="d2"/>
<skill name="perception" level="d2+1"/>
<skill name="bow" level="d3+1"/>
<item name="bow" amount="1"/>
<item name="money" amount="d20+9"/>
<subset chance="0.2">
<set name="random_herbalist"/>
<set name="rand_herbalist"/>
</subset>
</set>
<set name="random_herbalist">
<set name="rand_herbalist">
<skill name="herbalism" level="d2"/>
</set>
<set name="random_villagers">
<set name="rand_villagers">
<item name="money" amount="d80+19"/>
<skill name="cartmaking" level="d2-1"/>
<skill name="mining" level="d2-1"/>
@ -136,57 +136,57 @@
<item name="money" amount="d30+10"/>
</set>
<set name="random_plain">
<set name="rand_plain">
<item name="money" amount="d80+19"/>
<subset>
<set name="random_sword" chance="0.25"/>
<set name="random_spear" chance="0.25"/>
<set name="random_crossbow" chance="0.25"/>
<set name="random_bow" chance="0.25"/>
<set name="rand_sword" chance="0.25"/>
<set name="rand_spear" chance="0.25"/>
<set name="rand_crossbow" chance="0.25"/>
<set name="rand_bow" chance="0.25"/>
</subset>
<subset chance="0.4">
<set name="item_chain"/>
<set name="i_chain"/>
</subset>
<subset chance="0.3">
<set name="random_rider"/>
<set name="rand_rider"/>
</subset>
</set>
<set name="random_spear">
<set name="rand_spear">
<skill name="polearm" level="d3"/>
<item name="spear" amount="1"/>
</set>
<set name="random_sword">
<set name="rand_sword">
<skill name="melee" level="d3"/>
<item name="sword" amount="1"/>
</set>
<set name="random_crossbow">
<set name="rand_crossbow">
<skill name="crossbow" level="d3"/>
<item name="crossbow" amount="1"/>
</set>
<set name="random_bow">
<set name="rand_bow">
<skill name="bow" level="d3"/>
<item name="bow" amount="1"/>
</set>
<set name="random_rider">
<set name="rand_rider">
<item name="horse" amount="1"/>
<skill name="riding" level="d3"/>
</set>
<set name="recruited_dracoid">
<set name="new_dracoid">
<!-- dracoiden, von drachen rekrutiert -->
<skill name="polearm" level="d4+2"/>
<skill name="melee" level="d4+2"/>
<skill name="bow" level="d3+1"/>
<subset>
<!-- dracoiden haben immer eine von drei waffen -->
<set name="item_sword" chance="0.33"/>
<set name="item_spear" chance="0.33"/>
<set name="item_bow" chance="0.34"/>
<set name="i_sword" chance="0.33"/>
<set name="i_spear" chance="0.33"/>
<set name="i_bow" chance="0.34"/>
</subset>
</set>
@ -195,56 +195,52 @@
<item name="rustysword" amount="1"/>
<item name="rustychainmail" amount="d2-1"/>
<subset chance="0.3">
<set name="item_rustyshield"/>
<set name="i_rustyshield"/>
</subset>
</set>
<!-- single-item sets (that can be given a percentage-chance in a subset) -->
<set name="item_plate">
<set name="i_plate">
<item name="plate" amount="1"/>
</set>
<set name="item_spear">
<set name="i_spear">
<item name="spear" amount="1"/>
</set>
<set name="item_bow">
<set name="i_bow">
<item name="bow" amount="1"/>
</set>
<set name="item_sword">
<set name="i_sword">
<item name="sword" amount="1"/>
</set>
<set name="item_chain">
<set name="i_chain">
<item name="chainmail" amount="1"/>
</set>
<set name="item_log">
<item name="log" amount="1"/>
<set name="i_rustyshield">
<item name="rustyshield" amount="1"/>
</set>
<set name="item_rustychain">
<item name="rustychainmail" amount="1"/>
</set>
<set name="dragon_spoils">
<set name="spo_dragon">
<item name="dragonblood" amount="4"/>
<item name="dragonhead" amount="1"/>
</set>
<set name="youngdragon_spoils">
<set name="spo_youngdragon">
<item name="dragonblood" amount="1"/>
</set>
<set name="wyrm_spoils">
<set name="spo_wyrm">
<item name="dragonblood" amount="10"/>
<item name="dragonhead" amount="1"/>
</set>
<!-- sets that are used by the monster-spawning code -->
<set name="monster_dragon">
<set name="seed_dragon">
<skill name="magic" level="4"/>
<skill name="stealth" level="1"/>
<skill name="stamina" level="1"/>
@ -252,24 +248,24 @@
<item name="money" amount="d500+99"/>
</set>
<set name="monster_braineater">
<set name="seed_braineater">
<skill name="stealth" level="1"/>
<skill name="perception" level="1"/>
</set>
<set name="monster_seaserpent">
<set name="seed_seaserpent">
<skill name="magic" level="4"/>
<skill name="stealth" level="2"/>
<skill name="stamina" level="1"/>
<skill name="perception" level="3"/>
</set>
<set name="new_orc_unit">
<set name="new_orc">
<skill name="polearm" level="1"/>
<skill name="melee" level="1"/>
</set>
<set name="seaserpent_spoils">
<set name="spo_seaserpent">
<item name="dragonblood" amount="6"/>
<item name="seaserpenthead" amount="1"/>
</set>

View file

@ -2,26 +2,26 @@
<equipment>
<!-- equipment given to familiars -->
<set name="lynx_familiar">
<set name="fam_lynx">
<skill name="espionage" level="1"/>
<skill name="magic" level="1"/>
<skill name="stealth" level="1"/>
<skill name="perception" level="1"/>
</set>
<set name="tunnelworm_familiar">
<set name="fam_tunnelworm">
<skill name="magic" level="1"/>
<skill name="mining" level="1"/>
<skill name="forestry" level="1"/>
<skill name="stamina" level="1"/>
</set>
<set name="eagle_familiar">
<set name="fam_eagle">
<skill name="magic" level="1"/>
<skill name="perception" level="1"/>
</set>
<set name="rat_familiar">
<set name="fam_rat">
<skill name="magic" level="1"/>
<skill name="espionage" level="1"/>
<skill name="stealth" level="1"/>
@ -29,7 +29,7 @@
<skill name="stamina" level="6"/>
</set>
<set name="songdragon_familiar">
<set name="fam_songdragon">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="flee" level="2"/>
@ -37,7 +37,7 @@
<spell name="frighten" level="8"/>
</set>
<set name="nymph_familiar">
<set name="fam_nymph">
<skill name="magic" level="1"/>
<skill name="bow" level="1"/>
<skill name="herbalism" level="1"/>
@ -54,7 +54,7 @@
<spell name="appeasement" level="1"/>
</set>
<set name="unicorn_familiar">
<set name="fam_unicorn">
<skill name="magic" level="1"/>
<skill name="stealth" level="1"/>
<skill name="perception" level="1"/>
@ -67,12 +67,12 @@
<spell name="appeasement" level="1"/>
</set>
<set name="direwolf_familiar">
<set name="fam_direwolf">
<skill name="magic" level="1"/>
<skill name="perception" level="1"/>
</set>
<set name="ghost_familiar">
<set name="fam_ghost">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="steal_aura" level="6"/>
@ -80,7 +80,7 @@
<spell name="summonundead" level="6"/>
</set>
<set name="imp_familiar">
<set name="fam_imp">
<skill name="magic" level="1"/>
<skill name="espionage" level="1"/>
<skill name="stealth" level="1"/>
@ -92,7 +92,7 @@
<spell name="seduction" level="6"/>
</set>
<set name="dreamcat_familiar">
<set name="fam_dreamcat">
<skill name="magic" level="1"/>
<skill name="espionage" level="1"/>
<skill name="stealth" level="1"/>
@ -103,7 +103,7 @@
<spell name="transferauratraum" level="3"/>
</set>
<set name="fairy_familiar">
<set name="fam_fairy">
<skill name="magic" level="1"/>
<!-- spells -->
<spell name="appeasement" level="1"/>
@ -111,19 +111,19 @@
<spell name="seduction" level="6"/>
</set>
<set name="owl_familiar">
<set name="fam_owl">
<skill name="magic" level="1"/>
<skill name="espionage" level="1"/>
<skill name="stealth" level="1"/>
<skill name="perception" level="1"/>
</set>
<set name="hellcat_familiar">
<set name="fam_hellcat">
<skill name="magic" level="1"/>
<skill name="perception" level="1"/>
</set>
<set name="tiger_familiar">
<set name="fam_tiger">
<skill name="magic" level="1"/>
<skill name="perception" level="1"/>
</set>

View file

@ -1071,7 +1071,7 @@
<skill name="unarmed" modifier="1"/>
<attack type="1" damage="1d7"/>
</race>
<race name="shadowknight" maxaura="0.000000" regaura="0.000000" weight="1000" capacity="540" speed="1.000000" hp="1" damage="1d1" unarmedattack="0" unarmeddefense="0" attackmodifier="1" defensemodifier="1" scarepeasants="yes" walk="yes" canlearn="no" teach="no" noblock="yes">
<race name="shadowknight" maxaura="0.000000" regaura="0.000000" weight="0" capacity="540" speed="1.000000" hp="1" damage="1d1" unarmedattack="0" unarmeddefense="0" attackmodifier="1" defensemodifier="1" scarepeasants="yes" walk="yes" canlearn="no" teach="no" noblock="yes">
<ai splitsize="20000" moverandom="yes"/>
<attack type="1" damage="1d1"/>
</race>

View file

@ -4,18 +4,29 @@ set -e
ROOT=$(git rev-parse --show-toplevel)
[ -z $BUILD ] && BUILD=Debug ; export BUILD
UNIT_TESTS=$BUILD/eressea/test_eressea
RUN_TESTS=$BUILD/eressea/eressea
if [ "$1" = "-V" ]; then
VALGRIND=$(which valgrind)
if [ -n "$VALGRIND" ]; then
SUPP=share/ubuntu-12_04.supp
UNIT_TESTS="$VALGRIND --quiet --suppressions=$SUPP --error-exitcode=1 --leak-check=no $UNIT_TESTS"
RUN_TESTS="$VALGRIND --quiet --suppressions=$SUPP --error-exitcode=1 --leak-check=no $RUN_TESTS"
fi
fi
if [ ! -e $ROOT/$BUILD ]; then
echo "cannot find build directory $BUILD in $ROOT. did you run cmake-init?"
exit
fi
$ROOT/$BUILD/eressea/test_eressea
$UNIT_TESTS
cd $ROOT
[ -e eressea.ini ] || ln -sf conf/eressea.ini
$ROOT/$BUILD/eressea/eressea -v1 scripts/run-tests.lua
$ROOT/$BUILD/eressea/eressea -v1 scripts/run-tests-e2.lua
$ROOT/$BUILD/eressea/eressea -v1 scripts/run-tests-e3.lua
$ROOT/$BUILD/eressea/eressea --version
$RUN_TESTS -v1 scripts/run-tests.lua
$RUN_TESTS -v1 scripts/run-tests-e2.lua
$RUN_TESTS -v1 scripts/run-tests-e3.lua
$RUN_TESTS --version
rm -rf data reports orders.txt score score.alliances datum turn
cd $OLDWPD

View file

@ -22,6 +22,6 @@ s/cmake-init
s/build
cd $ROOT
inifile
s/runtests
s/runtests -V
integration_tests

View file

@ -1,4 +1,4 @@
if not config.autoseed then return nil end
if not config.autoseed or config.autoseed==0 then return nil end
local autoseed = {}
-- minimum required resources in the 7-hex neighborhood:
@ -65,9 +65,9 @@ local function seed(r, email, race, lang)
assert(f)
local u = unit.create(f, r)
assert(u)
equip_unit(u, "autoseed_faction")
equip_unit(u, "autoseed_unit")
equip_unit(u, "autoseed_" .. race, 7)
equip_unit(u, "seed_faction")
equip_unit(u, "seed_unit")
equip_unit(u, "seed_" .. race, 7)
unit.create(f, r, 5):set_skill("mining", 30)
unit.create(f, r, 5):set_skill("quarrying", 30)
f:set_origin(r)

View file

@ -1,6 +1,6 @@
-- Muschelplateau
if not config.embassy then return nil end
if not config.embassy or config.embassy==0 then return nil end
local embassy = {}
local home = nil

View file

@ -1,5 +1,5 @@
-- DEPRECATED
if not config.eternath then return nil end
if not config.eternath or config.eternath==0 then return nil end
-- implements parts of a quest in E2
-- this module is deprecated, because it puts functions in the global environment for at_building_action

View file

@ -0,0 +1,21 @@
local modules = {}
function add_module(pkg)
table.insert(modules, pkg)
end
local pkg = {}
function pkg.init()
for k, v in ipairs(modules) do
if v.init then v.init() end
end
end
function pkg.update()
for k, v in ipairs(modules) do
if v.update then v.update() end
end
end
return pkg

View file

@ -1,4 +1,4 @@
if not config.ponnuki then return nil end
if not config.ponnuki or config.ponnuki==0 then return nil end
local ponnuki = {}
local directions = { "NW", "NO", "O", "SO", "SW", "W" }

View file

@ -19,44 +19,49 @@ local function create_item(mage, level, name, number)
return level
end
local function create_potion(mage, level, name, force)
count = math.floor(force * 2 + 0.5)
return create_item(mage, level, name, count)
end
-- Wasser des Lebens
function create_potion_p2(r, mage, level, force)
return create_item(mage, level, "p2", level)
return create_potion(mage, level, "p2", force)
end
-- Siebenmeilentee
function create_potion_p0(r, mage, level, force)
return create_item(mage, level, "p0", level)
return create_potion(mage, level, "p0", force)
end
-- Wundsalbe
function create_potion_ointment(r, mage, level, force)
return create_item(mage, level, "ointment", level)
return create_potion(mage, level, "ointment", force)
end
-- Bauernblut
function create_potion_peasantblood(r, mage, level, force)
return create_item(mage, level, "peasantblood", level)
return create_potion(mage, level, "peasantblood", force)
end
-- Pferdeglueck
function create_potion_p9(r, mage, level, force)
return create_item(mage, level, "p9", level)
return create_potion(mage, level, "p9", force)
end
-- Schaffenstrunk
function create_potion_p3(r, mage, level, force)
return create_item(mage, level, "p3", level)
return create_potion(mage, level, "p3", force)
end
-- Heiltrank
function create_potion_p14(r, mage, level, force)
return create_item(mage, level, "p14", level)
return create_potion(mage, level, "p14", force)
end
-- Elixier der Macht
function create_potion_p13(r, mage, level, force)
return create_item(mage, level, "p13", level)
return create_potion(mage, level, "p13", force)
end
-- Erschaffe ein Flammenschwert

View file

@ -1,5 +1,5 @@
-- DEPRECATED
if not config.wedding then return nil end
if not config.wedding or config.wedding==0 then return nil end
-- this script contains the action functions for the two portals
-- used on the jadee/wildente wedding island. the two _action functions

View file

@ -1,4 +1,4 @@
if not config.xmas then return nil end
if not config.xmas or config.xmas==0 then return nil end
local gifts = {
e2 = {

View file

@ -8,6 +8,7 @@ function setup()
eressea.settings.set("rules.food.flags", "4")
eressea.settings.set("rules.ship.storms", "0")
eressea.settings.set("rules.encounters", "0")
eressea.settings.set("study.produceexp", "0")
end
function test_calendar()
@ -57,6 +58,7 @@ function test_dwarf_bonus()
u.building = building.create(r, "mine")
u.building.size = 10
u:add_item("money", 500) -- maintenance
assert_equal(1, u:get_skill("mining"))
process_orders()
assert_equal(70, u:get_item("iron"))
assert_equal(70, r:get_resource("iron"))

View file

@ -11,6 +11,26 @@ function setup()
eressea.settings.set("rules.encounters", "0")
end
function test_bug_2361_forget_magic()
-- https://bugs.eressea.de/view.php?id=2361
-- familiars cannot forget magic
local r = region.create(0, 0, "plain")
local f = faction.create("human")
local u = unit.create(f, r, 1)
local uf = unit.create(f, r, 1)
u:clear_orders()
u:add_order("VERGESSE Magie")
u:set_skill('magic', 5)
uf.race = 'unicorn'
uf:clear_orders()
uf:add_order("VERGESSE Magie")
uf:set_skill('magic', 5)
u.familiar = uf
process_orders()
assert_equal(0, u:get_skill('magic'))
assert_equal(5, uf:get_skill('magic'))
end
function test_mine_bonus()
local r = region.create(0, 0, "mountain")
r:set_resource("iron", 100)

View file

@ -18,8 +18,9 @@ local function file_exists(name)
if f~=nil then io.close(f) return true else return false end
end
local function assert_file(filename)
assert_equal(true, file_exists(filename))
local function assert_file(filename, exists)
if exists == nil then exists = true end
assert_equal(exists, file_exists(filename))
os.remove(filename)
end
@ -30,9 +31,9 @@ function test_process_turn()
assert_equal(0, write_reports())
assert_equal(0, eressea.write_game("test.dat"))
assert_file("data/test.dat")
assert_file("reports/" .. get_turn() .. "-ii.nr")
assert_file("reports/" .. get_turn() .. "-ii.cr")
assert_file("reports/" .. get_turn() .. "-ii.txt")
assert_file("reports/" .. get_turn() .. "-ii.nr", false)
assert_file("reports/" .. get_turn() .. "-ii.cr", false)
assert_file("reports/" .. get_turn() .. "-ii.txt", false)
assert_file("reports/" .. get_turn() .. "-777.nr")
assert_file("reports/" .. get_turn() .. "-777.cr")
assert_file("reports/" .. get_turn() .. "-777.txt")

View file

@ -116,6 +116,7 @@ set (ERESSEA_SRC
magic.c
market.c
morale.c
orderfile.c
randenc.c
renumber.c
volcano.c
@ -217,6 +218,7 @@ set(TESTS_SRC
monsters.test.c
move.test.c
names.test.c
orderfile.test.c
piracy.test.c
prefix.test.c
renumber.test.c

View file

@ -22,13 +22,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/building.h>
#include <kernel/item.h>
#include <kernel/pool.h>
#include "academy.h"
#include "study.h"
void academy_teaching_bonus(struct unit *u, skill_t sk, int student_days) {
if (student_days && sk != NOSKILL) {
/* actually days / STUDYDAYS * EXPERIENCEDAYS / MAX_STUDENTS */
learn_skill(u, sk, student_days / STUDYDAYS);
void academy_teaching_bonus(struct unit *u, skill_t sk, int students) {
if (students && sk != NOSKILL) {
/* actually students * EXPERIENCEDAYS / MAX_STUDENTS */
learn_skill(u, sk, students);
}
}

View file

@ -96,7 +96,7 @@ static int obs_read(struct attrib *a, void *owner, struct gamedata *data)
{
obs_data *od = (obs_data *)a->data.v;
read_reference(&od->f, data, read_faction_reference, resolve_faction);
read_faction_reference(data, &od->f, NULL);
READ_INT(data->store, &od->skill);
READ_INT(data->store, &od->timer);
return AT_READ_OK;
@ -114,7 +114,7 @@ static attrib *make_observer(faction *f, int perception)
return a;
}
int get_observer(region *r, faction *f) {
int get_observer(const region *r, const faction *f) {
if (fval(r, RF_OBSERVER)) {
attrib *a = a_find(r->attribs, &at_observer);
while (a && a->type == &at_observer) {

View file

@ -29,7 +29,7 @@ extern "C" {
extern void register_attributes(void);
void set_observer(struct region *r, struct faction *f, int perception, int turns);
int get_observer(struct region *r, struct faction *f);
int get_observer(const struct region *r, const struct faction *f);
#ifdef __cplusplus
}

View file

@ -30,7 +30,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
static int read_follow(attrib * a, void *owner, gamedata *data)
{
read_unit_reference(data); /* skip it */
READ_INT(data->store, NULL); /* skip it */
return AT_READ_FAIL;
}

View file

@ -45,9 +45,7 @@ write_hate(const attrib * a, const void *owner, struct storage *store)
static int read_hate(attrib * a, void *owner, gamedata *data)
{
int result = read_reference(&a->data.v, data, read_unit_reference,
resolve_unit);
if (result == 0 && !a->data.v) {
if (read_unit_reference(data, (unit **)&a->data.v, NULL) <= 0) {
return AT_READ_FAIL;
}
return AT_READ_OK;

View file

@ -67,8 +67,12 @@ static int keys_size(int n) {
assert(n > 0 && n <= 4096);
if (n <= 1) return 1;
if (n <= 4) return 4;
if (n <= 8) return 8;
if (n <= 16) return 16;
if (n <= 256) return 256;
if (n <= 512) return 512;
if (n <= 1024) return 1024;
if (n <= 2048) return 2048;
return 4096;
}

View file

@ -36,10 +36,9 @@ write_targetregion(const attrib * a, const void *owner, struct storage *store)
static int read_targetregion(attrib * a, void *owner, gamedata *data)
{
int result = read_reference(&a->data.v, data, read_region_reference,
RESOLVE_REGION(data->version));
if (result == 0 && !a->data.v)
if (read_region_reference(data, (region **)&a->data.v, NULL) <= 0) {
return AT_READ_FAIL;
}
return AT_READ_OK;
}

View file

@ -927,7 +927,7 @@ void kill_troop(troop dt)
if (drops != NULL) {
i_merge(&du->items, &drops);
}
sprintf(eqname, "%s_spoils", rc->_name);
sprintf(eqname, "spo_%s", rc->_name);
eq = get_equipment(eqname);
if (eq != NULL) {
equip_items(&du->items, eq);
@ -1295,7 +1295,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
if (oldpotiontype[P_HEAL] && !fval(&df->person[dt.index], FL_HEALING_USED)) {
if (i_get(du->items, oldpotiontype[P_HEAL]->itype) > 0) {
message *m = msg_message("battle::potionsave", "unit", du);
message *m = msg_message("potionsave", "unit", du);
message_faction(b, du->faction, m);
msg_release(m);
i_change(&du->items, oldpotiontype[P_HEAL]->itype, -1);
@ -1630,7 +1630,7 @@ selist *fighters(battle * b, const side * vs, int minrow, int maxrow, int mask)
static void report_failed_spell(struct battle * b, struct unit * mage, const struct spell *sp)
{
message *m = msg_message("battle::spell_failed", "unit spell", mage, sp);
message *m = msg_message("spell_failed", "unit spell", mage, sp);
message_all(b, m);
msg_release(m);
}
@ -2277,7 +2277,7 @@ void do_attack(fighter * af)
* Ladezeit neu und generiert die Meldung. */
if (af->catmsg >= 0) {
struct message *m =
msg_message("battle::killed", "unit dead", au, af->catmsg);
msg_message("killed_battle", "unit dead", au, af->catmsg);
message_all(b, m);
msg_release(m);
af->catmsg = -1;
@ -2752,10 +2752,10 @@ static void aftermath(battle * b)
battle_effects(b, dead_players);
for (s = b->sides; s != b->sides + b->nsides; ++s) {
message *seen = msg_message("battle::army_report",
message *seen = msg_message("army_report",
"index abbrev dead fled survived",
army_index(s), sideabkz(s, false), s->dead, s->flee, s->alive);
message *unseen = msg_message("battle::army_report",
message *unseen = msg_message("army_report",
"index abbrev dead fled survived",
army_index(s), "-?-", s->dead, s->flee, s->alive);
@ -2864,7 +2864,7 @@ static void print_fighters(battle * b, const side * s)
if (row == thisrow) {
if (m == NULL) {
m = msg_message("battle::row_header", "row", row);
m = msg_message("battle_row", "row", row);
message_all(b, m);
}
battle_punit(du, b);
@ -2927,7 +2927,7 @@ static void print_header(battle * b)
bufp = STRLCPY(bufp, lastf, size);
}
m = msg_message("battle::starters", "factions", zText);
m = msg_message("start_battle", "factions", zText);
message_faction(b, f, m);
msg_release(m);
}
@ -3049,10 +3049,10 @@ static void print_stats(battle * b)
unit *u = tf->unit;
message *m = NULL;
if (!is_attacker(tf)) {
m = msg_message("battle::tactics_lost", "unit", u);
m = msg_message("tactics_lost", "unit", u);
}
else {
m = msg_message("battle::tactics_won", "unit", u);
m = msg_message("tactics_won", "unit", u);
}
message_all(b, m);
msg_release(m);
@ -3579,9 +3579,9 @@ static int battle_report(battle * b)
message_faction(b, fac, msg_separator);
if (cont)
m = msg_message("battle::lineup", "turn", b->turn);
m = msg_message("lineup_battle", "turn", b->turn);
else
m = msg_message("battle::after", "");
m = msg_message("after_battle", "");
message_faction(b, fac, m);
msg_release(m);
@ -4073,7 +4073,7 @@ void do_battle(region * r)
bool fighting = false;
ship *sh;
if (msg_separator == NULL) {
msg_separator = msg_message("battle::section", "");
msg_separator = msg_message("section_battle", "");
}
fighting = start_battle(r, &b);
@ -4087,7 +4087,7 @@ void do_battle(region * r)
print_header(b);
if (!fighting) {
/* Niemand mehr da, Kampf kann nicht stattfinden. */
message *m = msg_message("battle::aborted", "");
message *m = msg_message("aborted_battle", "");
message_all(b, m);
msg_release(m);
free_battle(b);

View file

@ -3,6 +3,7 @@
#include <platform.h>
#include "json.h"
#include "orderfile.h"
#include <kernel/faction.h>
#include <kernel/item.h>

View file

@ -273,7 +273,7 @@ static int tolua_unit_set_guard(lua_State * L)
static const char *unit_getmagic(const unit * u)
{
sc_mage *mage = get_mage(u);
sc_mage *mage = get_mage_depr(u);
return mage ? magic_school[mage->magietyp] : NULL;
}
@ -286,7 +286,7 @@ static int tolua_unit_get_magic(lua_State * L)
static void unit_setmagic(unit * u, const char *type)
{
sc_mage *mage = get_mage(u);
sc_mage *mage = get_mage_depr(u);
int mtype;
for (mtype = 0; mtype != MAXMAGIETYP; ++mtype) {
if (strcmp(magic_school[mtype], type) == 0)
@ -638,8 +638,14 @@ static int tolua_unit_get_familiar(lua_State * L)
static int tolua_unit_set_familiar(lua_State * L)
{
unit *self = (unit *)tolua_tousertype(L, 1, 0);
create_newfamiliar(self, (unit *)tolua_tousertype(L, 2, 0));
unit *mag = (unit *)tolua_tousertype(L, 1, NULL);
unit *fam = (unit *)tolua_tousertype(L, 2, NULL);
if (fam) {
set_familiar(mag, fam);
}
else {
remove_familiar(mag);
}
return 0;
}
@ -747,7 +753,7 @@ static int tolua_unit_get_items(lua_State * L)
static int tolua_unit_get_spells(lua_State * L)
{
unit *self = (unit *) tolua_tousertype(L, 1, 0);
sc_mage *mage = self ? get_mage(self) : 0;
sc_mage *mage = self ? get_mage_depr(self) : 0;
spellbook *sb = mage ? mage->spellbook : 0;
selist *slist = 0;
if (sb) {

View file

@ -86,8 +86,7 @@ static void lc_done(struct attrib *a)
free(data);
}
static void
lc_write(const struct attrib *a, const void *owner, struct storage *store)
static void lc_write(const struct attrib *a, const void *owner, struct storage *store)
{
building_action *data = (building_action *)a->data.v;
const char *fname = data->fname;
@ -106,8 +105,7 @@ static int lc_read(struct attrib *a, void *owner, gamedata *data)
building *b = (building *)owner;
int result = 0;
if (data->version < ATTRIBOWNER_VERSION) {
result = read_reference(&b, data, read_building_reference, resolve_building);
assert(b == owner);
READ_INT(data->store, NULL);
}
READ_TOK(store, name, sizeof(name));
if (strcmp(name, "tunnel_action") == 0) {

View file

@ -101,7 +101,6 @@ struct locale *crtag_locale(void) {
static const char *crtag(const char *key)
{
/* TODO: those locale lookups are shit, but static kills testing */
const char *result;
result = LOC(crtag_locale(), key);
return result;
@ -254,7 +253,7 @@ cr_output_curses(struct stream *out, const faction * viewer, const void *obj, ob
}
while (a) {
if (fval(a->type, ATF_CURSE)) {
if (a->type == &at_curse) {
curse *c = (curse *)a->data.v;
message *msg;
@ -382,8 +381,8 @@ static int cr_skill(variant var, char *buffer, const void *userdata)
UNUSED_ARG(userdata);
if (sk != NOSKILL)
sprintf(buffer, "\"%s\"",
translate(mkname("skill", skillnames[sk]), skillname(sk,
report->locale)));
translate(mkname("skill", skillnames[sk]), skillname(sk,
report->locale)));
else
strcpy(buffer, "\"\"");
return 0;
@ -426,7 +425,7 @@ static int cr_resources(variant var, char *buffer, const void *userdata)
assert(name);
wp +=
sprintf(wp, "\"%d %s", rlist->number, translate(name, LOC(f->locale,
name)));
name)));
for (;;) {
rlist = rlist->next;
if (rlist == NULL)
@ -435,7 +434,7 @@ static int cr_resources(variant var, char *buffer, const void *userdata)
assert(name);
wp +=
sprintf(wp, ", %d %s", rlist->number, translate(name,
LOC(f->locale, name)));
LOC(f->locale, name)));
}
strcat(wp, "\"");
}
@ -621,86 +620,106 @@ static void cr_output_messages(FILE * F, message_list * msgs, faction * f)
}
/* prints a building */
static void
cr_output_building(FILE * F, building * b, const unit * owner, int fno,
faction * f)
static void cr_output_building(struct stream *out, building *b,
const unit *owner, int fno, faction *f)
{
const char *bname, *billusion;
fprintf(F, "BURG %d\n", b->no);
stream_printf(out, "BURG %d\n", b->no);
report_building(b, &bname, &billusion);
if (billusion) {
fprintf(F, "\"%s\";Typ\n", translate(billusion, LOC(f->locale,
stream_printf(out, "\"%s\";Typ\n", translate(billusion, LOC(f->locale,
billusion)));
if (owner && owner->faction == f) {
fprintf(F, "\"%s\";wahrerTyp\n", translate(bname, LOC(f->locale,
stream_printf(out, "\"%s\";wahrerTyp\n", translate(bname, LOC(f->locale,
bname)));
}
}
else {
fprintf(F, "\"%s\";Typ\n", translate(bname, LOC(f->locale, bname)));
stream_printf(out, "\"%s\";Typ\n", translate(bname, LOC(f->locale, bname)));
}
stream_printf(out, "\"%s\";Name\n", b->name);
if (b->display && b->display[0]) {
stream_printf(out, "\"%s\";Beschr\n", b->display);
}
if (b->size) {
stream_printf(out, "%d;Groesse\n", b->size);
}
fprintf(F, "\"%s\";Name\n", b->name);
if (b->display && b->display[0])
fprintf(F, "\"%s\";Beschr\n", b->display);
if (b->size)
fprintf(F, "%d;Groesse\n", b->size);
if (owner) {
fprintf(F, "%d;Besitzer\n", owner->no);
stream_printf(out, "%d;Besitzer\n", owner->no);
}
if (fno >= 0)
fprintf(F, "%d;Partei\n", fno);
if (b->besieged)
fprintf(F, "%d;Belagerer\n", b->besieged);
cr_output_curses_compat(F, f, b, TYP_BUILDING);
if (fno >= 0) {
stream_printf(out, "%d;Partei\n", fno);
}
if (b->besieged) {
stream_printf(out, "%d;Belagerer\n", b->besieged);
}
cr_output_curses(out, f, b, TYP_BUILDING);
}
static void cr_output_building_compat(FILE *F, building *b,
const unit *owner, int fno, faction *f)
{
/* TODO: eliminate this function */
stream strm;
fstream_init(&strm, F);
cr_output_building(&strm, b, owner, fno, f);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* prints a ship */
static void
cr_output_ship(FILE * F, const ship * sh, const unit * u, int fcaptain,
const faction * f, const region * r)
static void cr_output_ship(struct stream *out, const ship *sh, const unit *u,
int fcaptain, const faction *f, const region *r)
{
int w = 0;
assert(sh);
fprintf(F, "SCHIFF %d\n", sh->no);
fprintf(F, "\"%s\";Name\n", sh->name);
stream_printf(out, "SCHIFF %d\n", sh->no);
stream_printf(out, "\"%s\";Name\n", sh->name);
if (sh->display && sh->display[0])
fprintf(F, "\"%s\";Beschr\n", sh->display);
fprintf(F, "\"%s\";Typ\n", translate(sh->type->_name,
stream_printf(out, "\"%s\";Beschr\n", sh->display);
stream_printf(out, "\"%s\";Typ\n", translate(sh->type->_name,
LOC(f->locale, sh->type->_name)));
fprintf(F, "%d;Groesse\n", sh->size);
stream_printf(out, "%d;Groesse\n", sh->size);
if (sh->damage) {
int percent =
(sh->damage * 100 + DAMAGE_SCALE - 1) / (sh->size * DAMAGE_SCALE);
fprintf(F, "%d;Schaden\n", percent);
stream_printf(out, "%d;Schaden\n", percent);
}
if (u) {
fprintf(F, "%d;Kapitaen\n", u->no);
stream_printf(out, "%d;Kapitaen\n", u->no);
}
if (fcaptain >= 0) {
stream_printf(out, "%d;Partei\n", fcaptain);
}
if (fcaptain >= 0)
fprintf(F, "%d;Partei\n", fcaptain);
/* calculate cargo */
if (u && (u->faction == f || omniscient(f))) {
int n = 0, p = 0;
int mweight = shipcapacity(sh);
getshipweight(sh, &n, &p);
fprintf(F, "%d;capacity\n", mweight);
fprintf(F, "%d;cargo\n", n);
fprintf(F, "%d;speed\n", shipspeed(sh, u));
stream_printf(out, "%d;capacity\n", mweight);
stream_printf(out, "%d;cargo\n", n);
stream_printf(out, "%d;speed\n", shipspeed(sh, u));
}
/* shore */
w = NODIRECTION;
if (!fval(r->terrain, SEA_REGION))
w = sh->coast;
if (w != NODIRECTION)
fprintf(F, "%d;Kueste\n", w);
stream_printf(out, "%d;Kueste\n", w);
cr_output_curses_compat(F, f, sh, TYP_SHIP);
cr_output_curses(out, f, sh, TYP_SHIP);
}
static void cr_output_ship_compat(FILE *F, const ship *sh, const unit *u,
int fcaptain, const faction *f, const region *r)
{
/* TODO: eliminate this function */
stream strm;
fstream_init(&strm, F);
cr_output_ship(&strm, sh, u, fcaptain, f, r);
}
static int stream_order(stream *out, const struct order *ord) {
@ -798,11 +817,12 @@ void cr_output_unit(stream *out, const region * r, const faction * f,
if (fother) {
stream_printf(out, "%d;Anderepartei\n", fother->no);
}
} else if (!fval(u, UFL_ANON_FACTION)) {
}
else if (!fval(u, UFL_ANON_FACTION)) {
/* OBS: anonymity overrides everything */
/* we have no alliance, so we get duped */
stream_printf(out, "%d;Partei\n", (fother ? fother : u->faction)->no);
if (fother==f) {
if (fother == f) {
/* sieht aus wie unsere, ist es aber nicht. */
stream_printf(out, "1;Verraeter\n");
}
@ -929,12 +949,12 @@ void cr_output_unit(stream *out, const region * r, const faction * f,
}
stream_printf(out, "%d %d;%s\n", u->number * level_days(sv->level), esk,
translate(mkname("skill", skillnames[sk]), skillname(sk,
f->locale)));
f->locale)));
}
}
/* spells that this unit can cast */
mage = get_mage(u);
mage = get_mage_depr(u);
if (mage) {
int i, maxlevel = effskill(u, SK_MAGIC, 0);
cr_output_spells(out, u, maxlevel);
@ -944,7 +964,7 @@ void cr_output_unit(stream *out, const region * r, const faction * f,
if (sp) {
const char *name =
translate(mkname("spell", sp->sname), spell_name(sp,
f->locale));
f->locale));
stream_printf(out, "KAMPFZAUBER %d\n", i);
stream_printf(out, "\"%s\";name\n", name);
stream_printf(out, "%d;level\n", mage->combatspells[i].level);
@ -1201,7 +1221,7 @@ void cr_output_resources(stream *out, const faction * f, const region *r, bool s
if (result[n].number >= 0) {
pos =
cr_output_resource(pos, result[n].rtype, f->locale, result[n].number,
result[n].level);
result[n].level);
}
}
if (pos != cbuf) {
@ -1343,10 +1363,10 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
building *b;
ship *sh;
unit *u;
int stealthmod = stealth_modifier(r->seen.mode);
int stealthmod = stealth_modifier(r, f, r->seen.mode);
if (r->display && r->display[0])
fprintf(F, "\"%s\";Beschr\n", r->display);
if (r->land && r->land->display && r->land->display[0])
fprintf(F, "\"%s\";Beschr\n", r->land->display);
if (fval(r->terrain, LAND_REGION)) {
assert(r->land);
fprintf(F, "%d;Bauern\n", rpeasants(r));
@ -1458,7 +1478,7 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
const faction *sf = visible_faction(f, u);
fno = sf->no;
}
cr_output_building(F, b, u, fno, f);
cr_output_building_compat(F, b, u, fno, f);
}
/* ships */
@ -1470,14 +1490,14 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
fno = sf->no;
}
cr_output_ship(F, sh, u, fno, f, r);
cr_output_ship_compat(F, sh, u, fno, f, r);
}
/* visible units */
for (u = r->units; u; u = u->next) {
if (u->building || u->ship || (stealthmod > INT_MIN
&& cansee(f, r, u, stealthmod))) {
&& cansee_ex(f, r, u, stealthmod, r->seen.mode))) {
cr_output_unit_compat(F, r, f, u, r->seen.mode);
}
}
@ -1516,7 +1536,7 @@ report_computer(const char *filename, report_context * ctx, const char *bom)
fprintf(F, "VERSION %d\n", C_REPORT_VERSION);
fprintf(F, "\"UTF-8\";charset\n\"%s\";locale\n",
locale_name(f->locale));
locale_name(f->locale));
fprintf(F, "%d;noskillpoints\n", 1);
fprintf(F, "%lld;date\n", (long long)ctx->report_time);
fprintf(F, "\"%s\";Spiel\n", game_name());
@ -1683,7 +1703,7 @@ report_computer(const char *filename, report_context * ctx, const char *bom)
}
}
report_crtypes(F, f->locale);
if (f->locale!=crtag_locale()) {
if (f->locale != crtag_locale()) {
report_translations(F);
}
reset_translations();

View file

@ -266,7 +266,6 @@ void add_recruits(unit * u, int number, int wanted)
strlcpy(equipment, "new_", sizeof(equipment));
strlcat(equipment, u_race(u)->_name, sizeof(equipment));
strlcat(equipment, "_unit", sizeof(equipment));
equip_unit(unew, get_equipment(equipment));
if (unew != u) {
@ -661,7 +660,12 @@ static int forget_cmd(unit * u, order * ord)
init_order(ord);
s = gettoken(token, sizeof(token));
if ((sk = get_skill(s, u->faction->locale)) != NOSKILL) {
sk = get_skill(s, u->faction->locale);
if (sk != NOSKILL) {
if (sk == SK_MAGIC && is_familiar(u)) {
/* some units cannot forget their innate magical abilities */
return 0;
}
ADDMSG(&u->faction->msgs, msg_message("forget", "unit skill", u, sk));
set_level(u, sk, 0);
}

View file

@ -2,6 +2,17 @@
#include "settings.h"
#include "eressea.h"
#include "calendar.h"
#include "chaos.h"
#include "items.h"
#include "creport.h"
#include "report.h"
#include "names.h"
#include "reports.h"
#include "spells.h"
#include "vortex.h"
#include "wormhole.h"
#include <kernel/config.h>
#include <util/log.h>
@ -25,16 +36,6 @@
#include <util/message.h>
#include <races/races.h>
#include "calendar.h"
#include "chaos.h"
#include "items.h"
#include "creport.h"
#include "report.h"
#include "names.h"
#include "reports.h"
#include "spells.h"
#include "wormhole.h"
void game_done(void)
{
#ifdef CLEANUP_CODE
@ -53,6 +54,7 @@ void game_done(void)
calendar_cleanup();
free_functions();
free_config();
free_special_directions();
free_locales();
kernel_done();
}

View file

@ -418,10 +418,12 @@ message * give_men(int n, unit * u, unit * u2, struct order *ord)
set_racename(&u2->attribs, get_racename(u->attribs));
u_setrace(u2, u_race(u));
u2->irace = u->irace;
if (fval(u, UFL_HERO))
if (fval(u, UFL_HERO)) {
fset(u2, UFL_HERO);
else
}
else {
freset(u2, UFL_HERO);
}
}
/* Einheiten von Schiffen können nicht NACH in von
@ -589,7 +591,7 @@ void give_unit(unit * u, unit * u2, order * ord)
cmistake(u, ord, 155, MSG_COMMERCE);
return;
}
mage = get_mage(u);
mage = get_mage_depr(u);
if (!mage || u2->faction->magiegebiet != mage->magietyp) {
cmistake(u, ord, 157, MSG_COMMERCE);
return;

View file

@ -20,6 +20,7 @@ without prior permission by the authors of Eressea.
#include <util/bsdstring.h>
#include <util/event.h>
#include <util/functions.h>
#include <util/gamedata.h>
#include <util/log.h>
#include <util/parser.h>
#include <util/resolve.h>
@ -290,10 +291,10 @@ use_item_lua(unit *u, const item_type *itype, int amount, struct order *ord)
}
/* compat code for old data files */
static int caldera_read(trigger *t, struct gamedata *data)
static int caldera_read(trigger *t, gamedata *data)
{
UNUSED_ARG(t);
read_building_reference(data);
READ_INT(data->store, NULL);
return AT_READ_FAIL;
}

View file

@ -74,9 +74,9 @@ struct order *ord)
if (amount > MAXGAIN) {
amount = MAXGAIN;
}
teach->value += amount * STUDYDAYS;
if (teach->value > MAXGAIN * STUDYDAYS) {
teach->value = MAXGAIN * STUDYDAYS;
teach->days += amount * STUDYDAYS;
if (teach->days > MAXGAIN * STUDYDAYS) {
teach->days = MAXGAIN * STUDYDAYS;
}
i_change(&u->items, itype, -amount);
return 0;
@ -145,7 +145,7 @@ struct order *ord)
while (*ap && force > 0) {
curse *c;
attrib *a = *ap;
if (!(a->type->flags & ATF_CURSE)) {
if (a->type != &at_curse) {
do {
ap = &(*ap)->next;
} while (*ap && a->type == (*ap)->type);

View file

@ -63,7 +63,7 @@ int *casualties)
if (wp != NULL && wp->type == wtype)
++k;
}
msg = msg_message("battle::useflamingsword", "amount unit", k, fi->unit);
msg = msg_message("useflamingsword", "amount unit", k, fi->unit);
message_all(fi->side->battle, msg);
msg_release(msg);
fi->catmsg = 0;
@ -119,7 +119,7 @@ int *casualties)
if (af->person[i].reload == 0 && af->person[i].missile == wp)
++k;
}
msg = msg_message("battle::usecatapult", "amount unit", k, au);
msg = msg_message("usecatapult", "amount unit", k, au);
message_all(b, msg);
msg_release(msg);
af->catmsg = 0;

View file

@ -7,13 +7,40 @@
#include "region.h"
#include "group.h"
#include "faction.h"
#include "objtypes.h"
#include "plane.h"
#include <util/attrib.h>
#include <util/gamedata.h>
#include <storage.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
void read_allies(gamedata * data, faction *f)
{
ally **sfp = &f->allies;
for (;;) {
int aid;
READ_INT(data->store, &aid);
if (aid > 0) {
ally * al = ally_add(sfp, NULL);
int state;
if ((al->faction = findfaction(aid)) == NULL) {
ur_add(RESOLVE_FACTION | aid, (void **)&al->faction, NULL);
}
READ_INT(data->store, &state);
al->status = state & HELP_ALL;
sfp = &al->next;
}
else {
break;
}
}
}
ally * ally_find(ally *al, const struct faction *f) {
for (; al; al = al->next) {
if (al->faction == f) return al;

View file

@ -26,7 +26,9 @@ extern "C" {
struct attrib_type;
struct plane;
struct faction;
struct gamedata;
struct unit;
extern struct attrib_type at_npcfaction;
typedef struct ally {
@ -35,6 +37,7 @@ extern "C" {
int status;
} ally;
void read_allies(struct gamedata * data, struct faction *f);
ally * ally_find(ally *al, const struct faction *f);
ally * ally_add(ally **al_p, struct faction *f);
void ally_remove(ally **al_p, struct faction *f);

View file

@ -991,6 +991,7 @@ void free_construction(struct construction *cons)
{
while (cons) {
construction *next = cons->improvement;
free(cons->name);
free(cons->materials);
free(cons);
cons = next;

View file

@ -322,25 +322,26 @@ void write_building_reference(const struct building *b, struct storage *store)
WRITE_INT(store, (b && b->region) ? b->no : 0);
}
int resolve_building(variant id, void *address)
void resolve_building(building *b)
{
int result = 0;
building *b = NULL;
if (id.i != 0) {
b = findbuilding(id.i);
if (b == NULL) {
result = -1;
}
}
*(building **)address = b;
return result;
resolve(RESOLVE_BUILDING | b->no, b);
}
variant read_building_reference(gamedata * data)
int read_building_reference(gamedata * data, building **bp, resolve_fun fun)
{
variant result;
READ_INT(data->store, &result.i);
return result;
int id;
READ_INT(data->store, &id);
if (id > 0) {
*bp = findbuilding(id);
if (*bp == NULL) {
*bp = NULL;
ur_add(RESOLVE_BUILDING | id, (void**)bp, fun);
}
}
else {
*bp = NULL;
}
return id;
}
building *new_building(const struct building_type * btype, region * r,

View file

@ -20,7 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define H_KRNL_BUILDING
#include <kernel/types.h>
#include <util/variant.h>
#include <util/resolve.h>
#include <stddef.h>
#include <stdbool.h>
@ -152,10 +152,11 @@ extern "C" {
#include "build.h"
#define NOBUILDING NULL
int resolve_building(variant data, void *address);
#define RESOLVE_BUILDING (TYP_BUILDING << 24)
void resolve_building(building *b);
void write_building_reference(const struct building *b,
struct storage *store);
variant read_building_reference(struct gamedata *data);
int read_building_reference(struct gamedata * data, struct building **bp, resolve_fun fun);
struct building *findbuilding(int n);

View file

@ -606,37 +606,26 @@ int read_borders(gamedata *data)
{
struct storage *store = data->store;
for (;;) {
int bid = 0;
int fid, tid, bid;
char zText[32];
region *from, *to;
border_type *type;
READ_TOK(store, zText, sizeof(zText));
if (!strcmp(zText, "end"))
if (!strcmp(zText, "end")) {
break;
}
READ_INT(store, &bid);
type = find_bordertype(zText);
if (type == NULL) {
log_error("[read_borders] connection %d type %s is not registered", bid, zText);
assert(type || !"connection type not registered");
}
READ_INT(store, &bid);
if (data->version < UIDHASH_VERSION) {
int fx, fy, tx, ty;
READ_INT(store, &fx);
READ_INT(store, &fy);
READ_INT(store, &tx);
READ_INT(store, &ty);
from = findregion(fx, fy);
to = findregion(tx, ty);
}
else {
int fid, tid;
READ_INT(store, &fid);
READ_INT(store, &tid);
from = findregionbyid(fid);
to = findregionbyid(tid);
}
READ_INT(store, &fid);
READ_INT(store, &tid);
from = findregionbyid(fid);
to = findregionbyid(tid);
if (!to || !from) {
log_error("%s connection %d has missing regions", zText, bid);
if (type->read) {
@ -660,19 +649,6 @@ int read_borders(gamedata *data)
b->id = bid;
assert(bid <= nextborder);
type->read(b, data);
if (data->version < NOBORDERATTRIBS_VERSION) {
attrib *a = NULL;
int result = read_attribs(data, &a, b);
if (border_convert_cb) {
border_convert_cb(b, a);
}
while (a) {
a_remove(&a, a);
}
if (result < 0) {
return result;
}
}
if (!type->write) {
log_warning("invalid border '%s' between '%s' and '%s'\n", zText, regionname(from, 0), regionname(to, 0));
}

View file

@ -191,12 +191,7 @@ int curse_read(attrib * a, void *owner, gamedata *data)
READ_INT(store, &c->duration);
READ_FLT(store, &flt);
c->vigour = flt;
if (data->version < INTPAK_VERSION) {
ur = resolve_unit(read_int(data->store), &c->magician);
}
else {
ur = read_reference(&c->magician, data, read_unit_reference, resolve_unit);
}
ur = read_unit_reference(data, &c->magician, NULL);
if (data->version < CURSEFLOAT_VERSION) {
READ_INT(store, &n);
c->effect = (float)n;
@ -231,9 +226,7 @@ int curse_read(attrib * a, void *owner, gamedata *data)
READ_INT(store, &c->data.i);
}
if (c->type->typ == CURSETYP_REGION) {
int rr =
read_reference(&c->data.v, data, read_region_reference,
RESOLVE_REGION(data->version));
int rr = read_region_reference(data, (region **)&c->data.v, NULL);
if (ur == 0 && rr == 0 && !c->data.v) {
return AT_READ_FAIL;
}
@ -273,8 +266,7 @@ attrib_type at_curse = {
curse_age,
curse_write,
curse_read,
NULL,
ATF_CURSE
NULL
};
/* ------------------------------------------------------------- */
@ -363,7 +355,7 @@ void ct_checknames(void) {
static bool cmp_curse(const attrib * a, const void *data)
{
const curse *c = (const curse *)data;
if (a->type->flags & ATF_CURSE) {
if (a->type == &at_curse) {
if (!data || c == (curse *)a->data.v)
return true;
}
@ -375,7 +367,7 @@ curse *get_curse(attrib * ap, const curse_type * ctype)
attrib *a = ap;
if (!ctype) return NULL;
while (a) {
if (a->type->flags & ATF_CURSE) {
if (a->type == &at_curse) {
const attrib_type *at = a->type;
while (a && a->type == at) {
curse *c = (curse *)a->data.v;
@ -710,7 +702,7 @@ bool is_cursed_with(const attrib * ap, const curse * c)
const attrib *a = ap;
while (a) {
if ((a->type->flags & ATF_CURSE) && (c == (const curse *)a->data.v)) {
if ((a->type == &at_curse) && (c == (const curse *)a->data.v)) {
return true;
}
a = a->next;

View file

@ -311,9 +311,6 @@ extern "C" {
#define get_curseeffect(a, ctype) \
curse_geteffect(get_curse(a, ctype))
/* eressea-defined attribute-type flags */
#define ATF_CURSE ATF_USER_DEFINED
#ifdef __cplusplus
}
#endif

View file

@ -29,6 +29,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include <selist.h>
#include <critbit.h>
#include <util/log.h>
#include <util/rand.h>
#include <util/rng.h>
@ -37,43 +39,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <string.h>
#include <stdlib.h>
static equipment *equipment_sets;
equipment *get_or_create_equipment(const char *eqname)
{
equipment **eqp = &equipment_sets;
for (;;) {
struct equipment *eq = *eqp;
int i = eq ? strcmp(eq->name, eqname) : 1;
if (i > 0) {
eq = (equipment *)calloc(1, sizeof(equipment));
eq->name = strdup(eqname);
eq->next = *eqp;
memset(eq->skills, 0, sizeof(eq->skills));
*eqp = eq;
break;
}
else if (i == 0) {
break;
}
eqp = &eq->next;
}
return *eqp;
}
equipment *get_equipment(const char *eqname)
{
equipment *eq = equipment_sets;
for (; eq; eq = eq->next) {
int i = strcmp(eq->name, eqname);
if (i == 0)
return eq;
else if (i > 0)
break;
}
return NULL;
}
void equipment_setskill(equipment * eq, skill_t sk, const char *value)
{
if (eq != NULL) {
@ -156,7 +121,7 @@ void equip_unit_mask(struct unit *u, const struct equipment *eq, int mask)
if (eq->spells) {
selist * ql = eq->spells;
int qi;
sc_mage * mage = get_mage(u);
sc_mage * mage = get_mage_depr(u);
for (qi = 0; ql; selist_advance(&ql, &qi, 1)) {
lazy_spell *sbe = (lazy_spell *)selist_get(ql, qi);
@ -236,27 +201,111 @@ void free_ls(void *arg) {
free(ls);
}
void equipment_done(void) {
equipment **eqp = &equipment_sets;
while (*eqp) {
int i;
equipment *eq = *eqp;
*eqp = eq->next;
free(eq->name);
if (eq->spells) {
selist_foreach(eq->spells, free_ls);
selist_free(eq->spells);
}
while (eq->items) {
itemdata *next = eq->items->next;
free(eq->items->value);
free(eq->items);
eq->items = next;
}
/* TODO: subsets, skills */
for (i=0;i!=MAXSKILLS;++i) {
free(eq->skills[i]);
}
free(eq);
static critbit_tree cb_equipments = { 0 };
#define EQNAMELEN 24
typedef struct eq_entry {
char key[EQNAMELEN];
equipment *value;
} eq_entry;
typedef struct name_cb_data {
const equipment *find;
const char *result;
} name_cb_data;
static int equipment_name_cb(const void * match, const void * key, size_t keylen, void *cbdata) {
const eq_entry *ent = (const eq_entry *)match;
name_cb_data *query = (name_cb_data *)cbdata;
if (ent->value == query->find) {
query->result = ent->key;
return 1;
}
return 0;
}
const char *equipment_name(const struct equipment *eq)
{
name_cb_data data;
data.find = eq;
data.result = NULL;
cb_foreach(&cb_equipments, "", 0, equipment_name_cb, &data);
return data.result;
}
equipment *get_equipment(const char *eqname)
{
const void *match;
if (strlen(eqname) >= EQNAMELEN) {
log_warning("equipment name is longer than %d bytes: %s", EQNAMELEN - 1, eqname);
return NULL;
}
match = cb_find_str(&cb_equipments, eqname);
if (match) {
const eq_entry *ent = (const eq_entry *)match;
return ent->value;
}
return NULL;
}
equipment *create_equipment(const char *eqname)
{
size_t len = strlen(eqname);
eq_entry ent;
if (len >= EQNAMELEN) {
log_error("equipment name is longer than %d bytes: %s", EQNAMELEN-1, eqname);
len = EQNAMELEN-1;
}
memset(ent.key, 0, EQNAMELEN);
memcpy(ent.key, eqname, len);
ent.value = (equipment *)calloc(1, sizeof(equipment));
cb_insert(&cb_equipments, &ent, sizeof(ent));
return ent.value;
}
equipment *get_or_create_equipment(const char *eqname)
{
equipment *eq = get_equipment(eqname);
if (!eq) {
return create_equipment(eqname);
}
return eq;
}
static void free_equipment(equipment *eq) {
int i;
if (eq->spells) {
selist_foreach(eq->spells, free_ls);
selist_free(eq->spells);
}
while (eq->items) {
itemdata *next = eq->items->next;
free(eq->items->value);
free(eq->items);
eq->items = next;
}
/* TODO: subsets, skills */
for (i = 0; i != MAXSKILLS; ++i) {
free(eq->skills[i]);
}
}
static int free_equipment_cb(const void * match, const void * key, size_t keylen, void *cbdata) {
const eq_entry * ent = (const eq_entry *)match;
free_equipment(ent->value);
free(ent->value);
return 0;
}
void equipment_done(void) {
cb_foreach(&cb_equipments, "", 0, free_equipment_cb, 0);
cb_clear(&cb_equipments);
}

View file

@ -45,19 +45,19 @@ extern "C" {
} subset;
typedef struct equipment {
char *name;
struct itemdata *items;
char *skills[MAXSKILLS];
struct selist *spells;
struct subset *subsets;
struct equipment *next;
void(*callback) (const struct equipment *, struct unit *);
} equipment;
void equipment_done(void);
const char *equipment_name(const struct equipment *eq);
struct equipment *get_or_create_equipment(const char *eqname);
struct equipment *get_equipment(const char *eqname);
struct equipment *create_equipment(const char *eqname);
void equipment_setitem(struct equipment *eq,
const struct item_type *itype, const char *value);

View file

@ -10,7 +10,7 @@
#include <CuTest.h>
#include <tests.h>
void test_equipment(CuTest * tc)
static void test_equipment(CuTest * tc)
{
equipment * eq;
unit * u;
@ -39,16 +39,40 @@ void test_equipment(CuTest * tc)
CuAssertIntEquals(tc, 1, i_get(u->items, it_horses));
CuAssertIntEquals(tc, 5, get_level(u, SK_MAGIC));
mage = get_mage(u);
mage = get_mage_depr(u);
CuAssertPtrNotNull(tc, mage);
CuAssertPtrNotNull(tc, mage->spellbook);
CuAssertTrue(tc, u_hasspell(u, sp));
test_cleanup();
}
static void test_get_equipment(CuTest * tc)
{
equipment * eq;
test_setup();
eq = create_equipment("catapultammo123");
CuAssertPtrNotNull(tc, eq);
CuAssertStrEquals(tc, "catapultammo123", equipment_name(eq));
eq = get_equipment("catapultammo123");
CuAssertPtrNotNull(tc, eq);
CuAssertStrEquals(tc, "catapultammo123", equipment_name(eq));
eq = get_equipment("catapult");
CuAssertPtrEquals(tc, NULL, eq);
eq = create_equipment("catapult");
eq = get_equipment("catapult");
CuAssertPtrNotNull(tc, eq);
CuAssertStrEquals(tc, "catapult", equipment_name(eq));
eq = get_equipment("catapultammo123");
CuAssertPtrNotNull(tc, eq);
CuAssertStrEquals(tc, "catapultammo123", equipment_name(eq));
test_cleanup();
}
CuSuite *get_equipment_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_equipment);
SUITE_ADD_TEST(suite, test_get_equipment);
return suite;
}

View file

@ -203,21 +203,6 @@ const char *factionname(const faction * f)
return ibuf;
}
int resolve_faction(variant id, void *address)
{
int result = 0;
faction *f = NULL;
if (id.i != 0) {
f = findfaction(id.i);
if (f == NULL) {
result = -1;
}
}
assert(address);
*(faction **)address = f;
return result;
}
bool faction_id_is_unused(int id)
{
return findfaction(id) == NULL;
@ -332,10 +317,25 @@ bool checkpasswd(const faction * f, const char *passwd)
return true;
}
variant read_faction_reference(gamedata * data)
void resolve_faction(faction *f)
{
variant id;
READ_INT(data->store, &id.i);
resolve(RESOLVE_FACTION | f->no, f);
}
int read_faction_reference(gamedata * data, faction **fp, resolve_fun fun)
{
int id;
READ_INT(data->store, &id);
if (id > 0) {
*fp = findfaction(id);
if (*fp == NULL) {
*fp = NULL;
ur_add(RESOLVE_FACTION | id, (void **)fp, fun);
}
}
else {
*fp = NULL;
}
return id;
}

View file

@ -21,7 +21,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "skill.h"
#include "types.h"
#include <util/resolve.h>
#include <modules/score.h>
#include <stdbool.h>
#ifdef __cplusplus
@ -126,8 +129,10 @@ extern "C" {
void write_faction_reference(const struct faction *f,
struct storage *store);
variant read_faction_reference(struct gamedata *data);
int resolve_faction(variant data, void *addr);
int read_faction_reference(struct gamedata *data, struct faction **fp, resolve_fun fun);
#define RESOLVE_FACTION (TYP_FACTION << 24)
void resolve_faction(struct faction *f);
void renumber_faction(faction * f, int no);
void free_factions(void);

View file

@ -24,6 +24,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "ally.h"
#include "faction.h"
#include "unit.h"
#include "objtypes.h"
/* attrib includes */
#include <attributes/raceprefix.h>
@ -238,16 +239,16 @@ void read_groups(gamedata *data, faction * f)
g = new_group(f, buf, gid);
pa = &g->allies;
for (;;) {
ally *a;
variant fid;
fid = read_faction_reference(data);
if (fid.i <= 0)
break;
a = ally_add(pa, findfaction(fid.i));
READ_INT(store, &a->status);
if (!a->faction)
ur_add(fid, &a->faction, resolve_faction);
ally *al;
int id;
READ_INT(store, &id);
if (id == 0) break;
al = ally_add(pa, NULL);
al->faction = findfaction(id);
if (!al->faction) {
ur_add(RESOLVE_FACTION | id, (void **)&al->faction, NULL);
}
READ_INT(store, &al->status);
}
read_attribs(data, &g->attribs, g);
}

View file

@ -60,6 +60,12 @@ static critbit_tree cb_resources;
luxury_type *luxurytypes;
potion_type *potiontypes;
#define RTYPENAMELEN 24
typedef struct rt_entry {
char key[RTYPENAMELEN];
struct resource_type *value;
} rt_entry;
static int res_changeaura(unit * u, const resource_type * rtype, int delta)
{
assert(rtype != NULL);
@ -166,13 +172,19 @@ static int num_resources;
static void rt_register(resource_type * rtype)
{
char buffer[64];
const char * name = rtype->_name;
size_t len = strlen(name);
rt_entry ent;
assert(len < sizeof(buffer) - sizeof(rtype));
len = cb_new_kv(name, len, &rtype, sizeof(rtype), buffer);
cb_insert(&cb_resources, buffer, len);
if (len >= RTYPENAMELEN) {
log_error("resource name is longer than %d bytes: %s",
RTYPENAMELEN-1, name);
len = RTYPENAMELEN-1;
}
ent.value = rtype;
memset(ent.key, 0, RTYPENAMELEN);
memcpy(ent.key, name, len);
cb_insert(&cb_resources, &ent, sizeof(ent));
++num_resources;
}
@ -186,22 +198,11 @@ resource_type *rt_get_or_create(const char *name) {
else {
rtype->_name = strdup(name);
rt_register(rtype);
return rt_find(name);
}
}
return rtype;
}
static void it_register(item_type * itype)
{
char buffer[64];
const char * name = itype->rtype->_name;
size_t len = strlen(name);
assert(len < sizeof(buffer) - sizeof(itype));
len = cb_new_kv(name, len, &itype, sizeof(itype), buffer);
}
static const char *it_aliases[][2] = {
{ "Runenschwert", "runesword" },
{ "p12", "truthpotion" },
@ -244,7 +245,6 @@ item_type *it_get_or_create(resource_type *rtype) {
rtype->uchange = res_changeitem;
rtype->itype = itype;
rtype->flags |= RTF_ITEM;
it_register(itype);
return itype;
}
@ -389,13 +389,20 @@ const potion_type *resource2potion(const resource_type * rtype)
resource_type *rt_find(const char *name)
{
void * match;
resource_type *result = 0;
const void *match;
size_t len = strlen(name);
if (cb_find_prefix(&cb_resources, name, strlen(name) + 1, &match, 1, 0)) {
cb_get_kv(match, &result, sizeof(result));
if (len >= RTYPENAMELEN) {
log_warning("resource name is longer than %d bytes: %s",
RTYPENAMELEN-1, name);
return NULL;
}
return result;
match = cb_find_str(&cb_resources, name);
if (match) {
const rt_entry *ent = (const rt_entry *)match;
return ent->value;
}
return NULL;
}
item **i_find(item ** i, const item_type * it)
@ -517,6 +524,7 @@ static int icache_size;
#define ICACHE_MAX 100
void item_done(void) {
icache_size = ICACHE_MAX;
i_freeall(&icache);
icache_size = 0;
}
@ -791,14 +799,14 @@ int change_money(unit * u, int v)
return 0;
}
static int add_resourcename_cb(const void * match, const void * key, size_t keylen, void *data)
static int add_resourcename_cb(const void * match, const void * key,
size_t keylen, void *data)
{
struct locale * lang = (struct locale *)data;
int i = locale_index(lang);
critbit_tree * cb = rnames + i;
resource_type *rtype;
resource_type *rtype = ((rt_entry *)match)->value;
cb_get_kv(match, &rtype, sizeof(rtype));
for (i = 0; i != 2; ++i) {
char buffer[128];
const char * name = LOC(lang, resourcename(rtype, (i == 0) ? 0 : NMF_PLURAL));
@ -834,20 +842,20 @@ const resource_type *findresourcetype(const char *name, const struct locale *lan
else {
log_debug("findresourcetype: transliterate failed for '%s'\n", name);
}
return 0;
return NULL;
}
attrib_type at_showitem = {
"showitem"
};
static int add_itemname_cb(const void * match, const void * key, size_t keylen, void *data)
static int add_itemname_cb(const void * match, const void * key,
size_t keylen, void *data)
{
struct locale * lang = (struct locale *)data;
critbit_tree * cb = inames + locale_index(lang);
resource_type *rtype;
resource_type *rtype = ((rt_entry *)match)->value;
cb_get_kv(match, &rtype, sizeof(rtype));
if (rtype->itype) {
int i;
for (i = 0; i != 2; ++i) {
@ -951,7 +959,7 @@ void write_items(struct storage *store, item * ilist)
static void free_itype(item_type *itype) {
assert(itype);
free(itype->construction);
free_construction(itype->construction);
free(itype->_appearance[0]);
free(itype->_appearance[1]);
free(itype);
@ -979,9 +987,10 @@ void free_rtype(resource_type *rtype) {
free(rtype);
}
int free_rtype_cb(const void * match, const void * key, size_t keylen, void *cbdata) {
resource_type *rtype;
cb_get_kv(match, &rtype, sizeof(rtype));
static int free_rtype_cb(const void * match, const void * key,
size_t keylen, void *cbdata)
{
resource_type *rtype = ((rt_entry *)match)->value;;
free_rtype(rtype);
return 0;
}

View file

@ -173,12 +173,31 @@ static void test_core_resources(CuTest *tc) {
test_cleanup();
}
static void test_get_resource(CuTest *tc) {
resource_type *rtype;
test_setup();
CuAssertPtrEquals(tc, NULL, rt_find("catapultammo123"));
rtype = rt_get_or_create("catapultammo123");
CuAssertPtrNotNull(tc, rtype);
CuAssertPtrEquals(tc, rtype, rt_find("catapultammo123"));
CuAssertStrEquals(tc, "catapultammo123", rtype->_name);
CuAssertPtrEquals(tc, NULL, rt_find("catapult"));
rtype = rt_get_or_create("catapult");
CuAssertPtrEquals(tc, rtype, rt_find("catapult"));
CuAssertStrEquals(tc, "catapult", rtype->_name);
test_cleanup();
}
CuSuite *get_item_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_resourcename_no_appearance);
SUITE_ADD_TEST(suite, test_resourcename_with_appearance);
SUITE_ADD_TEST(suite, test_change_item);
SUITE_ADD_TEST(suite, test_get_resource);
SUITE_ADD_TEST(suite, test_resource_type);
SUITE_ADD_TEST(suite, test_finditemtype);
SUITE_ADD_TEST(suite, test_findresourcetype);

View file

@ -83,7 +83,7 @@ struct message *msg_feedback(const struct unit *u, struct order *ord,
variant var;
memset(args, 0, sizeof(args));
if (ord && ord->_noerror) {
if (ord && is_silent(ord)) {
return NULL;
}

View file

@ -46,10 +46,10 @@ extern "C" {
int level;
} msglevel;
extern struct message *msg_message(const char *name, const char *sig, ...);
extern struct message *msg_feedback(const struct unit *, struct order *cmd,
struct message *msg_message(const char *name, const char *sig, ...);
struct message *msg_feedback(const struct unit *, struct order *cmd,
const char *name, const char *sig, ...);
extern struct message *add_message(struct message_list **pm,
struct message *add_message(struct message_list **pm,
struct message *m);
void addmessage(struct region *r, struct faction *f, const char *s,
msg_t mtype, int level);

View file

@ -79,8 +79,8 @@ static void test_noerror(CuTest *tc) {
lang = test_create_locale();
u = test_create_unit(test_create_faction(NULL), test_create_region(0, 0, NULL));
u->thisorder = parse_order("!@move", lang);
CuAssertTrue(tc, u->thisorder->_persistent);
CuAssertTrue(tc, u->thisorder->_noerror);
CuAssertIntEquals(tc, K_MOVE | CMD_QUIET | CMD_PERSIST, u->thisorder->command);
CuAssertTrue(tc, !is_persistent(u->thisorder));
CuAssertPtrEquals(tc, NULL, msg_error(u, u->thisorder, 100));
CuAssertPtrEquals(tc, NULL, msg_feedback(u, u->thisorder, "error_unit_not_found", NULL));
test_cleanup();

View file

@ -30,12 +30,12 @@
#include <stdlib.h>
#include <string.h>
# define ORD_KEYWORD(ord) (ord)->data->_keyword
# define ORD_KEYWORD(ord) (keyword_t)((ord)->command & 0xFFFF)
# define ORD_LOCALE(ord) locale_array[(ord)->data->_lindex]->lang
# define ORD_STRING(ord) (ord)->data->_str
typedef struct locale_data {
struct order_data *short_orders[MAXKEYWORDS];
struct order_data *short_orders;
struct order_data *study_orders[MAXSKILLS];
const struct locale *lang;
} locale_data;
@ -46,7 +46,6 @@ typedef struct order_data {
const char *_str;
int _refcount;
int _lindex;
keyword_t _keyword;
} order_data;
static void release_data(order_data * data)
@ -94,7 +93,7 @@ char* get_command(const order *ord, char *sbuffer, size_t size) {
keyword_t kwd = ORD_KEYWORD(ord);
int bytes;
if (ord->_noerror) {
if (ord->command & CMD_QUIET) {
if (size > 0) {
*bufp++ = '!';
--size;
@ -103,7 +102,7 @@ char* get_command(const order *ord, char *sbuffer, size_t size) {
WARN_STATIC_BUFFER();
}
}
if (ord->_persistent) {
if (ord->command & CMD_PERSIST) {
if (size > 0) {
*bufp++ = '@';
--size;
@ -162,8 +161,7 @@ order *copy_order(const order * src)
if (src != NULL) {
order *ord = (order *)malloc(sizeof(order));
ord->next = NULL;
ord->_persistent = src->_persistent;
ord->_noerror = src->_noerror;
ord->command = src->command;
ord->data = src->data;
++ord->data->_refcount;
return ord;
@ -189,13 +187,12 @@ void free_orders(order ** olist)
}
}
static char *mkdata(order_data **pdata, size_t len, keyword_t kwd, int lindex, const char *str)
static char *mkdata(order_data **pdata, size_t len, int lindex, const char *str)
{
order_data *data;
char *result;
data = malloc(sizeof(order_data) + len + 1);
result = (char *)(data + 1);
data->_keyword = kwd;
data->_lindex = lindex;
data->_refcount = 0;
data->_str = 0;
@ -229,7 +226,7 @@ static order_data *create_data(keyword_t kwd, const char *sptr, int lindex)
const char *skname = skillname(sk, lang);
const char *spc = strchr(skname, ' ');
size_t len = strlen(skname);
char *dst = mkdata(&data, len + (spc ? 3 : 0), kwd, lindex, spc ? 0 : skname);
char *dst = mkdata(&data, len + (spc ? 3 : 0), lindex, spc ? 0 : skname);
locale_array[lindex]->study_orders[sk] = data;
if (spc) {
dst[0] = '\"';
@ -246,26 +243,24 @@ static order_data *create_data(keyword_t kwd, const char *sptr, int lindex)
/* orders with no parameter, only one order_data per order required */
else if (kwd != NOKEYWORD && *sptr == 0) {
data = locale_array[lindex]->short_orders[kwd];
data = locale_array[lindex]->short_orders;
if (data == NULL) {
mkdata(&data, 0, kwd, lindex, 0);
mkdata(&data, 0, lindex, 0);
data->_refcount = 1;
locale_array[lindex]->short_orders[kwd] = data;
locale_array[lindex]->short_orders = data;
}
++data->_refcount;
return data;
}
mkdata(&data, s ? strlen(s) : 0, kwd, lindex, s);
mkdata(&data, s ? strlen(s) : 0, lindex, s);
data->_refcount = 1;
return data;
}
static void clear_localedata(int lindex) {
int i;
for (i = 0; i != MAXKEYWORDS; ++i) {
release_data(locale_array[lindex]->short_orders[i]);
locale_array[lindex]->short_orders[i] = 0;
}
release_data(locale_array[lindex]->short_orders);
locale_array[lindex]->short_orders = NULL;
for (i = 0; i != MAXSKILLS; ++i) {
release_data(locale_array[lindex]->study_orders[i]);
locale_array[lindex]->study_orders[i] = 0;
@ -284,12 +279,12 @@ void close_orders(void) {
}
}
static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent,
static order *create_order_i(order *ord, keyword_t kwd, const char *sptr, bool persistent,
bool noerror, const struct locale *lang)
{
order *ord = NULL;
int lindex;
assert(ord);
if (kwd == NOKEYWORD || keyword_disabled(kwd)) {
log_error("trying to create an order for disabled keyword %s.", keyword(kwd));
return NULL;
@ -316,11 +311,12 @@ static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent,
}
locale_array[lindex]->lang = lang;
ord = (order *)malloc(sizeof(order));
ord->_persistent = persistent;
ord->_noerror = noerror;
ord->command = (int)kwd;
if (persistent) ord->command |= CMD_PERSIST;
if (noerror) ord->command |= CMD_QUIET;
ord->next = NULL;
while (isspace(*(unsigned char *)sptr)) ++sptr;
ord->data = create_data(kwd, sptr, lindex);
return ord;
@ -329,6 +325,7 @@ static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent,
order *create_order(keyword_t kwd, const struct locale * lang,
const char *params, ...)
{
order *ord;
char zBuffer[DISPLAYSIZE];
if (params) {
char *bufp = zBuffer;
@ -379,42 +376,40 @@ order *create_order(keyword_t kwd, const struct locale * lang,
else {
zBuffer[0] = 0;
}
return create_order_i(kwd, zBuffer, false, false, lang);
ord = (order *)malloc(sizeof(order));
return create_order_i(ord, kwd, zBuffer, false, false, lang);
}
order *parse_order(const char *s, const struct locale * lang)
{
assert(lang);
assert(s);
while (*s && !isalnum(*(unsigned char *)s) && !ispunct(*(unsigned char *)s)) {
++s;
}
if (*s != 0) {
keyword_t kwd;
const char *sptr;
keyword_t kwd = NOKEYWORD;
const char *sptr = s;
bool persistent = false, noerror = false;
const char * p;
while (*s == '!' || *s=='@') {
if (*s=='!') noerror = true;
else if (*s == '@') persistent = true;
++s;
}
sptr = s;
p = *sptr ? parse_token_depr(&sptr) : 0;
kwd = p ? get_keyword(p, lang) : NOKEYWORD;
if (p) {
while (*p == '!' || *p == '@') {
if (*p == '!') noerror = true;
else if (*p == '@') persistent = true;
++p;
}
kwd = get_keyword(p, lang);
}
if (kwd == K_MAKE) {
const char *s, *sp = sptr;
s = parse_token_depr(&sp);
if (s && isparam(s, lang, P_TEMP)) {
const char *sp = sptr;
p = parse_token_depr(&sp);
if (p && isparam(p, lang, P_TEMP)) {
kwd = K_MAKETEMP;
sptr = sp;
}
}
if (kwd != NOKEYWORD) {
while (isspace(*(unsigned char *)sptr)) ++sptr;
s = sptr;
return create_order_i(kwd, s, persistent, noerror, lang);
order *ord = (order *)malloc(sizeof(order));
return create_order_i(ord, kwd, sptr, persistent, noerror, lang);
}
}
return NULL;
@ -561,10 +556,15 @@ bool is_persistent(const order * ord)
case K_KOMMENTAR:
return true;
default:
return ord->_persistent || is_repeated(kwd);
return (ord->command & CMD_PERSIST) || is_repeated(kwd);
}
}
bool is_silent(const order * ord)
{
return (ord->command & CMD_QUIET) != 0;
}
char *write_order(const order * ord, char *buffer, size_t size)
{
if (ord == 0) {
@ -595,5 +595,5 @@ keyword_t init_order(const struct order *ord)
{
assert(ord && ord->data);
init_tokens_str(ord->data->_str);
return ord->data->_keyword;
return ORD_KEYWORD(ord);
}

View file

@ -32,32 +32,36 @@ extern "C" {
struct order_data;
#define CMD_QUIET 0x010000
#define CMD_PERSIST 0x020000
#define CMD_DEFAULT 0x040000
typedef struct order {
struct order *next;
/* do not access this data: */
struct order_data *data;
bool _persistent;
bool _noerror;
int command;
} order;
/* constructor */
extern order *create_order(keyword_t kwd, const struct locale *lang,
order *create_order(keyword_t kwd, const struct locale *lang,
const char *params, ...);
extern order *parse_order(const char *s, const struct locale *lang);
extern void replace_order(order ** dst, order * orig, const order * src);
order *parse_order(const char *s, const struct locale *lang);
void replace_order(order ** dst, order * orig, const order * src);
/* reference counted copies of orders: */
extern order *copy_order(const order * ord);
extern void free_order(order * ord);
extern void free_orders(order ** olist);
order *copy_order(const order * ord);
void free_order(order * ord);
void free_orders(order ** olist);
extern void push_order(struct order **olist, struct order *ord);
void push_order(struct order **olist, struct order *ord);
/* access functions for orders */
keyword_t getkeyword(const order * ord);
void set_order(order ** destp, order * src);
char* get_command(const order *ord, char *buffer, size_t size);
bool is_persistent(const order * ord);
bool is_silent(const order * ord);
bool is_exclusive(const order * ord);
bool is_repeated(keyword_t kwd);
bool is_long(keyword_t kwd);

View file

@ -38,8 +38,7 @@ static void test_parse_order(CuTest *tc) {
ord = parse_order("MOVE NORTH", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertTrue(tc, !ord->_noerror);
CuAssertTrue(tc, !ord->_persistent);
CuAssertIntEquals(tc, K_MOVE, ord->command);
CuAssertIntEquals(tc, K_MOVE, getkeyword(ord));
CuAssertStrEquals(tc, "move NORTH", get_command(ord, cmd, sizeof(cmd)));
@ -49,26 +48,37 @@ static void test_parse_order(CuTest *tc) {
ord = parse_order("!MOVE NORTH", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertTrue(tc, ord->_noerror);
CuAssertTrue(tc, !ord->_persistent);
CuAssertPtrNotNull(tc, ord->data);
CuAssertIntEquals(tc, K_MOVE, getkeyword(ord));
CuAssertIntEquals(tc, K_MOVE | CMD_QUIET, ord->command);
free_order(ord);
ord = parse_order("@MOVE NORTH", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertTrue(tc, !ord->_noerror);
CuAssertTrue(tc, ord->_persistent);
free_order(ord);
ord = parse_order("@!MOVE NORTH", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertTrue(tc, ord->_noerror);
CuAssertTrue(tc, ord->_persistent);
CuAssertPtrNotNull(tc, ord->data);
CuAssertIntEquals(tc, K_MOVE, getkeyword(ord));
CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST, ord->command);
free_order(ord);
ord = parse_order("!@MOVE NORTH", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertTrue(tc, ord->_noerror);
CuAssertTrue(tc, ord->_persistent);
CuAssertPtrNotNull(tc, ord->data);
CuAssertIntEquals(tc, K_MOVE, getkeyword(ord));
CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST | CMD_QUIET, ord->command);
free_order(ord);
ord = parse_order("@!MOVE NORTH", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertPtrNotNull(tc, ord->data);
CuAssertIntEquals(tc, K_MOVE, getkeyword(ord));
CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST | CMD_QUIET, ord->command);
free_order(ord);
ord = parse_order(" !@MOVE NORTH", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertPtrNotNull(tc, ord->data);
CuAssertIntEquals(tc, K_MOVE, getkeyword(ord));
CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST | CMD_QUIET, ord->command);
free_order(ord);
test_cleanup();
@ -204,16 +214,84 @@ static void test_get_command(CuTest *tc) {
lang = test_create_locale();
ord = create_order(K_MAKE, lang, "iron");
CuAssertStrEquals(tc, "make iron", get_command(ord, buf, sizeof(buf)));
ord->_noerror = true;
ord->command |= CMD_QUIET;
CuAssertStrEquals(tc, "!make iron", get_command(ord, buf, sizeof(buf)));
ord->_persistent = true;
ord->command |= CMD_PERSIST;
CuAssertStrEquals(tc, "!@make iron", get_command(ord, buf, sizeof(buf)));
ord->_noerror = false;
ord->command = K_MAKE | CMD_PERSIST;
CuAssertStrEquals(tc, "@make iron", get_command(ord, buf, sizeof(buf)));
free_order(ord);
test_cleanup();
}
static void test_is_persistent(CuTest *tc) {
order *ord;
struct locale *lang;
test_setup();
lang = test_create_locale();
ord = parse_order("@invalid", lang);
CuAssertPtrEquals(tc, NULL, ord);
ord = parse_order("give", lang);
CuAssertIntEquals(tc, K_GIVE, ord->command);
CuAssertTrue(tc, !is_persistent(ord));
free_order(ord);
ord = parse_order("@give", lang);
CuAssertTrue(tc, !is_repeated(K_GIVE));
CuAssertIntEquals(tc, K_GIVE | CMD_PERSIST, ord->command);
CuAssertTrue(tc, is_persistent(ord));
free_order(ord);
ord = parse_order("make", lang);
CuAssertTrue(tc, is_repeated(K_MAKE));
CuAssertIntEquals(tc, K_MAKE , ord->command);
CuAssertTrue(tc, is_persistent(ord));
free_order(ord);
ord = parse_order("@move", lang);
CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST, ord->command);
CuAssertTrue(tc, !is_persistent(ord));
free_order(ord);
ord = parse_order("// comment", lang);
CuAssertTrue(tc, is_persistent(ord));
CuAssertIntEquals(tc, K_KOMMENTAR, ord->command);
free_order(ord);
test_cleanup();
}
static void test_is_silent(CuTest *tc) {
order *ord;
struct locale *lang;
test_setup();
lang = test_create_locale();
ord = parse_order("make", lang);
CuAssertIntEquals(tc, K_MAKE, ord->command);
CuAssertTrue(tc, !is_silent(ord));
free_order(ord);
ord = parse_order("!make", lang);
CuAssertIntEquals(tc, K_MAKE | CMD_QUIET, ord->command);
CuAssertTrue(tc, is_silent(ord));
free_order(ord);
ord = parse_order("@invalid", lang);
CuAssertPtrEquals(tc, NULL, ord);
ord = parse_order("// comment", lang);
CuAssertTrue(tc, is_persistent(ord));
CuAssertIntEquals(tc, K_KOMMENTAR, ord->command);
free_order(ord);
test_cleanup();
}
CuSuite *get_order_suite(void)
{
CuSuite *suite = CuSuiteNew();
@ -227,5 +305,7 @@ CuSuite *get_order_suite(void)
SUITE_ADD_TEST(suite, test_skip_token);
SUITE_ADD_TEST(suite, test_getstrtoken);
SUITE_ADD_TEST(suite, test_get_command);
SUITE_ADD_TEST(suite, test_is_persistent);
SUITE_ADD_TEST(suite, test_is_silent);
return suite;
}

View file

@ -258,39 +258,6 @@ unsigned char index)
return (rel + ursprung_y(f, pl, NULL) + plane_center_y(pl));
}
static int resolve_plane(variant id, void *addr)
{
int result = 0;
plane *pl = NULL;
if (id.i != 0) {
pl = getplanebyid(id.i);
if (pl == NULL) {
result = -1;
}
}
*(plane **)addr = pl;
return result;
}
void write_plane_reference(const plane * u, struct storage *store)
{
WRITE_INT(store, u ? (u->id) : 0);
}
int read_plane_reference(plane ** pp, struct storage *store)
{
variant id;
READ_INT(store, &id.i);
if (id.i == 0) {
*pp = NULL;
return AT_READ_FAIL;
}
*pp = getplanebyid(id.i);
if (*pp == NULL)
ur_add(id, pp, resolve_plane);
return AT_READ_OK;
}
void free_plane(plane *pl) {
free(pl->name);
free(pl);

View file

@ -69,8 +69,6 @@ extern "C" {
struct plane *get_homeplane(void);
int rel_to_abs(const struct plane *pl, const struct faction *f,
int rel, unsigned char index);
void write_plane_reference(const plane * p, struct storage *store);
int read_plane_reference(plane ** pp, struct storage *store);
int plane_width(const plane * pl);
int plane_height(const plane * pl);
void adjust_coordinates(const struct faction *f, int *x, int *y, const struct plane *pl);

View file

@ -561,21 +561,15 @@ void write_race_reference(const race * rc, struct storage *store)
WRITE_TOK(store, rc ? rc->_name : "none");
}
variant read_race_reference(struct storage *store)
struct race * read_race_reference(struct storage *store)
{
variant result;
char zName[20];
READ_TOK(store, zName, sizeof(zName));
if (strcmp(zName, "none") == 0) {
result.v = NULL;
return result;
return NULL;
}
else {
result.v = rc_find_i(zName);
}
assert(result.v != NULL);
return result;
return rc_find_i(zName);
}
void register_race_function(race_func func, const char *name) {

View file

@ -263,7 +263,7 @@ extern "C" {
void write_race_reference(const struct race *rc,
struct storage *store);
variant read_race_reference(struct storage *store);
struct race *read_race_reference(struct storage *store);
const char *raceprefix(const struct unit *u);
void register_race_function(race_func, const char *);

View file

@ -745,11 +745,17 @@ int rtrees(const region * r, int ageclass)
int rsettrees(const region * r, int ageclass, int value)
{
if (!r->land)
if (!r->land) {
assert(value == 0);
}
else {
assert(value >= 0);
return r->land->trees[ageclass] = value;
if (value <= MAXTREES) {
return r->land->trees[ageclass] = value;
}
else {
r->land->trees[ageclass] = MAXTREES;
}
}
return 0;
}
@ -819,8 +825,8 @@ void free_land(land_region * lr)
lr->demands = d->next;
free(d);
}
if (lr->name)
free(lr->name);
free(lr->name);
free(lr->display);
free(lr);
}
@ -888,7 +894,6 @@ void free_region(region * r)
{
if (last == r)
last = NULL;
free(r->display);
if (r->land)
free_land(r->land);
@ -1082,7 +1087,6 @@ void terraform_region(region * r, const terrain_type * terrain)
terraform_resources(r);
if (!fval(terrain, LAND_REGION)) {
region_setinfo(r, NULL);
if (r->land) {
free_land(r->land);
r->land = NULL;
@ -1251,46 +1255,22 @@ int production(const region * r)
return p;
}
int resolve_region_coor(variant id, void *address)
void resolve_region(region *r)
{
region *r = findregion(id.sa[0], id.sa[1]);
if (r) {
*(region **)address = r;
return 0;
}
*(region **)address = NULL;
return -1;
resolve(RESOLVE_REGION | r->uid, r);
}
int resolve_region_id(variant id, void *address)
{
region *r = NULL;
if (id.i != 0) {
r = findregionbyid(id.i);
if (r == NULL) {
*(region **)address = NULL;
return -1;
}
}
*(region **)address = r;
return 0;
}
variant read_region_reference(gamedata *data)
int read_region_reference(gamedata * data, region **rp, resolve_fun fun)
{
struct storage * store = data->store;
variant result;
if (data->version < UIDHASH_VERSION) {
int n;
READ_INT(store, &n);
result.sa[0] = (short)n;
READ_INT(store, &n);
result.sa[1] = (short)n;
int id = 0;
READ_INT(store, &id);
*rp = findregionbyid(id);
if (*rp == NULL) {
ur_add(RESOLVE_REGION | id, (void **)rp, fun);
}
else {
READ_INT(store, &result.i);
}
return result;
return id;
}
void write_region_reference(const region * r, struct storage *store)
@ -1438,13 +1418,14 @@ faction *update_owners(region * r)
void region_setinfo(struct region *r, const char *info)
{
free(r->display);
r->display = info ? strdup(info) : 0;
assert(r->land);
free(r->land->display);
r->land->display = (info && info[0]) ? strdup(info) : 0;
}
const char *region_getinfo(const region * r)
{
return r->display ? r->display : "";
return (r->land && r->land->display) ? r->land->display : "";
}
void region_setname(struct region *r, const char *name)

View file

@ -19,13 +19,17 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef H_KRNL_REGION
#define H_KRNL_REGION
#include <stddef.h>
#include <stdbool.h>
#include <util/resolve.h>
#include "types.h"
#include "direction.h"
#include <stddef.h>
#include <stdbool.h>
#define MAXLUXURIES 16 /* there must be no more than MAXLUXURIES kinds of luxury goods in any game */
#define MAXREGIONS 524287 /* must be prime for hashing. 262139 was a little small */
#define MAXTREES 100 * 1000 * 1000 /* bug 2360: some players are crazy */
/* FAST_CONNECT: regions are directly connected to neighbours, saves doing
a hash-access each time a neighbour is needed, 6 extra pointers per hex */
@ -95,7 +99,7 @@ extern "C" {
typedef struct land_region {
char *name;
/* TODO: demand kann nach Konvertierung entfernt werden. */
char *display;
demand *demands;
const struct item_type *herbtype;
short herbs;
@ -121,7 +125,6 @@ extern "C" {
int uid; /* a unique id */
int x, y;
struct plane *_plane; /* to access, use rplane(r) */
char *display;
int flags;
unsigned short age;
struct message_list *msgs;
@ -256,11 +259,10 @@ extern "C" {
int region_get_morale(const region * r);
void region_set_morale(region * r, int morale, int turn);
#define RESOLVE_REGION (TYP_REGION << 24)
void resolve_region(region *r);
void write_region_reference(const struct region *r, struct storage *store);
variant read_region_reference(struct gamedata *data);
int resolve_region_coor(variant id, void *address);
int resolve_region_id(variant id, void *address);
#define RESOLVE_REGION(version) ((version<UIDHASH_VERSION)?resolve_region_coor:resolve_region_id)
int read_region_reference(struct gamedata *data, region **rp, resolve_fun fun);
const char *regionname(const struct region *r, const struct faction *f);

View file

@ -78,10 +78,29 @@ static void test_region_getset_resource(CuTest *tc) {
test_cleanup();
}
static void test_trees(CuTest *tc) {
region *r;
test_setup();
r = test_create_region(0, 0, NULL);
rsettrees(r, 0, 1000);
rsettrees(r, 1, 2000);
rsettrees(r, 2, 3000);
CuAssertIntEquals(tc, 1000, rtrees(r, 0));
CuAssertIntEquals(tc, 2000, rtrees(r, 1));
CuAssertIntEquals(tc, 3000, rtrees(r, 2));
rsettrees(r, 0, MAXTREES);
CuAssertIntEquals(tc, MAXTREES, rtrees(r, 0));
rsettrees(r, 0, MAXTREES+100);
CuAssertIntEquals(tc, MAXTREES, rtrees(r, 0));
test_cleanup();
}
CuSuite *get_region_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_terraform);
SUITE_ADD_TEST(suite, test_trees);
SUITE_ADD_TEST(suite, test_region_getset_resource);
SUITE_ADD_TEST(suite, test_region_get_owner);
return suite;

View file

@ -24,11 +24,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "alchemy.h"
#include "alliance.h"
#include "ally.h"
#include "connection.h"
#include "building.h"
#include "connection.h"
#include "equipment.h"
#include "faction.h"
#include "group.h"
#include "item.h"
#include "magic.h"
#include "messages.h"
#include "move.h"
#include "objtypes.h"
@ -51,6 +53,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <attributes/attributes.h>
#include <attributes/key.h>
#include <triggers/timeout.h>
#include <triggers/shock.h>
/* util includes */
#include <util/assert.h>
@ -120,217 +123,6 @@ char *rns(FILE * f, char *c, size_t size)
return c;
}
static unit *unitorders(FILE * F, int enc, struct faction *f)
{
int i;
unit *u;
if (!f)
return NULL;
i = getid();
u = findunitg(i, NULL);
if (u && u->faction == f) {
order **ordp;
if (!fval(u, UFL_ORDERS)) {
/* alle wiederholbaren, langen befehle werden gesichert: */
fset(u, UFL_ORDERS);
u->old_orders = u->orders;
ordp = &u->old_orders;
while (*ordp) {
order *ord = *ordp;
keyword_t kwd = getkeyword(ord);
if (!is_repeated(kwd)) {
*ordp = ord->next;
ord->next = NULL;
free_order(ord);
}
else {
ordp = &ord->next;
}
}
}
else {
free_orders(&u->orders);
}
u->orders = 0;
ordp = &u->orders;
for (;;) {
const char *s;
/* Erst wenn wir sicher sind, dass kein Befehl
* eingegeben wurde, checken wir, ob nun eine neue
* Einheit oder ein neuer Spieler drankommt */
s = getbuf(F, enc);
if (s == NULL)
break;
if (s[0]) {
if (s[0] != '@') {
char token[64];
const char *stok = s;
stok = parse_token(&stok, token, sizeof(token));
if (stok) {
bool quit = false;
param_t param = findparam(stok, u->faction->locale);
switch (param) {
case P_UNIT:
case P_REGION:
quit = true;
break;
case P_FACTION:
case P_NEXT:
case P_GAMENAME:
/* these terminate the orders, so we apply extra checking */
if (strlen(stok) >= 3) {
quit = true;
break;
}
else {
quit = false;
}
break;
default:
break;
}
if (quit) {
break;
}
}
}
/* Nun wird der Befehl erzeut und eingehängt */
*ordp = parse_order(s, u->faction->locale);
if (*ordp) {
ordp = &(*ordp)->next;
}
else {
ADDMSG(&f->msgs, msg_message("parse_error", "unit command", u, s));
}
}
}
}
else {
return NULL;
}
return u;
}
static faction *factionorders(void)
{
faction *f = NULL;
int fid = getid();
f = findfaction(fid);
if (f != NULL && !fval(f, FFL_NPC)) {
char token[128];
const char *pass = gettoken(token, sizeof(token));
if (!checkpasswd(f, (const char *)pass)) {
log_debug("Invalid password for faction %s", itoa36(fid));
ADDMSG(&f->msgs, msg_message("wrongpasswd", "password", pass));
return 0;
}
/* Die Partei hat sich zumindest gemeldet, so dass sie noch
* nicht als untätig gilt */
f->lastorders = turn;
}
else {
log_debug("orders for invalid faction %s", itoa36(fid));
}
return f;
}
int readorders(const char *filename)
{
FILE *F = NULL;
const char *b;
int nfactions = 0;
struct faction *f = NULL;
F = fopen(filename, "r");
if (!F) {
perror(filename);
return -1;
}
log_info("reading orders from %s", filename);
/* TODO: recognize UTF8 BOM */
b = getbuf(F, enc_gamedata);
/* Auffinden der ersten Partei, und danach abarbeiten bis zur letzten
* Partei */
while (b) {
char token[128];
const struct locale *lang = f ? f->locale : default_locale;
param_t p;
const char *s;
init_tokens_str(b);
s = gettoken(token, sizeof(token));
p = findparam_block(s, lang, true);
switch (p) {
case P_GAMENAME:
case P_FACTION:
f = factionorders();
if (f) {
++nfactions;
}
b = getbuf(F, enc_gamedata);
break;
/* in factionorders wird nur eine zeile gelesen:
* diejenige mit dem passwort. Die befehle der units
* werden geloescht, und die Partei wird als aktiv
* vermerkt. */
case P_UNIT:
if (!f || !unitorders(F, enc_gamedata, f)) {
do {
b = getbuf(F, enc_gamedata);
if (!b) {
break;
}
init_tokens_str(b);
s = gettoken(token, sizeof(token));
p = (s && s[0] != '@') ? findparam(s, lang) : NOPARAM;
} while ((p != P_UNIT || !f) && p != P_FACTION && p != P_NEXT
&& p != P_GAMENAME);
}
break;
/* Falls in unitorders() abgebrochen wird, steht dort entweder eine neue
* Partei, eine neue Einheit oder das File-Ende. Das switch() wird erneut
* durchlaufen, und die entsprechende Funktion aufgerufen. Man darf buf
* auf alle Fälle nicht überschreiben! Bei allen anderen Einträgen hier
* muss buf erneut gefüllt werden, da die betreffende Information in nur
* einer Zeile steht, und nun die nächste gelesen werden muss. */
case P_NEXT:
f = NULL;
b = getbuf(F, enc_gamedata);
break;
default:
b = getbuf(F, enc_gamedata);
break;
}
}
fclose(F);
log_info("done reading orders for %d factions", nfactions);
return 0;
}
/* ------------------------------------------------------------- */
/* #define INNER_WORLD */
@ -375,7 +167,7 @@ race_t typus2race(unsigned char typus)
return NORACE;
}
static void read_alliances(struct gamedata *data)
static void read_alliances(gamedata *data)
{
storage *store = data->store;
char pbuf[8];
@ -397,8 +189,7 @@ static void read_alliances(struct gamedata *data)
READ_INT(store, &al->flags);
}
if (data->version >= ALLIANCELEADER_VERSION) {
read_reference(&al->_leader, data, read_faction_reference,
resolve_faction);
read_faction_reference(data, &al->_leader, NULL);
READ_INT(store, &id);
}
else {
@ -418,7 +209,6 @@ void read_planes(gamedata *data) {
READ_INT(store, &nread);
while (--nread >= 0) {
int id;
variant fno;
plane *pl;
READ_INT(store, &id);
@ -457,9 +247,10 @@ void read_planes(gamedata *data) {
else {
/* WATCHERS - eliminated in February 2016, ca. turn 966 */
if (data->version < NOWATCH_VERSION) {
fno = read_faction_reference(data);
while (fno.i) {
fno = read_faction_reference(data);
int fno;
READ_INT(data->store, &fno);
while (fno) {
READ_INT(data->store, &fno);
}
}
}
@ -490,7 +281,7 @@ void write_planes(storage *store) {
}
}
void write_alliances(struct gamedata *data)
void write_alliances(gamedata *data)
{
alliance *al = alliances;
while (al) {
@ -507,22 +298,7 @@ void write_alliances(struct gamedata *data)
WRITE_SECTION(data->store);
}
static int resolve_owner(variant id, void *address)
{
region_owner *owner = (region_owner *)address;
int result = 0;
faction *f = NULL;
if (id.i != 0) {
f = findfaction(id.i);
if (f == NULL) {
log_error("region has an invalid owner (%s)", itoa36(id.i));
}
}
owner->owner = f;
return result;
}
static void read_owner(struct gamedata *data, region_owner ** powner)
static void read_owner(gamedata *data, region_owner ** powner)
{
int since_turn;
@ -538,9 +314,7 @@ static void read_owner(struct gamedata *data, region_owner ** powner)
owner->flags = 0;
}
if (data->version >= OWNER_3_VERSION) {
int id;
READ_INT(data->store, &id);
owner->last_owner = id ? findfaction(id) : NULL;
read_faction_reference(data, &owner->last_owner, NULL);
}
else if (data->version >= OWNER_2_VERSION) {
int id;
@ -553,7 +327,7 @@ static void read_owner(struct gamedata *data, region_owner ** powner)
else {
owner->last_owner = NULL;
}
read_reference(owner, data, &read_faction_reference, &resolve_owner);
read_faction_reference(data, &owner->owner, NULL);
*powner = owner;
}
else {
@ -561,7 +335,7 @@ static void read_owner(struct gamedata *data, region_owner ** powner)
}
}
static void write_owner(struct gamedata *data, region_owner * owner)
static void write_owner(gamedata *data, region_owner * owner)
{
if (owner) {
faction *f;
@ -601,8 +375,7 @@ int current_turn(void)
return cturn;
}
static void
writeorder(struct gamedata *data, const struct order *ord,
static void writeorder(gamedata *data, const struct order *ord,
const struct locale *lang)
{
char obuf[1024];
@ -611,7 +384,58 @@ writeorder(struct gamedata *data, const struct order *ord,
WRITE_STR(data->store, obuf);
}
unit *read_unit(struct gamedata *data)
static void read_skills(gamedata *data, unit *u)
{
if (data->version < SKILLSORT_VERSION) {
for (;;) {
int n = NOSKILL, level, weeks;
skill_t sk;
READ_INT(data->store, &n);
sk = (skill_t)n;
if (sk == NOSKILL) break;
READ_INT(data->store, &level);
READ_INT(data->store, &weeks);
if (level) {
skill *sv = add_skill(u, sk);
sv->level = sv->old = (unsigned char)level;
sv->weeks = (unsigned char)weeks;
}
}
}
else {
int i;
READ_INT(data->store, &u->skill_size);
u->skills = malloc(sizeof(skill)*u->skill_size);
for (i = 0; i != u->skill_size; ++i) {
skill *sv = u->skills + i;
int val;
READ_INT(data->store, &val);
sv->id = (skill_t)val;
READ_INT(data->store, &sv->level);
sv->old = sv->level;
READ_INT(data->store, &sv->weeks);
}
}
}
static void write_skills(gamedata *data, const unit *u) {
int i;
skill_t sk = NOSKILL;
WRITE_INT(data->store, u->skill_size);
for (i = 0; i != u->skill_size; ++i) {
skill *sv = u->skills + i;
#ifndef NDEBUG
assert(sv->id > sk);
sk = sv->id;
assert(sv->weeks <= sv->level * 2 + 1);
#endif
WRITE_INT(data->store, sv->id);
WRITE_INT(data->store, sv->level);
WRITE_INT(data->store, sv->weeks);
}
}
unit *read_unit(gamedata *data)
{
unit *u;
const race *rc;
@ -758,20 +582,7 @@ unit *read_unit(struct gamedata *data)
set_order(&u->thisorder, NULL);
assert(u_race(u));
for (;;) {
int n = NOSKILL, level, weeks;
skill_t sk;
READ_INT(data->store, &n);
sk = (skill_t)n;
if (sk == NOSKILL) break;
READ_INT(data->store, &level);
READ_INT(data->store, &weeks);
if (level) {
skill *sv = add_skill(u, sk);
sv->level = sv->old = (unsigned char)level;
sv->weeks = (unsigned char)weeks;
}
}
read_skills(data, u);
read_items(data->store, &u->items);
READ_INT(data->store, &u->hp);
if (u->hp < u->number) {
@ -779,13 +590,14 @@ unit *read_unit(struct gamedata *data)
u->hp = u->number;
}
read_attribs(data, &u->attribs, u);
resolve_unit(u);
return u;
}
void write_unit(struct gamedata *data, const unit * u)
void write_unit(gamedata *data, const unit * u)
{
order *ord;
int i, p = 0;
int p = 0;
unsigned int flags = u->flags & UFL_SAVEMASK;
const race *irace = u_irace(u);
@ -833,18 +645,7 @@ void write_unit(struct gamedata *data, const unit * u)
WRITE_SECTION(data->store);
assert(u_race(u));
for (i = 0; i != u->skill_size; ++i) {
skill *sv = u->skills + i;
assert(sv->weeks <= sv->level * 2 + 1);
if (sv->level > 0) {
WRITE_INT(data->store, sv->id);
WRITE_INT(data->store, sv->level);
WRITE_INT(data->store, sv->weeks);
}
}
WRITE_INT(data->store, -1);
WRITE_SECTION(data->store);
write_skills(data, u);
write_items(data->store, u->items);
WRITE_SECTION(data->store);
if (u->hp == 0 && data->version < NORCSPELL_VERSION) {
@ -857,11 +658,24 @@ void write_unit(struct gamedata *data, const unit * u)
WRITE_SECTION(data->store);
}
static region *readregion(struct gamedata *data, int x, int y)
static void read_regioninfo(gamedata *data, const region *r, char *info, size_t len) {
if (lomem) {
READ_STR(data->store, NULL, 0);
}
else {
READ_STR(data->store, info, len);
if (unicode_utf8_trim(info) != 0) {
log_warning("trim region %d info to '%s'", r->uid, info);
}
}
}
static region *readregion(gamedata *data, int x, int y)
{
region *r = findregion(x, y);
const terrain_type *terrain;
char name[NAMESIZE];
char info[DISPLAYSIZE];
int uid = 0;
int n;
@ -887,16 +701,11 @@ static region *readregion(struct gamedata *data, int x, int y)
}
r->land = 0;
}
if (lomem) {
READ_STR(data->store, NULL, 0);
if (data->version < LANDDISPLAY_VERSION) {
read_regioninfo(data, r, info, sizeof(info));
}
else {
char info[DISPLAYSIZE];
READ_STR(data->store, info, sizeof(info));
if (unicode_utf8_trim(info)!=0) {
log_warning("trim region %d info to '%s'", uid, info);
};
region_setinfo(r, info);
info[0] = '\0';
}
READ_STR(data->store, name, sizeof(name));
@ -913,15 +722,19 @@ static region *readregion(struct gamedata *data, int x, int y)
if (fval(r->terrain, LAND_REGION)) {
r->land = calloc(1, sizeof(land_region));
READ_STR(data->store, name, sizeof(name));
if (unicode_utf8_trim(name)!=0) {
log_warning("trim region %d name to '%s'", uid, name);
};
if (unicode_utf8_trim(name) != 0) {
log_warning("trim region %d name to '%s'", uid, name);
};
r->land->name = strdup(name);
}
if (r->land) {
int i;
rawmaterial **pres = &r->resources;
if (data->version >= LANDDISPLAY_VERSION) {
read_regioninfo(data, r, info, sizeof(info));
}
region_setinfo(r, info);
READ_INT(data->store, &i);
if (i < 0) {
log_error("number of trees in %s is %d.", regionname(r, NULL), i);
@ -999,7 +812,11 @@ static region *readregion(struct gamedata *data, int x, int y)
READ_INT(data->store, &n);
rsetmoney(r, n);
}
else {
if (info[0]) {
log_error("%s %d has a description: %s", r->terrain->_name, r->uid, info);
}
}
assert(r->terrain != NULL);
if (r->land) {
@ -1038,27 +855,27 @@ region *read_region(gamedata *data)
READ_INT(store, &x);
READ_INT(store, &y);
r = readregion(data, x, y);
resolve_region(r);
return r;
}
void writeregion(struct gamedata *data, const region * r)
void writeregion(gamedata *data, const region * r)
{
assert(r);
assert(data);
WRITE_INT(data->store, r->uid);
WRITE_STR(data->store, region_getinfo(r));
WRITE_TOK(data->store, r->terrain->_name);
WRITE_INT(data->store, r->flags & RF_SAVEMASK);
WRITE_INT(data->store, r->age);
WRITE_SECTION(data->store);
if (fval(r->terrain, LAND_REGION)) {
if (r->land) {
const item_type *rht;
struct demand *demand;
rawmaterial *res = r->resources;
assert(r->land);
WRITE_STR(data->store, (const char *)r->land->name);
WRITE_STR(data->store, region_getinfo(r));
assert(rtrees(r, 0) >= 0);
assert(rtrees(r, 1) >= 0);
assert(rtrees(r, 2) >= 0);
@ -1094,11 +911,9 @@ void writeregion(struct gamedata *data, const region * r)
}
WRITE_TOK(data->store, "end");
WRITE_SECTION(data->store);
#if RELEASE_VERSION>=REGIONOWNER_VERSION
WRITE_INT(data->store, region_get_morale(r));
write_owner(data, r->land->ownership);
WRITE_SECTION(data->store);
#endif
}
write_attribs(data->store, r->attribs, r);
WRITE_SECTION(data->store);
@ -1112,33 +927,6 @@ void write_region(gamedata *data, const region *r)
writeregion(data, r);
}
static ally **addally(const faction * f, ally ** sfp, int aid, int state)
{
struct faction *af = findfaction(aid);
ally *sf;
state &= ~HELP_OBSERVE;
state &= ~HELP_TRAVEL;
state &= HelpMask();
if (state == 0)
return sfp;
while (*sfp) {
sfp = &(*sfp)->next;
}
sf = ally_add(sfp, af);
if (!sf->faction) {
variant id;
id.i = aid;
ur_add(id, &sf->faction, resolve_faction);
}
sf->status = state & HELP_ALL;
return &sf->next;
}
int get_spell_level_faction(const spell * sp, void * cbdata)
{
static spellbook * common = 0;
@ -1215,9 +1003,8 @@ void _test_write_password(gamedata *data, const faction *f) {
write_password(data, f);
}
faction *read_faction(struct gamedata * data)
faction *read_faction(gamedata * data)
{
ally **sfp;
int planes, n;
faction *f;
char name[DISPLAYSIZE];
@ -1346,28 +1133,17 @@ faction *read_faction(struct gamedata * data)
/* mistakes were made in the past*/
f->options &= ~want(O_JSON);
}
sfp = &f->allies;
for (;;) {
int aid = 0;
READ_INT(data->store, &aid);
if (aid > 0) {
int state;
READ_INT(data->store, &state);
sfp = addally(f, sfp, aid, state);
}
else {
break;
}
}
read_allies(data, f);
read_groups(data, f);
f->spellbook = 0;
if (data->version >= REGIONOWNER_VERSION) {
read_spellbook(FactionSpells() ? &f->spellbook : 0, data, get_spell_level_faction, (void *)f);
}
resolve_faction(f);
return f;
}
void write_faction(struct gamedata *data, const faction * f)
void write_faction(gamedata *data, const faction * f)
{
ally *sf;
ursprung *ur;
@ -1448,7 +1224,7 @@ static int cb_sb_maxlevel(spellbook_entry *sbe, void *cbdata) {
int readgame(const char *filename)
{
int n;
int n, stream_version;
char path[MAX_PATH];
gamedata gdata = { 0 };
storage store;
@ -1465,11 +1241,8 @@ int readgame(const char *filename)
return -1;
}
sz = fread(&gdata.version, sizeof(int), 1, F);
if (sz != sizeof(int) || gdata.version >= INTPAK_VERSION) {
int stream_version;
size_t sz = fread(&stream_version, sizeof(int), 1, F);
assert((sz == 1 && stream_version == STREAM_VERSION) || !"unsupported data format");
}
sz = fread(&stream_version, sizeof(int), 1, F);
assert((sz == 1 && stream_version == STREAM_VERSION) || !"unsupported data format");
assert(gdata.version >= MIN_VERSION || !"unsupported data format");
assert(gdata.version <= MAX_VERSION || !"unsupported data format");
@ -1538,7 +1311,8 @@ struct building *read_building(gamedata *data) {
log_error("building too big: %s (%s size %d of %d), fixing.", buildingname(b), b->type->_name, b->size, b->type->maxsize);
b->size = b->type->maxsize;
}
return b;
resolve_building(b);
return b;
}
void write_ship(gamedata *data, const ship *sh)
@ -1556,7 +1330,7 @@ void write_ship(gamedata *data, const ship *sh)
write_attribs(store, sh->attribs, sh);
}
ship *read_ship(struct gamedata *data)
ship *read_ship(gamedata *data)
{
char name[DISPLAYSIZE];
ship *sh;
@ -1607,7 +1381,78 @@ ship *read_ship(struct gamedata *data)
}
int read_game(gamedata *data) {
static void fix_familiars(void) {
region *r;
for (r = regions; r; r = r->next) {
unit * u;
for (u = r->units; u; u = u->next) {
if (u->_race != u->faction->race && (u->_race->flags & RCF_FAMILIAR)) {
/* unit is potentially a familiar */
attrib * a = a_find(u->attribs, &at_mage);
attrib * am = a_find(u->attribs, &at_familiarmage);
if (am) {
sc_mage *mage = a ? (sc_mage *)a->data.v : NULL;
/* a familiar */
if (!mage) {
log_error("%s seems to be a familiar with no magic.",
unitname(u));
mage = create_mage(u, M_GRAY);
}
if (!mage->spellbook) {
char eqname[32];
equipment *eq;
snprintf(eqname, sizeof(eqname), "fam_%s", u->_race->_name);
eq = get_equipment(eqname);
if (eq && eq->spells) {
log_error("%s seems to be a familiar with no spells.",
unitname(u));
/* magical familiar, no spells */
equip_unit_mask(u, eq, EQUIP_SPELLS);
}
}
}
else if (a) {
/* not a familiar, but magical */
attrib * ae = a_find(u->attribs, &at_eventhandler);
if (ae) {
trigger **tlist;
tlist = get_triggers(ae, "destroy");
if (tlist) {
trigger *t;
unit *um = NULL;
for (t = *tlist; t; t = t->next) {
if (t->type == &tt_shock) {
um = (unit *)t->data.v;
break;
}
}
if (um) {
attrib *af = a_find(um->attribs, &at_familiar);
log_error("%s seems to be a broken familiar of %s.",
unitname(u), unitname(um));
if (af) {
unit * uf = (unit *)af->data.v;
log_error("%s already has a familiar: %s.",
unitname(um), unitname(uf));
}
else {
set_familiar(um, u);
}
}
else {
log_error("%s seems to be a broken familiar with no trigger.", unitname(u));
}
}
}
}
}
}
}
}
int read_game(gamedata *data)
{
int p, nread;
faction *f, **fp;
region *r;
@ -1671,6 +1516,9 @@ int read_game(gamedata *data) {
/* Burgen */
READ_INT(store, &p);
if (p > 0 && !r->land) {
log_error("%s, uid=%d has %d buildings", regionname(r, NULL), r->uid, p);
}
bp = &r->buildings;
while (--p >= 0) {
@ -1726,10 +1574,6 @@ int read_game(gamedata *data) {
}
read_borders(data);
/* Unaufgeloeste Zeiger initialisieren */
log_debug("fixing unresolved references.");
resolve();
log_debug("updating area information for lighthouses.");
for (r = regions; r; r = r->next) {
if (r->flags & RF_LIGHTHOUSE) {
@ -1755,7 +1599,7 @@ int read_game(gamedata *data) {
else {
for (u = f->units; u; u = u->nextF) {
if (data->version < SPELL_LEVEL_VERSION) {
sc_mage *mage = get_mage(u);
sc_mage *mage = get_mage_depr(u);
if (mage) {
faction *f = u->faction;
int skl = effskill(u, SK_MAGIC, 0);
@ -1783,6 +1627,11 @@ int read_game(gamedata *data) {
}
}
}
if (data->version < FAMILIAR_FIX_VERSION) {
fix_familiars();
}
if (loadplane || maxregions >= 0) {
remove_empty_factions();
}

View file

@ -43,7 +43,6 @@ extern "C" {
/* TODO: is this *really* still in use? */
extern int enc_gamedata;
int readorders(const char *filename);
int readgame(const char *filename);
int writegame(const char *filename);

View file

@ -116,12 +116,15 @@ static void test_readwrite_region(CuTest * tc)
gamedata data;
storage store;
region *r;
const char * lipsum = "Lorem ipsum dolor sit amet";
test_setup();
r = test_create_region(0, 0, 0);
free(r->land->name);
r->land->name = strdup(" Hodor ");
CuAssertStrEquals(tc, " Hodor ", r->land->name);
region_setinfo(r, lipsum);
CuAssertStrEquals(tc, lipsum, r->land->display);
mstream_init(&data.strm);
gamedata_init(&data, &store, RELEASE_VERSION);
write_region(&data, r);
@ -132,6 +135,7 @@ static void test_readwrite_region(CuTest * tc)
r = read_region(&data);
CuAssertPtrNotNull(tc, r);
CuAssertStrEquals(tc, "Hodor", r->land->name);
CuAssertStrEquals(tc, lipsum, r->land->display);
regions = r;
mstream_done(&data.strm);

View file

@ -277,6 +277,10 @@ int gift_items(unit * u, int flags)
u->items = NULL;
break;
}
else if (!u3) {
/* pick a last-chance recipient: */
u3 = u2;
}
}
}
if (u->items && u3) {
@ -644,9 +648,7 @@ void a_writesiege(const attrib * a, const void *owner, struct storage *store)
int a_readsiege(attrib * a, void *owner, gamedata *data)
{
int result = read_reference(&a->data.v, data, read_building_reference,
resolve_building);
if (result == 0 && !a->data.v) {
if (read_building_reference(data, (building **)&a->data.v, NULL) <= 0) {
return AT_READ_FAIL;
}
return AT_READ_OK;
@ -747,25 +749,26 @@ void write_unit_reference(const unit * u, struct storage *store)
WRITE_INT(store, (u && u->region) ? u->no : 0);
}
int resolve_unit(variant id, void *address)
void resolve_unit(unit *u)
{
unit *u = NULL;
if (id.i != 0) {
u = findunit(id.i);
if (u == NULL) {
*(unit **)address = NULL;
return -1;
}
}
*(unit **)address = u;
return 0;
resolve(RESOLVE_UNIT | u->no, u);
}
variant read_unit_reference(gamedata *data)
int read_unit_reference(gamedata * data, unit **up, resolve_fun fun)
{
variant var;
READ_INT(data->store, &var.i);
return var;
int id;
READ_INT(data->store, &id);
if (id > 0) {
*up = findunit(id);
if (*up == NULL) {
*up = NULL;
ur_add(RESOLVE_UNIT | id, (void **)up, fun);
}
}
else {
*up = NULL;
}
return id;
}
int get_level(const unit * u, skill_t id)
@ -773,7 +776,7 @@ int get_level(const unit * u, skill_t id)
assert(id != NOSKILL);
if (skill_enabled(id)) {
skill *sv = u->skills;
while (sv != u->skills + u->skill_size) {
while (sv != u->skills + u->skill_size && sv->id <= id) {
if (sv->id == id) {
return sv->level;
}
@ -795,7 +798,7 @@ void set_level(unit * u, skill_t sk, int value)
remove_skill(u, sk);
return;
}
while (sv != u->skills + u->skill_size) {
while (sv != u->skills + u->skill_size && sv->id <= sk) {
if (sv->id == sk) {
sk_set(sv, value);
return;
@ -1060,7 +1063,7 @@ void clone_men(const unit * u, unit * dst, int n)
transfer_curse(u, dst, n);
}
set_number(dst, dst->number + n);
dst->hp += u->hp * dst->number / u->number;
dst->hp += u->hp * n / u->number;
assert(dst->hp >= dst->number);
/* TODO: Das ist schnarchlahm! und gehoert nicht hierhin */
a = a_find(dst->attribs, &at_effect);
@ -1186,35 +1189,39 @@ void set_number(unit * u, int count)
void remove_skill(unit * u, skill_t sk)
{
skill *sv = u->skills;
for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
int i;
skill *sv;
for (i = 0; i != u->skill_size; ++i) {
sv = u->skills + i;
if (sv->id == sk) {
skill *sl = u->skills + u->skill_size - 1;
if (sl != sv) {
*sv = *sl;
}
memmove(sv, sv + 1, (u->skill_size - i - 1) * sizeof(skill));
--u->skill_size;
return;
}
}
}
skill *add_skill(unit * u, skill_t id)
skill *add_skill(unit * u, skill_t sk)
{
skill *sv = u->skills;
#ifndef NDEBUG
for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
assert(sv->id != id);
skill *sv;
int i;
for (i=0; i != u->skill_size; ++i) {
sv = u->skills+i;
if (sv->id >= sk) break;
}
u->skills = realloc(u->skills, (1 + u->skill_size) * sizeof(skill));
sv = u->skills + i;
if (i < u->skill_size) {
assert(sv->id != sk);
memmove(sv + 1, sv, sizeof(skill) * (u->skill_size - i));
}
#endif
++u->skill_size;
u->skills = realloc(u->skills, u->skill_size * sizeof(skill));
sv = (u->skills + u->skill_size - 1);
sv->level = 0;
sv->weeks = 1;
sv->old = 0;
sv->id = id;
if (id == SK_MAGIC && u->faction && !fval(u->faction, FFL_NPC)) {
sv->id = sk;
if (sk == SK_MAGIC && u->faction && !fval(u->faction, FFL_NPC)) {
assert(u->number <= 1);
assert(max_magicians(u->faction) >= u->number);
}
@ -1224,9 +1231,10 @@ skill *add_skill(unit * u, skill_t id)
skill *unit_skill(const unit * u, skill_t sk)
{
skill *sv = u->skills;
while (sv != u->skills + u->skill_size) {
if (sv->id == sk)
while (sv != u->skills + u->skill_size && sv->id <= sk) {
if (sv->id == sk) {
return sv;
}
++sv;
}
return NULL;
@ -1235,7 +1243,7 @@ skill *unit_skill(const unit * u, skill_t sk)
bool has_skill(const unit * u, skill_t sk)
{
skill *sv = u->skills;
while (sv != u->skills + u->skill_size) {
while (sv != u->skills + u->skill_size && sv->id <= sk) {
if (sv->id == sk) {
return (sv->level > 0);
}
@ -1484,14 +1492,6 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace,
if (f) {
assert(faction_alive(f));
u_setfaction(u, f);
if (f->locale) {
order *deford = default_order(f->locale);
if (deford) {
set_order(&u->thisorder, NULL);
addlist(&u->orders, deford);
}
}
}
set_number(u, number);
@ -1814,7 +1814,7 @@ void u_setrace(struct unit *u, const struct race *rc)
void unit_add_spell(unit * u, sc_mage * m, struct spell * sp, int level)
{
sc_mage *mage = m ? m : get_mage(u);
sc_mage *mage = m ? m : get_mage_depr(u);
if (!mage) {
log_debug("adding new spell %s to a previously non-mage unit %s\n", sp->sname, unitname(u));
@ -1828,7 +1828,7 @@ void unit_add_spell(unit * u, sc_mage * m, struct spell * sp, int level)
struct spellbook * unit_get_spellbook(const struct unit * u)
{
sc_mage * mage = get_mage(u);
sc_mage * mage = get_mage_depr(u);
if (mage) {
if (mage->spellbook) {
return mage->spellbook;

View file

@ -19,7 +19,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef H_KRNL_UNIT_H
#define H_KRNL_UNIT_H
#include <util/variant.h>
#include <util/resolve.h>
#include "types.h"
#include "skills.h"
#include <stddef.h>
@ -182,12 +182,13 @@ extern "C" {
#define GIFT_FRIENDS 1<<1
#define GIFT_PEASANTS 1<<2
int gift_items(struct unit *u, int flags);
void make_zombie(unit * u);
void make_zombie(struct unit * u);
/* see resolve.h */
int resolve_unit(variant data, void *address);
#define RESOLVE_UNIT (TYP_UNIT << 24)
void resolve_unit(struct unit *u);
void write_unit_reference(const struct unit *u, struct storage *store);
variant read_unit_reference(struct gamedata *data);
int read_unit_reference(struct gamedata * data, struct unit **up, resolve_fun fun);
bool leave(struct unit *u, bool force);
bool can_leave(struct unit *u);

View file

@ -31,7 +31,7 @@ static void test_remove_empty_units(CuTest *tc) {
unit *u;
int uid;
test_cleanup();
test_setup();
test_create_world();
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
@ -48,7 +48,7 @@ static void test_remove_empty_units_in_region(CuTest *tc) {
unit *u;
int uid;
test_cleanup();
test_setup();
test_create_world();
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
@ -69,7 +69,7 @@ static void test_remove_units_without_faction(CuTest *tc) {
unit *u;
int uid;
test_cleanup();
test_setup();
test_create_world();
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
@ -85,7 +85,7 @@ static void test_remove_units_with_dead_faction(CuTest *tc) {
unit *u;
int uid;
test_cleanup();
test_setup();
test_create_world();
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
@ -101,7 +101,7 @@ static void test_scale_number(CuTest *tc) {
unit *u;
const struct potion_type *ptype;
test_cleanup();
test_setup();
test_create_world();
ptype = new_potiontype(it_get_or_create(rt_get_or_create("hodor")), 1);
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
@ -122,7 +122,7 @@ static void test_scale_number(CuTest *tc) {
static void test_unit_name(CuTest *tc) {
unit *u;
test_cleanup();
test_setup();
test_create_world();
u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0));
renumber_unit(u, 666);
@ -274,7 +274,7 @@ static void test_skill_familiar(CuTest *tc) {
CuAssertIntEquals(tc, 6, effskill(mag, SK_PERCEPTION, 0));
/* make them mage and familiar to each other */
CuAssertIntEquals(tc, true, create_newfamiliar(mag, fam));
create_newfamiliar(mag, fam);
/* when they are in the same region, the mage gets half their skill as a bonus */
CuAssertIntEquals(tc, 6, effskill(fam, SK_PERCEPTION, 0));
@ -287,33 +287,11 @@ static void test_skill_familiar(CuTest *tc) {
test_cleanup();
}
static void test_age_familiar(CuTest *tc) {
unit *mag, *fam;
test_cleanup();
mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
fam = test_create_unit(mag->faction, test_create_region(0, 0, 0));
CuAssertPtrEquals(tc, 0, get_familiar(mag));
CuAssertPtrEquals(tc, 0, get_familiar_mage(fam));
CuAssertIntEquals(tc, true, create_newfamiliar(mag, fam));
CuAssertPtrEquals(tc, fam, get_familiar(mag));
CuAssertPtrEquals(tc, mag, get_familiar_mage(fam));
a_age(&fam->attribs, fam);
a_age(&mag->attribs, mag);
CuAssertPtrEquals(tc, fam, get_familiar(mag));
CuAssertPtrEquals(tc, mag, get_familiar_mage(fam));
set_number(fam, 0);
a_age(&mag->attribs, mag);
CuAssertPtrEquals(tc, 0, get_familiar(mag));
test_cleanup();
}
static void test_inside_building(CuTest *tc) {
unit *u;
building *b;
test_cleanup();
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
b = test_create_building(u->region, 0);
@ -334,9 +312,50 @@ static void test_inside_building(CuTest *tc) {
test_cleanup();
}
static void test_skills(CuTest *tc) {
unit *u;
skill *sv;
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
sv = add_skill(u, SK_ALCHEMY);
CuAssertPtrNotNull(tc, sv);
CuAssertPtrEquals(tc, sv, u->skills);
CuAssertIntEquals(tc, 1, u->skill_size);
CuAssertIntEquals(tc, SK_ALCHEMY, sv->id);
CuAssertIntEquals(tc, 0, sv->level);
CuAssertIntEquals(tc, 1, sv->weeks);
CuAssertIntEquals(tc, 0, sv->old);
sv = add_skill(u, SK_BUILDING);
CuAssertPtrNotNull(tc, sv);
CuAssertIntEquals(tc, 2, u->skill_size);
CuAssertIntEquals(tc, SK_ALCHEMY, u->skills[0].id);
CuAssertIntEquals(tc, SK_BUILDING, u->skills[1].id);
sv = add_skill(u, SK_LONGBOW);
CuAssertPtrNotNull(tc, sv);
CuAssertPtrEquals(tc, sv, unit_skill(u, SK_LONGBOW));
CuAssertIntEquals(tc, 3, u->skill_size);
CuAssertIntEquals(tc, SK_ALCHEMY, u->skills[0].id);
CuAssertIntEquals(tc, SK_LONGBOW, u->skills[1].id);
CuAssertIntEquals(tc, SK_BUILDING, u->skills[2].id);
CuAssertTrue(tc, !has_skill(u, SK_LONGBOW));
set_level(u, SK_LONGBOW, 1);
CuAssertTrue(tc, has_skill(u, SK_LONGBOW));
remove_skill(u, SK_LONGBOW);
CuAssertIntEquals(tc, SK_BUILDING, u->skills[1].id);
CuAssertIntEquals(tc, 2, u->skill_size);
remove_skill(u, SK_LONGBOW);
CuAssertIntEquals(tc, SK_BUILDING, u->skills[1].id);
CuAssertIntEquals(tc, 2, u->skill_size);
remove_skill(u, SK_BUILDING);
CuAssertIntEquals(tc, SK_ALCHEMY, u->skills[0].id);
CuAssertIntEquals(tc, 1, u->skill_size);
CuAssertTrue(tc, !has_skill(u, SK_LONGBOW));
test_cleanup();
}
static void test_limited_skills(CuTest *tc) {
unit *u;
test_cleanup();
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
CuAssertIntEquals(tc, false, has_limited_skills(u));
set_level(u, SK_ENTERTAINMENT, 1);
@ -548,6 +567,27 @@ static void test_clone_men(CuTest *tc) {
test_cleanup();
}
static void test_transfermen(CuTest *tc) {
unit *u1, *u2;
region *r;
faction *f;
test_setup();
r = test_create_region(0, 0, NULL);
f = test_create_faction(NULL);
u1 = test_create_unit(f, r);
scale_number(u1, 3500);
u2 = test_create_unit(f, r);
scale_number(u2, 3500);
CuAssertIntEquals(tc, 70000, u1->hp);
CuAssertIntEquals(tc, 70000, u2->hp);
transfermen(u1, u2, u1->number);
CuAssertIntEquals(tc, 7000, u2->number);
CuAssertIntEquals(tc, 140000, u2->hp);
CuAssertIntEquals(tc, 0, u1->number);
CuAssertIntEquals(tc, 0, u1->hp);
test_cleanup();
}
CuSuite *get_unit_suite(void)
{
CuSuite *suite = CuSuiteNew();
@ -557,6 +597,7 @@ CuSuite *get_unit_suite(void)
SUITE_ADD_TEST(suite, test_unit_name_from_race);
SUITE_ADD_TEST(suite, test_update_monster_name);
SUITE_ADD_TEST(suite, test_clone_men);
SUITE_ADD_TEST(suite, test_transfermen);
SUITE_ADD_TEST(suite, test_remove_unit);
SUITE_ADD_TEST(suite, test_remove_empty_units);
SUITE_ADD_TEST(suite, test_remove_units_without_faction);
@ -568,8 +609,8 @@ CuSuite *get_unit_suite(void)
SUITE_ADD_TEST(suite, test_skillmod);
SUITE_ADD_TEST(suite, test_skill_hunger);
SUITE_ADD_TEST(suite, test_skill_familiar);
SUITE_ADD_TEST(suite, test_age_familiar);
SUITE_ADD_TEST(suite, test_inside_building);
SUITE_ADD_TEST(suite, test_skills);
SUITE_ADD_TEST(suite, test_limited_skills);
SUITE_ADD_TEST(suite, test_renumber_unit);
SUITE_ADD_TEST(suite, test_name_unit);

View file

@ -5,7 +5,7 @@
#ifndef ERESSEA_VERSION
/* the version number, if it was not passed to make with -D */
#define ERESSEA_VERSION "3.13.0"
#define ERESSEA_VERSION "3.14.0"
#endif
const char *eressea_version(void) {

View file

@ -960,7 +960,7 @@ static void add_spells(equipment * eq, xmlNodeSetPtr nsetItems)
equipment_addspell(eq, name, level);
}
else {
log_error("spell '%s' for equipment-set '%s' has no level\n", name, eq->name);
log_error("spell '%s' for equipment-set '%s' has no level\n", name, equipment_name(eq));
}
xmlFree(propValue);
}
@ -980,7 +980,7 @@ static void add_skills(equipment * eq, xmlNodeSetPtr nsetSkills)
assert(propValue != NULL);
sk = findskill((const char *)propValue);
if (sk == NOSKILL) {
log_error("unknown skill '%s' in equipment-set %s\n", (const char *)propValue, eq->name);
log_error("unknown skill '%s' in equipment-set %s\n", (const char *)propValue, equipment_name(eq));
xmlFree(propValue);
}
else {
@ -1044,7 +1044,7 @@ add_subsets(xmlDocPtr doc, equipment * eq, xmlNodeSetPtr nsetSubsets)
}
}
if (totalChance > 1.0f) {
log_error("total chance exceeds 1.0: %f in equipment set %s.\n", totalChance, eq->name);
log_error("total chance exceeds 1.0: %f in equipment set %s.\n", totalChance, equipment_name(eq));
}
}
xmlXPathFreeObject(xpathResult);
@ -1069,9 +1069,13 @@ static int parse_equipment(xmlDocPtr doc)
xmlChar *propName = xmlGetProp(node, BAD_CAST "name");
if (propName != NULL) {
equipment *eq = get_or_create_equipment((const char *)propName);
equipment *eq = get_equipment((const char *)propName);
xmlXPathObjectPtr xpathResult;
if (!eq) {
eq = create_equipment((const char *)propName);
}
xpath->node = node;
xpathResult = xmlXPathEvalExpression(BAD_CAST "callback", xpath);

View file

@ -2,6 +2,7 @@
#include <kernel/config.h>
#include "keyword.h"
#include <util/bsdstring.h>
#include <util/language.h>
#include <util/umlaut.h>
#include <util/log.h>
@ -20,7 +21,7 @@ const char * keyword(keyword_t kwd)
if (!result[0]) {
strcpy(result, "keyword::");
}
strcpy(result + 9, keywords[kwd]);
strlcpy(result + 9, keywords[kwd], sizeof(result) - 9);
return result;
}

View file

@ -1559,11 +1559,11 @@ int display_cmd(unit * u, struct order *ord)
break;
case P_REGION:
if (u->faction != region_get_owner(r)) {
if (!r->land || u->faction != region_get_owner(r)) {
cmistake(u, ord, 147, MSG_EVENT);
break;
}
s = &r->display;
s = &r->land->display;
break;
default:
@ -3007,10 +3007,100 @@ int checkunitnumber(const faction * f, int add)
return 0;
}
void maketemp_cmd(unit *u, order **olist)
{
order *makeord;
int err = checkunitnumber(u->faction, 1);
makeord = *olist;
if (err) {
if (err == 1) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, makeord,
"too_many_units_in_alliance",
"allowed", maxunits(u->faction)));
}
else {
ADDMSG(&u->faction->msgs,
msg_feedback(u, makeord,
"too_many_units_in_faction",
"allowed", maxunits(u->faction)));
}
*olist = makeord->next;
makeord->next = NULL;
free_order(makeord);
while (*olist) {
keyword_t kwd;
order * ord = *olist;
*olist = ord->next;
ord->next = NULL;
kwd = getkeyword(ord);
free_order(ord);
if (kwd == K_END) {
break;
}
}
}
else {
char token[128];
const char *s;
int alias;
ship *sh;
unit *u2;
order **ordp, **oinsert;
#ifndef NDEBUG
keyword_t kwd = init_order(makeord);
assert(kwd == K_MAKETEMP);
#endif
alias = getid();
s = gettoken(token, sizeof(token));
if (s && s[0] == '\0') {
/* empty name? => generate one */
s = NULL;
}
u2 = create_unit(u->region, u->faction, 0, u->faction->race, alias, s, u);
fset(u2, UFL_ISNEW);
a_add(&u2->attribs, a_new(&at_alias))->data.i = alias;
sh = leftship(u);
if (sh) {
set_leftship(u2, sh);
}
setstatus(u2, u->status);
/* copy orders until K_END from u to u2 */
ordp = &makeord->next;
oinsert = &u2->orders;
while (*ordp) {
order *ord = *ordp;
*ordp = ord->next;
if (getkeyword(ord) == K_END) {
ord->next = NULL;
free_order(ord);
break;
}
*oinsert = ord;
oinsert = &ord->next;
*oinsert = NULL;
}
*olist = *ordp;
makeord->next = NULL;
free_order(makeord);
if (!u2->orders) {
order *deford = default_order(u2->faction->locale);
if (deford) {
set_order(&u2->thisorder, NULL);
addlist(&u2->orders, deford);
}
}
}
}
void new_units(void)
{
region *r;
unit *u, *u2;
unit *u;
/* neue einheiten werden gemacht und ihre befehle (bis zum "ende" zu
* ihnen rueberkopiert, damit diese einheiten genauso wie die alten
@ -3028,73 +3118,13 @@ void new_units(void)
}
while (*ordp) {
order *makeord = *ordp;
if (getkeyword(makeord) == K_MAKETEMP) {
char token[128], *name = NULL;
const char *s;
int alias;
ship *sh;
order **newordersp;
int err = checkunitnumber(u->faction, 1);
if (err) {
if (err == 1) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, makeord,
"too_many_units_in_alliance",
"allowed", maxunits(u->faction)));
}
else {
ADDMSG(&u->faction->msgs,
msg_feedback(u, makeord,
"too_many_units_in_faction",
"allowed", maxunits(u->faction)));
}
ordp = &makeord->next;
while (*ordp) {
order *ord = *ordp;
if (getkeyword(ord) == K_END)
break;
*ordp = ord->next;
ord->next = NULL;
free_order(ord);
}
continue;
}
init_order(makeord);
alias = getid();
s = gettoken(token, sizeof(token));
if (s && s[0]) {
name = strdup(s);
}
u2 = create_unit(r, u->faction, 0, u->faction->race, alias, name, u);
if (name != NULL)
free(name); /* TODO: use a buffer on the stack instead? */
fset(u2, UFL_ISNEW);
a_add(&u2->attribs, a_new(&at_alias))->data.i = alias;
sh = leftship(u);
if (sh) {
set_leftship(u2, sh);
}
setstatus(u2, u->status);
ordp = &makeord->next;
newordersp = &u2->orders;
while (*ordp) {
order *ord = *ordp;
if (getkeyword(ord) == K_END)
break;
*ordp = ord->next;
ord->next = NULL;
*newordersp = ord;
newordersp = &ord->next;
}
order *ord = *ordp;
if (getkeyword(ord) == K_MAKETEMP) {
maketemp_cmd(u, ordp);
}
else {
ordp = &ord->next;
}
if (*ordp == makeord)
ordp = &makeord->next;
}
}
}
@ -3357,7 +3387,7 @@ static int faction_getmages(faction * f, unit ** results, int numresults)
for (u = f->units; u; u = u->nextF) {
if (u->number > 0) {
sc_mage *mage = get_mage(u);
sc_mage *mage = get_mage_depr(u);
if (mage) {
int level = effskill(u, SK_MAGIC, 0);
if (level > maxlevel) {
@ -3416,7 +3446,7 @@ static void update_spells(void)
show_new_spells(f, maxlevel, faction_get_spellbook(f));
for (i = 0; i != MAXMAGES && mages[i]; ++i) {
unit * u = mages[i];
sc_mage *mage = get_mage(u);
sc_mage *mage = get_mage_depr(u);
if (mage && mage->spellbook) {
int level = effskill(u, SK_MAGIC, 0);
show_new_spells(f, level, mage->spellbook);
@ -4254,7 +4284,6 @@ bool
cansee(const faction * f, const region * r, const unit * u, int modifier)
{
int stealth, rings;
unit *u2 = r->units;
if (u->faction == f || omniscient(f)) {
return true;
@ -4273,42 +4302,39 @@ cansee(const faction * f, const region * r, const unit * u, int modifier)
}
}
if (leftship(u))
return true;
while (u2 && u2->faction != f)
u2 = u2->next;
if (u2 == NULL)
return false;
/* simple visibility, just gotta have a unit in the region to see 'em */
if (is_guard(u) || usiege(u) || u->building || u->ship) {
/* simple visibility, just gotta have a viewer in the region to see 'em */
if (leftship(u) || is_guard(u) || usiege(u) || u->building || u->ship) {
return true;
}
rings = invisible(u, NULL);
stealth = eff_stealth(u, r) - modifier;
while (u2) {
if (rings < u->number || invisible(u, u2) < u->number) {
if (skill_enabled(SK_PERCEPTION)) {
int observation = effskill(u2, SK_PERCEPTION, 0);
unit *u2;
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->faction == f) {
if (rings < u->number || invisible(u, u2) < u->number) {
if (skill_enabled(SK_PERCEPTION)) {
int observation = effskill(u2, SK_PERCEPTION, 0);
if (observation >= stealth) {
if (observation >= stealth) {
return true;
}
}
else {
return true;
}
}
else {
return true;
}
}
/* find next unit in our faction */
do {
u2 = u2->next;
} while (u2 && u2->faction != f);
}
return false;
return (rings <= 0 && stealth <= 0);
}
bool cansee_ex(const faction * f, const region * r, const unit * u, int modifier, seen_mode mode)
{
UNUSED_ARG(mode);
return cansee(f, r, u, modifier);
}
bool cansee_unit(const unit * u, const unit * target, int modifier)

View file

@ -98,7 +98,9 @@ extern "C" {
void nmr_warnings(void);
bool cansee(const struct faction *f, const struct region *r,
bool cansee_ex(const struct faction * f, const struct region * r,
const struct unit * u, int modifier, seen_mode mode);
bool cansee(const struct faction * f, const struct region * r,
const struct unit *u, int modifier);
bool cansee_durchgezogen(const struct faction *f, const struct region *r,
const struct unit *u, int modifier);

View file

@ -227,7 +227,7 @@ static void test_display_cmd(CuTest *tc) {
ord = create_order(K_DISPLAY, f->locale, "%s Hodor", LOC(f->locale, parameters[P_REGION]));
CuAssertIntEquals(tc, 0, display_cmd(u, ord));
CuAssertPtrEquals(tc, NULL, r->display);
CuAssertPtrEquals(tc, NULL, r->land->display);
free_order(ord);
test_cleanup();
@ -428,6 +428,63 @@ static void test_unit_limit(CuTest * tc)
test_cleanup();
}
static void test_maketemp(CuTest * tc)
{
faction *f;
unit *u, *u2;
test_setup();
f = test_create_faction(NULL);
u = test_create_unit(f, test_create_region(0, 0, NULL));
u->orders = create_order(K_MAKETEMP, f->locale, "1");
u->orders->next = create_order(K_ENTERTAIN, f->locale, NULL);
u->orders->next->next = create_order(K_END, f->locale, NULL);
u->orders->next->next->next = create_order(K_TAX, f->locale, NULL);
new_units();
CuAssertIntEquals(tc, 2, f->num_units);
CuAssertPtrNotNull(tc, u2 = u->next);
CuAssertPtrNotNull(tc, u2->orders);
CuAssertPtrEquals(tc, NULL, u2->orders->next);
CuAssertIntEquals(tc, K_ENTERTAIN, getkeyword(u2->orders));
CuAssertPtrNotNull(tc, u->orders);
CuAssertPtrEquals(tc, NULL, u->orders->next);
CuAssertIntEquals(tc, K_TAX, getkeyword(u->orders));
test_cleanup();
}
static void test_maketemp_default_order(CuTest * tc)
{
faction *f;
unit *u, *u2;
test_setup();
config_set("orders.default", "work");
f = test_create_faction(NULL);
u = test_create_unit(f, test_create_region(0, 0, NULL));
new_units();
CuAssertIntEquals(tc, 1, f->num_units);
u->orders = create_order(K_MAKETEMP, f->locale, "1");
u->orders->next = create_order(K_END, f->locale, NULL);
u->orders->next->next = create_order(K_TAX, f->locale, NULL);
new_units();
CuAssertIntEquals(tc, 2, f->num_units);
CuAssertPtrNotNull(tc, u2 = u->next);
CuAssertPtrNotNull(tc, u2->orders);
CuAssertPtrEquals(tc, NULL, u2->orders->next);
CuAssertIntEquals(tc, K_WORK, getkeyword(u2->orders));
CuAssertPtrNotNull(tc, u->orders);
CuAssertPtrEquals(tc, NULL, u->orders->next);
CuAssertIntEquals(tc, K_TAX, getkeyword(u->orders));
test_cleanup();
}
static void test_limit_new_units(CuTest * tc)
{
faction *f;
@ -449,6 +506,8 @@ static void test_limit_new_units(CuTest * tc)
CuAssertPtrNotNull(tc, u->next);
CuAssertIntEquals(tc, 2, f->num_units);
CuAssertPtrEquals(tc, NULL, u->orders);
u->orders = create_order(K_MAKETEMP, f->locale, "1");
new_units();
CuAssertIntEquals(tc, 2, f->num_units);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_faction"));
@ -458,6 +517,8 @@ static void test_limit_new_units(CuTest * tc)
config_set("rules.limit.faction", "3");
config_set("rules.limit.alliance", "2");
CuAssertPtrEquals(tc, NULL, u->orders);
u->orders = create_order(K_MAKETEMP, f->locale, "1");
new_units();
CuAssertIntEquals(tc, 2, f->num_units);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance"));
@ -466,6 +527,8 @@ static void test_limit_new_units(CuTest * tc)
u = test_create_unit(test_create_faction(NULL), u->region);
setalliance(u->faction, al);
CuAssertPtrEquals(tc, NULL, u->orders);
u->orders = create_order(K_MAKETEMP, f->locale, "1");
new_units();
CuAssertIntEquals(tc, 2, f->num_units);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance"));
@ -1552,9 +1615,114 @@ static void test_armedmen(CuTest *tc) {
test_cleanup();
}
static void test_cansee(CuTest *tc) {
unit *u, *u2;
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
u2 = test_create_unit(test_create_faction(0), u->region);
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
set_level(u2, SK_STEALTH, 1);
CuAssertTrue(tc, !cansee(u->faction, u->region, u2, 0));
set_level(u, SK_PERCEPTION, 1);
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
test_cleanup();
}
static void test_cansee_spell(CuTest *tc) {
unit *u2;
faction *f;
test_setup();
f = test_create_faction(0);
u2 = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
CuAssertTrue(tc, cansee_ex(f, u2->region, u2, 0, seen_spell));
CuAssertTrue(tc, cansee_ex(f, u2->region, u2, 0, seen_battle));
set_level(u2, SK_STEALTH, 1);
CuAssertTrue(tc, !cansee_ex(f, u2->region, u2, 0, seen_spell));
CuAssertTrue(tc, cansee_ex(f, u2->region, u2, 1, seen_spell));
CuAssertTrue(tc, cansee_ex(f, u2->region, u2, 1, seen_battle));
test_cleanup();
}
static void test_cansee_ring(CuTest *tc) {
unit *u, *u2;
item_type *itype[2];
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
u2 = test_create_unit(test_create_faction(0), u->region);
scale_number(u2, 2);
itype[0] = test_create_itemtype("roi");
itype[1] = test_create_itemtype("aots");
CuAssertPtrNotNull(tc, get_resourcetype(R_RING_OF_INVISIBILITY));
CuAssertPtrEquals(tc, itype[0]->rtype, (void *)get_resourcetype(R_RING_OF_INVISIBILITY));
CuAssertPtrNotNull(tc, get_resourcetype(R_AMULET_OF_TRUE_SEEING));
CuAssertPtrEquals(tc, itype[1]->rtype, (void *)get_resourcetype(R_AMULET_OF_TRUE_SEEING));
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
/* a single ring is not enough to hide two people */
i_change(&u2->items, itype[0], 1);
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
/* two rings can hide two people */
i_change(&u2->items, itype[0], 1);
CuAssertTrue(tc, !cansee(u->faction, u->region, u2, 0));
/* one amulet negates one of the two rings */
i_change(&u->items, itype[1], 1);
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
test_cleanup();
}
static void test_cansee_sphere(CuTest *tc) {
unit *u, *u2;
item_type *itype[2];
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
u2 = test_create_unit(test_create_faction(0), u->region);
itype[0] = test_create_itemtype("sphereofinv");
itype[1] = test_create_itemtype("aots");
CuAssertPtrNotNull(tc, get_resourcetype(R_SPHERE_OF_INVISIBILITY));
CuAssertPtrEquals(tc, itype[0]->rtype, (void *)get_resourcetype(R_SPHERE_OF_INVISIBILITY));
CuAssertPtrNotNull(tc, get_resourcetype(R_AMULET_OF_TRUE_SEEING));
CuAssertPtrEquals(tc, itype[1]->rtype, (void *)get_resourcetype(R_AMULET_OF_TRUE_SEEING));
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
/* a single sphere can hide 100 people */
scale_number(u2, 100);
i_change(&u2->items, itype[0], 1);
CuAssertTrue(tc, !cansee(u->faction, u->region, u2, 0));
/* one single amulet negates it? */
i_change(&u->items, itype[1], 1);
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
/* number of people inside the sphere does not matter? */
scale_number(u2, 99);
CuAssertTrue(tc, cansee(u->faction, u->region, u2, 0));
test_cleanup();
}
CuSuite *get_laws_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_maketemp_default_order);
SUITE_ADD_TEST(suite, test_maketemp);
SUITE_ADD_TEST(suite, test_nmr_warnings);
SUITE_ADD_TEST(suite, test_ally_cmd);
SUITE_ADD_TEST(suite, test_name_cmd);
@ -1618,6 +1786,10 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_immigration);
SUITE_ADD_TEST(suite, test_demon_hunger);
SUITE_ADD_TEST(suite, test_armedmen);
SUITE_ADD_TEST(suite, test_cansee);
SUITE_ADD_TEST(suite, test_cansee_ring);
SUITE_ADD_TEST(suite, test_cansee_sphere);
SUITE_ADD_TEST(suite, test_cansee_spell);
return suite;
}

View file

@ -42,6 +42,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/building.h>
#include <kernel/callbacks.h>
#include <kernel/curse.h>
#include <kernel/equipment.h>
#include <kernel/faction.h>
#include <kernel/item.h>
#include <kernel/messages.h>
@ -322,17 +323,27 @@ attrib_type at_mage = {
bool is_mage(const unit * u)
{
return get_mage(u) != NULL;
return get_mage_depr(u) != NULL;
}
sc_mage *get_mage(const unit * u)
{
attrib *a = a_find(u->attribs, &at_mage);
if (a) {
return (sc_mage *)a->data.v;
}
return NULL;
}
sc_mage *get_mage_depr(const unit * u)
{
if (has_skill(u, SK_MAGIC)) {
attrib *a = a_find(u->attribs, &at_mage);
if (a)
return a->data.v;
if (a) {
return (sc_mage *)a->data.v;
}
}
return (sc_mage *)NULL;
return NULL;
}
/* ------------------------------------------------------------- */
@ -355,9 +366,7 @@ static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
}
sp = find_spell(token);
if (!sp) {
log_warning("read_seenspell: could not find spell '%s'\n", token);
}
if (!sp) {
log_info("read_seenspell: could not find spell '%s'\n", token);
return AT_READ_FAIL;
}
a->data.v = sp;
@ -479,12 +488,10 @@ sc_mage *create_mage(unit * u, magic_t mtyp)
attrib *a;
a = a_find(u->attribs, &at_mage);
if (a != NULL) {
a_remove(&u->attribs, a);
if (a == NULL) {
a = a_add(&u->attribs, a_new(&at_mage));
}
a = a_add(&u->attribs, a_new(&at_mage));
mage = a->data.v;
mage = (sc_mage *)a->data.v;
mage->magietyp = mtyp;
return mage;
}
@ -507,7 +514,7 @@ int u_hasspell(const unit *u, const struct spell *sp)
int get_combatspelllevel(const unit * u, int nr)
{
sc_mage *m = get_mage(u);
sc_mage *m = get_mage_depr(u);
assert(nr < MAXCOMBATSPELLS);
if (m) {
@ -525,7 +532,7 @@ const spell *get_combatspell(const unit * u, int nr)
sc_mage *m;
assert(nr < MAXCOMBATSPELLS);
m = get_mage(u);
m = get_mage_depr(u);
if (m) {
return m->combatspells[nr].sp;
}
@ -534,7 +541,7 @@ const spell *get_combatspell(const unit * u, int nr)
void set_combatspell(unit * u, spell * sp, struct order *ord, int level)
{
sc_mage *mage = get_mage(u);
sc_mage *mage = get_mage_depr(u);
int i = -1;
assert(mage || !"trying to set a combat spell for non-mage");
@ -574,7 +581,7 @@ void unset_combatspell(unit * u, spell * sp)
int nr = 0;
int i;
m = get_mage(u);
m = get_mage_depr(u);
if (!m)
return;
@ -610,7 +617,7 @@ int get_spellpoints(const unit * u)
{
sc_mage *m;
m = get_mage(u);
m = get_mage_depr(u);
if (!m)
return 0;
@ -621,7 +628,7 @@ void set_spellpoints(unit * u, int sp)
{
sc_mage *m;
m = get_mage(u);
m = get_mage_depr(u);
if (!m)
return;
@ -638,7 +645,7 @@ int change_spellpoints(unit * u, int mp)
sc_mage *m;
int sp;
m = get_mage(u);
m = get_mage_depr(u);
if (!m) {
return 0;
}
@ -657,7 +664,7 @@ static int get_spchange(const unit * u)
{
sc_mage *m;
m = get_mage(u);
m = get_mage_depr(u);
if (!m)
return 0;
@ -711,7 +718,7 @@ int change_maxspellpoints(unit * u, int csp)
{
sc_mage *m;
m = get_mage(u);
m = get_mage_depr(u);
if (!m) {
return 0;
}
@ -729,7 +736,7 @@ int countspells(unit * u, int step)
sc_mage *m;
int count;
m = get_mage(u);
m = get_mage_depr(u);
if (!m)
return 0;
@ -1313,7 +1320,7 @@ bool fumble(region * r, unit * u, const spell * sp, int cast_grade)
}
/* CHAOSPATZERCHANCE 10 : +10% Chance zu Patzern */
mage = get_mage(u);
mage = get_mage_depr(u);
if (mage->magietyp == M_DRAIG) {
fumble_chance += CHAOSPATZERCHANCE;
}
@ -2139,16 +2146,6 @@ void free_castorders(castorder * co)
return;
}
/* ------------------------------------------------------------- */
/***
** at_familiarmage
**/
typedef struct familiar_data {
unit *mage;
unit *familiar;
} famililar_data;
bool is_familiar(const unit * u)
{
attrib *a = a_find(u->attribs, &at_familiarmage);
@ -2181,7 +2178,7 @@ static int sm_familiar(const unit * u, const region * r, skill_t sk, int value)
}
}
static void set_familiar(unit * mage, unit * familiar)
void set_familiar(unit * mage, unit * familiar)
{
/* if the skill modifier for the mage does not yet exist, add it */
attrib *a = a_find(mage->attribs, &at_skillmod);
@ -2192,10 +2189,8 @@ static void set_familiar(unit * mage, unit * familiar)
a = a->next;
}
if (a == NULL) {
attrib *an = a_add(&mage->attribs, a_new(&at_skillmod));
skillmod_data *smd = (skillmod_data *)an->data.v;
smd->special = sm_familiar;
smd->skill = NOSKILL;
a = make_skillmod(NOSKILL, sm_familiar, 0.0, 0);
a_add(&mage->attribs, a);
}
a = a_find(mage->attribs, &at_familiar);
@ -2203,17 +2198,19 @@ static void set_familiar(unit * mage, unit * familiar)
a = a_add(&mage->attribs, a_new(&at_familiar));
a->data.v = familiar;
}
else
else {
assert(!a->data.v || a->data.v == familiar);
/* TODO: Diese Attribute beim Tod des Familiars entfernen: */
}
/* TODO: Diese Attribute beim Tod des Familiars entfernen: */
a = a_find(familiar->attribs, &at_familiarmage);
if (a == NULL) {
a = a_add(&familiar->attribs, a_new(&at_familiarmage));
a->data.v = mage;
}
else
else {
assert(!a->data.v || a->data.v == mage);
}
}
void remove_familiar(unit * mage)
@ -2229,70 +2226,52 @@ void remove_familiar(unit * mage)
while (a && a->type == &at_skillmod) {
an = a->next;
smd = (skillmod_data *)a->data.v;
if (smd->special == sm_familiar)
if (smd->special == sm_familiar) {
a_remove(&mage->attribs, a);
}
a = an;
}
}
bool create_newfamiliar(unit * mage, unit * familiar)
void create_newfamiliar(unit * mage, unit * fam)
{
/* if the skill modifier for the mage does not yet exist, add it */
attrib *a;
attrib *afam = a_find(mage->attribs, &at_familiar);
attrib *amage = a_find(familiar->attribs, &at_familiarmage);
/* skills and spells: */
const struct equipment *eq;
char eqname[64];
const race *rc = u_race(fam);
if (afam == NULL) {
afam = a_add(&mage->attribs, a_new(&at_familiar));
}
afam->data.v = familiar;
if (amage == NULL) {
amage = a_add(&familiar->attribs, a_new(&at_familiarmage));
}
amage->data.v = mage;
set_familiar(mage, fam);
snprintf(eqname, sizeof(eqname), "fam_%s", rc->_name);
eq = get_equipment(eqname);
if (eq != NULL) {
equip_unit(fam, eq);
}
else {
log_info("could not perform initialization for familiar %s.\n", rc->_name);
}
/* TODO: Diese Attribute beim Tod des Familiars entfernen: */
/* Wenn der Magier stirbt, dann auch der Vertraute */
add_trigger(&mage->attribs, "destroy", trigger_killunit(familiar));
add_trigger(&mage->attribs, "destroy", trigger_killunit(fam));
/* Wenn der Vertraute stirbt, dann bekommt der Magier einen Schock */
add_trigger(&familiar->attribs, "destroy", trigger_shock(mage));
a = a_find(mage->attribs, &at_skillmod);
while (a && a->type == &at_skillmod) {
skillmod_data *smd = (skillmod_data *)a->data.v;
if (smd->special == sm_familiar)
break;
a = a->next;
}
if (a == NULL) {
attrib *an = a_add(&mage->attribs, a_new(&at_skillmod));
skillmod_data *smd = (skillmod_data *)an->data.v;
smd->special = sm_familiar;
smd->skill = NOSKILL;
}
return true;
add_trigger(&fam->attribs, "destroy", trigger_shock(mage));
}
static int resolve_familiar(variant data, void *addr)
{
unit *familiar;
int result = resolve_unit(data, &familiar);
if (result == 0 && familiar) {
static void * resolve_familiar(int id, void *data) {
if (data) {
unit *familiar = (unit *)data;
attrib *a = a_find(familiar->attribs, &at_familiarmage);
if (a != NULL && a->data.v) {
unit *mage = (unit *)a->data.v;
set_familiar(mage, familiar);
}
}
*(unit **)addr = familiar;
return result;
return data;
}
static int read_familiar(attrib * a, void *owner, struct gamedata *data)
{
int result =
read_reference(&a->data.v, data, read_unit_reference, resolve_familiar);
if (result == 0 && a->data.v == NULL) {
if (read_unit_reference(data, (unit **)&a->data.v, resolve_familiar) <= 0) {
return AT_READ_FAIL;
}
return AT_READ_OK;
@ -2357,53 +2336,42 @@ unit *has_clone(unit * mage)
return NULL;
}
static int resolve_clone(variant data, void *addr)
{
unit *clone;
int result = resolve_unit(data, &clone);
if (result == 0 && clone) {
static void * resolve_clone(int id, void *data) {
if (data) {
unit *clone = (unit *)data;
attrib *a = a_find(clone->attribs, &at_clonemage);
if (a != NULL && a->data.v) {
unit *mage = (unit *)a->data.v;
set_clone(mage, clone);
}
}
*(unit **)addr = clone;
return result;
return data;
}
static int read_clone(attrib * a, void *owner, struct gamedata *data)
{
int result =
read_reference(&a->data.v, data, read_unit_reference, resolve_clone);
if (result == 0 && a->data.v == NULL) {
if (read_unit_reference(data, (unit **)&a->data.v, resolve_clone) <= 0) {
return AT_READ_FAIL;
}
return AT_READ_OK;
}
/* mages */
static int resolve_mage(variant data, void *addr)
{
unit *mage;
int result = resolve_unit(data, &mage);
if (result == 0 && mage) {
static void * resolve_mage(int id, void *data) {
if (data) {
unit *mage = (unit *)data;
attrib *a = a_find(mage->attribs, &at_familiar);
if (a != NULL && a->data.v) {
unit *familiar = (unit *)a->data.v;
set_familiar(mage, familiar);
}
}
*(unit **)addr = mage;
return result;
return data;
}
static int read_magician(attrib * a, void *owner, struct gamedata *data)
{
int result =
read_reference(&a->data.v, data, read_unit_reference, resolve_mage);
if (result == 0 && a->data.v == NULL) {
if (read_unit_reference(data, (unit **)&a->data.v, resolve_mage) <= 0) {
return AT_READ_FAIL;
}
return AT_READ_OK;
@ -3019,30 +2987,36 @@ int cast_spell(struct castorder *co)
static critbit_tree cb_spellbooks;
#define SBNAMELEN 16
typedef struct sb_entry {
char key[SBNAMELEN];
spellbook *value;
} sb_entry;
spellbook * get_spellbook(const char * name)
{
char buffer[64];
spellbook * result;
void * match;
size_t len = strlen(name);
const void * match;
if (cb_find_prefix(&cb_spellbooks, name, strlen(name), &match, 1, 0) > 0) {
cb_get_kv(match, &result, sizeof(result));
if (len >= SBNAMELEN) {
log_error("spellbook name is longer than %d bytes: %s", SBNAMELEN-1, name);
return NULL;
}
else {
size_t len = strlen(name);
result = create_spellbook(name);
assert(strlen(name) + sizeof(result) < sizeof(buffer));
len = cb_new_kv(name, len, &result, sizeof(result), buffer);
if (cb_insert(&cb_spellbooks, buffer, len) == CB_EXISTS) {
match = cb_find_str(&cb_spellbooks, name);
if (!match) {
sb_entry ent;
memset(ent.key, 0, SBNAMELEN);
memcpy(ent.key, name, len);
ent.value = create_spellbook(name);
if (cb_insert(&cb_spellbooks, &ent, sizeof(ent)) == CB_EXISTS) {
log_error("cb_insert failed although cb_find returned nothing for spellbook=%s", name);
assert(!"should not happen");
}
result = 0;
if (cb_find_prefix(&cb_spellbooks, name, strlen(name), &match, 1, 0) > 0) {
cb_get_kv(match, &result, sizeof(result));
}
return ent.value;
}
return result;
return ((const sb_entry *)match)->value;
}
void free_spellbook(spellbook *sb) {
@ -3051,9 +3025,8 @@ void free_spellbook(spellbook *sb) {
}
static int free_spellbook_cb(const void *match, const void *key, size_t keylen, void *data) {
spellbook *sb;
cb_get_kv(match, &sb, sizeof(sb));
free_spellbook(sb);
const sb_entry *ent = (const sb_entry *)match;
free_spellbook(ent->value);
return 0;
}

View file

@ -38,6 +38,10 @@ extern "C" {
#define IRONGOLEM_CRUMBLE 15 /* monatlich Chance zu zerfallen */
#define STONEGOLEM_CRUMBLE 10 /* monatlich Chance zu zerfallen */
extern const char *magic_school[MAXMAGIETYP];
extern struct attrib_type at_familiar;
extern struct attrib_type at_familiarmage;
/* ------------------------------------------------------------- */
/* Spruchparameter
* Wir suchen beim Parsen des Befehls erstmal nach lokalen Objekten,
@ -82,11 +86,6 @@ extern "C" {
#define TARGET_RESISTS (1<<0)
#define TARGET_NOTFOUND (1<<1)
/* ------------------------------------------------------------- */
/* Magierichtungen */
extern const char *magic_school[MAXMAGIETYP];
/* ------------------------------------------------------------- */
/* Magier:
* - Magierichtung
@ -222,6 +221,7 @@ extern "C" {
/* macht die struct unit zu einem neuen Magier: legt die struct u->mage an
* und initialisiert den Magiertypus mit mtyp. */
sc_mage *get_mage(const struct unit *u);
sc_mage *get_mage_depr(const struct unit *u);
/* gibt u->mage zurück, bei nicht-Magiern *NULL */
bool is_mage(const struct unit *u);
/* gibt true, wenn u->mage gesetzt. */
@ -324,16 +324,15 @@ extern "C" {
/* Sprüche in der struct region */
/* (sind in curse) */
extern struct unit *get_familiar(const struct unit *u);
extern struct unit *get_familiar_mage(const struct unit *u);
extern struct unit *get_clone(const struct unit *u);
extern struct unit *get_clone_mage(const struct unit *u);
extern struct attrib_type at_familiar;
extern struct attrib_type at_familiarmage;
extern void remove_familiar(struct unit *mage);
extern bool create_newfamiliar(struct unit *mage, struct unit *familiar);
extern void create_newclone(struct unit *mage, struct unit *familiar);
extern struct unit *has_clone(struct unit *mage);
void set_familiar(struct unit * mage, struct unit * familiar);
struct unit *get_familiar(const struct unit *u);
struct unit *get_familiar_mage(const struct unit *u);
struct unit *get_clone(const struct unit *u);
struct unit *get_clone_mage(const struct unit *u);
void remove_familiar(struct unit *mage);
void create_newfamiliar(struct unit *mage, struct unit *familiar);
void create_newclone(struct unit *mage, struct unit *familiar);
struct unit *has_clone(struct unit *mage);
const char *spell_info(const struct spell *sp,
const struct locale *lang);

View file

@ -6,6 +6,7 @@
#include <kernel/building.h>
#include <kernel/race.h>
#include <kernel/equipment.h>
#include <kernel/faction.h>
#include <kernel/order.h>
#include <kernel/item.h>
@ -51,7 +52,18 @@ void test_updatespells(CuTest * tc)
test_cleanup();
}
void test_spellbooks(CuTest * tc)
static void test_get_spellbook(CuTest * tc)
{
spellbook *sb;
test_setup();
CuAssertPtrNotNull(tc, sb = get_spellbook("hodorhodorhodor"));
CuAssertPtrEquals(tc, sb, get_spellbook("hodorhodorhodor"));
CuAssertTrue(tc, sb != get_spellbook("hodor"));
test_cleanup();
}
static void test_spellbooks(CuTest * tc)
{
spell *sp;
spellbook *herp, *derp;
@ -445,24 +457,6 @@ static void test_max_spellpoints(CuTest *tc) {
test_cleanup();
}
static void test_familiar_mage(CuTest *tc) {
unit *um, *uf, *ut;
test_setup();
um = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
uf = test_create_unit(um->faction, um->region);
ut = test_create_unit(um->faction, um->region);
set_number(ut, 0);
CuAssertTrue(tc, create_newfamiliar(um, uf));
CuAssertTrue(tc, is_familiar(uf));
CuAssertTrue(tc, !is_familiar(um));
CuAssertPtrEquals(tc, um, get_familiar_mage(uf));
CuAssertPtrEquals(tc, uf, get_familiar(um));
CuAssertPtrEquals(tc, NULL, give_men(1, um, ut, NULL));
CuAssertPtrEquals(tc, ut, get_familiar_mage(uf));
test_cleanup();
}
static void test_illusioncastle(CuTest *tc)
{
building *b;
@ -484,12 +478,135 @@ static void test_illusioncastle(CuTest *tc)
test_cleanup();
}
static void test_is_mage(CuTest *tc) {
unit *u;
sc_mage *mage;
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
CuAssertPtrEquals(tc, NULL, get_mage(u));
CuAssertTrue(tc, !is_mage(u));
set_level(u, SK_MAGIC, 1);
CuAssertTrue(tc, !is_mage(u));
CuAssertPtrEquals(tc, NULL, get_mage(u));
CuAssertPtrNotNull(tc, mage = create_mage(u, M_CERDDOR));
CuAssertPtrEquals(tc, mage, get_mage(u));
CuAssertTrue(tc, is_mage(u));
test_cleanup();
}
static void test_get_mage(CuTest *tc) {
unit *u;
sc_mage *mage;
test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
CuAssertPtrEquals(tc, NULL, get_mage(u));
CuAssertPtrEquals(tc, NULL, get_mage_depr(u));
CuAssertPtrNotNull(tc, mage = create_mage(u, M_CERDDOR));
CuAssertPtrEquals(tc, mage, get_mage(u));
CuAssertPtrEquals(tc, NULL, get_mage_depr(u));
set_level(u, SK_MAGIC, 1);
CuAssertPtrEquals(tc, mage, get_mage(u));
CuAssertPtrEquals(tc, mage, get_mage_depr(u));
test_cleanup();
}
static void test_familiar_set(CuTest *tc) {
unit *mag, *fam;
test_setup();
mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
fam = test_create_unit(mag->faction, test_create_region(0, 0, 0));
CuAssertPtrEquals(tc, NULL, get_familiar(mag));
CuAssertPtrEquals(tc, NULL, get_familiar_mage(fam));
CuAssertPtrEquals(tc, NULL, a_find(mag->attribs, &at_skillmod));
set_familiar(mag, fam);
CuAssertPtrEquals(tc, fam, get_familiar(mag));
CuAssertPtrEquals(tc, mag, get_familiar_mage(fam));
CuAssertPtrNotNull(tc, a_find(mag->attribs, &at_skillmod));
remove_familiar(mag);
CuAssertPtrEquals(tc, NULL, get_familiar(mag));
CuAssertPtrEquals(tc, NULL, a_find(mag->attribs, &at_skillmod));
test_cleanup();
}
static void test_familiar_age(CuTest *tc) {
unit *mag, *fam;
test_setup();
mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
fam = test_create_unit(mag->faction, test_create_region(0, 0, 0));
set_familiar(mag, fam);
CuAssertPtrEquals(tc, fam, get_familiar(mag));
CuAssertPtrEquals(tc, mag, get_familiar_mage(fam));
a_age(&fam->attribs, fam);
a_age(&mag->attribs, mag);
CuAssertPtrEquals(tc, fam, get_familiar(mag));
CuAssertPtrEquals(tc, mag, get_familiar_mage(fam));
set_number(fam, 0);
a_age(&mag->attribs, mag);
CuAssertPtrEquals(tc, NULL, get_familiar(mag));
test_cleanup();
}
static void test_familiar_equip(CuTest *tc) {
unit *mag, *u;
equipment *eq;
const item_type * itype;
spell *sp;
sc_mage * mage;
test_setup();
itype = test_create_itemtype("horse");
CuAssertPtrNotNull(tc, itype);
sp = create_spell("testspell");
CuAssertPtrNotNull(tc, sp);
eq = get_or_create_equipment("fam_human");
equipment_setitem(eq, itype, "1");
equipment_setskill(eq, SK_ENTERTAINMENT, "5");
equipment_addspell(eq, sp->sname, 1);
mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
u = test_create_unit(mag->faction, test_create_region(0, 0, 0));
set_familiar(mag, u);
create_newfamiliar(mag, u);
CuAssertIntEquals(tc, 1, i_get(u->items, itype));
CuAssertIntEquals(tc, 5, get_level(u, SK_ENTERTAINMENT));
CuAssertIntEquals(tc, 0, get_level(u, SK_MAGIC));
mage = get_mage(u);
CuAssertPtrNotNull(tc, mage);
CuAssertPtrNotNull(tc, mage->spellbook);
set_level(u, SK_MAGIC, 1);
CuAssertPtrEquals(tc, mage, get_mage_depr(u));
CuAssertTrue(tc, u_hasspell(u, sp));
test_cleanup();
}
CuSuite *get_familiar_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_familiar_equip);
SUITE_ADD_TEST(suite, test_familiar_set);
SUITE_ADD_TEST(suite, test_familiar_age);
return suite;
}
CuSuite *get_magic_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_is_mage);
SUITE_ADD_TEST(suite, test_get_mage);
SUITE_ADD_TEST(suite, test_multi_cast);
SUITE_ADD_TEST(suite, test_updatespells);
SUITE_ADD_TEST(suite, test_spellbooks);
SUITE_ADD_TEST(suite, test_get_spellbook);
SUITE_ADD_TEST(suite, test_pay_spell);
SUITE_ADD_TEST(suite, test_pay_spell_failure);
SUITE_ADD_TEST(suite, test_getspell_unit);
@ -502,6 +619,5 @@ CuSuite *get_magic_suite(void)
SUITE_ADD_TEST(suite, test_magic_resistance);
SUITE_ADD_TEST(suite, test_max_spellpoints);
SUITE_ADD_TEST(suite, test_illusioncastle);
DISABLE_TEST(suite, test_familiar_mage);
return suite;
}

View file

@ -147,8 +147,7 @@ newfaction *read_newfactions(const char *filename)
faction *f;
char race[20], email[64], lang[8], password[16];
newfaction *nf, **nfi;
int bonus = 0, subscription = 0;
int alliance = 0;
int alliance = 0, subscription = 0;
if (fgets(buf, sizeof(buf), F) == NULL)
break;
@ -156,8 +155,8 @@ newfaction *read_newfactions(const char *filename)
email[0] = '\0';
password[0] = '\0';
if (sscanf(buf, "%54s %20s %8s %d %d %16s %d", email, race, lang, &bonus,
&subscription, password, &alliance) < 3) {
if (sscanf(buf, "%54s %20s %8s %16s %d %d", email, race, lang,
password, &subscription, &alliance) < 3) {
break;
}
if (email[0] == '#') {
@ -228,7 +227,6 @@ newfaction *read_newfactions(const char *filename)
}
}
nf->lang = get_locale(lang);
nf->bonus = bonus;
assert(nf->race && nf->email && nf->lang);
nfi = &newfactions;
while (*nfi) {

View file

@ -24,7 +24,6 @@ extern "C" {
char *password;
const struct locale *lang;
const struct race *race;
int bonus;
int subscription;
bool oldregions;
struct alliance *allies;

View file

@ -1,4 +1,4 @@
/*
/*
+-------------------+ Christian Schlittchen <corwin@amber.kn-bremen.de>
| | Enno Rehling <enno@eressea.de>
| Eressea PBEM host | Katja Zedel <katze@felidae.kn-bremen.de>
@ -14,7 +14,7 @@
#include <kernel/config.h>
#include "xmas.h"
/* kernel includes */
/* kernel includes */
#include <kernel/building.h>
#include <kernel/faction.h>
#include <kernel/item.h>
@ -50,10 +50,7 @@ static void xmasgate_write(const trigger * t, struct storage *store)
static int xmasgate_read(trigger * t, struct gamedata *data)
{
int bc =
read_reference(&t->data.v, data, read_building_reference,
resolve_building);
if (bc == 0 && !t->data.v) {
if (read_building_reference(data, (building **)&t->data.v, NULL) <= 0) {
return AT_READ_FAIL;
}
return AT_READ_OK;

View file

@ -49,6 +49,7 @@
#include <kernel/pool.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h>
#include <kernel/unit.h>
@ -191,6 +192,7 @@ void monsters_desert(struct faction *monsters)
int monster_attacks(unit * monster, bool rich_only)
{
const race *rc_serpent = get_race(RC_SEASERPENT);
if (monster->status < ST_AVOID) {
region *r = monster->region;
unit *u2;
@ -199,6 +201,12 @@ int monster_attacks(unit * monster, bool rich_only)
for (u2 = r->units; u2; u2 = u2->next) {
if (u2->faction != monster->faction && cansee(monster->faction, r, u2, 0) && !in_safe_building(u2, monster)) {
int m = get_money(u2);
if (u_race(monster) == rc_serpent) {
/* attack bigger ships only */
if (!u2->ship || u2->ship->type->cargo <= 50000) {
continue;
}
}
if (!rich_only || m > 0) {
order *ord = monster_attack(monster, u2);
if (ord) {
@ -604,7 +612,7 @@ static void recruit_dracoids(unit * dragon, int size)
name_unit(un);
change_money(dragon, -un->number * 50);
equip_unit(un, get_equipment("recruited_dracoid"));
equip_unit(un, get_equipment("new_dracoid"));
setstatus(un, ST_FIGHT);
for (weapon = un->items; weapon; weapon = weapon->next) {
@ -855,7 +863,7 @@ static int nrand(int start, int sub)
unit *spawn_seaserpent(region *r, faction *f) {
unit *u = create_unit(r, f, 1, get_race(RC_SEASERPENT), 0, NULL, NULL);
fset(u, UFL_ISNEW | UFL_MOVED);
equip_unit(u, get_equipment("monster_seaserpent"));
equip_unit(u, get_equipment("seed_seaserpent"));
return u;
}
@ -886,7 +894,7 @@ void spawn_dragons(void)
u = create_unit(r, monsters, nrand(30, 20) + 1, get_race(RC_DRAGON), 0, NULL, NULL);
}
fset(u, UFL_ISNEW | UFL_MOVED);
equip_unit(u, get_equipment("monster_dragon"));
equip_unit(u, get_equipment("seed_dragon"));
log_debug("spawning %d %s in %s.\n", u->number,
LOC(default_locale,
@ -1124,7 +1132,7 @@ faction *get_or_create_monsters(void)
if (!f) {
const race *rc = rc_get_or_create("dragon");
const char *email = config_get("monster.email");
f = addfaction(email ? email : "noreply@eressea.de", NULL, rc, default_locale, 0);
f = addfaction(email ? email : NULL, NULL, rc, default_locale, 0);
renumber_faction(f, MONSTER_ID);
faction_setname(f, "Monster");
fset(f, FFL_NPC | FFL_NOIDLEOUT);

View file

@ -7,6 +7,8 @@
#include <kernel/order.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/region.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/unit.h>
@ -41,43 +43,43 @@ static order *find_order(const char *expected, const unit *unit)
return NULL;
}
static void create_monsters(faction **player, faction **monsters, unit **u, unit **m) {
static void create_monsters(unit **up, unit **um) {
race* rc;
region *r;
faction *fp, *fm;
test_cleanup();
test_create_horse();
default_locale = test_create_locale();
*player = test_create_faction(NULL);
*monsters = get_or_create_monsters();
assert(rc_find((*monsters)->race->_name));
rc = rc_get_or_create((*monsters)->race->_name);
fp = test_create_faction(NULL);
fm = get_or_create_monsters();
assert(rc_find(fm->race->_name));
rc = rc_get_or_create(fm->race->_name);
fset(rc, RCF_UNARMEDGUARD|RCF_NPC|RCF_DRAGON);
fset(*monsters, FFL_NOIDLEOUT);
assert(fval(*monsters, FFL_NPC) && fval((*monsters)->race, RCF_UNARMEDGUARD) && fval((*monsters)->race, RCF_NPC) && fval(*monsters, FFL_NOIDLEOUT));
fset(fm, FFL_NOIDLEOUT);
assert(fval(fm, FFL_NPC) && fval(fm->race, RCF_UNARMEDGUARD) && fval(fm->race, RCF_NPC) && fval(fm, FFL_NOIDLEOUT));
test_create_region(-1, 0, test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO));
test_create_region(1, 0, 0);
r = test_create_region(0, 0, 0);
*u = test_create_unit(*player, r);
unit_setid(*u, 1);
*m = test_create_unit(*monsters, r);
unit_setstatus(*m, ST_FIGHT);
*up = test_create_unit(fp, r);
unit_setid(*up, 1);
*um = test_create_unit(fm, r);
unit_setstatus(*um, ST_FIGHT);
}
static void test_monsters_attack(CuTest * tc)
{
faction *f, *f2;
unit *u, *m;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
setguard(m, true);
config_set("rules.monsters.attack_chance", "1");
plan_monsters(f2);
plan_monsters(m->faction);
CuAssertPtrNotNull(tc, find_order("attack 1", m));
test_cleanup();
@ -85,11 +87,10 @@ static void test_monsters_attack(CuTest * tc)
static void test_monsters_attack_ocean(CuTest * tc)
{
faction *f, *f2;
region *r;
unit *u, *m;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
r = findregion(-1, 0); /* ocean */
u = test_create_unit(u->faction, r);
unit_setid(u, 2);
@ -98,7 +99,7 @@ static void test_monsters_attack_ocean(CuTest * tc)
config_set("rules.monsters.attack_chance", "1");
plan_monsters(f2);
plan_monsters(m->faction);
CuAssertPtrNotNull(tc, find_order("attack 2", m));
test_cleanup();
@ -106,10 +107,9 @@ static void test_monsters_attack_ocean(CuTest * tc)
static void test_monsters_waiting(CuTest * tc)
{
faction *f, *f2;
unit *u, *m;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
setguard(m, true);
fset(m, UFL_ISNEW);
monster_attacks(m, false);
@ -119,14 +119,16 @@ static void test_monsters_waiting(CuTest * tc)
static void test_seaserpent_piracy(CuTest * tc)
{
faction *f, *f2;
region *r;
unit *u, *m;
race *rc;
ship_type * stype;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
stype = test_create_shiptype("yacht");
r = findregion(-1, 0); /* ocean */
u = test_create_unit(u->faction, r);
u->ship = test_create_ship(r, stype);
unit_setid(u, 2);
m = test_create_unit(m->faction, r);
u_setrace(m, rc = test_create_race("seaserpent"));
@ -136,25 +138,28 @@ static void test_seaserpent_piracy(CuTest * tc)
config_set("rules.monsters.attack_chance", "1");
plan_monsters(f2);
stype->cargo = 50000;
plan_monsters(m->faction);
CuAssertPtrNotNull(tc, find_order("piracy", m));
CuAssertPtrEquals(tc, NULL, find_order("attack 2", m));
stype->cargo = 50001;
plan_monsters(m->faction);
CuAssertPtrNotNull(tc, find_order("attack 2", m));
test_cleanup();
}
static void test_monsters_attack_not(CuTest * tc)
{
faction *f, *f2;
unit *u, *m;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
setguard(m, true);
setguard(u, true);
config_set("rules.monsters.attack_chance", "0");
plan_monsters(f2);
plan_monsters(m->faction);
CuAssertPtrEquals(tc, 0, find_order("attack 1", m));
test_cleanup();
@ -162,11 +167,10 @@ static void test_monsters_attack_not(CuTest * tc)
static void test_dragon_attacks_the_rich(CuTest * tc)
{
faction *f, *f2;
unit *u, *m;
const item_type *i_silver;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
init_resources();
setguard(m, true);
@ -180,8 +184,7 @@ static void test_dragon_attacks_the_rich(CuTest * tc)
config_set("rules.monsters.attack_chance", "0.00001");
plan_monsters(f2);
plan_monsters(m->faction);
CuAssertPtrNotNull(tc, find_order("attack 1", m));
CuAssertPtrNotNull(tc, find_order("loot", m));
test_cleanup();
@ -191,12 +194,11 @@ extern void random_growl(const unit *u, region *tr, int rand);
static void test_dragon_moves(CuTest * tc)
{
faction *f, *f2;
region *r;
unit *u, *m;
struct message *msg;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
rsetmoney(findregion(1, 0), 1000);
r = findregion(0, 0); /* plain */
rsetpeasants(r, 0);
@ -204,8 +206,8 @@ static void test_dragon_moves(CuTest * tc)
set_level(m, SK_WEAPONLESS, 10);
config_set("rules.monsters.attack_chance", ".0");
plan_monsters(f2);
plan_monsters(m->faction);
CuAssertPtrNotNull(tc, find_order("move east", m));
mt_register(mt_new_va("dragon_growl", "dragon:unit", "number:int", "target:region", "growl:string", 0));
@ -224,11 +226,10 @@ static void test_dragon_moves(CuTest * tc)
static void test_monsters_learn_exp(CuTest * tc)
{
faction *f, *f2;
unit *u, *m;
skill* sk;
create_monsters(&f, &f2, &u, &m);
create_monsters(&u, &m);
config_set("study.produceexp", "30");
u_setrace(u, u_race(m));

View file

@ -89,14 +89,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <limits.h>
#include <float.h>
/* Bewegungsweiten: */
#define BP_WALKING 4
#define BP_RIDING 6
#define BP_UNICORN 9
#define BP_DRAGON 4
#define BP_NORMAL 3
#define BP_ROAD 2
int *storms;
typedef struct traveldir {
@ -265,7 +257,7 @@ get_transporters(const item * itm, int *p_animals, int *p_acap, int *p_vehicles,
*p_acap = acap;
}
static int ridingcapacity(unit * u)
static int ridingcapacity(const unit * u)
{
int vehicles = 0, vcap = 0;
int animals = 0, acap = 0;
@ -433,7 +425,7 @@ bool canswim(unit * u)
return false;
}
static int canride(unit * u)
static int walk_mode(const unit * u)
{
int horses = 0, maxhorses, unicorns = 0, maxunicorns;
int skill = effskill(u, SK_RIDING, 0);
@ -460,17 +452,17 @@ static int canride(unit * u)
if (!(u_race(u)->flags & RCF_HORSE)
&& ((horses == 0 && unicorns == 0)
|| horses > maxhorses || unicorns > maxunicorns)) {
return 0;
return BP_WALKING;
}
if (ridingcapacity(u) - eff_weight(u) >= 0) {
if (horses == 0 && unicorns >= u->number && !(u_race(u)->flags & RCF_HORSE)) {
return 2;
return BP_UNICORN;
}
return 1;
return BP_RIDING;
}
return 0;
return BP_WALKING;
}
static bool cansail(const region * r, ship * sh)
@ -1399,9 +1391,9 @@ static void make_route(unit * u, order * ord, region_list ** routep)
* Normalerweise verliert man 3 BP pro Region, bei Straßen nur 2 BP.
* Außerdem: Wenn Einheit transportiert, nur halbe BP
*/
static int movement_speed(unit * u)
int movement_speed(const unit * u)
{
int mp = BP_WALKING;
int mp = 0;
const race *rc = u_race(u);
double dk = rc->speed;
assert(u->number);
@ -1415,6 +1407,10 @@ static int movement_speed(unit * u)
mp = BP_DRAGON;
break;
default:
mp = walk_mode(u);
if (mp>=BP_RIDING) {
dk = 1.0;
}
break;
}
@ -1426,38 +1422,22 @@ static int movement_speed(unit * u)
}
}
switch (canride(u)) {
case 1: /* Pferd */
mp = BP_RIDING;
break;
/* unicorn in inventory */
if (u->number <= i_get(u->items, it_find("fairyboot"))) {
mp *= 2;
}
case 2: /* Einhorn */
mp = BP_UNICORN;
break;
default:
/* Siebenmeilentee */
if (get_effect(u, oldpotiontype[P_FAST]) >= u->number) {
mp *= 2;
change_effect(u, oldpotiontype[P_FAST], -u->number);
}
/* unicorn in inventory */
if (u->number <= i_get(u->items, it_find("fairyboot"))) {
mp *= 2;
}
/* Im Astralraum sind Tyb und Ill-Magier doppelt so schnell.
* Nicht kumulativ mit anderen Beschleunigungen! */
if (mp * dk <= BP_WALKING * u_race(u)->speed && is_astral(u->region)
&& is_mage(u)) {
sc_mage *mage = get_mage(u);
if (mage->magietyp == M_TYBIED || mage->magietyp == M_ILLAUN) {
/* Im Astralraum sind Tyb und Ill-Magier doppelt so schnell.
* Nicht kumulativ mit anderen Beschleunigungen! */
if (mp * dk <= BP_WALKING * u_race(u)->speed && is_astral(u->region)) {
sc_mage *mage = get_mage(u);
if (mage && (mage->magietyp == M_TYBIED || mage->magietyp == M_ILLAUN)) {
if (has_skill(u, SK_MAGIC)) {
mp *= 2;
}
}
break;
}
return (int)(dk * mp);
}
@ -1631,7 +1611,7 @@ static const region_list *travel_route(unit * u,
if (mode == TRAVEL_RUNNING) {
walkmode = 0;
}
if (canride(u)) {
else if (walk_mode(u) >= BP_RIDING) {
walkmode = 1;
produceexp(u, SK_RIDING, u->number);
}
@ -2044,7 +2024,7 @@ static const region_list *travel_i(unit * u, const region_list * route_begin,
const region_list * route_end, order * ord, int mode, follower ** followers)
{
region *r = u->region;
int mp;
if (u->building && !can_leave(u)) {
cmistake(u, u->thisorder, 150, MSG_MOVE);
return route_begin;
@ -2060,7 +2040,15 @@ static const region_list *travel_i(unit * u, const region_list * route_begin,
cmistake(u, ord, 42, MSG_MOVE);
return route_begin;
}
route_end = cap_route(r, route_begin, route_end, movement_speed(u));
mp = movement_speed(u);
/* Siebenmeilentee */
if (get_effect(u, oldpotiontype[P_FAST]) >= u->number) {
mp *= 2;
change_effect(u, oldpotiontype[P_FAST], -u->number);
}
route_end = cap_route(r, route_begin, route_end, mp);
route_end = travel_route(u, route_begin, route_end, ord, mode);
if (u->flags&UFL_FOLLOWED) {

View file

@ -36,6 +36,14 @@ extern "C" {
extern struct attrib_type at_shiptrail;
extern int *storms;
/* Bewegungsweiten: */
#define BP_WALKING 4
#define BP_RIDING 6
#define BP_UNICORN 9
#define BP_DRAGON 4
#define BP_NORMAL 3
#define BP_ROAD 2
/* die Zahlen sind genau äquivalent zu den race Flags */
#define MV_CANNOTMOVE (1<<5)
#define MV_FLY (1<<7) /* kann fliegen */
@ -70,6 +78,7 @@ extern "C" {
struct ship *move_ship(struct ship *sh, struct region *from,
struct region *to, struct region_list *route);
int walkingcapacity(const struct unit *u);
int movement_speed(const struct unit * u);
void follow_unit(struct unit *u);
struct unit *owner_buildingtyp(const struct region *r,
const struct building_type *bt);

View file

@ -522,9 +522,33 @@ static void test_ship_leave_trail(CuTest *tc) {
test_cleanup();
}
static void test_movement_speed(CuTest *tc) {
unit * u;
race * rc;
const struct item_type *it_horse;
test_setup();
it_horse = test_create_horse();
rc = test_create_race(NULL);
u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, NULL));
rc->speed = 1.0;
CuAssertIntEquals(tc, BP_WALKING, movement_speed(u));
rc->speed = 2.0;
CuAssertIntEquals(tc, 2 * BP_WALKING, movement_speed(u));
set_level(u, SK_RIDING, 1);
i_change(&u->items, it_horse, 1);
CuAssertIntEquals(tc, BP_RIDING, movement_speed(u));
test_cleanup();
}
CuSuite *get_move_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_movement_speed);
SUITE_ADD_TEST(suite, test_walkingcapacity);
SUITE_ADD_TEST(suite, test_ship_not_allowed_in_coast);
SUITE_ADD_TEST(suite, test_ship_leave_trail);

245
src/orderfile.c Normal file
View file

@ -0,0 +1,245 @@
#include <platform.h>
#include <kernel/config.h>
#include "orderfile.h"
#include <kernel/faction.h>
#include <kernel/unit.h>
#include <kernel/order.h>
#include <kernel/messages.h>
#include <util/base36.h>
#include <util/message.h>
#include <util/language.h>
#include <util/log.h>
#include <util/filereader.h>
#include <util/parser.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
static unit *unitorders(input *in, faction *f)
{
int i;
unit *u;
if (!f)
return NULL;
i = getid();
u = findunitg(i, NULL);
if (u && u->faction == f) {
order **ordp;
if (!fval(u, UFL_ORDERS)) {
/* alle wiederholbaren, langen befehle werden gesichert: */
fset(u, UFL_ORDERS);
u->old_orders = u->orders;
ordp = &u->old_orders;
while (*ordp) {
order *ord = *ordp;
keyword_t kwd = getkeyword(ord);
if (!is_repeated(kwd)) {
*ordp = ord->next;
ord->next = NULL;
free_order(ord);
}
else {
ordp = &ord->next;
}
}
}
else {
free_orders(&u->orders);
}
u->orders = 0;
ordp = &u->orders;
for (;;) {
const char *s;
/* Erst wenn wir sicher sind, dass kein Befehl
* eingegeben wurde, checken wir, ob nun eine neue
* Einheit oder ein neuer Spieler drankommt */
s = in->getbuf(in->data);
if (s == NULL)
break;
if (s[0]) {
if (s[0] != '@') {
char token[64];
const char *stok = s;
stok = parse_token(&stok, token, sizeof(token));
if (stok) {
bool quit = false;
param_t param = findparam(stok, u->faction->locale);
switch (param) {
case P_UNIT:
case P_REGION:
quit = true;
break;
case P_FACTION:
case P_NEXT:
case P_GAMENAME:
/* these terminate the orders, so we apply extra checking */
if (strlen(stok) >= 3) {
quit = true;
break;
}
else {
quit = false;
}
break;
default:
break;
}
if (quit) {
break;
}
}
}
/* Nun wird der Befehl erzeut und eingehängt */
*ordp = parse_order(s, u->faction->locale);
if (*ordp) {
ordp = &(*ordp)->next;
}
else {
ADDMSG(&f->msgs, msg_message("parse_error", "unit command", u, s));
}
}
}
}
else {
return NULL;
}
return u;
}
static faction *factionorders(void)
{
faction *f = NULL;
int fid = getid();
f = findfaction(fid);
if (f != NULL && !fval(f, FFL_NPC)) {
char token[128];
const char *pass = gettoken(token, sizeof(token));
if (!checkpasswd(f, (const char *)pass)) {
log_debug("Invalid password for faction %s", itoa36(fid));
ADDMSG(&f->msgs, msg_message("wrongpasswd", "password", pass));
return 0;
}
/* Die Partei hat sich zumindest gemeldet, so dass sie noch
* nicht als untätig gilt */
f->lastorders = turn;
}
else {
log_debug("orders for invalid faction %s", itoa36(fid));
}
return f;
}
int read_orders(input *in)
{
const char *b;
int nfactions = 0;
struct faction *f = NULL;
/* TODO: recognize UTF8 BOM */
b = in->getbuf(in->data);
/* Auffinden der ersten Partei, und danach abarbeiten bis zur letzten
* Partei */
while (b) {
char token[128];
const struct locale *lang = f ? f->locale : default_locale;
param_t p;
const char *s;
init_tokens_str(b);
s = gettoken(token, sizeof(token));
p = findparam_block(s, lang, true);
switch (p) {
case P_GAMENAME:
case P_FACTION:
f = factionorders();
if (f) {
++nfactions;
}
b = in->getbuf(in->data);
break;
/* in factionorders wird nur eine zeile gelesen:
* diejenige mit dem passwort. Die befehle der units
* werden geloescht, und die Partei wird als aktiv
* vermerkt. */
case P_UNIT:
if (!f || !unitorders(in, f)) {
do {
b = in->getbuf(in->data);
if (!b) {
break;
}
init_tokens_str(b);
s = gettoken(token, sizeof(token));
p = (s && s[0] != '@') ? findparam(s, lang) : NOPARAM;
} while ((p != P_UNIT || !f) && p != P_FACTION && p != P_NEXT
&& p != P_GAMENAME);
}
break;
/* Falls in unitorders() abgebrochen wird, steht dort entweder eine neue
* Partei, eine neue Einheit oder das File-Ende. Das switch() wird erneut
* durchlaufen, und die entsprechende Funktion aufgerufen. Man darf buf
* auf alle Fälle nicht überschreiben! Bei allen anderen Einträgen hier
* muss buf erneut gefüllt werden, da die betreffende Information in nur
* einer Zeile steht, und nun die nächste gelesen werden muss. */
case P_NEXT:
f = NULL;
b = in->getbuf(in->data);
break;
default:
b = in->getbuf(in->data);
break;
}
}
log_info("done reading orders for %d factions", nfactions);
return 0;
}
static const char * file_getbuf(void *data)
{
FILE *F = (FILE *)data;
return getbuf(F, ENCODING_UTF8);
}
int readorders(const char *filename)
{
input in;
int result;
FILE *F = NULL;
F = fopen(filename, "r");
if (!F) {
perror(filename);
return -1;
}
log_info("reading orders from %s", filename);
in.getbuf = file_getbuf;
in.data = F;
result = read_orders(&in);
fclose(F);
return result;
}

21
src/orderfile.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef H_ORDERFILE
#define H_ORDERFILE
#include <skill.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct input {
const char *(*getbuf)(void *data);
void *data;
} input;
int read_orders(struct input *in);
int readorders(const char *filename);
#ifdef __cplusplus
}
#endif
#endif

88
src/orderfile.test.c Normal file
View file

@ -0,0 +1,88 @@
#include <platform.h>
#include <kernel/config.h>
#include "orderfile.h"
#include <kernel/faction.h>
#include <CuTest.h>
#include <tests.h>
static const char *getbuf_null(void *data)
{
return NULL;
}
static void test_read_orders(CuTest *tc) {
input in;
test_setup();
in.getbuf = getbuf_null;
in.data = NULL;
CuAssertIntEquals(tc, 0, read_orders(&in));
test_cleanup();
}
typedef struct order_list {
const char **orders;
int next;
} order_list;
static const char *getbuf_list(void *data)
{
order_list * olist = (order_list *)data;
return olist->orders[olist->next++];
}
static void test_faction_password_okay(CuTest *tc) {
input in;
faction *f;
order_list olist;
const char *orders[] = { "ERESSEA 1 password", NULL };
test_setup();
f = test_create_faction(NULL);
renumber_faction(f, 1);
CuAssertIntEquals(tc, 1, f->no);
faction_setpassword(f, "password");
f->lastorders = turn - 1;
olist.orders = orders;
olist.next = 0;
in.getbuf = getbuf_list;
in.data = &olist;
CuAssertIntEquals(tc, 0, read_orders(&in));
CuAssertIntEquals(tc, 2, olist.next);
CuAssertIntEquals(tc, turn, f->lastorders);
test_cleanup();
}
static void test_faction_password_bad(CuTest *tc) {
input in;
faction *f;
order_list olist;
const char *orders[] = { "ERESSEA 1 password", NULL };
test_setup();
f = test_create_faction(NULL);
renumber_faction(f, 1);
CuAssertIntEquals(tc, 1, f->no);
faction_setpassword(f, "patzword");
f->lastorders = turn - 1;
olist.orders = orders;
olist.next = 0;
in.getbuf = getbuf_list;
in.data = &olist;
CuAssertIntEquals(tc, 0, read_orders(&in));
CuAssertIntEquals(tc, 2, olist.next);
CuAssertIntEquals(tc, turn - 1, f->lastorders);
test_cleanup();
}
CuSuite *get_orderfile_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_read_orders);
SUITE_ADD_TEST(suite, test_faction_password_okay);
SUITE_ADD_TEST(suite, test_faction_password_bad);
return suite;
}

Some files were not shown because too many files have changed in this diff Show more