Help browser added. Some basic help is included, but it should be extended.
This commit is contained in:
parent
874384333d
commit
77fc29a1b9
19 changed files with 1999 additions and 59 deletions
172
data/help.cfg
Normal file
172
data/help.cfg
Normal file
|
@ -0,0 +1,172 @@
|
|||
[help]
|
||||
[toplevel]
|
||||
sections=introduction,gameplay
|
||||
#topics=about
|
||||
[/toplevel]
|
||||
|
||||
[section]
|
||||
id=introduction
|
||||
title="Introduction"
|
||||
topics=introduction
|
||||
[/section]
|
||||
|
||||
[section]
|
||||
id=gameplay
|
||||
title="Gameplay"
|
||||
topics=fundamentals,recruit_and_recall,movement,combat,damage_types_and_resistance,time_of_day,experience_and_advancement,healing,income_and_upkeep,wrap_up
|
||||
[/section]
|
||||
|
||||
[topic]
|
||||
id=introduction
|
||||
title=Introduction
|
||||
text="<img>src=misc/logo.png align=middle</img>
|
||||
|
||||
Battle for Wesnoth is a turn-based fantasy strategy game somewhat unusual amongst modern strategy games. While other games strive for complexity, both in rules and gameplay, Battle for Wesnoth strives for simplicity of rules and gameplay. This does not make the game simple, however - from these simple rules arise a wealth of strategy, making the game easy to learn but a challenge to master."
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=fundamentals
|
||||
title="Fundamentals of Gameplay"
|
||||
text="This page outlines all you need to know to play Battle for Wesnoth. It covers how to play and the basic mechanics behind the game. For more information on conquering the game, you may wish to consult <ref>dst=basic_strategy text='Basic Strategy'</ref> and <ref>dst=multiplayer_strategy text='Multiplayer Strategy'</ref>. Keep in mind that this is just an outline - for special exceptions and situations, please follow the links included.
|
||||
|
||||
To begin with, it's best to click the <italic>text=Tutorial</italic> button at the main menu. This will take you to the interactive tutorial, which will teach you the basics of Wesnoth. After this, it is recommended that you play the Heir to the Throne campaign first - click <italic>text=Campaign</italic> then <italic>text='Heir to the Throne'</italic>. As Battle for Wesnoth can be quite challenging, you may wish to start on <italic>text=Easy</italic>.
|
||||
|
||||
<img>src=help/tooltip.png align=right float=yes</img>While playing, keep in mind that if you mouse-over many items on the menu bar to the right, a brief description will pop up explaining that item. This is especially useful when you encounter new abilities for the first time."
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=recruit_and_recall
|
||||
title="Recruiting and Recalling"
|
||||
text="<img>src=help/recruit.png align=left float=yes</img>At the start of any battle, and at times during it, you will need to recruit units into your army. To recruit, you must have your leader (Konrad in the Heir to the Throne campaign) on the Keep square of a Castle. Then you may recruit by either choosing Recruit from the menu or right-clicking on a hex and selecting <italic>text=Recruit</italic>. This brings up the recruit menu, which lists units available for recruitment, along with their gold cost. Click on a unit to see its statistics to the left, then press the recruit button to recruit it.
|
||||
|
||||
If you right-clicked on a castle hex and selected recruit, the new unit will appear in that square. Otherwise, it will appear in a free square near the keep. You may only recruit as many units as you have free hexes in your castle, and you cannot spend more gold than you actually have on recruiting.
|
||||
|
||||
Recruited units come with two random <ref>dst=traits text=Traits</ref> which modify their statistics.
|
||||
|
||||
In later scenarios, you may also Recall survivors from earlier battles. Recalling functions identically to Recruiting, save that recalling costs a standard 20 gold and presents you with a list of all surviving units from previous scenarios.
|
||||
|
||||
Keep in mind that units not only cost gold to Recruit or Recall, they also require money to support. See <ref>dst=income_and_upkeep text='Income and Upkeep'</ref> for more information.
|
||||
"
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=movement
|
||||
title=Movement
|
||||
text="Movement in Battle for Wesnoth is simple: Simply click on the unit you wish to move, then click on the hex you wish to move it to. When selected, all the hexes a unit can move to this turn will be highlighted, and all those it can't made dull. Mousing over a dull square will show the number of turns required to reach it, and clicking will cause the unit to move towards it by the fastest route over this and subsequent turns.
|
||||
|
||||
Each unit has a certain number of movement points which are expended when moving into a new hex, depending on the <ref>dst=terrain text=Terrain</ref> of that particular hex. For instance, grassland nearly always costs 1 movement point to enter. Exactly how many movement points are spent entering a hex depends on the unit type - in forest, elvish units only spend 1 movement point, most human and orc units spend 2, while horsemen spend 3. You can learn how many unit points a unit requires to enter a certain terrain type by right-clicking on it, selecting Unit Description, and then clicking <italic>text='Terrain Modifiers'</italic>.
|
||||
|
||||
Another thing to keep in mind while moving is <ref>dst=zones_of_control text='Zones of Control'</ref>. Each unit generates a zone of control in the hexes immediately surrounding it, and any enemy unit entering those hexes immediately ends its movement. Learning how to use zones of control to your advantage is an important part of Wesnoth, as only <ref>dst=ability_skirmish text='Skirmishers'</ref> can ignore zones of control."
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=combat
|
||||
title=Combat
|
||||
text="There are two types of combat in Battle for Wesnoth, short- and long-ranged, but both of them take place between units in adjacent hexes. Short-range combat usually involves weapons such as swords, axes or fangs, while long-range combat usually involves weapons such as bows, spears and fireballs. Combat is relatively straightforward; the attacker and defender alternate attacks until each has used their allotted number of attacks.
|
||||
|
||||
<header>text='Order and number of attacks'</header>
|
||||
|
||||
The attacker gets the first attack, then the defender. This continues to alternate until each unit has used up all of its attacks. The number of attacks a unit gets varies; for instance, an Elvish fighter with a 5-4 sword attack may make 4 swings each dealing 5 damage, while an Orcish Grunt with 9-2 only gets 2 swings (but at 9 damage).
|
||||
|
||||
<header>text='Chance to hit'</header>
|
||||
|
||||
With two exceptions, the chance to hit a unit is based solely on its defense rating in the <ref>dst=terrain text=Terrain</ref> it is currently standing in. This may be found by right-clicking a unit, selecting Unit Description, and then clicking <italic>text='Terrain Modifiers'</italic>. For instance, elves have a defense rating of 70% in forest, meaning a unit attacking them has only a 30% chance of hitting them. Conversely, the elf's chance of hitting the attacker in return depends on what terrain the attacker is in.
|
||||
There are two exceptions to this rule: <ref>dst=magical_attacks text='Magical attacks'</ref> and <ref>dst=ability_marksman text=Marksmen</ref>. Magical attacks always have a 70% chance to hit, regardless of terrain, and Marksmen always have at least a 60% chance to hit, regardless of terrain.
|
||||
|
||||
<header>text=Damage</header>
|
||||
|
||||
Each blow which hits causes a base amount of damage depending on the attack type. For instance, an Elvish Fighter with a 5-4 sword attack causes a base of 5 damage. This is usually modified by two things: <ref>dst=damage_type_and_resistance text=Resistance</ref> and <ref>dst=time_of_day text='Time of Day'</ref>, both of which are explained below.
|
||||
|
||||
A few units have special abilities which affect damage dealt in combat. The most common of these is <ref>dst=ability_charg text=Charge</ref>, which doubles the damage dealt by both attacker and defender when the unit with Charge attacks."
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=damage_types_and_resistance
|
||||
title="Damage Types and Resistance"
|
||||
text="In Wesnoth, there are three types of damage usually associated with physical attacks: Blade, Pierce and Impact damage. Additionally, there are three further types of damage usually associated with magical attacks: Fire, Cold and Holy attacks. Different units may have resistances which alter the damage which they take from certain damage types.
|
||||
|
||||
Resistances work very simply: If a unit has 40% resistance against a damage type, then they will suffer 40% less damage when hit with that damage type. It is also possible for a unit to have weakness; if a unit has -100% resistance against a damage type, it will suffer 100% more damage when hit by that type.
|
||||
|
||||
For example, Skeletons are highly resistant to Blade and Pierce damage, but are vulnerable to Impact and Fire damage, and extremely vulnerable to Holy damage."
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=time_of_day
|
||||
title="Time of Day"
|
||||
text="Wesnoth games usually take place with a regular cycle between day and night. The current time of day can be observed under the minimap in the upper right.
|
||||
|
||||
The usual day/night cycle runs:
|
||||
|
||||
Dawn <jump>to=160</jump><img>src=Dawn.png align=here</img>
|
||||
Morning <jump>to=160</jump><img>src=Morning.png align=here</img>
|
||||
Afternoon <jump>to=160</jump><img>src=Afternoon.png align=here</img>
|
||||
Dusk <jump>to=160</jump><img>src=Dusk.png align=here</img>
|
||||
First Watch <jump>to=160</jump><img>src=FirstWatch.png align=here</img>
|
||||
Second Watch <jump>to=160</jump><img>src=SecondWatch.png align=here</img>
|
||||
|
||||
Morning and Afternoon count as day, First and Second Watch count as night.
|
||||
|
||||
The time of day affects the damage of certain units as follows -
|
||||
|
||||
Lawful units get +25% damage in daytime, and -25% damage at night.
|
||||
Chaotic units get +25% damage at night, and -25% in daytime.
|
||||
Neutral units are unaffected by the time of day.
|
||||
|
||||
<img>src=Underground.png align=right float=yes</img>Keep in mind that some scenarios take place underground, where it is perpetually night!"
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=experience_and_advancement
|
||||
title="Experience and Advancement"
|
||||
text="If both units survive a combat, they gain a number of experience points equal to the level of the unit they're fighting. If a unit kills another in combat, however, it gains much more experience - 4 for a level 0 unit, 8 for level 1, 16 for level 2, 24 for level 3, and so forth.
|
||||
|
||||
Units have a certain amount of experience required to advance (this is 20% less for units with the Intelligent trait). Once they achieve this amount, they immediately advance to the next level, healing fully in the process. In some cases, you will be given a choice of advancement options.
|
||||
|
||||
Keep in mind that while most units have three levels, not all do, and that occasional units (such as <ref>dst=unit_mage text=Mages</ref>)may have four."
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=healing
|
||||
title="Healing"
|
||||
text="In combat, your units will inevitably take damage. Wesnoth offers several ways for your units to heal, all of which take place at the beginning of your turn, before you take action.
|
||||
|
||||
Resting: A unit which neither moves nor attacks will heal 2HP in its next turn.
|
||||
Villages: A unit which starts a turn in a village will heal 8HP.
|
||||
<ref>dst=ability_regeneration text=Regeneration</ref>: Certain units (such as trolls) will automatically heal 8HP every turn.
|
||||
Healing units: Units with the <ref>dst=ability_heals text=Heals</ref> ability will heal all friendly units immediately beside them for 4HP each turn, or prevent Poison from dealing damage.
|
||||
|
||||
Curing units: Units with the <ref>dst=ability_cures text=Cures</ref> ability will heal all friendly units immediately beside them for 8HP each turn, or cure a unit of Poison.
|
||||
|
||||
Keep in mind that while Resting can be combined with other forms of healing, villages, regeneration, and healing/curing cannot combine with each other. Also, units heal fully between scenarios.
|
||||
"
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=income_and_upkeep
|
||||
title="Income and Upkeep"
|
||||
text="In Wesnoth, it is not enough simply to recruit units and fight. You must watch your gold as well, especially in campaigns, where you can carry extra gold over from one scenario to the next. There are two aspects to this; Income and Upkeep.
|
||||
|
||||
Income is simple. For every village you control, you gain one gold a turn. Thus, if you have ten villages, you would normally gain ten gold. Unfortunately, your Upkeep costs are subtracted from this income, as detailed below.
|
||||
|
||||
Upkeep is also fairly simple. Each unit requires an amount of Upkeep equal to its level. You can support as many levels <italic>text=worth</italic> of units as you have villages for free. However, for each level of unit beyond the number of villages you have, you must pay one gold per turn. For example, if you have twelve level one units and ten villages, you would have to pay two gold a turn in upkeep.
|
||||
|
||||
These costs are subtracted from your Income, so in the case of twelve levels of units and ten villages, your resultant Income would be 8 gold a turn.
|
||||
There are two main exceptions to Upkeep. Firstly, units with the Loyal trait will only ever count as level one for upkeep purposes, regardless of their actual level. Secondly, units you begin the scenario with (such as Konrad or Delfador), or units who join you during a scenario (such as the Horseman in the second level of Heir to the Throne) will never charge any upkeep.
|
||||
"
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=wrap_up
|
||||
title="Wrap Up"
|
||||
text="This concludes the fundamentals of Wesnoth. You might want to read up on Basic Strategy, or familiarise yourself with <ref>dst=traits text=Traits</ref> and <ref>dst=abilities text=Abilities</ref>, but you now know everything you need to know to play the Heir to the Throne scenario. Have fun, and good luck!"
|
||||
[/topic]
|
||||
|
||||
[topic]
|
||||
id=about
|
||||
title="About"
|
||||
# Does not work yet, but soon! :)
|
||||
generator="generate_about"
|
||||
[/topic]
|
||||
|
||||
[/help]
|
||||
|
|
@ -23,7 +23,7 @@ height=600
|
|||
[menu]
|
||||
title=main_menu
|
||||
image=lite
|
||||
items=speak,objectives,unitlist,recruit,recall,statustable,endturn,undo,redo,save,load,statistics,preferences,quit
|
||||
items=speak,objectives,unitlist,recruit,recall,statustable,endturn,undo,redo,save,load,statistics,preferences,help,quit
|
||||
rect=3,1,100,22
|
||||
xanchor=fixed
|
||||
yanchor=fixed
|
||||
|
|
BIN
images/help/closed_section.png
Normal file
BIN
images/help/closed_section.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
images/help/open_section.png
Normal file
BIN
images/help/open_section.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 862 B |
BIN
images/help/recruit.png
Normal file
BIN
images/help/recruit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
BIN
images/help/tooltip.png
Normal file
BIN
images/help/tooltip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
images/help/topic.png
Normal file
BIN
images/help/topic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -43,6 +43,7 @@ wesnoth_SOURCES = about.cpp \
|
|||
game_events.cpp \
|
||||
gamestatus.cpp \
|
||||
halo.cpp \
|
||||
help.cpp \
|
||||
hotkeys.cpp \
|
||||
image.cpp \
|
||||
intro.cpp \
|
||||
|
@ -108,6 +109,7 @@ wesnoth_SOURCES = about.cpp \
|
|||
game_events.hpp \
|
||||
gamestatus.hpp \
|
||||
halo.hpp \
|
||||
help.hpp \
|
||||
hotkeys.hpp \
|
||||
image.hpp \
|
||||
intro.hpp \
|
||||
|
@ -184,6 +186,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
|
|||
game_events.cpp \
|
||||
gamestatus.cpp \
|
||||
halo.cpp \
|
||||
help.cpp \
|
||||
hotkeys.cpp \
|
||||
image.cpp \
|
||||
intro.cpp \
|
||||
|
@ -247,6 +250,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
|
|||
game_events.hpp \
|
||||
gamestatus.hpp \
|
||||
halo.hpp \
|
||||
help.hpp \
|
||||
hotkeys.hpp \
|
||||
image.hpp \
|
||||
intro.hpp \
|
||||
|
|
|
@ -185,6 +185,7 @@ wesnoth_SOURCES = about.cpp \
|
|||
game_events.cpp \
|
||||
gamestatus.cpp \
|
||||
halo.cpp \
|
||||
help.cpp \
|
||||
hotkeys.cpp \
|
||||
image.cpp \
|
||||
intro.cpp \
|
||||
|
@ -250,6 +251,7 @@ wesnoth_SOURCES = about.cpp \
|
|||
game_events.hpp \
|
||||
gamestatus.hpp \
|
||||
halo.hpp \
|
||||
help.hpp \
|
||||
hotkeys.hpp \
|
||||
image.hpp \
|
||||
intro.hpp \
|
||||
|
@ -327,6 +329,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
|
|||
game_events.cpp \
|
||||
gamestatus.cpp \
|
||||
halo.cpp \
|
||||
help.cpp \
|
||||
hotkeys.cpp \
|
||||
image.cpp \
|
||||
intro.cpp \
|
||||
|
@ -390,6 +393,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
|
|||
game_events.hpp \
|
||||
gamestatus.hpp \
|
||||
halo.hpp \
|
||||
help.hpp \
|
||||
hotkeys.hpp \
|
||||
image.hpp \
|
||||
intro.hpp \
|
||||
|
@ -451,8 +455,8 @@ am_wesnoth_OBJECTS = about.$(OBJEXT) actions.$(OBJEXT) ai.$(OBJEXT) \
|
|||
dialogs.$(OBJEXT) display.$(OBJEXT) events.$(OBJEXT) \
|
||||
filesystem.$(OBJEXT) font.$(OBJEXT) game.$(OBJEXT) \
|
||||
game_config.$(OBJEXT) game_events.$(OBJEXT) \
|
||||
gamestatus.$(OBJEXT) halo.$(OBJEXT) hotkeys.$(OBJEXT) \
|
||||
image.$(OBJEXT) intro.$(OBJEXT) key.$(OBJEXT) \
|
||||
gamestatus.$(OBJEXT) halo.$(OBJEXT) help.$(OBJEXT) \
|
||||
hotkeys.$(OBJEXT) image.$(OBJEXT) intro.$(OBJEXT) key.$(OBJEXT) \
|
||||
language.$(OBJEXT) log.$(OBJEXT) map.$(OBJEXT) \
|
||||
map_label.$(OBJEXT) mapgen.$(OBJEXT) mapgen_dialog.$(OBJEXT) \
|
||||
mouse.$(OBJEXT) multiplayer.$(OBJEXT) \
|
||||
|
@ -481,19 +485,19 @@ am_wesnoth_editor_OBJECTS = editor.$(OBJEXT) editor_layout.$(OBJEXT) \
|
|||
dialogs.$(OBJEXT) display.$(OBJEXT) events.$(OBJEXT) \
|
||||
filesystem.$(OBJEXT) font.$(OBJEXT) game_config.$(OBJEXT) \
|
||||
game_events.$(OBJEXT) gamestatus.$(OBJEXT) halo.$(OBJEXT) \
|
||||
hotkeys.$(OBJEXT) image.$(OBJEXT) intro.$(OBJEXT) key.$(OBJEXT) \
|
||||
language.$(OBJEXT) log.$(OBJEXT) map_label.$(OBJEXT) \
|
||||
map.$(OBJEXT) mapgen.$(OBJEXT) mapgen_dialog.$(OBJEXT) \
|
||||
mouse.$(OBJEXT) network.$(OBJEXT) pathfind.$(OBJEXT) \
|
||||
playlevel.$(OBJEXT) playturn.$(OBJEXT) preferences.$(OBJEXT) \
|
||||
race.$(OBJEXT) replay.$(OBJEXT) reports.$(OBJEXT) \
|
||||
sdl_utils.$(OBJEXT) show_dialog.$(OBJEXT) sound.$(OBJEXT) \
|
||||
statistics.$(OBJEXT) team.$(OBJEXT) terrain.$(OBJEXT) \
|
||||
theme.$(OBJEXT) tooltips.$(OBJEXT) unit.$(OBJEXT) \
|
||||
unit_display.$(OBJEXT) unit_types.$(OBJEXT) video.$(OBJEXT) \
|
||||
button.$(OBJEXT) file_chooser.$(OBJEXT) menu.$(OBJEXT) \
|
||||
progressbar.$(OBJEXT) textbox.$(OBJEXT) scrollbar.$(OBJEXT) \
|
||||
slider.$(OBJEXT) widget.$(OBJEXT)
|
||||
help.$(OBJEXT) hotkeys.$(OBJEXT) image.$(OBJEXT) \
|
||||
intro.$(OBJEXT) key.$(OBJEXT) language.$(OBJEXT) log.$(OBJEXT) \
|
||||
map_label.$(OBJEXT) map.$(OBJEXT) mapgen.$(OBJEXT) \
|
||||
mapgen_dialog.$(OBJEXT) mouse.$(OBJEXT) network.$(OBJEXT) \
|
||||
pathfind.$(OBJEXT) playlevel.$(OBJEXT) playturn.$(OBJEXT) \
|
||||
preferences.$(OBJEXT) race.$(OBJEXT) replay.$(OBJEXT) \
|
||||
reports.$(OBJEXT) sdl_utils.$(OBJEXT) show_dialog.$(OBJEXT) \
|
||||
sound.$(OBJEXT) statistics.$(OBJEXT) team.$(OBJEXT) \
|
||||
terrain.$(OBJEXT) theme.$(OBJEXT) tooltips.$(OBJEXT) \
|
||||
unit.$(OBJEXT) unit_display.$(OBJEXT) unit_types.$(OBJEXT) \
|
||||
video.$(OBJEXT) button.$(OBJEXT) file_chooser.$(OBJEXT) \
|
||||
menu.$(OBJEXT) progressbar.$(OBJEXT) textbox.$(OBJEXT) \
|
||||
scrollbar.$(OBJEXT) slider.$(OBJEXT) widget.$(OBJEXT)
|
||||
wesnoth_editor_OBJECTS = $(am_wesnoth_editor_OBJECTS)
|
||||
wesnoth_editor_LDADD = $(LDADD)
|
||||
wesnoth_editor_DEPENDENCIES =
|
||||
|
@ -519,13 +523,14 @@ am__depfiles_maybe = depfiles
|
|||
@AMDEP_TRUE@ ./$(DEPDIR)/game.Po ./$(DEPDIR)/game_config.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/game_events.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/gamestatus.Po ./$(DEPDIR)/halo.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/hotkeys.Po ./$(DEPDIR)/image.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/intro.Po ./$(DEPDIR)/key.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/language.Po ./$(DEPDIR)/log.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/map.Po ./$(DEPDIR)/map_label.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/map_manip.Po ./$(DEPDIR)/mapgen.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/mapgen_dialog.Po ./$(DEPDIR)/menu.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/mouse.Po ./$(DEPDIR)/multiplayer.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/help.Po ./$(DEPDIR)/hotkeys.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/image.Po ./$(DEPDIR)/intro.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/key.Po ./$(DEPDIR)/language.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/log.Po ./$(DEPDIR)/map.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/map_label.Po ./$(DEPDIR)/map_manip.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/mapgen.Po ./$(DEPDIR)/mapgen_dialog.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/menu.Po ./$(DEPDIR)/mouse.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/multiplayer.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/multiplayer_client.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/multiplayer_connect.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/multiplayer_lobby.Po \
|
||||
|
@ -637,6 +642,7 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/game_events.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gamestatus.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/halo.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hotkeys.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intro.Po@am__quote@
|
||||
|
|
|
@ -192,7 +192,8 @@ SDL_Surface* render_text_internal(TTF_Font* font,const std::string& str,
|
|||
|
||||
SDL_Surface* render_text(TTF_Font* font,const std::string& text, const SDL_Color& colour, int style)
|
||||
{
|
||||
const std::vector<std::string> lines = config::split(text,'\n');
|
||||
// XXX Changed by erl, to not strip when rendering text. Works everywhere?
|
||||
const std::vector<std::string> lines = config::split(text,'\n', config::REMOVE_EMPTY);
|
||||
std::vector<shared_sdl_surface> surfaces;
|
||||
size_t width = 0, height = 0;
|
||||
for(std::vector<std::string>::const_iterator ln = lines.begin(); ln != lines.end(); ++ln) {
|
||||
|
|
1311
src/help.cpp
Normal file
1311
src/help.cpp
Normal file
File diff suppressed because it is too large
Load diff
384
src/help.hpp
Normal file
384
src/help.hpp
Normal file
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
||||
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#ifndef HELP_HPP_INCLUDED
|
||||
#define HELP_HPP_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
#include "display.hpp"
|
||||
#include "sdl_utils.hpp"
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
#include "widgets/scrollbar.hpp"
|
||||
|
||||
#include "widgets/widget.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace help {
|
||||
|
||||
struct help_manager {
|
||||
help_manager(const config *help_config);
|
||||
~help_manager();
|
||||
};
|
||||
|
||||
/// A topic contains a title, an id and some text.
|
||||
struct topic {
|
||||
topic(const std::string _title, const std::string _id, const std::string _text)
|
||||
: title(_title), id(_id), text(_text) {}
|
||||
topic() : title(""), id(""), text("") {}
|
||||
/// Two topics are equal if their IDs are equal.
|
||||
bool operator==(const topic &) const;
|
||||
/// Comparison on the ID.
|
||||
bool operator<(const topic &) const;
|
||||
std::string title, id, text;
|
||||
};
|
||||
|
||||
/// A section contains topics and sections along with title and ID.
|
||||
struct section {
|
||||
section(const std::string _title, const std::string _id, const std::vector<topic> &_topics,
|
||||
const std::vector<section> &_sections)
|
||||
: title(_title), id(_id), topics(_topics), sections(_sections) {}
|
||||
section() : title(""), id("") {}
|
||||
/// Two sections are equal if their IDs are equal.
|
||||
bool operator==(const section &) const;
|
||||
/// Comparison on the ID.
|
||||
bool operator<(const section &) const;
|
||||
void clear();
|
||||
std::string title, id;
|
||||
std::vector<topic> topics;
|
||||
std::vector<section> sections;
|
||||
};
|
||||
|
||||
/// To be used as a function object to locate sections and topics
|
||||
/// with a specified ID.
|
||||
class has_id {
|
||||
public:
|
||||
has_id(const std::string &id) : id_(id) {}
|
||||
bool operator()(const topic &t) { return t.id == id_; }
|
||||
bool operator()(const section &s) { return s.id == id_; }
|
||||
private:
|
||||
const std::string id_;
|
||||
};
|
||||
|
||||
/// The menu to the left in the help browser, where topics can be
|
||||
/// navigated through and chosen.
|
||||
class help_menu : public gui::menu {
|
||||
public:
|
||||
help_menu(display& disp, const section &toplevel, int max_height=-1);
|
||||
void bg_backup();
|
||||
void bg_restore();
|
||||
int process(int x, int y, bool button,bool up_arrow,bool down_arrow,
|
||||
bool page_up, bool page_down, int select_item=-1);
|
||||
|
||||
/// Overloaded from menu so that the background can be saved.
|
||||
void set_loc(int x, int y);
|
||||
/// Overloaded from menu so that the background can be saved.
|
||||
void set_width(int w);
|
||||
/// Overloaded from menu so that the background can be saved.
|
||||
void set_max_height(const int new_height);
|
||||
|
||||
/// Make the topic the currently selected one, and expand all
|
||||
/// sections that need to be expanded to show it.
|
||||
void select_topic(const topic &t);
|
||||
|
||||
/// If a topic has been chosen, return that topic, otherwise
|
||||
/// NULL. If one topic is returned, it will not be returned again,
|
||||
/// if it is not re-chosen.
|
||||
const topic *chosen_topic();
|
||||
protected:
|
||||
void handle_event(const SDL_Event &event);
|
||||
|
||||
private:
|
||||
/// Information about an item that is visible in the menu.
|
||||
struct visible_item {
|
||||
visible_item(const section *_sec, const std::string &visible_string);
|
||||
visible_item(const topic *_t, const std::string &visible_string);
|
||||
// Invariant, one if these should be NULL. The constructors
|
||||
// enforce it.
|
||||
const topic *t;
|
||||
const section *sec;
|
||||
std::string visible_string;
|
||||
bool operator==(const visible_item &vis_item) const;
|
||||
bool operator==(const section &sec) const;
|
||||
bool operator==(const topic &t) const;
|
||||
};
|
||||
|
||||
/// Regenerate what items are visible by checking what sections are
|
||||
/// expanded.
|
||||
void update_visible_items(const section &top_level, unsigned starting_level=0);
|
||||
|
||||
/// Return true if the section is expanded.
|
||||
bool expanded(const section &sec);
|
||||
|
||||
/// Mark a section as expanded. Do not update the visible items or
|
||||
/// anything.
|
||||
void expand(const section &sec);
|
||||
|
||||
/// Contract (close) a section. That is, mark it as not expanded,
|
||||
/// visible items are not updated.
|
||||
void contract(const section &sec);
|
||||
|
||||
/// Return the string to use as the menu-string for sections at the
|
||||
/// specified level.
|
||||
std::string get_string_to_show(const section &sec, const unsigned level);
|
||||
/// Return the string to use as the menu-string for topics at the
|
||||
/// specified level.
|
||||
std::string get_string_to_show(const topic &topic, const unsigned level);
|
||||
|
||||
/// Draw the currently visible items.
|
||||
void display_visible_items();
|
||||
|
||||
/// Internal recursive thingie.
|
||||
bool select_topic_internal(const topic &t, const section &sec);
|
||||
|
||||
display &disp_;
|
||||
std::vector<visible_item> visible_items_;
|
||||
const section &toplevel_;
|
||||
std::set<const section*> expanded_;
|
||||
surface_restorer restorer_;
|
||||
SDL_Rect rect_;
|
||||
topic const *chosen_topic_;
|
||||
int internal_width_;
|
||||
visible_item selected_item_;
|
||||
};
|
||||
|
||||
/// Thrown when the help system fails to parse something.
|
||||
struct parse_error {
|
||||
parse_error(const std::string& msg) : message(msg) {}
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/// The area where the content is shown in the help browser.
|
||||
class help_text_area : public gui::widget, public gui::scrollable {
|
||||
public:
|
||||
help_text_area(display &disp, const section &toplevel);
|
||||
/// Display the topic.
|
||||
void show_topic(const topic &t);
|
||||
|
||||
/// Return the ID that is crossreferenced at the (screen)
|
||||
/// coordinates x, y. If no cross-reference is there, return the
|
||||
/// empty string.
|
||||
std::string ref_at(const int x, const int y);
|
||||
|
||||
/// Return the width of the area where text fit.
|
||||
int text_width() const;
|
||||
|
||||
void scroll(int pos);
|
||||
|
||||
void set_dirty(bool dirty);
|
||||
|
||||
/// Scroll the contents up an amount. If how_much is below zero the
|
||||
/// amount will depend on the height.
|
||||
void scroll_up(const int how_much=-1);
|
||||
|
||||
/// Scroll the contents down an amount. If how_much is below zero
|
||||
/// the amount will depend on the height.
|
||||
void scroll_down(const int how_much=-1);
|
||||
private:
|
||||
enum ALIGNMENT {LEFT, MIDDLE, RIGHT, HERE};
|
||||
/// Convert a string to an alignment. Throw parse_error if
|
||||
/// unsuccesful.
|
||||
ALIGNMENT str_to_align(const std::string &s);
|
||||
|
||||
/// An item that is displayed in the text area. Contains the surface
|
||||
/// that should be blitted along with some other information.
|
||||
struct item {
|
||||
|
||||
item(shared_sdl_surface surface, int x, int y, const std::string text="",
|
||||
const std::string reference_to="", bool floating=false,
|
||||
ALIGNMENT alignment=HERE);
|
||||
|
||||
item(shared_sdl_surface surface, int x, int y,
|
||||
bool floating, ALIGNMENT=HERE);
|
||||
|
||||
/// Relative coordinates of this item.
|
||||
SDL_Rect rect;
|
||||
shared_sdl_surface surf;
|
||||
|
||||
// If this item contains text, this will contain that text.
|
||||
std::string text;
|
||||
|
||||
// If this item contains a cross-reference, this is the id
|
||||
// of the referenced topic.
|
||||
std::string ref_to;
|
||||
|
||||
// If this item is floating, that is, if things should be filled
|
||||
// around it.
|
||||
bool floating;
|
||||
ALIGNMENT align;
|
||||
};
|
||||
|
||||
/// Function object to find an item at the specified coordinates.
|
||||
class item_at {
|
||||
public:
|
||||
item_at(const int x, const int y) : x_(x), y_(y) {}
|
||||
bool operator()(const item&) const;
|
||||
private:
|
||||
const int x_, y_;
|
||||
};
|
||||
|
||||
/// Update the vector with items, creating surfaces for everything
|
||||
/// and putting things where they belong. parsed_items should be a
|
||||
/// vector with parsed strings, such as parse_text returns.
|
||||
void set_items(const std::vector<std::string> &parsed_items,
|
||||
const std::string &title);
|
||||
|
||||
// Create appropriate items from configs. Items will be added to the
|
||||
// internal vector. These methods check that the necessary
|
||||
// attributes are specified.
|
||||
void handle_ref_cfg(const config &cfg);
|
||||
void handle_img_cfg(const config &cfg);
|
||||
void handle_bold_cfg(const config &cfg);
|
||||
void handle_italic_cfg(const config &cfg);
|
||||
void handle_header_cfg(const config &cfg);
|
||||
void handle_jump_cfg(const config &cfg);
|
||||
|
||||
void handle_event(const SDL_Event &event);
|
||||
void draw();
|
||||
void process();
|
||||
|
||||
/// Update the scrollbar to take account for the current items.
|
||||
void update_scrollbar();
|
||||
|
||||
/// Get the current amount of scrolling that should be
|
||||
/// added/substracted from the locations to get the desired effect.
|
||||
unsigned get_scroll_offset() const;
|
||||
|
||||
/// Add an item with text. If ref_dst is something else than the
|
||||
/// empty string, the text item will be underlined to show that it
|
||||
/// is a cross-reference. The item will also remember what the
|
||||
/// reference points to. If font_size is below zero, the default
|
||||
/// will be used.
|
||||
void add_text_item(const std::string text, const std::string ref_dst="",
|
||||
int font_size=-1, bool bold=false, bool italic=false);
|
||||
|
||||
/// Add an image item with the specified attributes.
|
||||
void add_img_item(const std::string path, const std::string alignment, const bool floating);
|
||||
|
||||
/// Move the current input point to the next line.
|
||||
void down_one_line();
|
||||
|
||||
/// Adjust the heights of the items in the last row to make it look
|
||||
/// good .
|
||||
void adjust_last_row();
|
||||
|
||||
/// Return the width that remain on the line the current input point is at.
|
||||
int get_remaining_width();
|
||||
|
||||
/// Return the least x coordinate at which something of the
|
||||
/// specified height can be drawn at the specified y coordinate
|
||||
/// without interfering with floating images.
|
||||
int get_min_x(const int y, const int height=0);
|
||||
|
||||
/// Analogous with get_min_x but return the maximum X.
|
||||
int get_max_x(const int y, const int height=0);
|
||||
|
||||
/// Find the lowest y coordinate where a floating img of the
|
||||
/// specified width and at the specified x coordinate can be
|
||||
/// placed. Start looking at desired_y and continue downwards. Only
|
||||
/// check against other floating things, since text and inline
|
||||
/// images only can be above this place if called correctly.
|
||||
int get_y_for_floating_img(const int width, const int x, const int desired_y);
|
||||
|
||||
/// Add an item to the internal list, update the locations and row
|
||||
/// height.
|
||||
void add_item(const item& itm);
|
||||
|
||||
std::vector<item> items_;
|
||||
std::vector<size_t> last_row_; // Indexes in items_.
|
||||
display &disp_;
|
||||
const section &toplevel_;
|
||||
topic const *shown_topic_;
|
||||
const int title_spacing_;
|
||||
// The current input location when creating items.
|
||||
std::pair<int, int> curr_loc_;
|
||||
const unsigned min_row_height_;
|
||||
unsigned curr_row_height_;
|
||||
gui::scrollbar scrollbar_;
|
||||
bool use_scrollbar_;
|
||||
gui::button uparrow_, downarrow_;
|
||||
/// The height of all items in total.
|
||||
int contents_height_;
|
||||
};
|
||||
|
||||
|
||||
/// A help browser widget.
|
||||
class help_browser : public gui::widget {
|
||||
public:
|
||||
help_browser(display &disp, section &toplevel);
|
||||
|
||||
// Overloaded from widget so that the layout may be adjusted to fit
|
||||
// the new dimensions.
|
||||
void set_location(const SDL_Rect& rect);
|
||||
void set_location(int x, int y);
|
||||
void set_width(int w);
|
||||
void set_height(int h);
|
||||
|
||||
void set_dirty(bool dirty);
|
||||
void adjust_layout();
|
||||
|
||||
void process();
|
||||
/// Display the topic with the specified identifier. Open the menu
|
||||
/// on the right location and display the topic in the text area.
|
||||
void show_topic(const std::string &topic_id);
|
||||
|
||||
private:
|
||||
void handle_event(const SDL_Event &event);
|
||||
display &disp_;
|
||||
help_menu menu_;
|
||||
help_text_area text_area_;
|
||||
const section &toplevel_;
|
||||
};
|
||||
|
||||
/// Parse a help config, return the top level section. Return an empty
|
||||
/// section if cfg is NULL.
|
||||
section parse_config(const config *cfg);
|
||||
|
||||
/// Search for the topic with the specified identifier in the section
|
||||
/// and it's subsections. Return the found topic, or NULL if none could
|
||||
/// be found.
|
||||
const topic *find_topic(const section &sec, const std::string &id);
|
||||
|
||||
/// Parse a text string. Return a vector with the different parts of the
|
||||
/// text. Each markup item is a separate part while the text between
|
||||
/// markups are separate parts.
|
||||
std::vector<std::string> parse_text(const std::string &text);
|
||||
|
||||
/// Convert the contents to wml attributes, surrounded within
|
||||
/// [element_name]...[/element_name]. Return the resulting WML.
|
||||
std::string convert_to_wml(const std::string &element_name, const std::string &contents);
|
||||
|
||||
/// Return true if s is a representation of a truth value
|
||||
/// (yes/true/...), otherwise false.
|
||||
bool get_bool(const std::string &s);
|
||||
|
||||
/// Make a best effort to word wrap s. All parts are less than width.
|
||||
std::vector<std::string> split_in_width(const std::string &s, const int font_size,
|
||||
const unsigned width);
|
||||
|
||||
std::string remove_first_space(const std::string& text);
|
||||
|
||||
/// Return a lowercase copy of s.
|
||||
std::string to_lower(const std::string &s);
|
||||
|
||||
/// Return the first word in s, not removing any spaces in the start of
|
||||
/// it.
|
||||
std::string get_first_word(const std::string &s);
|
||||
|
||||
/// Open a help dialog. The help topic will have the topic with id
|
||||
/// show_topic open if it is not the empty string.
|
||||
void show_help(display &disp, const std::string show_topic="", int xloc=-1, int yloc=-1);
|
||||
|
||||
} // End namespace help.
|
||||
|
||||
#endif
|
|
@ -90,6 +90,7 @@ HOTKEY_COMMAND string_to_command(const std::string& str)
|
|||
m.insert(val("search",HOTKEY_SEARCH));
|
||||
m.insert(val("speaktoally",HOTKEY_SPEAK_ALLY));
|
||||
m.insert(val("speaktoall",HOTKEY_SPEAK_ALL));
|
||||
m.insert(val("help",HOTKEY_HELP));
|
||||
}
|
||||
|
||||
const std::map<std::string,HOTKEY_COMMAND>::const_iterator i = m.find(str);
|
||||
|
@ -453,7 +454,11 @@ void execute_command(display& disp, HOTKEY_COMMAND command, command_executor* ex
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
case HOTKEY_HELP:
|
||||
if(executor) {
|
||||
executor->show_help();
|
||||
}
|
||||
break;
|
||||
case HOTKEY_EDIT_SET_TERRAIN:
|
||||
if(executor)
|
||||
executor->edit_set_terrain();
|
||||
|
|
|
@ -35,7 +35,7 @@ enum HOTKEY_COMMAND { HOTKEY_CYCLE_UNITS, HOTKEY_END_UNIT_TURN, HOTKEY_LEADER,
|
|||
HOTKEY_OBJECTIVES, HOTKEY_UNIT_LIST, HOTKEY_STATISTICS, HOTKEY_QUIT_GAME,
|
||||
HOTKEY_LABEL_TERRAIN, HOTKEY_SHOW_ENEMY_MOVES, HOTKEY_BEST_ENEMY_MOVES,
|
||||
HOTKEY_DELAY_SHROUD, HOTKEY_UPDATE_SHROUD, HOTKEY_CONTINUE_MOVE,
|
||||
HOTKEY_SEARCH, HOTKEY_SPEAK_ALLY, HOTKEY_SPEAK_ALL,
|
||||
HOTKEY_SEARCH, HOTKEY_SPEAK_ALLY, HOTKEY_SPEAK_ALL, HOTKEY_HELP,
|
||||
|
||||
//editing specific commands
|
||||
HOTKEY_EDIT_SET_TERRAIN,
|
||||
|
@ -119,6 +119,7 @@ public:
|
|||
virtual void update_shroud_now() {}
|
||||
virtual void continue_move() {}
|
||||
virtual void search() {}
|
||||
virtual void show_help() {}
|
||||
|
||||
// Map editor stuff.
|
||||
virtual void edit_set_terrain() {}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "filesystem.hpp"
|
||||
#include "game_events.hpp"
|
||||
#include "halo.hpp"
|
||||
#include "help.hpp"
|
||||
#include "hotkeys.hpp"
|
||||
#include "intro.hpp"
|
||||
#include "language.hpp"
|
||||
|
@ -337,6 +338,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
|
|||
|
||||
game_events::manager events_manager(*level,gui,map,units,teams,
|
||||
state_of_game,status,gameinfo);
|
||||
help::help_manager help_manager(game_config.child("help"));
|
||||
|
||||
//find a list of 'items' (i.e. overlays) on the level, and add them
|
||||
const config::child_list& overlays = level->get_children("item");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "actions.hpp"
|
||||
#include "events.hpp"
|
||||
#include "help.hpp"
|
||||
#include "hotkeys.hpp"
|
||||
#include "image.hpp"
|
||||
#include "language.hpp"
|
||||
|
@ -919,6 +920,7 @@ bool turn_info::can_execute_command(hotkey::HOTKEY_COMMAND command) const
|
|||
case hotkey::HOTKEY_STATISTICS:
|
||||
case hotkey::HOTKEY_QUIT_GAME:
|
||||
case hotkey::HOTKEY_SEARCH:
|
||||
case hotkey::HOTKEY_HELP:
|
||||
return true;
|
||||
|
||||
case hotkey::HOTKEY_SAVE_GAME:
|
||||
|
@ -2019,6 +2021,11 @@ void turn_info::search()
|
|||
create_textbox(floating_textbox::TEXTBOX_SEARCH,msg.str());
|
||||
}
|
||||
|
||||
void turn_info::show_help()
|
||||
{
|
||||
help::show_help(gui_);
|
||||
}
|
||||
|
||||
void turn_info::do_search(const std::string& new_search)
|
||||
{
|
||||
if(new_search.empty() == false && new_search != last_search_)
|
||||
|
|
|
@ -151,6 +151,7 @@ private:
|
|||
virtual void update_shroud_now();
|
||||
virtual void continue_move();
|
||||
virtual void search();
|
||||
virtual void show_help();
|
||||
virtual hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command) const;
|
||||
|
||||
void do_search(const std::string& str);
|
||||
|
|
|
@ -16,8 +16,8 @@ const size_t menu_cell_padding = 10;
|
|||
namespace gui {
|
||||
|
||||
menu::menu(display& disp, const std::vector<std::string>& items,
|
||||
bool click_selects, int max_height)
|
||||
: max_height_(max_height), max_items_(-1), item_height_(-1),
|
||||
bool click_selects, int max_height, int max_width)
|
||||
: max_height_(max_height), max_width_(max_width), max_items_(-1), item_height_(-1),
|
||||
display_(&disp), x_(0), y_(0), cur_help_(-1,-1), help_string_(-1), buffer_(NULL),
|
||||
selected_(click_selects ? -1:0), click_selects_(click_selects),
|
||||
previous_button_(true), drawn_(false), show_result_(false),
|
||||
|
@ -113,7 +113,6 @@ int menu::width() const
|
|||
width_ += scrollbar_.get_max_width();
|
||||
}
|
||||
}
|
||||
|
||||
return width_;
|
||||
}
|
||||
|
||||
|
@ -155,6 +154,13 @@ void menu::set_width(int w)
|
|||
itemRects_.clear();
|
||||
}
|
||||
|
||||
SDL_Rect menu::get_rect() const
|
||||
{
|
||||
SDL_Rect r = {x_, y_, max_width_ < 0 ? width_ : max_width_,
|
||||
max_height_ < 0 ? height_ : max_height_};
|
||||
return r;
|
||||
}
|
||||
|
||||
void menu::redraw()
|
||||
{
|
||||
if(x_ == 0 && y_ == 0) {
|
||||
|
@ -189,7 +195,7 @@ void menu::erase_item(size_t index)
|
|||
}
|
||||
}
|
||||
|
||||
void menu::set_items(const std::vector<std::string>& items) {
|
||||
void menu::set_items(const std::vector<std::string>& items, bool strip_spaces) {
|
||||
items_.clear();
|
||||
itemRects_.clear();
|
||||
column_widths_.clear();
|
||||
|
@ -197,6 +203,7 @@ void menu::set_items(const std::vector<std::string>& items) {
|
|||
height_ = -1; // Force recalculation of the height.
|
||||
width_ = -1; // Force recalculation of the width.
|
||||
max_items_ = -1; // Force recalculation of the max items.
|
||||
item_height_ = -1; // Force recalculation of the item height.
|
||||
// Scrollbar and buttons will be reanabled if they are needed.
|
||||
scrollbar_.enable(false);
|
||||
uparrow_.hide(true);
|
||||
|
@ -205,7 +212,7 @@ void menu::set_items(const std::vector<std::string>& items) {
|
|||
selected_ = click_selects_ ? -1:0;
|
||||
for (std::vector<std::string>::const_iterator item = items.begin();
|
||||
item != items.end(); ++item) {
|
||||
items_.push_back(config::quoted_split(*item,',',false));
|
||||
items_.push_back(config::quoted_split(*item,',',!strip_spaces));
|
||||
|
||||
//make sure there is always at least one item
|
||||
if(items_.back().empty())
|
||||
|
@ -227,6 +234,10 @@ void menu::set_items(const std::vector<std::string>& items) {
|
|||
void menu::set_max_height(const int new_max_height) {
|
||||
max_height_ = new_max_height;
|
||||
}
|
||||
|
||||
void menu::set_max_width(const int new_max_width) {
|
||||
max_width_ = new_max_width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -527,18 +538,29 @@ namespace {
|
|||
|
||||
SDL_Rect item_size(const std::string& item) {
|
||||
SDL_Rect res = {0,0,0,0};
|
||||
if(item.empty() == false && item[0] == ImagePrefix) {
|
||||
const std::string image_name(item.begin()+1,item.end());
|
||||
SDL_Surface* const img = image::get_image(image_name,image::UNSCALED);
|
||||
if(img != NULL) {
|
||||
res.w = img->w;
|
||||
res.h = img->h;
|
||||
std::vector<std::string> img_text_items = config::split(item, menu::IMG_TEXT_SEPERATOR);
|
||||
for (std::vector<std::string>::const_iterator it = img_text_items.begin();
|
||||
it != img_text_items.end(); it++) {
|
||||
if (res.w > 0 || res.h > 0) {
|
||||
// Not the first item, add the spacing.
|
||||
res.w += 5;
|
||||
}
|
||||
const std::string str = *it;
|
||||
if(str.empty() == false && str[0] == ImagePrefix) {
|
||||
const std::string image_name(str.begin()+1,str.end());
|
||||
SDL_Surface* const img = image::get_image(image_name,image::UNSCALED);
|
||||
if(img != NULL) {
|
||||
res.w += img->w;
|
||||
res.h = maximum<int>(img->h, res.h);
|
||||
}
|
||||
} else {
|
||||
const SDL_Rect area = {0,0,10000,10000};
|
||||
const SDL_Rect font_size =
|
||||
font::draw_text(NULL,area,menu_font_size,font::NORMAL_COLOUR,str,0,0);
|
||||
res.w += font_size.w;
|
||||
res.h = maximum<int>(font_size.h, res.h);
|
||||
}
|
||||
} else {
|
||||
const SDL_Rect area = {0,0,10000,10000};
|
||||
res = font::draw_text(NULL,area,menu_font_size,font::NORMAL_COLOUR,item,0,0);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -588,7 +610,6 @@ void menu::draw_item(int item)
|
|||
}
|
||||
|
||||
clear_item(item);
|
||||
|
||||
gui::draw_solid_tinted_rectangle(x_,rect.y,width()-scrollbar_.get_width(),rect.h,
|
||||
item == selected_ ? 150:0,0,0,
|
||||
item == selected_ ? 0.6 : 0.2,
|
||||
|
@ -600,20 +621,35 @@ void menu::draw_item(int item)
|
|||
|
||||
int xpos = rect.x;
|
||||
for(size_t i = 0; i != items_[item].size(); ++i) {
|
||||
const std::string& str = items_[item][i];
|
||||
if(str.empty() == false && str[0] == ImagePrefix) {
|
||||
const std::string image_name(str.begin()+1,str.end());
|
||||
SDL_Surface* const img = image::get_image(image_name,image::UNSCALED);
|
||||
if(img != NULL && xpos+img->w < display_->x() && rect.y+img->h < display_->y()) {
|
||||
display_->blit_surface(xpos,rect.y,img);
|
||||
const int last_x = xpos;
|
||||
std::string str = items_[item][i];
|
||||
std::vector<std::string> img_text_items = config::split(str, IMG_TEXT_SEPERATOR);
|
||||
for (std::vector<std::string>::const_iterator it = img_text_items.begin();
|
||||
it != img_text_items.end(); it++) {
|
||||
str = *it;
|
||||
if(str.empty() == false && str[0] == ImagePrefix) {
|
||||
const std::string image_name(str.begin()+1,str.end());
|
||||
SDL_Surface* const img = image::get_image(image_name,image::UNSCALED);
|
||||
const int max_width = max_width_ < 0 ? display_->x() :
|
||||
minimum<int>(max_width_, display_->x() - xpos);
|
||||
if(img != NULL && (xpos - rect.x) + img->w < max_width
|
||||
&& rect.y+img->h < display_->y()) {
|
||||
const size_t y = rect.y + (rect.h - img->h)/2;
|
||||
display_->blit_surface(xpos,y,img);
|
||||
xpos += img->w + 5;
|
||||
}
|
||||
} else {
|
||||
const std::string to_show = max_width_ > -1 ?
|
||||
font::make_text_ellipsis(str, menu_font_size,
|
||||
max_width_ - (xpos - rect.x)
|
||||
- scrollbar_.get_width()) : str;
|
||||
const SDL_Rect& text_size = font::text_area(str,menu_font_size);
|
||||
const size_t y = rect.y + (rect.h - text_size.h)/2;
|
||||
font::draw_text(display_,area,menu_font_size,font::NORMAL_COLOUR,to_show,xpos,y);
|
||||
xpos += text_size.w + 5;
|
||||
}
|
||||
|
||||
} else {
|
||||
const SDL_Rect& text_size = font::text_area(str,menu_font_size);
|
||||
const size_t y = rect.y + (rect.h - text_size.h)/2;
|
||||
font::draw_text(display_,area,menu_font_size,font::NORMAL_COLOUR,str,xpos,y);
|
||||
}
|
||||
xpos += widths[i];
|
||||
xpos = last_x + widths[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class menu : public events::handler, public scrollable
|
|||
{
|
||||
public:
|
||||
menu(display& disp, const std::vector<std::string>& items,
|
||||
bool click_selects=false, int max_height=-1);
|
||||
bool click_selects=false, int max_height=-1, int max_width=-1);
|
||||
|
||||
int height() const;
|
||||
int width() const;
|
||||
|
@ -28,6 +28,9 @@ public:
|
|||
|
||||
void set_loc(int x, int y);
|
||||
void set_width(int w);
|
||||
// Get the rect bounding the menu. If the max_height/max_width is
|
||||
// set, use those, otherwise use the current values.
|
||||
SDL_Rect get_rect() const;
|
||||
|
||||
void redraw(); //forced redraw
|
||||
|
||||
|
@ -36,12 +39,15 @@ public:
|
|||
|
||||
void erase_item(size_t index);
|
||||
|
||||
void set_items(const std::vector<std::string>& items);
|
||||
/// Set new items to show and redraw/recalculate everything. If
|
||||
/// strip_spaces is false, spaces will remain at the item edges.
|
||||
void set_items(const std::vector<std::string>& items, bool strip_spaces=true);
|
||||
|
||||
/// Set a new max height for this menu. Note that this does not take
|
||||
/// effect immideately, only after certain operations that clear
|
||||
/// everything, such as set_items().
|
||||
void set_max_height(const int new_max_height);
|
||||
void set_max_width(const int new_max_width);
|
||||
|
||||
size_t nitems() const { return items_.size(); }
|
||||
|
||||
|
@ -55,19 +61,23 @@ public:
|
|||
void scroll(int pos);
|
||||
|
||||
void set_dirty() { drawn_ = false; }
|
||||
|
||||
enum { HELP_STRING_SEPERATOR = '|' };
|
||||
enum { IMG_TEXT_SEPERATOR = 1 }; // Re-evaluate if this should be
|
||||
// something else to be settable
|
||||
// from WML.
|
||||
|
||||
protected:
|
||||
void handle_event(const SDL_Event& event);
|
||||
|
||||
private:
|
||||
size_t max_items_onscreen() const;
|
||||
|
||||
int max_height_;
|
||||
int max_height_, max_width_;
|
||||
mutable int max_items_, item_height_;
|
||||
|
||||
void calculate_position();
|
||||
void key_press(SDLKey key);
|
||||
|
||||
void handle_event(const SDL_Event& event);
|
||||
|
||||
bool show_scrollbar() const;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue