There was a slight problem with this I noticed (inevitably) right
after I sent this mail.

The problem is if you press skip rather than next on the last page of
the last story segment, it doesn't do anything.

This isn't actually a trivial fix, because of the question of what
skip should really mean.

The definition I used is this: skip means go to the end of the story
(making the fact that it contains parts hidden from the user) unless
already at the end of the story, in which case go to the game.

The changes accomplish this by virtue of making the controller
slightly cleverer; it now knows whether its the last segment, and on
that basis will either skip internally (to find the end), skip
externally (to find another segment which is the end) or indicate the
game should be entered (if its the last and at its own end).

The underlying assumption behind this patch really is that the story
screens are a single "book" as they appear to the naive user, grouped
into parts for implementation reasons and convenience. Essentially
this patch enforces that model for back and skip.

There _should_ be a line using

next_itor = itor+1

rather than

next_itor = ++itor; ---itor;

However touching config.hpp takes 2+ hours to rebuild on my machine,
so I'm sending you what I have for now (operator+ is unimplemented; i
found out the time requirement by implementing operator--).

The more succinct

next_itor = ++itor--;

blows the compiler's mind (compiles but segfaults in resource tracking code).



On 5 April 2010 02:52, Ed <icelus...@gmail.com> wrote:
> Here's a first attempt at a patch for the back button for story screens:
>
> http://wiki.wesnoth.org/Good_Ideas
> http://www.wesnoth.org/forum/viewtopic.php?t=8857
>
> There is already a patch in the tracker for this:
>
> https://gna.org/patch/?1536
>
> However I didn't realise someone had started when I wrote this, and
> this is rather further along than that one is.
>
> Problems with that patch:
>
> * Back doesn't work properly (try it on the last page of the intro
> where it shows you the map) - can't go back.
> * Doesn't wire up back button in all the situations next button is clickable.
> * Probably violates screen size in USE_TINY_GUI mode
> * There's no key shortcut for back.
>
> Features this patch has:
>
> * Handles back properly so that you can go all the way back to the
> start from the end.
> * Handles skip properly so that all segments are skipped not just per
> segment (but can still go back so this is ok!)
> * Adds functional quit button.
> * Adds key controls: RIGHT is added for next, LEFT and BACKSPACE for back.
>
> Improves the implementation:
> * no longer throws quit exception to quit (was just caught and
> suppressed anyway).
> * play story segments from either end so that back can work properly.
> * adds reverse direction iteration for config::const_child_iterator (was 
> needed)
>
Index: src/storyscreen/render.cpp
===================================================================
--- src/storyscreen/render.cpp	(revision 41922)
+++ src/storyscreen/render.cpp	(working copy)
@@ -81,13 +81,17 @@
 
 namespace storyscreen {
 
-part_ui::part_ui(part& p, display& disp, gui::button& next_button, gui::button& skip_button)
+part_ui::part_ui(part& p, display& disp, 
+		 gui::button& next_button, gui::button& back_button, 
+		 gui::button& skip_button, gui::button& quit_button)
 	: p_(p)
 	, disp_(disp)
 	, video_(disp.video())
 	, keys_()
 	, next_button_(next_button)
+	, back_button_(back_button)
 	, skip_button_(skip_button)
+	, quit_button_(quit_button)
 	, ret_(NEXT)
 	, scale_factor_(1.0)
 	, base_rect_()
@@ -139,8 +143,10 @@
 	buttons_x_ = video_.getx() - 50;
 	buttons_y_ = base_rect_.y + base_rect_.h - 20;
 
-	next_button_.set_location(buttons_x_, buttons_y_ - 20);
-	skip_button_.set_location(buttons_x_, buttons_y_);
+	next_button_.set_location(buttons_x_, buttons_y_ - 60);
+	back_button_.set_location(buttons_x_, buttons_y_ - 40);
+	skip_button_.set_location(buttons_x_, buttons_y_ - 20);
+	quit_button_.set_location(buttons_x_, buttons_y_);
 
 #else // elif !defined(USE_TINY_GUI)
 
@@ -163,12 +169,16 @@
 		buttons_y_ = video_.gety() - 40;
 		break;
 	}
-	next_button_.set_location(buttons_x_, buttons_y_ - 30);
-	skip_button_.set_location(buttons_x_, buttons_y_);
+	next_button_.set_location(buttons_x_, buttons_y_ - 90);
+	back_button_.set_location(buttons_x_, buttons_y_ - 60);
+	skip_button_.set_location(buttons_x_, buttons_y_ - 30);
+	quit_button_.set_location(buttons_x_, buttons_y_);
 #endif
 
     next_button_.set_volatile(true);
+    back_button_.set_volatile(true);
     skip_button_.set_volatile(true);
+    quit_button_.set_volatile(true);
 }
 
 void part_ui::prepare_floating_images()
@@ -210,10 +220,18 @@
 					ret_ = SKIP;
 					return false;
 				}
+				else if(quit_button_.pressed()) {
+					ret_ = QUIT;
+					return false;
+				}
 				else if(next_button_.pressed()) {
 					ret_ = NEXT;
 					return false;
 				}
+				else if(back_button_.pressed()) {
+					ret_ = BACK;
+					return false;
+				}
 
 				disp_.delay(fi.display_delay() / 50);
 
@@ -238,7 +256,9 @@
 			}
 		}
 
-		if(keys_[SDLK_ESCAPE] || next_button_.pressed() || skip_button_.pressed()) {
+		if(keys_[SDLK_ESCAPE] || 
+		   next_button_.pressed() || back_button_.pressed() || 
+		   skip_button_.pressed() || quit_button_.pressed()) {
 			skip = true;
 			++fi_n;
 			continue;
@@ -429,10 +449,10 @@
 		// by the buttons being hidden and unhidden in this scope.
 		update_locker locker(video_);
 
-		//back_button_.hide();
 		next_button_.hide();
+		back_button_.hide();
 		skip_button_.hide();
-		//quit_button_.hide();
+		quit_button_.hide();
 
 #ifndef LOW_MEM
 		blur_area(video_, fix_text_y, fix_text_h);
@@ -450,8 +470,12 @@
 		// Make GUI1 buttons aware of background modifications
 		next_button_.set_location(next_button_.location());
 		next_button_.hide(false);
+		back_button_.set_location(back_button_.location());
+		back_button_.hide(false);
 		skip_button_.set_location(skip_button_.location());
 		skip_button_.hide(false);
+		quit_button_.set_location(quit_button_.location());
+		quit_button_.hide(false);
 	}
 
 	if(imgs_.empty()) {
@@ -479,9 +503,9 @@
 			++scan.y;
 		}
 
-		const bool keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER];
+		const bool next_keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER] || keys_[SDLK_RIGHT];
 
-		if((keydown && !last_key) || next_button_.pressed()) {
+		if((next_keydown && !last_key) || next_button_.pressed()) {
 			if(skip == true || scan_finished) {
 				ret_ = NEXT;
 				break;
@@ -490,11 +514,22 @@
 			}
 		}
 
-		last_key = keydown;
+		const bool back_keydown = keys_[SDLK_BACKSPACE] || keys_[SDLK_LEFT];
 
+		if((back_keydown && !last_key) || back_button_.pressed()) {
+			ret_ = BACK;
+			break;
+		}
+
+		last_key = next_keydown || back_keydown;
+
 		if(keys_[SDLK_ESCAPE] || skip_button_.pressed()) {
 			ret_ = SKIP;
 			return;
+		} 
+		else if(quit_button_.pressed()) {
+			ret_ = QUIT;
+			return;
 		}
 
 		events::pump();
@@ -517,20 +552,32 @@
 {
 	bool last_key = true;
 	while(true) {
-		const bool keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER];
+		const bool next_keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER] || keys_[SDLK_RIGHT];
 
-		if((keydown && !last_key) || next_button_.pressed()) {
+		if((next_keydown && !last_key) || next_button_.pressed()) {
 			ret_ = NEXT;
 			break;
 		}
 
-		last_key = keydown;
+		const bool back_keydown = keys_[SDLK_BACKSPACE] || keys_[SDLK_LEFT];
 
+		if((back_keydown && !last_key) || back_button_.pressed()) {
+			ret_ = BACK;
+			break;
+		}
+
+		last_key = next_keydown || back_keydown;
+
 		if(keys_[SDLK_ESCAPE] || skip_button_.pressed()) {
 			ret_ = SKIP;
 			return;
 		}
 
+		if(quit_button_.pressed()) {
+			ret_ = QUIT;
+			return;
+		}
+
 		events::pump();
 		events::raise_process_event();
 		events::raise_draw_event();
Index: src/storyscreen/interface.cpp
===================================================================
--- src/storyscreen/interface.cpp	(revision 41922)
+++ src/storyscreen/interface.cpp	(working copy)
@@ -47,20 +47,20 @@
 	}
 } // end anonymous namespace
 
-void show_storyscreen(display& disp, const vconfig& story_cfg, const std::string& scenario_name)
+storyscreen::STORY_RESULT show_storyscreen(display& disp, const vconfig& story_cfg, 
+					   const std::string& scenario_name,
+					   storyscreen::START_POSITION startpos,
+					   bool segment_is_last)
 {
 	LOG_NG << "entering storyscreen procedure...\n";
 
-	storyscreen::controller ctl(disp, story_cfg, scenario_name);
+	storyscreen::controller ctl(disp, story_cfg, scenario_name, segment_is_last);
 
-	try {
-		ctl.show();
-	} catch(storyscreen::controller::quit const&) {
-		LOG_NG << "leaving storyscreen for titlescreen...\n";
-		STUB();
-	}
+	storyscreen::STORY_RESULT ret = ctl.show(startpos);
 
 	LOG_NG << "leaving storyscreen procedure...\n";
+
+	return ret;
 }
 
 void show_endscreen(display& /*disp*/, const t_string& /*text*/, unsigned int /*duration*/)
Index: src/storyscreen/controller.cpp
===================================================================
--- src/storyscreen/controller.cpp	(revision 41922)
+++ src/storyscreen/controller.cpp	(working copy)
@@ -42,12 +42,14 @@
 
 namespace storyscreen {
 
-controller::controller(display& disp, const vconfig& data, const std::string& scenario_name)
+controller::controller(display& disp, const vconfig& data, const std::string& scenario_name,
+		       bool segment_is_last)
 	: disp_(disp)
 	, disp_resize_lock_()
 	, evt_context_()
 	, data_(data)
 	, scenario_name_(scenario_name)
+	, segment_is_last_(segment_is_last)
 	, parts_()
 {
 	ASSERT_LOG(resources::state_of_game != NULL, "Ouch: gamestate is NULL when initializing storyscreen controller");
@@ -118,17 +120,17 @@
 	}
 }
 
-void controller::show()
+STORY_RESULT controller::show(START_POSITION startpos)
 {
 	if(parts_.empty()) {
 		LOG_NG << "no storyscreen parts to show\n";
-		return;
+		return NEXT;
 	}
 
 	gui::button next_button(disp_.video(),_("Next") + std::string(" >"));
+	gui::button back_button(disp_.video(),std::string("< ")+_("Back"));
 	gui::button skip_button(disp_.video(),_("Skip"));
-	// TODO:
-	//  gui::button back_button(disp_.video(),std::string("< ")+_("Next"));
+	gui::button quit_button(disp_.video(),_("Quit"));
 
 	// Build renderer cache unless built for a low-memory environment;
 	// caching the scaled backgrounds can take over a decent amount of memory.
@@ -136,18 +138,28 @@
 	std::vector< render_pointer_type > uis_;
 	foreach(part_pointer_type p, parts_) {
 		ASSERT_LOG( p != NULL, "Ouch: hit NULL storyscreen part in collection" );
-		render_pointer_type const rpt(new part_ui(*p, disp_, next_button, skip_button));
+		render_pointer_type const rpt(new part_ui(*p, disp_, next_button, back_button, skip_button, quit_button));
 		uis_.push_back(rpt);
 	}
 #endif
 
 	size_t k = 0;
+	switch(startpos) {
+	case START_BEGINNING:
+		break;
+	case START_END:
+		k = parts_.size() -1;
+		break;
+	default:
+		assert(false);
+		break;
+	}
 
 	while(k < parts_.size()) {
 #ifndef LOW_MEM
 		render_reference_type render_interface = *uis_[k];
 #else
-		render_value_type render_interface(*parts_[k], disp_, next_button, skip_button);
+		render_value_type render_interface(*parts_[k], disp_, next_button, back_button, skip_button, quit_button);
 #endif
 
 		LOG_NG << "displaying storyscreen part " << k+1 << " of " << parts_.size() << '\n';
@@ -157,18 +169,37 @@
 			++k;
 			break;
 		case part_ui::BACK:
-			// If we are at the first page we can't go back.
 			if(k > 0) {
 				--k;
+			} 
+			else {
+				return BACK;
 			}
 			break;
 		case part_ui::SKIP:
-			k = parts_.size();
+			if(segment_is_last_) {
+				if(k < parts_.size() - 1) {
+					// not at the end of this segment
+					k = parts_.size() - 1;
+				} else {
+					// we are at the end of this segment
+					// what comes next is the game
+					return NEXT;
+				}
+			} else {
+				// we want to fast forward all the way
+				// to the last segment
+				return SKIP;
+			}
 			break;
+		case part_ui::QUIT:
+			return QUIT;
 		default:
-			throw quit();
+			assert(false);
+			return QUIT;
 		}
 	}
+	return NEXT;
 }
 
 } // end namespace storyscreen
Index: src/storyscreen/render.hpp
===================================================================
--- src/storyscreen/render.hpp	(revision 41922)
+++ src/storyscreen/render.hpp	(working copy)
@@ -45,9 +45,9 @@
 	/** Storyscreen result. */
 	enum RESULT {
 		NEXT,	/**< The user pressed the go-next button. */
-		BACK,	/**< The user pressed the go-back button (TODO: not implemented). */
+		BACK,	/**< The user pressed the go-back button. */
 		SKIP,	/**< The user pressed the skip button. */
-		QUIT	/**< The user pressed the quit button (TODO: not implemented). */
+		QUIT	/**< The user pressed the quit button. */
 	};
 
 	/**
@@ -57,7 +57,9 @@
 	 * @param next_button Next button. Shouldn't be destroyed before the part_ui object.
 	 * @param skip_button Skip button. Shouldn't be destroyed before the part_ui object.
 	 */
-	part_ui(part& p, display& disp, gui::button& next_button, gui::button& skip_button);
+        part_ui(part& p, display& disp, 
+                gui::button& next_button, gui::button& back_button, 
+                gui::button& skip_button, gui::button& quit_button);
 
 	/**
 	 * Render and display the storyscreen, process and return user input.
@@ -70,10 +72,10 @@
 	CVideo& video_; // convenience, it's currently obtained from disp_
 	CKey keys_;     // convenience
 
-	//gui::button& back_button_;
 	gui::button& next_button_;
+	gui::button& back_button_;
 	gui::button& skip_button_;
-	//gui::button& quit_button_;
+	gui::button& quit_button_;
 
 	RESULT ret_;
 
Index: src/storyscreen/interface.hpp
===================================================================
--- src/storyscreen/interface.hpp	(revision 41922)
+++ src/storyscreen/interface.hpp	(working copy)
@@ -27,6 +27,22 @@
 
 #include <string>
 
+namespace storyscreen {
+
+enum STORY_RESULT {
+	NEXT,
+	BACK,
+	SKIP,
+	QUIT
+};
+
+enum START_POSITION {
+	START_BEGINNING,
+	START_END
+};
+
+} /* storyscreen namespace */
+
 /**
  * Function to show an introduction sequence using story WML.
  * The WML config data (story_cfg) has a format similar to:
@@ -41,8 +57,12 @@
  * storyline,and 'img' is a background image. Each part of the sequence will
  * be displayed in turn, with the user able to go to the next part, or skip
  * it entirely.
+ * @return is NEXT if the story played to the end, BACK if the story played to the beginning, and QUIT if the story was quit
  */
-void show_storyscreen(display& disp, const vconfig& story_cfg, const std::string& scenario_name);
+storyscreen::STORY_RESULT show_storyscreen(display& disp, const vconfig& story_cfg, 
+					   const std::string& scenario_name,
+					   storyscreen::START_POSITION startpos,
+					   bool segment_is_last);
 
 /**
  * Displays a simple fading screen with any user-provided text.
Index: src/storyscreen/controller.hpp
===================================================================
--- src/storyscreen/controller.hpp	(revision 41922)
+++ src/storyscreen/controller.hpp	(working copy)
@@ -21,6 +21,7 @@
 #define STORYSCREEN_CONTROLLER_HPP_INCLUDED
 
 #include "events.hpp"
+#include "interface.hpp"
 #include "variable.hpp"
 #include "video.hpp"
 
@@ -39,12 +40,13 @@
 class controller
 {
 public:
-	controller(display& disp, const vconfig& data, const std::string& scenario_name);
+	controller(display& disp, const vconfig& data, const std::string& scenario_name,
+		   bool segment_is_last);
 
 	/**
 	 * Display all story screen parts in a first..last sequence.
 	 */
-	void show();
+	STORY_RESULT show(START_POSITION startpos=START_BEGINNING);
 
 private:
 	typedef boost::shared_ptr< part    > part_pointer_type;
@@ -67,14 +69,13 @@
 
 	vconfig data_;
 	std::string scenario_name_;
+	bool segment_is_last_;
 
 	// The part cache.
 	std::vector< part_pointer_type > parts_;
 
 public:
 	struct no_parts {};
-	struct quit {};
-
 };
 
 } // end namespace storyscreen
Index: src/playsingle_controller.cpp
===================================================================
--- src/playsingle_controller.cpp	(revision 41922)
+++ src/playsingle_controller.cpp	(working copy)
@@ -286,8 +286,39 @@
 	sound::commit_music_changes();
 
 	if(!skip_replay) {
-		foreach (const config &s, story) {
-			show_storyscreen(*gui_, vconfig(s, true), level_["name"]);
+		config::const_child_iterator itor = story.first;
+		storyscreen::START_POSITION startpos = storyscreen::START_BEGINNING;
+		while(itor != story.second) {
+			const config::const_child_iterator next_itor = ++itor;
+			--itor;
+			const bool segment_is_last = (next_itor == story.second);
+			storyscreen::STORY_RESULT result = show_storyscreen(*gui_, vconfig(*itor, true), level_["name"], 
+									    startpos, segment_is_last);
+			switch(result) {
+			case storyscreen::NEXT:
+				if(itor != story.second) {
+					++itor;
+					startpos = storyscreen::START_BEGINNING;
+				}
+				break;
+			case storyscreen::BACK:
+				if(itor != story.first) {
+					--itor;
+					startpos = storyscreen::START_END;
+				}
+				break;
+			case storyscreen::SKIP:
+				itor = story.second;
+				--itor;
+				startpos = storyscreen::START_END;
+				break;
+			case storyscreen::QUIT:
+				return QUIT;
+			default:
+				assert(false);
+				itor = story.second;
+				break;
+			}
 		}
 	}
 	gui_->labels().read(level_);
Index: src/config.hpp
===================================================================
--- src/config.hpp	(revision 41922)
+++ src/config.hpp	(working copy)
@@ -131,6 +131,8 @@
 
 		const_child_iterator &operator++() { ++i_; return *this; }
 		const_child_iterator operator++(int) { return const_child_iterator(i_++); }
+		const_child_iterator &operator--() { --i_; return *this; }
+		const_child_iterator operator--(int) { return const_child_iterator(i_--); }
 
 		const config &operator*() const { return **i_; }
 		const config *operator->() const { return &**i_; }
Index: changelog
===================================================================
--- changelog	(revision 41922)
+++ changelog	(working copy)
@@ -26,6 +26,7 @@
    * Fixed picking the proper locale, the problem only occurred on some
      systems
    * Added a way to compile wesnoth on windows by using CMake + MSVC9.
+   * Added back and quit buttons to the story screens.
 
 Version 1.8.0:
  * AI:
_______________________________________________
Wesnoth-dev mailing list
Wesnoth-dev@gna.org
https://mail.gna.org/listinfo/wesnoth-dev

Reply via email to