diff -Nur wesnoth-0.8.8/data/game.cfg wesnoth-patch/data/game.cfg
--- wesnoth-0.8.8/data/game.cfg	2004-11-22 23:47:37.000000000 +0100
+++ wesnoth-patch/data/game.cfg	2004-12-12 19:59:31.776315000 +0100
@@ -38,6 +38,7 @@
 	rest_heal_amount=2
 	recall_cost=20
 	kill_experience=8
+	balanced_fight_mode=0
 
 	title="misc/title.png"
 	logo="misc/logo.png"
diff -Nur wesnoth-0.8.8/README.balanced_fight_mode wesnoth-patch/README.balanced_fight_mode
--- wesnoth-0.8.8/README.balanced_fight_mode	1970-01-01 01:00:00.000000000 +0100
+++ wesnoth-patch/README.balanced_fight_mode	2004-12-12 23:04:38.920922292 +0100
@@ -0,0 +1,93 @@
+balanced_fight_mode patch
+-------------------------
+
+- Goals
+
+The aim of this patch is to modify the way fight results are
+calculated to suppress extreme outcomes with as few changes
+to the current game mechanics as possible. In the ideal case, 
+the player would not notice its presence during play, except 
+for the fact that he will find himself to having to do fewer 
+save-loads due to rotten (vs. the ususal bad) luck.
+
+- What the patch does
+
+For each attack with more than one try, the patch supresses
+the least likely outcome, which is either all-hits (if the 
+hit probability is <50%) or all-misses (if the hit probability
+is >50%) and balances this by increasing/reducing the hit
+probability in the opposite case such that the EV is constant.
+The patch is only active during the last round of fighting, so
+tactical aspects like killing-before-getting-killed or abilities
+like firststrike are not affected. Also, the patch has no effect
+on weapons such as the pistol attack of Dwarvish Thunderer with 
+a single attempt.
+
+Consider the following example:
+
+Your mage has 3 tries to hit with a probability of 70%. Assuming
+you have missed the first 2 tries, then you will always his on
+the last try. OTOH, if you hit on your first 2 tries, then the
+probability to also hit on the last strike is reduced from
+70% to 65% (64.49% would be optimal, but wesnoth operates with 
+integer percentages). The EV for the inflicted damage remains
+exactly the same as 2.7%, the chance to miss 3 times in a row,
+results in an increased EV of 0.027 hits which is exactly balanced
+by reducing the last hit probability by 5.51% after 2 hits 
+(probability 49%) which results in a decreased EV of 
+0.49*0.0551 = 0.027 hits.
+
+The specturum of possible outcomes thus changes from
+
+0/1/2/3 hits: 2.7 / 18.9 / 44.1 / 34.3 % 
+to            0   / 21.6 / 46.8 / 31.6 %
+
+As you can see, the changes should be small enough to go 
+unnoticed with units with 3 or more strikes. The most 
+visible change will be with units that only have 2 strikes:
+A horseman on open terrain (hit probability 60%) will always 
+hit at least once, which should make combat with mounted troops 
+a bit less of a crapshoot.
+
+- Download and Installation (Linux)
+
+You can get the patch from 
+
+  http://tph.tuwien.ac.at/~oemer/wesnoth/
+  
+The filename is 
+
+  wesnoth-0.8.8-balanced_fight_mode.patch
+  
+and is only tested with wesnoth version 0.8.8.
+
+After downloading the patch, change into the
+wesnoth source directory and apply it with
+
+  patch -p1 < wesnoth-0.8.8-balanced_fight_mode.patch
+
+After that, do the usual
+
+  ./configure
+  make
+  make install
+
+For compatibility reasons, the patch is disabled by default.
+To activate the patch, edit your game.cfg file, usually in
+
+  /usr/local/share/wesnoth/data/game.cfg
+  
+and change the line 
+
+          balanced_fight_mode=0
+
+to
+
+          balanced_fight_mode=1
+
+- Feedback
+
+The patch is only minimally tested. Please report any problems
+and feedback to oemer@tph.tuwien.ac.at.
+
+Bernhard Oemer v/o Ignatius
diff -Nur wesnoth-0.8.8/src/actions.cpp wesnoth-patch/src/actions.cpp
--- wesnoth-0.8.8/src/actions.cpp	2004-12-03 02:19:29.000000000 +0100
+++ wesnoth-patch/src/actions.cpp	2004-12-12 21:30:01.737297000 +0100
@@ -559,6 +559,19 @@
 	return s.str();
 }
 
+// calculate the probability delta spice=(1-p)^n/p^(n-1) for the cooked
+// hitting probabilities when game_config::balanced_fight_mode==1
+
+int spice(int p,int n) 
+{
+	assert(p>=50);
+	int q=100-p;
+	int s=q;
+	for(int i=1;i<n;i++) { s*=q; s/=p; }
+if(s<=0) ERR_NW << "s=0 when p=" << p << " n=" << n << "\n";
+	return s;
+}
+
 void attack(display& gui, const gamemap& map, 
             std::vector<team>& teams,
             gamemap::location attacker,
@@ -597,6 +610,8 @@
 
 	const int orig_attacks = stats.nattacks;
 	const int orig_defends = stats.ndefends;
+        int attack_hits=0;
+        int defend_hits=0;
 	int to_the_death = stats.to_the_death ? 30 : 0;
 
 	static const std::string poison_string("poison");
@@ -608,6 +623,25 @@
 			const int ran_num = get_random();
 			bool hits = (ran_num%100) < stats.chance_to_hit_defender;
 
+			// cook hit probability for balanced_fight_mode if necessary
+                        if(game_config::balanced_fight_mode && stats.nattacks==1 && orig_attacks>1 && stats.chance_to_hit_defender>0) {
+                        	int cooked_chance_to_hit=stats.chance_to_hit_defender;
+                        	if(attack_hits==orig_attacks-1) {
+                        		if(stats.chance_to_hit_defender<=50)
+						cooked_chance_to_hit=0;
+                        		else
+						cooked_chance_to_hit-=spice(stats.chance_to_hit_defender,orig_attacks);
+				} else if(attack_hits==0) {
+                        		if(stats.chance_to_hit_defender<50)
+						cooked_chance_to_hit+=spice(100-stats.chance_to_hit_defender,orig_attacks);
+                        		else
+						cooked_chance_to_hit=100;
+				}
+				hits = (ran_num%100) < cooked_chance_to_hit;
+				if(stats.chance_to_hit_defender!=cooked_chance_to_hit) LOG_NG << "cooked chance_to_hit_defender from " << orig_attacks << " x " << stats.chance_to_hit_defender << "% to " << cooked_chance_to_hit << "% after " << attack_hits << " hits.\n";
+                        }
+                        if(hits) attack_hits++;
+
 			//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();
@@ -758,6 +792,25 @@
 			const int ran_num = get_random();
 			bool hits = (ran_num%100) < stats.chance_to_hit_attacker;
 
+			// cook hit probability for balanced_fight_mode if necessary
+                        if(game_config::balanced_fight_mode && stats.ndefends==1 && orig_defends>1 && stats.chance_to_hit_attacker>0) {
+                        	int cooked_chance_to_hit=stats.chance_to_hit_attacker;
+                        	if(defend_hits==orig_defends-1) {
+                        		if(stats.chance_to_hit_attacker<=50)
+						cooked_chance_to_hit=0;
+                        		else
+						cooked_chance_to_hit-=spice(stats.chance_to_hit_attacker,orig_defends);
+				} else if(defend_hits==0) {
+                        		if(stats.chance_to_hit_attacker<50)
+						cooked_chance_to_hit+=spice(100-stats.chance_to_hit_attacker,orig_defends);
+                        		else
+						cooked_chance_to_hit=100;
+				}
+				hits = (ran_num%100) < cooked_chance_to_hit;
+				if(stats.chance_to_hit_attacker!=cooked_chance_to_hit) LOG_NG << "cooked chance_to_hit_attacker from " << orig_defends << " x " << stats.chance_to_hit_attacker << "% to " << cooked_chance_to_hit << "% after " << defend_hits << " hits.\n";
+                        }
+                        if(hits) defend_hits++;
+
 			//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();
diff -Nur wesnoth-0.8.8/src/game_config.cpp wesnoth-patch/src/game_config.cpp
--- wesnoth-0.8.8/src/game_config.cpp	2004-11-18 23:00:12.000000000 +0100
+++ wesnoth-patch/src/game_config.cpp	2004-12-12 14:48:50.025675000 +0100
@@ -30,6 +30,7 @@
 	int recall_cost = 20;
 	int kill_experience = 8;
 	int leadership_bonus = 25;
+        int balanced_fight_mode = 0;
 	const std::string version = VERSION;
 	bool debug = false, editor = false;
 
@@ -95,6 +96,7 @@
 		rest_heal_amount = atoi(v["rest_heal_amount"].c_str());
 		recall_cost = atoi(v["recall_cost"].c_str());
 		kill_experience = atoi(v["kill_experience"].c_str());
+		balanced_fight_mode = atoi(v["balanced_fight_mode"].c_str());
 
 		game_icon = v["icon"];
 		game_title = v["title"];
diff -Nur wesnoth-0.8.8/src/game_config.hpp wesnoth-patch/src/game_config.hpp
--- wesnoth-0.8.8/src/game_config.hpp	2004-09-22 02:05:33.000000000 +0200
+++ wesnoth-patch/src/game_config.hpp	2004-12-12 14:46:27.915317000 +0100
@@ -30,6 +30,7 @@
 	extern int recall_cost;
 	extern int kill_experience;
 	extern int leadership_bonus;
+        extern int balanced_fight_mode;
 	extern const std::string version;
 
 	extern bool debug, editor;

