This commit is contained in:
uid66289 2003-12-23 21:04:20 +00:00
parent 27a1a5d41f
commit 5c890a420f
49 changed files with 963 additions and 325 deletions

View file

@ -1,9 +1,9 @@
Version 0.6:
* radical storyline changes, this breaks saves from older versions
* fog of war
* support for multiple campaigns
* enemy units do not vanish anymore when enemy leader is killed
* new scenarios for 'Heir to the Throne' (Konrad's Tale):
* Isle of the Damned
* Northern Winter
* The Lost General
* Hasty Alliance
@ -17,6 +17,7 @@ Version 0.6:
* Dwarven Doors
* Mountain Pass
* Valley of Death
* support for multiple campaigns
* new campaign started: 'Hordes of the Undead'
* multiplayer improvements:
* show minimap when selecting map for new game
@ -47,10 +48,12 @@ Version 0.6:
* Elvish Scout
* Pikeman
* Dwarvish Steelclad
* undead are now immune to poinson and plague
* Konrad now starts as Fighter, which is 1st level unit
* undead are now immune to poison and plague
* reduced movement for Elvish Lord
* reduced cost of Mage
* changed Mage attack to fire based
* 'Mage of Light' has now both cure and illumination
* changed Shaman slowing attack to 3-2 from 4-1, and reduced cost from 21 to 18
* changed 'Red Mage' to neutral alignment
* removed 'Elvish Outrider' as evolution from Horseman
@ -61,8 +64,8 @@ Version 0.6:
* 'Blood Bat' unit description clarified
* Halberdier changed to 3rd level unit
* Spearman now advances to Pikeman
* Fire resistance added to 'Red Mage' branch units
* Holy resistance added to Holy units.
* Increased fire resistance for 'Red Mage' branch units
* Increased holy resistance made higher for Holy units.
* auto-naming (currently elves and humans only) of units and renaming of units
* units now have races defined in cfg-files
* more and improved unit graphics and animations
@ -86,6 +89,7 @@ Version 0.6:
* added in support for some special effects: flashing and scrolling/tremors
* added coding support for displaying different terrain images (particularly useful for villages) at different times of the day
* fixed minor bugs in unit configuration files for Elvish Sharpshooter and Fencer
* fixed problem where 'goto numbers' can overwrite part of the right panel
* footsteps disappear as you walk over them
* added engine support for weapons that can reach multiple hexes
* added some utility macros to WML
@ -122,6 +126,7 @@ Version 0.6:
* fixed tool tips to work again
* fix hitpoint bar problem on MacOSX
* added diagnostic to attack calculations
* added error logging and correction for combat related syncing errors
* client_type=ai in preferences-file make player join networked multiplayer games as AI player
* added some comments to code

2
configure vendored
View file

@ -1337,7 +1337,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PACKAGE=wesnoth
MAJOR_VERSION="0"
MINOR_VERSION="6RC2"
MINOR_VERSION="6RC3"
MICRO_VERSION=""
if test "x$MICRO_VERSION" = "x"; then

View file

@ -6,7 +6,7 @@ AC_INIT(wesnoth, 0.5, davidnwhite@optusnet.com.au, Battle for Wesnoth)
AC_REVISION($Version: 0.5$)
PACKAGE=wesnoth
MAJOR_VERSION="0"
MINOR_VERSION="6RC2"
MINOR_VERSION="6RC3"
MICRO_VERSION=""
if test "x$MICRO_VERSION" = "x"; then

View file

@ -14,7 +14,7 @@
{STORM_TRIDENT 10 5}
[side]
type=Commander
type=Fighter
description=Konrad
side=1
canrecruit=1

View file

@ -25,7 +25,6 @@ Defeat
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Thief,Merman
enemy=2,3
[/side]

View file

@ -19,6 +19,7 @@
objectives="
Victory:
@Defeat both enemy leaders
@Defeat at least one enemy leader, and resist until time expires
Defeat:
#Death of Konrad
#Turns run out"
@ -499,12 +500,18 @@ The main cage where they keep most of the mermen is in the south-east!"
[/message]
[/event]
[event]
name=victory
#the ship they came on.
[item]
x=30
y=7
image=galleon.png
[/item]
#define BAY_OF_PEARLS_VICTORY
[message]
id=msg3_16
description=Konrad
message=At last, we have freed the mermen. Go back to the ocean and leave in peace.
message=At last, we have freed the mermen. Go back to the ocean and live in peace.
[/message]
[role]
type=Merman Lord,Merman
@ -545,7 +552,7 @@ The main cage where they keep most of the mermen is in the south-east!"
[message]
id=msg3_22
description=Konrad
message=Oh no! What should we do?
message=Oh no! What shall we do?
[/message]
[message]
@ -573,7 +580,7 @@ The main cage where they keep most of the mermen is in the south-east!"
[/message]
[role]
type=Elvish Champion,Elvish Marshal,Elvish Captain,Elvish Hero,Knight,Elvish Outrider,Paladin,Mage,White Mage,Red Mage
type=Elvish Champion,Elvish Marshal,Elvish Captain,Elvish Hero,Knight,Elvish Outrider,Paladin,Mage,White Mage,Red Mage,Elvish Fighter,Elvish Archer,Elvish Shaman,Horseman
role=Supporter
[/role]
[message]
@ -587,18 +594,215 @@ The main cage where they keep most of the mermen is in the south-east!"
message=You will prevail. I have faith in you. Travel north. Elensefar is but three days travel if you make haste.
[/message]
[message]
id=msg3_29
id=msg3_29a
description=Konrad
message=Very well. Until we meet again, my dear friend!
message=Very well. But how do I get to Elensefar?
[/message]
[message]
id=msg3_30
id=msg3_30a
description=Delfador
message=Farewell, prince.
message=It is north-west of here, but a few leagues inland from the coast. There are two ways you may get there, by ship, or by land. Each way has its own dangers and perils. You must choose how you wish to get there.
[option]
id=msg3_30_optiona
message="Ships? Ugh! I have been sea sick for the last time. We shall walk!"
[command]
[message]
id=msg3_30_farewell
description=Delfador
message=Safe journey to you, Konrad. Until we meet again!
[/message]
[/command]
[command]
[kill]
description=Delfador
[/kill]
[/command]
[command]
[endlevel]
result=victory
next_scenario=Muff_Malals_Peninsula
bonus=yes
[/endlevel]
[/command]
[/option]
[option]
id=msg3_30_optionb
message="At least going by ship we may get a little rest for ourselves. By sea it is!"
[command]
[message]
id=msg3_30_farewella
description=Delfador
message=Safe voyage to you then, Konrad. May the weather be fair.
[/message]
[/command]
[command]
[kill]
description=Delfador
[/kill]
[/command]
[command]
[endlevel]
result=victory
next_scenario=Isle_of_the_Damned
bonus=yes
[/endlevel]
[/command]
[/option]
[/message]
[kill]
description=Delfador
[/kill]
#enddef
#track which enemy leaders are dead
[event]
name=die
[filter]
description=Dwaba-Kukai
[/filter]
[if]
[variable]
name=land_orc_dead
equals=yes
[/variable]
[then]
{BAY_OF_PEARLS_VICTORY}
[/then]
[else]
[set_variable]
name=sea_orc_dead
value=yes
[/set_variable]
[/else]
[/if]
[/event]
[event]
name=die
[filter]
description=Managa'Gwin
[/filter]
[if]
[variable]
name=sea_orc_dead
equals=yes
[/variable]
[then]
{BAY_OF_PEARLS_VICTORY}
[/then]
[else]
[set_variable]
name=land_orc_dead
value=yes
[/set_variable]
[/else]
[/if]
[/event]
[event]
name=time over
[command]
[unit]
x=18
y=1
description=Delfador
type=Elder Mage
[/unit]
[/command]
[command]
#if neither of the enemies are dead, automatically lose
[if]
[variable]
name=sea_orc_dead
not_equals=yes
[/variable]
[variable]
name=land_orc_dead
not_equals=yes
[/variable]
[then]
[message]
id=msg3_31
description=Delfador
message="Have you not been able to defeat our foes in all these days? They have summoned reinforcements. Surely now our doom is upon us!"
[/message]
[endlevel]
result=defeat
[/endlevel]
[/then]
#if we have killed at least one Orcish leader, we
#go on to the next scenario
[else]
[command]
[message]
id=msg3_32
description=Delfador
message="Konrad! We cannot spend anymore time here. Though it would be good to defeat the Orcs and free all we can, more urgent business calls us!"
[/message]
[message]
id=msg3_33
description=Konrad
message=Delfador, thank goodness you have survived! This has been a tough battle, but why can we not finish it? Why must we leave?
[/message]
[message]
id=msg3_34
description=Delfador
message=I bear ill tidings: Asheviere has attacked the city of Elensefar, breaking the treaty between Wesnoth and Elensefar. You must lead our men to the city, to help defend it -- or to recapture it if it falls before you arrive.
[/message]
[message]
id=msg3_35
description=Konrad
message=I must do this? But you are coming with me, aren't you, Delfador?
[/message]
[message]
id=msg3_36
description=Delfador
message=I am afraid not Konrad. I have found some important documents that need seeing to. I must ride at once to make council with the Elves. I will meet you in Elensefar, after you have secured it.
[/message]
[message]
id=msg3_37
description=Konrad
message=I fear I will struggle to do this on my own, but what must be must be. How do I get to Elensefar?
[/message]
[/command]
#if we killed the orc at sea, we travel by ship
#if we killed the orc on land, we travel by land
[command]
[if]
[variable]
name=sea_orc_dead
equals=yes
[/variable]
[then]
[message]
id=msg3_38
description=Delfador
message="Since you have broken the Orc's hegemony over the seas, going by ship would be safest. Sail along the coast, and you can land mere miles from Elensefar. Make haste!"
[/message]
[endlevel]
result=victory
next_scenario=Isle_of_the_Damned
[/endlevel]
[/then]
[else]
[message]
id=msg3_39
description=Delfador
message="With the Orcs controlling the seas, going by ship would not be safe. Travel by land, Elensefar is only six day's march up the coast. Make haste!"
[/message]
[endlevel]
result=victory
next_scenario=Muff_Malals_Peninsula
[/endlevel]
[/else]
[/if]
[/command]
[/else]
[/if]
[/command]
[/event]
[/scenario]

View file

@ -17,11 +17,7 @@
[bigmap]
image=misc/map.png
[dot]
type=cross
x=187
y=187
[/dot]
{CROSS 378 140}
[/bigmap]
objectives="
@ -74,7 +70,6 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider
enemy=2,3,4
[/side]

View file

@ -39,7 +39,7 @@ Defeat:
[/bigmap]
[side]
type=Commander
type=Fighter
description=Konrad
side=1
canrecruit=1
@ -62,11 +62,11 @@ Defeat:
[target]
description=Delfador
value=100
[end]
[/target]
[target]
description=Konrad
value=100
[end]
[/target]
#enddef
#else
#define HIGH_PRIORITY_TARGETS
@ -205,7 +205,7 @@ Defeat:
id=msg1_9hard
description=Knafa-Tan
message="Remember, it is rumored that the filthy mage, and one that he protects is in these parts. They are the ones we want!"
[end]
[/message]
#else
[message]
id=msg1_9

View file

@ -17,11 +17,10 @@
[bigmap]
image=misc/map.png
[dot]
type=cross
x=187
y=187
[/dot]
{DOT 305 202}
{DOT 313 195}
{DOT 321 188}
{CROSS 321 182}
[/bigmap]
objectives="
@ -38,7 +37,7 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider
recruit=Gryphon Rider
enemy=2,3
[/side]

View file

@ -26,6 +26,10 @@ Defeat:
[bigmap]
image=misc/map.png
[dot]
{DOT 262 225}
{DOT 272 214}
{DOT 288 207}
{CROSS 298 208}
type=cross
x=187
y=187
@ -63,7 +67,6 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Merman,Thief
enemy=2,3
[/side]

View file

@ -34,7 +34,6 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider,Dwarvish Fighter,Dwarvish Thunderer
enemy=3
shroud=yes
[/side]

View file

@ -0,0 +1,170 @@
[scenario]
{DAWN}
{MORNING}
{AFTERNOON}
{DUSK}
{FIRST_WATCH}
{SECOND_WATCH}
id=Isle_of_the_Damned
next_scenario=The_Siege_of_Elensefar
name=Isle of the Damned
map=isle_damned
turns=24
music="wesnoth-3.ogg"
objectives="
Victory:
@Defeat both enemy leaders
@Resist until the end of the turns
Defeat:
#Death of Konrad"
disallow_recall=yes
[bigmap]
image=misc/map.png
{DOT 162 367}
{DOT 158 372}
{DOT 151 379}
{DOT 137 385}
{DOT 122 383}
{DOT 109 377}
{DOT 99 369}
{DOT 94 374}
{CROSS 93 386}
[/bigmap]
[side]
description=Konrad
type=Commander
side=1
canrecruit=1
enemy=2,3
controller=human
[unit]
description=Kalba
type=Merman
side=1
x=25
y=10
[/unit]
[unit]
description=Gnaba
type=Merman
side=1
x=29
y=12
[/unit]
[/side]
[side]
description=Haf-Mal
type=Lich
side=2
canrecruit=1
enemy=1
recruit=Skeleton,Vampire Bat,Ghost,Dark Adept,Chocobone
recruitment_pattern=scout,scout,fighter
{GOLD 80 140 200}
[/side]
[side]
description=Jarmal-Gorg
type=Lich
side=3
canrecruit=1
enemy=1
recruit=Skeleton,Skeleton Archer,Walking Corpse,Vampire Bat,Ghost,Dark Adept
{GOLD 80 140 200}
[/side]
[story]
[part]
id=isledamn_1
story="But the voyage did not go as smoothly as had been hoped. A storm lashed down on the ship, all hands were on deck, trying desperately to save the ship. As Konrad attempted to secure the mast, a sudden gust of wind flung him overboard..."
image="misc/map.png"
[/part]
[part]
id=isledamn_2
story="The mermen saved Konrad from the seas, but were not able to get him back to the ship. Instead, they had to resort to taking him to a nearby island..."
image="misc/map.png"
[/part]
[/story]
[event]
name=start
[message]
description=Konrad
id=isledamn_3
message="Whew, I survived. But now where am I? Is this island inhabited?"
[/message]
[message]
description=Kalba
id=isledamn_4
message="There have been ill tidings about this island, my lord. It is said that the fiends of the undead have overtaken it, and it has fallen into ugly wasteland."
[/message]
[message]
description=Konrad
id=isledamn_5
message="Let us hope these rumors are not true! I have none of my men with me! How would I defend myself?"
[/message]
[message]
description=Kalba
id=isledamn_6
message="There are still some Elves on this island, my lord, perhaps if you recruit some of them to help you, we might have some hope of holding off the undead hordes!"
[/message]
[/event]
#define ISLE_GALLEON_ARRIVE
[command]
[move_unit_fake]
type=Galleon
x=23,23,23,23,23,24
y=1,2,3,4,5,5
[/move_unit_fake]
[/command]
[command]
[item]
x=24
y=5
image=galleon.png
[/item]
[/command]
#enddef
[event]
name=enemies defeated
[command]
[message]
description=Konrad
message="We have wrested control of the island from the evil undead! Now all we have to do is wait for the ship to arrive, so we can make our way to Elensefar!"
[/message]
[/command]
{ISLE_GALLEON_ARRIVE}
[/event]
[event]
name=time over
{ISLE_GALLEON_ARRIVE}
[command]
[message]
speaker=narrator
image=elvish-fighter.png
message="Thank goodness we have found you, sir! Come aboard quickly, we shall take you away from this horrible island!"
[/message]
[/command]
[command]
[message]
description=Konrad
message="It is a shame complete victory could not be ours, but thank goodness I am rescued! On to Elensefar!"
[/message]
[/command]
[command]
[endlevel]
result=victory
[/endlevel]
[/command]
[/event]
[/scenario]

View file

@ -17,11 +17,9 @@
[bigmap]
image=misc/map.png
[dot]
type=cross
x=187
y=187
[/dot]
{DOT 354 167}
{DOT 355 157}
{CROSS 356 147}
[/bigmap]
objectives="
@ -38,7 +36,6 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider
enemy=2,3
fog=yes
[/side]
@ -61,15 +58,8 @@ Defeat:
recruitment_pattern=fighter
villages_per_scout=0
#ifdef EASY
gold=150
#endif
#ifdef NORMAL
gold=200
#endif
#ifdef HARD
gold=280
#endif
{GOLD 150 200 280}
enemy=1
[/side]
@ -87,18 +77,8 @@ Defeat:
#endif
recruitment_pattern=fighter,scout,scout,mixed fighter
villages_per_scout=0
#ifdef EASY
gold=200
income=20
#endif
#ifdef NORMAL
gold=250
income=30
#endif
#ifdef HARD
gold=350
income=50
#endif
{GOLD 150 250 350}
{INCOME 15 30 50}
enemy=1
village_value=0
leader_value=10

View file

@ -27,7 +27,7 @@ Defeat:
image=misc/map.png
{DOT 164 348}
{DOT 158 340}
{DOT 142 355}
{DOT 142 338}
{DOT 127 340}
{CROSS 122 331}
[/bigmap]
@ -224,6 +224,11 @@ Defeat:
[kill]
description=Moremirmu
[/kill]
[message]
id=msg4_12b
description=Konrad
message="Victory is ours! Let us just hope that this delay will not hamper our quest to save Elensefar. We must move onward with haste!"
[/message]
[/event]
[event]
name=die

View file

@ -26,11 +26,9 @@ Defeat:
[bigmap]
image=misc/map.png
[dot]
type=cross
x=77
y=235
[/dot]
{DOT 336 180}
{DOT 344 174}
{CROSS 354 167}
[/bigmap]
[side]
@ -41,7 +39,6 @@ Defeat:
experience=0
side=1
canrecruit=1
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider
enemy=2,3
controller=human
[/side]

View file

@ -33,7 +33,6 @@ Defeat
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider
enemy=2
shroud=yes
[/side]

View file

@ -53,8 +53,7 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief
enemy=2
enemy=2
[/side]
[side]

View file

@ -34,7 +34,6 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider,Dwarvish Fighter,Dwarvish Thunderer
enemy=2,3,4,5,6
shroud=yes
[/side]
@ -48,20 +47,8 @@ Defeat:
recruit=Goblin Knight,Wolf Rider,Troll,Orcish Warrior,Orcish Crossbow
enemy=1
#ifdef EASY
gold=300
income=10
#endif
#ifdef MEDIUM
gold=400
income=20
#endif
#ifdef HARD
gold=550
income=30
#endif
{GOLD 150 300 400}
{INCOME 5 10 20}
[/side]
[side]
type=Troll Warrior
@ -72,21 +59,8 @@ Defeat:
recruitment_pattern=fighter
recruit=Troll,Troll Warrior,Ogre
enemy=1
#ifdef EASY
gold=300
income=10
#endif
#ifdef MEDIUM
gold=400
income=20
#endif
#ifdef HARD
gold=550
income=30
#endif
{GOLD 150 300 400}
{INCOME 5 10 20}
[/side]
[side]
@ -99,20 +73,8 @@ Defeat:
recruit=Troll,Troll Warrior,Ogre,Goblin Knight,Wolf Rider,Troll Whelp
enemy=1
#ifdef EASY
gold=200
income=10
#endif
#ifdef MEDIUM
gold=300
income=20
#endif
#ifdef HARD
gold=450
income=30
#endif
{GOLD 150 230 350}
{INCOME 5 10 20}
[/side]
[side]
@ -124,21 +86,8 @@ Defeat:
recruitment_pattern=fighter,fighter,scout
recruit=Troll,Troll Warrior,Ogre,Goblin Knight,Wolf Rider,Troll Whelp
enemy=1
#ifdef EASY
gold=200
income=15
#endif
#ifdef MEDIUM
gold=300
income=20
#endif
#ifdef HARD
gold=450
income=30
#endif
{GOLD 150 230 350}
{INCOME 5 10 20}
[/side]
#dummy enemy that forces finding the Sceptre

View file

@ -123,7 +123,7 @@ Defeat:
[/message]
[message]
id=msg5_4
description=Advisor
role=Advisor
message=There are so many of them. This will not be easy! And look to the south, it seems that the undead are allied with the Orcs!
[/message]
[message]
@ -139,7 +139,7 @@ Defeat:
[/event]
[event]
name=turn 6
name=turn 5
[unit]
description=Reglok
type=Thief
@ -178,7 +178,7 @@ Defeat:
[/message]
[message]
id=msg5_10
description=Advisor
role=Advisor
message=Thieves hmmm? Who says we can trust such as you?
[/message]
[message]
@ -187,9 +187,41 @@ Defeat:
message=We would understand if you don't trust us, of course, but it is in both our best interests to rid the city of the Orcs!
[/message]
[message]
id=msg5_12
id=msg5_12a
description=Konrad
message=Very well, you may join us.
message=Hmm...I have to consider this...
[option]
id=accept_thieves
message="Very well. You may join us."
[command]
[message]
id=msg5_13
description=Gamlel
message=We will serve you well, for we respect the help you are providing to our city. You shall find that there is honor, even among thieves.
[/message]
[set_variable]
name=have_thieves
value=yes
[/set_variable]
[can_recruit]
type=Thief
[/can_recruit]
[/command]
[/option]
[option]
id=reject_thieves
message="We cannot trust your motives. We shall rescue the city on our own!"
[command]
[message]
id=msg5_14
description=Gamlel
message="Very well, we shall begone then. You shall have to recapture the city without our help!"
[/message]
[kill]
type=Thief
[/kill]
[/command]
[/option]
[/message]
[/event]

View file

@ -26,7 +26,6 @@ Defeat
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Thief,Merman
enemy=2,3
[/side]

View file

@ -27,7 +27,6 @@ Defeat
canrecruit=1
controller=human
hitpoints=80
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Thief,Merman
enemy=2,3,4,5,6
[/side]

View file

@ -33,7 +33,7 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief,Gryphon Rider,Dwarvish Fighter,Dwarvish Thunderer
recruit=Dwarvish Fighter,Dwarvish Thunderer
enemy=3,4
shroud=yes
[/side]

View file

@ -25,11 +25,10 @@ Defeat:
[bigmap]
image=misc/map.png
[dot]
type=cross
x=207
y=147
[/dot]
{DOT 250 255}
{DOT 251 246}
{DOT 253 239}
{CROSS 253 231}
[/bigmap]
[side]
@ -38,7 +37,6 @@ Defeat:
side=1
canrecruit=1
controller=human
recruit=Elvish Scout,Elvish Fighter,Elvish Archer,Horseman,Mage,Elvish Shaman,Merman,Thief
recruitment_pattern=scout,fighter,archer
aggression=-0.5
village_value=1.0

View file

@ -20,7 +20,7 @@ Defeat:
[side]
race=Elves
type=Commander
type=Fighter
description=Konrad
experience=0
side=1

View file

@ -7,7 +7,7 @@ hitpoints=45
ability=leadership
movement_type=smallfoot
movement=6
experience=80
experience=60
level=2
alignment=lawful
advanceto=Lord
@ -29,15 +29,15 @@ get_hit_sound=groan.wav
name=sword
type=blade
range=short
damage=6
damage=8
number=4
[frame]
begin=-350
end=-300
begin=-250
end=-200
image=konrad-commander.png
[/frame]
[frame]
begin=-300
begin=-200
end=-100
image="konrad-commander-attack1.png"
[/frame]
@ -66,7 +66,7 @@ get_hit_sound=groan.wav
name=bow
type=pierce
range=long
damage=3
damage=6
number=3
[sound]
time=-100

39
data/units/Fighter.cfg Normal file
View file

@ -0,0 +1,39 @@
[unit]
name=Fighter
race=human
image=konrad-young.png
profile=misc/konrad.png
hitpoints=32
movement_type=smallfoot
movement=6
experience=28
level=1
alignment=lawful
advanceto=Commander
cost=100
usage=mixed fighter
unit_description="The fighter is skilled with use of the sword in battle. Young and brash, he is vulnerable to attack from enemies. However he has the potential to one day become a great warrior"
get_hit_sound=groan.wav
[attack]
name=sword
type=blade
range=short
damage=6
number=3
[frame]
begin=-250
end=-100
image=konrad-young.png
[/frame]
[frame]
begin=-100
end=50
image="konrad-young-attack.png"
[/frame]
[sound]
time=-250
sound=sword-swish.wav
[/sound]
[/attack]
[/unit]

View file

@ -12,7 +12,7 @@ alignment=chaotic
advanceto=null
cost=22
usage=scout
unit_desc="Some goblins train their wolves to dont fear file. Wielding tochs and nets goblin pillager bring havok to his enemies."
unit_description="Some goblins train their wolves to overcome their fear of fire. Wielding torches and nets, Goblin Pillagers bring havoc to their enemies."
get_hit_sound=groan.wav
[attack]
name=torch

View file

@ -3,7 +3,7 @@ name=Lord
race=human
image=konrad-lord.png
profile=misc/konrad.png
hitpoints=65
hitpoints=68
ability=leadership
movement_type=smallfoot
movement=6
@ -19,7 +19,7 @@ get_hit_sound=groan.wav
name=sword
type=blade
range=short
damage=11
damage=14
number=4
[/attack]
[attack]

View file

@ -20,5 +20,10 @@ usage=fighter
range=short
damage=14
number=2
[frame]
begin=-100
end=100
image=troll-grunt-attack.png
[/frame]
[/attack]
[/unit]

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

View file

@ -341,15 +341,72 @@ void attack(display& gui, const gamemap& map,
while(stats.nattacks > 0 || stats.ndefends > 0) {
if(stats.nattacks > 0) {
const bool hits = (get_random()%100) < stats.chance_to_hit_defender;
const bool dies = gui.unit_attack(attacker,defender,
hits ? stats.damage_defender_takes : 0,
a->second.attacks()[attack_with]);
const int ran_num = get_random();
bool hits = (ran_num%100) < stats.chance_to_hit_defender;
//make sure that if we're serializing a game here,
//we got the same results as the game did originally
const config* ran_results = get_random_results();
if(ran_results != NULL) {
const int results_chance = atoi((*ran_results)["chance"].c_str());
const bool results_hits = (*ran_results)["hits"] == "yes";
const int results_damage = atoi((*ran_results)["damage"].c_str());
if(results_chance != stats.chance_to_hit_defender) {
std::cerr << "SYNC ERROR: In attack " << a->second.type().name() << " vs "
<< d->second.type().name() << ": chance to hit defender is inconsistent. Data source: "
<< results_chance << "; Calculation: " << stats.chance_to_hit_defender
<< " (over-riding game calculations with data source results)\n";
hits = results_hits;
} else if(hits != results_hits) {
std::cerr << "SYNC ERROR: In attack " << a->second.type().name() << " vs "
<< d->second.type().name() << ": the data source says the hit was "
<< (results_hits ? "successful" : "unsuccessful") << ", while in-game calculations say the hit was "
<< (hits ? "successful" : "unsuccessful")
<< " random number: " << ran_num << " = " << (ran_num%100) << "/" << results_chance
<< " (over-riding game calculations with data source results)\n";
hits = results_hits;
} else if(results_damage != stats.damage_defender_takes) {
std::cerr << "SYNC ERROR: In attack " << a->second.type().name() << " vs "
<< d->second.type().name() << ": the data source says the hit did "
<< results_damage << " damage, while in-game calculations show the hit doing "
<< stats.damage_defender_takes << " damage (over-riding game calculations with data source results)\n";
stats.damage_defender_takes = results_damage;
}
}
bool dies = gui.unit_attack(attacker,defender,
hits ? stats.damage_defender_takes : 0,
a->second.attacks()[attack_with]);
if(ran_results == NULL) {
config cfg;
cfg["hits"] = (hits ? "yes" : "no");
cfg["dies"] = (dies ? "yes" : "no");
char buf[50];
sprintf(buf,"%d",stats.damage_defender_takes);
cfg["damage"] = buf;
sprintf(buf,"%d",stats.chance_to_hit_defender);
cfg["chance"] = buf;
set_random_results(cfg);
} else {
const bool results_dies = (*ran_results)["dies"] == "yes";
if(results_dies != dies) {
std::cerr << "SYNC ERROR: In attack" << a->second.type().name() << " vs "
<< d->second.type().name() << ": the data source the unit "
<< (results_dies ? "perished" : "survived") << " while in-game calculations show the unit "
<< (dies ? "perished" : "survived") << " (over-riding game calculations with data source results)\n";
dies = results_dies;
}
}
if(dies) {
attackerxp = 8*d->second.type().level();
if(d->second.type().level() == 0)
attackerxp = 4;
a->second.get_experience(attackerxp);
attackerxp = 0;
defenderxp = 0;
gamemap::location loc = d->first;
@ -397,16 +454,72 @@ void attack(display& gui, const gamemap& map,
}
if(stats.ndefends > 0) {
const bool hits = (get_random()%100) < stats.chance_to_hit_attacker;
const bool dies = gui.unit_attack(defender,attacker,
const int ran_num = get_random();
bool hits = (ran_num%100) < stats.chance_to_hit_attacker;
//make sure that if we're serializing a game here,
//we got the same results as the game did originally
const config* ran_results = get_random_results();
if(ran_results != NULL) {
const int results_chance = atoi((*ran_results)["chance"].c_str());
const bool results_hits = (*ran_results)["hits"] == "yes";
const int results_damage = atoi((*ran_results)["damage"].c_str());
if(results_chance != stats.chance_to_hit_attacker) {
std::cerr << "SYNC ERROR: In defend " << a->second.type().name() << " vs "
<< d->second.type().name() << ": chance to hit attacker is inconsistent. Data source: "
<< results_chance << "; Calculation: " << stats.chance_to_hit_attacker
<< " (over-riding game calculations with data source results)\n";
hits = results_hits;
} else if(hits != results_hits) {
std::cerr << "SYNC ERROR: In defend " << a->second.type().name() << " vs "
<< d->second.type().name() << ": the data source says the hit was "
<< (results_hits ? "successful" : "unsuccessful") << ", while in-game calculations say the hit was "
<< (hits ? "successful" : "unsuccessful")
<< " random number: " << ran_num << " = " << (ran_num%100) << "/" << results_chance
<< " (over-riding game calculations with data source results)\n";
hits = results_hits;
} else if(results_damage != stats.damage_attacker_takes) {
std::cerr << "SYNC ERROR: In defend " << a->second.type().name() << " vs "
<< d->second.type().name() << ": the data source says the hit did "
<< results_damage << " damage, while in-game calculations show the hit doing "
<< stats.damage_attacker_takes << " damage (over-riding game calculations with data source results)\n";
stats.damage_attacker_takes = results_damage;
}
}
bool dies = gui.unit_attack(defender,attacker,
hits ? stats.damage_attacker_takes : 0,
d->second.attacks()[stats.defend_with]);
if(ran_results == NULL) {
config cfg;
cfg["hits"] = (hits ? "yes" : "no");
cfg["dies"] = (dies ? "yes" : "no");
char buf[50];
sprintf(buf,"%d",stats.damage_attacker_takes);
cfg["damage"] = buf;
sprintf(buf,"%d",stats.chance_to_hit_attacker);
cfg["chance"] = buf;
set_random_results(cfg);
} else {
const bool results_dies = (*ran_results)["dies"] == "yes";
if(results_dies != dies) {
std::cerr << "SYNC ERROR: In defend" << a->second.type().name() << " vs "
<< d->second.type().name() << ": the data source the unit "
<< (results_dies ? "perished" : "survived") << " while in-game calculations show the unit "
<< (dies ? "perished" : "survived") << " (over-riding game calculations with data source results)\n";
dies = results_dies;
}
}
if(dies) {
defenderxp = 8*a->second.type().level();
if(a->second.type().level() == 0)
defenderxp = 4;
d->second.get_experience(defenderxp);
defenderxp = 0;
attackerxp = 0;
gamemap::location loc = a->first;
@ -742,6 +855,10 @@ void check_victory(std::map<gamemap::location,unit>& units,
}
if(found_enemies == false) {
if(found_human) {
game_events::fire("enemies defeated");
}
throw end_level_exception(found_human ? VICTORY : DEFEAT);
}

View file

@ -572,6 +572,17 @@ config::const_child_itors config::child_range(const std::string& key) const
}
}
const config::child_list& config::get_children(const std::string& key) const
{
const child_map::const_iterator i = children.find(key);
if(i != children.end()) {
return i->second;
} else {
static const child_list dummy;
return dummy;
}
}
config* config::child(const std::string& key)
{
const child_map::const_iterator i = children.find(key);

View file

@ -107,6 +107,8 @@ struct config
child_itors child_range(const std::string& key);
const_child_itors child_range(const std::string& key) const;
const child_list& get_children(const std::string& key) const;
config* child(const std::string& key);
const config* child(const std::string& key) const;

View file

@ -1077,7 +1077,7 @@ void display::draw_tile(int x, int y, SDL_Surface* unit_image,
Pixel grid_colour = SDL_MapRGB(dst->format,0,0,0);
const bool show_unit_colour = preferences::show_side_colours() && it != units_.end();
const bool show_unit_colour = preferences::show_side_colours() && !fogged(x,y) && it != units_.end();
if(show_unit_colour) {
const SDL_Color& colour = font::get_side_colour(it->second.side());
grid_colour = SDL_MapRGB(dst->format,colour.r,colour.g,colour.b);
@ -1344,13 +1344,14 @@ void display::draw_footstep(const gamemap::location& loc, int xloc, int yloc)
draw_unit(xloc,yloc,image,hflip,vflip,0.5);
if(show_time && route_.move_left > 0 && route_.move_left < 10) {
const SDL_Rect rect = {0,0,this->mapx(),this->y()};
static std::string str(1,'x');
str[0] = '0' + route_.move_left + 1;
const SDL_Rect& text_area =
font::draw_text(NULL,screen_area(),18,font::BUTTON_COLOUR,str,0,0);
font::draw_text(NULL,rect,18,font::BUTTON_COLOUR,str,0,0);
const int x = xloc + int(zoom_/2.0) - text_area.w/2;
const int y = yloc + int(zoom_/2.0) - text_area.h/2;
font::draw_text(this,screen_area(),18,font::BUTTON_COLOUR,str,x,y);
font::draw_text(this,rect,18,font::BUTTON_COLOUR,str,x,y);
}
}

View file

@ -64,7 +64,7 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
try {
LEVEL_RESULT res = REPLAY;
state.label = scenario->values["name"];
state.label = translate_string_default((*scenario)["id"],(*scenario)["name"]);
recorder.set_save_info(state);
@ -122,7 +122,7 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
//if this isn't the last scenario, then save the game
if(scenario != NULL) {
state.label = (*scenario)["name"];
state.label = translate_string_default((*scenario)["id"],(*scenario)["name"]);
const int should_save = dialogs::get_save_name(disp,
string_table["save_game_message"],

View file

@ -22,7 +22,7 @@ namespace game_config
const int cure_amount = 8;
const int curer_heals_per_turn = 18;
const int recall_cost = 20;
const std::string version = "0.6RC2";
const std::string version = "0.6RC3";
bool debug = false;
#ifdef WESNOTH_PATH

View file

@ -23,6 +23,7 @@
#include <cstdlib>
#include <deque>
#include <iostream>
#include <iterator>
#include <set>
#include <string>
@ -134,6 +135,7 @@ std::map<gamemap::location,unit>* units = NULL;
std::vector<team>* teams = NULL;
game_state* state_of_game = NULL;
game_data* game_data_ptr = NULL;
std::set<std::string> used_items;
bool events_init() { return screen != NULL; }
@ -173,7 +175,7 @@ public:
return cfg_->children["filter_second"];
}
void handle_event(const queued_event& event_info, config* cfg=NULL);
void handle_event(const queued_event& event_info, const config* cfg=NULL);
private:
std::string name_;
@ -218,31 +220,79 @@ std::vector<gamemap::location> multiple_locs(const config& cfg)
std::multimap<std::string,event_handler> events_map;
void event_handler::handle_event(const queued_event& event_info, config* cfg)
void event_handler::handle_event(const queued_event& event_info, const config* cfg)
{
if(cfg == NULL)
cfg = cfg_;
std::vector<config*>::const_iterator i;
//sub commands that need to be handled in a guaranteed ordering
std::vector<config*>& commands = cfg->children["command"];
for(std::vector<config*>::iterator cmd = commands.begin();
cmd != commands.end(); ++cmd) {
handle_event(event_info,*cmd);
const config::child_list& commands = cfg->get_children("command");
for(i = commands.begin(); i != commands.end(); ++i) {
handle_event(event_info,*i);
}
//allow a side to recruit a new type of unit
const config::child_list& allow_recruit = cfg->get_children("allow_recruit");
for(i = allow_recruit.begin(); i != allow_recruit.end(); ++i) {
const int side = maximum<int>(1,atoi((**i)["side"].c_str()));
const size_t index = side-1;
if(index > teams->size())
continue;
const std::string& type = (**i)["type"];
(*teams)[index].recruits().insert(type);
if(index == 0) {
state_of_game->can_recruit.insert(type);
}
}
//remove the ability to recruit a unit from a certain side
const config::child_list& disallow_recruit = cfg->get_children("disallow_recruit");
for(i = disallow_recruit.begin(); i != disallow_recruit.end(); ++i) {
const int side = maximum<int>(1,atoi((**i)["side"].c_str()));
const size_t index = side-1;
if(index > teams->size())
continue;
const std::string& type = (**i)["type"];
(*teams)[index].recruits().erase(type);
if(index == 0) {
state_of_game->can_recruit.erase(type);
}
}
const config::child_list& set_recruit = cfg->get_children("set_recruit");
for(i = set_recruit.begin(); i != set_recruit.end(); ++i) {
const int side = maximum<int>(1,atoi((**i)["side"].c_str()));
const size_t index = side-1;
if(index > teams->size())
continue;
std::vector<std::string> recruit = config::split((**i)["recruit"]);
if(recruit.size() == 1 && recruit.back() == "")
recruit.clear();
std::set<std::string>& can_recruit = (*teams)[index].recruits();
can_recruit.clear();
std::copy(recruit.begin(),recruit.end(),std::inserter(can_recruit,can_recruit.end()));
if(index == 0) {
state_of_game->can_recruit = can_recruit;
}
}
//sounds
std::vector<config*>& sounds = cfg->children["sound"];
for(std::vector<config*>::iterator sfx = sounds.begin();
sfx != sounds.end(); ++sfx) {
sound::play_sound((*sfx)->values["name"]);
const config::child_list& sounds = cfg->get_children("sound");
for(i = sounds.begin(); i != sounds.end(); ++i) {
sound::play_sound((**i)["name"]);
}
std::vector<config*>& colour_adjust = cfg->children["colour_adjust"];
for(std::vector<config*>::const_iterator col = colour_adjust.begin();
col != colour_adjust.end(); ++col) {
const int r = atoi((**col)["red"].c_str());
const int g = atoi((**col)["green"].c_str());
const int b = atoi((**col)["blue"].c_str());
const config::child_list& colour_adjust = cfg->get_children("colour_adjust");
for(i = colour_adjust.begin(); i != colour_adjust.end(); ++i) {
const int r = atoi((**i)["red"].c_str());
const int g = atoi((**i)["green"].c_str());
const int b = atoi((**i)["blue"].c_str());
screen->adjust_colours(r,g,b);
screen->invalidate_all();
screen->draw(true,true);
@ -276,11 +326,10 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
//an award of gold to a particular side
std::vector<config*>& gold = cfg->children["gold"];
for(std::vector<config*>::iterator gd = gold.begin(); gd!=gold.end();++gd) {
string_map& values = (*gd)->values;
const std::string& side = values["side"];
const std::string& amount = values["amount"];
const config::child_list& gold = cfg->get_children("gold");
for(i = gold.begin(); i != gold.end(); ++i) {
const std::string& side = (**i)["side"];
const std::string& amount = (**i)["amount"];
const int side_num = side.empty() ? 1 : atoi(side.c_str());
const int amount_num = atoi(amount.c_str());
const size_t team_index = side_num-1;
@ -291,16 +340,14 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
//moving a 'unit' - i.e. a dummy unit that is just moving for
//the visual effect
std::vector<config*>& move_unit_fake = cfg->children["move_unit_fake"];
for(std::vector<config*>::iterator muf = move_unit_fake.begin();
muf != move_unit_fake.end(); ++muf) {
const std::string& type = (*muf)->values["type"];
const game_data::unit_type_map::const_iterator itor =
game_data_ptr->unit_types.find(type);
const config::child_list& move_unit_fake = cfg->get_children("move_unit_fake");
for(i = move_unit_fake.begin(); i != move_unit_fake.end(); ++i) {
const std::string& type = (**i)["type"];
const game_data::unit_type_map::const_iterator itor = game_data_ptr->unit_types.find(type);
if(itor != game_data_ptr->unit_types.end()) {
unit dummy_unit(&itor->second,0);
const std::vector<std::string> xvals = config::split((*muf)->values["x"]);
const std::vector<std::string> yvals = config::split((*muf)->values["y"]);
const std::vector<std::string> xvals = config::split((**i)["x"]);
const std::vector<std::string> yvals = config::split((**i)["y"]);
std::vector<gamemap::location> path;
for(size_t i = 0; i != minimum(xvals.size(),yvals.size()); ++i) {
path.push_back(gamemap::location(atoi(xvals[i].c_str())-1,
@ -312,17 +359,16 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
//setting a variable
std::vector<config*>& set_vars = cfg->children["set_variable"];
for(std::vector<config*>::iterator var = set_vars.begin();
var != set_vars.end(); ++var) {
string_map& vals = (*var)->values;
const std::string& name = vals["name"];
const std::string& value = vals["value"];
const config::child_list& set_vars = cfg->get_children("set_variable");
for(i = set_vars.begin(); i != set_vars.end(); ++i) {
const std::string& name = (**i)["name"];
const std::string& value = (**i)["value"];
if(value.empty() == false) {
state_of_game->variables[name] = value;
}
const std::string& add = vals["add"];
const std::string& add = (**i)["add"];
if(add.empty() == false) {
double value = atof(state_of_game->variables[name].c_str());
value += atof(add.c_str());
@ -331,7 +377,7 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
state_of_game->variables[name] = buf;
}
const std::string& multiply = vals["multiply"];
const std::string& multiply = (**i)["multiply"];
if(multiply.empty() == false) {
double value = atof(state_of_game->variables[name].c_str());
value *= atof(multiply.c_str());
@ -342,28 +388,26 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
//conditional statements
std::vector<config*>& conditionals = cfg->children["if"];
for(std::vector<config*>::iterator cond = conditionals.begin();
cond != conditionals.end(); ++cond) {
const config::child_list& conditionals = cfg->get_children("if");
for(i = conditionals.begin(); i != conditionals.end(); ++i) {
const std::string type = game_events::conditional_passed(
*state_of_game,units,**cond) ? "then":"else";
*state_of_game,units,**i) ? "then":"else";
//if the if statement passed, then execute all 'then' statements,
//otherwise execute 'else' statements
std::vector<config*>& commands = (*cond)->children[type];
for(std::vector<config*>::iterator cmd = commands.begin();
const std::vector<config*>& commands = (*i)->get_children(type);
for(std::vector<config*>::const_iterator cmd = commands.begin();
cmd != commands.end(); ++cmd) {
handle_event(event_info,*cmd);
}
}
//if we are assigning a role to a unit from the available units list
std::vector<config*>& assign_role = cfg->children["role"];
for(std::vector<config*>::iterator rl = assign_role.begin();
rl != assign_role.end(); ++rl) {
const config::child_list& assign_role = cfg->get_children("role");
for(i = assign_role.begin(); i != assign_role.end(); ++i) {
//get a list of the types this unit can be
std::vector<std::string> types = config::split((*rl)->values["type"]);
std::vector<std::string> types = config::split((**i)["type"]);
//iterate over all the types, and for each type, try to find
//a unit that matches
@ -375,7 +419,7 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
std::map<gamemap::location,unit>::iterator itor;
for(itor = units->begin(); itor != units->end(); ++itor) {
if(itor->second.matches_filter(cfg)) {
itor->second.assign_role((*rl)->values["role"]);
itor->second.assign_role((**i)["role"]);
break;
}
}
@ -388,7 +432,7 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
for(ui = state_of_game->available_units.begin();
ui != state_of_game->available_units.end(); ++ui) {
if(ui->matches_filter(cfg)) {
ui->assign_role((*rl)->values["role"]);
ui->assign_role((**i)["role"]);
break;
}
}
@ -404,10 +448,9 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
break;
}
std::vector<config*>& remove_overlays = cfg->children["removeitem"];
for(std::vector<config*>::iterator rm = remove_overlays.begin();
rm != remove_overlays.end(); ++rm) {
gamemap::location loc(**rm);
const config::child_list& remove_overlays = cfg->get_children("removeitem");
for(i = remove_overlays.begin(); i != remove_overlays.end(); ++i) {
gamemap::location loc(**i);
if(!loc.valid()) {
loc = event_info.loc1;
}
@ -416,25 +459,23 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
//hiding units
std::vector<config*>& hide = cfg->children["hide_unit"];
for(std::vector<config*>::iterator hd = hide.begin();
hd != hide.end(); ++hd) {
const gamemap::location loc(**hd);
const config::child_list& hide = cfg->get_children("hide_unit");
for(i = hide.begin(); i != hide.end(); ++i) {
const gamemap::location loc(**i);
screen->hide_unit(loc);
screen->draw_tile(loc.x,loc.y);
}
if(cfg->children["unhide_unit"].empty() == false) {
if(cfg->child("unhide_unit") != NULL) {
const gamemap::location loc = screen->hide_unit(gamemap::location());
screen->draw_tile(loc.x,loc.y);
}
//adding new items
std::vector<config*>& add_overlays = cfg->children["item"];
for(std::vector<config*>::iterator ni = add_overlays.begin();
ni != add_overlays.end(); ++ni) {
gamemap::location loc(**ni);
const std::string& img = (*ni)->values["image"];
const config::child_list& add_overlays = cfg->get_children("item");
for(i = add_overlays.begin(); i != add_overlays.end(); ++i) {
gamemap::location loc(**i);
const std::string& img = (**i)["image"];
if(!img.empty()) {
screen->add_overlay(loc,img);
screen->draw_tile(loc.x,loc.y);
@ -442,13 +483,12 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
//changing the terrain
std::vector<config*>& terrain_changes = cfg->children["terrain"];
for(std::vector<config*>::iterator tc = terrain_changes.begin();
tc != terrain_changes.end(); ++tc) {
const std::vector<gamemap::location> locs = multiple_locs(**tc);
const config::child_list& terrain_changes = cfg->get_children("terrain");
for(i = terrain_changes.begin(); i != terrain_changes.end(); ++i) {
const std::vector<gamemap::location> locs = multiple_locs(**i);
for(std::vector<gamemap::location>::const_iterator loc = locs.begin(); loc != locs.end(); ++loc) {
const std::string& terrain_type = (**tc)["letter"];
const std::string& terrain_type = (**i)["letter"];
if(terrain_type.size() > 0) {
game_map->set_terrain(*loc,terrain_type[0]);
screen->recalculate_minimap();
@ -458,11 +498,10 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
//if we should spawn a new unit on the map somewhere
std::vector<config*>& new_units = cfg->children["unit"];
for(std::vector<config*>::iterator ui = new_units.begin();
ui != new_units.end(); ++ui) {
unit new_unit(*game_data_ptr,**ui);
gamemap::location loc(**ui);
const config::child_list& new_units = cfg->get_children("unit");
for(i = new_units.begin(); i != new_units.end(); ++i) {
unit new_unit(*game_data_ptr,**i);
gamemap::location loc(**i);
if(game_map->on_board(loc)) {
loc = find_vacant_tile(*game_map,*units,loc);
@ -474,32 +513,28 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
//if we should recall units that match a certain description
std::vector<config*>& recalls = cfg->children["recall"];
for(std::vector<config*>::iterator ir = recalls.begin();
ir != recalls.end(); ++ir) {
const config::child_list& recalls = cfg->get_children("recall");
for(i = recalls.begin(); i != recalls.end(); ++i) {
std::vector<unit>& avail = state_of_game->available_units;
for(std::vector<unit>::iterator u = avail.begin();
u != avail.end(); ++u) {
if(u->matches_filter(**ir)) {
recruit_unit(*game_map,1,*units,*u,gamemap::location(),
screen,false);
for(std::vector<unit>::iterator u = avail.begin(); u != avail.end(); ++u) {
if(u->matches_filter(**i)) {
recruit_unit(*game_map,1,*units,*u,gamemap::location(),screen,false);
avail.erase(u);
break;
}
}
}
std::vector<config*>& objects = cfg->children["object"];
for(std::vector<config*>::iterator obj = objects.begin();
obj != objects.end(); ++obj) {
string_map& values = (*obj)->values;
//if this item has already been used
if(values["used"].empty() == false)
continue;
const config::child_list& objects = cfg->get_children("object");
for(i = objects.begin(); i != objects.end(); ++i) {
const config& values = **i;
const std::string& id = values["id"];
//if this item has already been used
if(used_items.count(id))
continue;
const std::string image = values["image"];
std::string caption = values["name"];
@ -507,15 +542,14 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
if(caption_lang.empty() == false)
caption = caption_lang;
const std::map<gamemap::location,unit>::iterator u =
units->find(event_info.loc1);
const std::map<gamemap::location,unit>::iterator u = units->find(event_info.loc1);
if(u == units->end())
continue;
std::string text;
std::vector<config*>& filters = (*obj)->children["filter"];
const config::child_list& filters = (*i)->get_children("filter");
if(filters.empty() || u->second.matches_filter(*filters[0])) {
const std::string& lang = string_table[id];
@ -524,13 +558,13 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
else
text = values["description"];
u->second.add_modification("object",**obj);
u->second.add_modification("object",**i);
screen->remove_overlay(event_info.loc1);
screen->select_hex(event_info.loc1);
screen->invalidate_unit();
//mark that this item won't be used again
values["used"] = "true";
used_items.insert(id);
} else {
const std::string& lang = string_table[id + "_cannot_use"];
if(!lang.empty())
@ -551,10 +585,9 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
screen->draw();
}
std::vector<config*>& messages = cfg->children["message"];
for(std::vector<config*>::iterator msg = messages.begin();
msg != messages.end(); ++msg) {
string_map& values = (*msg)->values;
const config::child_list& messages = cfg->get_children("message");
for(i = messages.begin(); i != messages.end(); ++i) {
const config& values = **i;
std::map<gamemap::location,unit>::iterator speaker = units->end();
if(values["speaker"] == "unit") {
@ -564,7 +597,7 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
} else if(values["speaker"] != "narrator") {
for(speaker = units->begin(); speaker != units->end();
++speaker){
if(speaker->second.matches_filter(**msg))
if(speaker->second.matches_filter(**i))
break;
}
@ -575,21 +608,21 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
}
}
const string_map::const_iterator sfx = values.find("sound");
if(sfx != values.end()) {
sound::play_sound(sfx->second);
const std::string& sfx = values["sound"];
if(sfx != "") {
sound::play_sound(sfx);
}
const std::string& id = values["id"];
std::string image = (*msg)->values["image"];
std::string image = values["image"];
std::string caption;
const std::string& lang_caption = string_table[id + "_caption"];
if(!lang_caption.empty())
caption = lang_caption;
else
caption = (*msg)->values["caption"];
caption = values["caption"];
if(speaker != units->end()) {
screen->highlight_hex(speaker->first);
@ -610,12 +643,12 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
std::vector<std::string> options;
std::vector<std::vector<config*>*> option_events;
std::vector<config*>& menu_items = (*msg)->children["option"];
for(std::vector<config*>::iterator mi = menu_items.begin();
std::cerr << "building menu items...\n";
const std::vector<config*>& menu_items = (*i)->get_children("option");
for(std::vector<config*>::const_iterator mi = menu_items.begin();
mi != menu_items.end(); ++mi) {
const std::string& lang_msg = string_table[(*mi)->values["id"]];
const std::string& msg = lang_msg.empty() ?
(*mi)->values["message"] : lang_msg;
const std::string& msg = translate_string_default((**mi)["id"],(**mi)["message"]);
options.push_back(msg);
option_events.push_back(&(*mi)->children["command"]);
}
@ -638,8 +671,11 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
throw replay::error();
}
const std::string& val =
cfg->children["choose"].front()->values["value"];
if(size_t(option_chosen) >= options.size()) {
option_chosen = 0;
}
const std::string& val = cfg->children["choose"].front()->values["value"];
option_chosen = atol(val.c_str());
} else if(options.empty() == false) {
@ -648,40 +684,38 @@ void event_handler::handle_event(const queued_event& event_info, config* cfg)
if(options.empty() == false) {
assert(size_t(option_chosen) < menu_items.size());
std::vector<config*>& events = *option_events[option_chosen];
for(std::vector<config*>::iterator ev = events.begin();
const std::vector<config*>& events = *option_events[option_chosen];
for(std::vector<config*>::const_iterator ev = events.begin();
ev != events.end(); ++ev) {
handle_event(event_info,*ev);
}
}
}
std::vector<config*>& dead_units = cfg->children["kill"];
for(std::vector<config*>::iterator du = dead_units.begin();
du != dead_units.end(); ++du) {
const config::child_list& dead_units = cfg->get_children("kill");
for(i = dead_units.begin(); i != dead_units.end(); ++i) {
for(std::map<gamemap::location,unit>::iterator i = units->begin();
i != units->end(); ++i) {
while(i->second.matches_filter(**du) && i != units->end()) {
units->erase(i);
i = units->begin();
for(std::map<gamemap::location,unit>::iterator un = units->begin();
un != units->end(); ++un) {
while(un != units->end() && un->second.matches_filter(**i)) {
units->erase(un);
un = units->begin();
}
}
std::vector<unit>& avail_units = state_of_game->available_units;
for(std::vector<unit>::iterator j = avail_units.begin();
j != avail_units.end(); ++j) {
while(j->matches_filter(**du) && j != avail_units.end()) {
while(j != avail_units.end() && j->matches_filter(**i)) {
j = avail_units.erase(j);
}
}
}
//adding of new events
std::vector<config*>& new_events = cfg->children["event"];
for(std::vector<config*>::iterator ne = new_events.begin();
ne != new_events.end(); ++ne) {
event_handler new_handler(*ne);
const config::child_list& new_events = cfg->get_children("event");
for(i = new_events.begin(); i != new_events.end(); ++i) {
event_handler new_handler(*i);
events_map.insert(std::pair<std::string,event_handler>(
new_handler.name(),new_handler));
}
@ -832,6 +866,8 @@ manager::manager(config& cfg, display& gui_, gamemap& map_,
units = &units_;
state_of_game = &state_of_game_;
game_data_ptr = &game_data_;
used_items.clear();
}
manager::~manager() {

View file

@ -18,6 +18,7 @@
#include <algorithm>
#include <cstdio>
#include <iterator>
#include <sstream>
time_of_day::time_of_day(config& cfg)
@ -117,6 +118,14 @@ game_state read_game(game_data& data, config* cfg)
res.starting_pos = *starts[0];
}
res.can_recruit.clear();
const std::string& can_recruit_str = (*cfg)["can_recruit"];
if(can_recruit_str != "") {
const std::vector<std::string> can_recruit = config::split(can_recruit_str);
std::copy(can_recruit.begin(),can_recruit.end(),std::inserter(res.can_recruit,res.can_recruit.end()));
}
return res;
}
@ -151,6 +160,16 @@ void write_game(const game_state& game, config& cfg)
}
cfg.children["start"].push_back(new config(game.starting_pos));
std::stringstream can_recruit;
std::copy(game.can_recruit.begin(),game.can_recruit.end(),std::ostream_iterator<std::string>(can_recruit,","));
std::string can_recruit_str = can_recruit.str();
//remove the trailing comma
if(can_recruit_str.size() > 0)
can_recruit_str.resize(can_recruit_str.size()-1);
cfg["can_recruit"] = can_recruit_str;
}
//a structure for comparing to save_info objects based on their modified time.

View file

@ -88,10 +88,12 @@ struct game_state
std::string campaign_type; //type of the game - campaign, multiplayer etc
std::string scenario; //the scenario being played
int gold; //amount of gold the player has saved
std::vector<unit> available_units; //units the player has to recall
std::vector<unit> available_units; //units the player may recall
std::map<std::string,std::string> variables; //variables that have been set
std::string difficulty; //the difficulty level the game is being played on.
std::set<std::string> can_recruit; //units the player has the ability to recruit
//if the game is saved mid-level, we have a series of replay steps to
//take the game up to the position it was saved at.
config replay_data;

View file

@ -36,7 +36,7 @@ const std::string& translate_string(const std::string& str)
const std::string& translate_string_default(const std::string& str, const std::string& default_val)
{
const string_map::const_iterator i = string_table.find(str);
if(i != string_table.end())
if(i != string_table.end() && i->second != "")
return i->second;
else
return default_val;

View file

@ -64,10 +64,10 @@ gamemap::TERRAIN gamemap::underlying_terrain(TERRAIN terrain) const
}
}
gamemap::location::location(config& cfg) : x(-1), y(-1)
gamemap::location::location(const config& cfg) : x(-1), y(-1)
{
const std::string& xstr = cfg.values["x"];
const std::string& ystr = cfg.values["y"];
const std::string& xstr = cfg["x"];
const std::string& ystr = cfg["y"];
//the co-ordinates in config files will be 1-based, while we
//want them as 0-based

View file

@ -57,7 +57,7 @@ public:
location() : x(-1), y(-1) {}
location(int x, int y) : x(x), y(y) {}
location(config& cfg);
location(const config& cfg);
bool valid() const { return x >= 0 && y >= 0; }

View file

@ -25,6 +25,7 @@
#include "tooltips.hpp"
#include <iostream>
#include <iterator>
LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
config* level, CVideo& video,
@ -68,26 +69,35 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
if(gold.empty())
gold = "100";
const int minimum_gold = 100;
int ngold = ::atoi(gold.c_str());
if(ui == unit_cfg.begin() && state_of_game.gold >= minimum_gold &&
if(ui == unit_cfg.begin() && state_of_game.gold >= ngold &&
(*level)["disallow_recall"] != "yes")
ngold = state_of_game.gold;
const gamemap::location& start_pos =
map.starting_position(new_unit.side());
const gamemap::location& start_pos = map.starting_position(new_unit.side());
if(!start_pos.valid()) {
std::stringstream err;
err << "No starting position for side " << new_unit.side();
throw gamestatus::load_game_failed(err.str());
if(!start_pos.valid() && new_unit.side() == 1) {
throw gamestatus::load_game_failed("No starting position for side 1");
}
if(start_pos.valid()) {
units.insert(std::pair<gamemap::location,unit>(
map.starting_position(new_unit.side()), new_unit));
}
units.insert(std::pair<gamemap::location,unit>(
map.starting_position(new_unit.side()), new_unit));
teams.push_back(team(**ui,ngold));
//if the game state specifies units that can be recruited for the player
//then add them
if(teams.size() == 1 && state_of_game.can_recruit.empty() == false) {
std::copy(state_of_game.can_recruit.begin(),state_of_game.can_recruit.end(),
std::inserter(teams.back().recruits(),teams.back().recruits().end()));
}
if(teams.size() == 1) {
state_of_game.can_recruit = teams.back().recruits();
}
if(first_human_team == -1 && teams.back().is_human()) {
first_human_team = teams.size()-1;
}
@ -373,10 +383,6 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
} catch(end_level_exception&) {
}
if((*level)["disallow_recall"] == "yes") {
return VICTORY;
}
//add all the units that survived the scenario
for(std::map<gamemap::location,unit>::iterator un =
units.begin(); un != units.end(); ++un) {
@ -387,6 +393,10 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
}
}
if((*level)["disallow_recall"] == "yes") {
return VICTORY;
}
const int remaining_gold = teams[0].gold();
const int finishing_bonus_per_turn =
map.towers().size()*game_config::tower_income;

View file

@ -613,6 +613,11 @@ void turn_info::show_menu()
menu.push_back(string_table[*items]);
}
static const std::string create_unit_debug = "Create Unit (debug)";
if(game_config::debug) {
menu.push_back(create_unit_debug);
}
if(un != units_.end()) {
menu.push_back(string_table["describe_unit"]);
@ -631,7 +636,24 @@ void turn_info::show_menu()
menu.pop_back();
}
if(result == string_table["describe_unit"]) {
if(result == create_unit_debug) {
std::vector<std::string> options;
std::vector<unit> unit_choices;
for(game_data::unit_type_map::const_iterator i = gameinfo_.unit_types.begin();
i != gameinfo_.unit_types.end(); ++i) {
options.push_back(i->first);
unit_choices.push_back(unit(&i->second,1,false));
}
const int choice = gui::show_dialog(gui_,NULL,"","Create unit (debug):",
gui::OK_CANCEL,&options,&unit_choices);
if(choice >= 0 && choice < unit_choices.size()) {
units_.erase(last_hex_);
units_.insert(std::pair<gamemap::location,unit>(last_hex_,unit_choices[choice]));
gui_.invalidate(last_hex_);
gui_.invalidate_unit();
}
} else if(result == string_table["describe_unit"]) {
unit_description();
} else if(result == string_table["rename_unit"]) {
rename_unit();
@ -1003,7 +1025,7 @@ void turn_info::rename_unit()
void turn_info::save_game()
{
std::stringstream stream;
stream << translate_string(state_of_game_.scenario) << " " << string_table["turn"]
stream << state_of_game_.label << " " << string_table["turn"]
<< " " << status_.turn();
std::string label = stream.str();
@ -1189,8 +1211,12 @@ void turn_info::recall()
<< string_table["level"] << ": "
<< unit->type().level() << ","
<< string_table["xp"] << ": "
<< unit->experience() << "/"
<< unit->max_experience();
<< unit->experience() << "/";
if(unit->type().advances_to().empty())
option << "-";
else
option << unit->max_experience();
options.push_back(option.str());
}

View file

@ -58,6 +58,16 @@ int get_random()
return random_generator->get_random();
}
const config* get_random_results()
{
return random_generator->get_random_results();
}
void set_random_results(const config& cfg)
{
random_generator->set_random_results(cfg);
}
replay::replay() : pos_(0), current_(NULL), skip_(0)
{}
@ -287,6 +297,19 @@ int replay::get_random()
}
}
const config* replay::get_random_results() const
{
assert(current_ != NULL);
return current_->child("results");
}
void replay::set_random_results(const config& cfg)
{
assert(current_ != NULL);
current_->children["results"].clear();
current_->children["results"].push_back(new config(cfg));
}
void replay::start_replay()
{
pos_ = 0;
@ -537,7 +560,9 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>::const_iterator tgt = units.find(dst);
if(tgt == units.end()) {
std::cerr << "unfound defender for attack\n";
std::cerr << "unfound defender for attack: "
<< (src.x+1) << "," << (src.y+1) << " -> "
<< (dst.x+1) << "," << (dst.y+1) << "\n";
throw replay::error();
}

View file

@ -19,6 +19,9 @@
int get_random();
const config* get_random_results();
void set_random_results(const config& cfg);
class replay
{
public:
@ -50,6 +53,8 @@ public:
void undo();
int get_random();
const config* get_random_results() const;
void set_random_results(const config& cfg);
void start_replay();
config* get_next_action();

View file

@ -379,7 +379,7 @@ int show_dialog(display& disp, SDL_Surface* image,
text_widget_height + check_button_height;
if(total_width > scr->w - 100 || total_height > scr->h - 100)
return false;
return 0;
int xloc = scr->w/2 - total_width/2;
int yloc = scr->h/2 - total_height/2;
@ -399,9 +399,12 @@ int show_dialog(display& disp, SDL_Surface* image,
//make sure that the dialog doesn't overlap the right part of the screen
if(xloc + total_width+border_size >= disp.mapx()-1) {
std::cerr << "in if...\n";
xloc = disp.mapx()-(total_width+border_size+2);
if(xloc < 0)
return -1;
if(xloc < 0) {
std::cerr << "dialog is too large to fit on-screen!\n";
return 0;
}
}
const int button_hpadding = total_width - button_widths;

View file

@ -157,6 +157,11 @@ const std::set<std::string>& team::recruits() const
return info_.can_recruit;
}
std::set<std::string>& team::recruits()
{
return info_.can_recruit;
}
const std::vector<std::string>& team::recruitment_pattern() const
{
return info_.recruitment_pattern;

View file

@ -69,6 +69,7 @@ public:
void spend_gold(int amount);
const std::set<std::string>& recruits() const;
std::set<std::string>& recruits();
const std::vector<std::string>& recruitment_pattern() const;
const std::string& name() const;