# HG changeset patch
# User unc0rr
# Date 1241122424 0
# Node ID 7845c77c8d31c1994056f4f6d5a1cd25fa0f9b83
# Parent  73b0bcc4396da8c4b3ae7450e59df1c56d8d297f
nemo's great patch:
 - Speech bubbles
 - Vampirism + karma

diff -r 73b0bcc4396d -r 7845c77c8d31 QTfrontend/ammoSchemeModel.cpp
--- a/QTfrontend/ammoSchemeModel.cpp	Sun Apr 26 15:47:03 2009 +0000
+++ b/QTfrontend/ammoSchemeModel.cpp	Thu Apr 30 20:13:44 2009 +0000
@@ -32,11 +32,13 @@
 		<< QVariant(false)         // laser sight    6
 		<< QVariant(false)         // invulnerable   7
 		<< QVariant(true)          // add mines      8
-		<< QVariant(100)           // damage modfier 9
-		<< QVariant(45)            // turn time      10
-		<< QVariant(100)           // init health    11
-		<< QVariant(15)            // sudden death   12
-		<< QVariant(5)             // case prob      13
+		<< QVariant(false)         // vampiric       9
+		<< QVariant(false)         // karma          10
+		<< QVariant(100)           // damage modfier 11
+		<< QVariant(45)            // turn time      12
+		<< QVariant(100)           // init health    13
+		<< QVariant(15)            // sudden death   14
+		<< QVariant(5)             // case prob      15
 		;
 
 AmmoSchemeModel::AmmoSchemeModel(QObject* parent, const QString & fileName) :
@@ -61,11 +63,13 @@
 		<< "laser"            //  6
 		<< "invulnerability"  //  7
 		<< "mines"            //  8
-		<< "damagefactor"     //  9
-		<< "turntime"         // 10
-		<< "health"           // 11
-		<< "suddendeath"      // 12
-		<< "caseprobability"  // 13
+		<< "vampiric"         //  9
+		<< "karma"            // 10
+		<< "damagefactor"     // 11
+		<< "turntime"         // 12
+		<< "health"           // 13
+		<< "suddendeath"      // 14
+		<< "caseprobability"  // 15
 		;
 
 	QList<QVariant> proMode;
@@ -79,11 +83,13 @@
 		<< QVariant(false)         // laser sight    6
 		<< QVariant(false)         // invulnerable   7
 		<< QVariant(false)         // add mines      8
-		<< QVariant(100)           // damage modfier 9
-		<< QVariant(15)            // turn time      10
-		<< QVariant(100)           // init health    11
-		<< QVariant(15)            // sudden death   12
-		<< QVariant(0)             // case prob      13
+		<< QVariant(false)         // vampiric       9
+		<< QVariant(false)         // karma          10
+		<< QVariant(100)           // damage modfier 11
+		<< QVariant(15)            // turn time      12
+		<< QVariant(100)           // init health    13
+		<< QVariant(15)            // sudden death   14
+		<< QVariant(0)             // case prob      15
 		;
 
 	QList<QVariant> shoppa;
@@ -97,11 +103,13 @@
 		<< QVariant(false)         // laser sight    6
 		<< QVariant(false)         // invulnerable   7
 		<< QVariant(false)         // add mines      8
-		<< QVariant(100)           // damage modfier 9
-		<< QVariant(30)            // turn time      10
-		<< QVariant(100)           // init health    11
-		<< QVariant(50)            // sudden death   12
-		<< QVariant(1)             // case prob      13
+		<< QVariant(false)         // vampiric       9
+		<< QVariant(false)         // karma          10
+		<< QVariant(100)           // damage modfier 11
+		<< QVariant(30)            // turn time      12
+		<< QVariant(100)           // init health    13
+		<< QVariant(50)            // sudden death   14
+		<< QVariant(1)             // case prob      15
 		;
 
 	QList<QVariant> basketball;
@@ -115,11 +123,13 @@
 		<< QVariant(false)         // laser sight    6
 		<< QVariant(true)          // invulnerable   7
 		<< QVariant(false)         // add mines      8
-		<< QVariant(100)           // damage modfier 9
-		<< QVariant(30)            // turn time      10
-		<< QVariant(100)           // init health    11
-		<< QVariant(15)            // sudden death   12
-		<< QVariant(0)             // case prob      13
+		<< QVariant(false)         // vampiric       9
+		<< QVariant(false)         // karma          10
+		<< QVariant(100)           // damage modfier 11
+		<< QVariant(30)            // turn time      12
+		<< QVariant(100)           // init health    13
+		<< QVariant(15)            // sudden death   14
+		<< QVariant(0)             // case prob      15
 		;
 
 	schemes.append(defaultScheme);
diff -r 73b0bcc4396d -r 7845c77c8d31 QTfrontend/gamecfgwidget.cpp
--- a/QTfrontend/gamecfgwidget.cpp	Sun Apr 26 15:47:03 2009 +0000
+++ b/QTfrontend/gamecfgwidget.cpp	Thu Apr 30 20:13:44 2009 +0000
@@ -107,13 +107,17 @@
 		result |= 0x80;
 	if (schemeData(8).toBool())
 		result |= 0x100;
+	if (schemeData(9).toBool())
+		result |= 0x200;
+	if (schemeData(10).toBool())
+		result |= 0x400;
 
 	return result;
 }
 
 quint32 GameCFGWidget::getInitHealth() const
 {
-	return schemeData(11).toInt();
+	return schemeData(13).toInt();
 }
 
 QStringList GameCFGWidget::getFullConfig() const
@@ -121,10 +125,10 @@
 	QStringList sl;
 	sl.append("eseed " + pMapContainer->getCurrentSeed());
 	sl.append(QString("e$gmflags %1").arg(getGameFlags()));
-	sl.append(QString("e$damagepct %1").arg(schemeData(9).toInt()));
-	sl.append(QString("e$turntime %1").arg(schemeData(10).toInt() * 1000));
-	sl.append(QString("e$sd_turns %1").arg(schemeData(12).toInt()));
-	sl.append(QString("e$casefreq %1").arg(schemeData(13).toInt()));
+	sl.append(QString("e$damagepct %1").arg(schemeData(11).toInt()));
+	sl.append(QString("e$turntime %1").arg(schemeData(12).toInt() * 1000));
+	sl.append(QString("e$sd_turns %1").arg(schemeData(14).toInt()));
+	sl.append(QString("e$casefreq %1").arg(schemeData(15).toInt()));
 	sl.append(QString("e$template_filter %1").arg(pMapContainer->getTemplateFilter()));
 
 	QString currentMap = pMapContainer->getCurrentMap();
diff -r 73b0bcc4396d -r 7845c77c8d31 QTfrontend/hedgewars.qrc
--- a/QTfrontend/hedgewars.qrc	Sun Apr 26 15:47:03 2009 +0000
+++ b/QTfrontend/hedgewars.qrc	Thu Apr 30 20:13:44 2009 +0000
@@ -53,6 +53,8 @@
     <file>res/btnMines.png</file>
     <file>res/btnTeamsDivide.png</file>
     <file>res/btnSolid.png</file>
+    <file>res/btnVampiric.png</file>
+    <file>res/btnKarma.png</file>
     <file>res/iconBox.png</file>
     <file>res/iconHealth.png</file>
     <file>res/iconSuddenDeath.png</file>
diff -r 73b0bcc4396d -r 7845c77c8d31 QTfrontend/hwconsts.cpp.in
--- a/QTfrontend/hwconsts.cpp.in	Sun Apr 26 15:47:03 2009 +0000
+++ b/QTfrontend/hwconsts.cpp.in	Thu Apr 30 20:13:44 2009 +0000
@@ -29,14 +29,14 @@
 QStringList * Themes;
 QStringList * mapList;
 
-QString * cDefaultAmmoStore = new QString("939192942219912103223511100120100000");
+QString * cDefaultAmmoStore = new QString("9391929422199121032235111001201000001");
 QList< QPair<QString, QString> > cDefaultAmmos =
 	QList< QPair<QString, QString> >()
 	<< qMakePair(QString("Default"), *cDefaultAmmoStore)
-	<< qMakePair(QString("Crazy"),     QString("999999999999999999299999999999999929"))
-	<< qMakePair(QString("Pro mode"),  QString("909000900000000000000900000000000000"))
-	<< qMakePair(QString("Shoppa"),    QString("000000990000000000000000000000000000"))
-	<< qMakePair(QString("Basketball"),QString("000000900000090000000000000000000000"))
+	<< qMakePair(QString("Crazy"),     QString("9999999999999999992999999999999999299"))
+	<< qMakePair(QString("Pro mode"),  QString("9090009000000000000009000000000000000"))
+	<< qMakePair(QString("Shoppa"),    QString("0000009900000000000000000000000000000"))
+	<< qMakePair(QString("Basketball"),QString("0000009000000900000000000000000000000"))
 	;
 
 QColor * color1 = new QColor(221,   0,   0);
diff -r 73b0bcc4396d -r 7845c77c8d31 QTfrontend/pages.cpp
--- a/QTfrontend/pages.cpp	Sun Apr 26 15:47:03 2009 +0000
+++ b/QTfrontend/pages.cpp	Thu Apr 30 20:13:44 2009 +0000
@@ -916,6 +916,14 @@
 	TBW_mines->setText(ToggleButtonWidget::tr("Add Mines"));
 	glGMLayout->addWidget(TBW_mines,1,3,1,1);
 
+	TBW_vampiric = new ToggleButtonWidget(gbGameModes, ":/res/btnVampiric.png");
+	TBW_vampiric->setText(ToggleButtonWidget::tr("Vampirism"));
+	glGMLayout->addWidget(TBW_vampiric,2,0,1,1);
+
+	TBW_karma = new ToggleButtonWidget(gbGameModes, ":/res/btnKarma.png");
+	TBW_karma->setText(ToggleButtonWidget::tr("Karma"));
+	glGMLayout->addWidget(TBW_karma,2,1,1,1);
+
 	// Right
 	QLabel * l;
 	
@@ -1031,11 +1039,13 @@
 	mapper->addMapping(TBW_laserSight->button(), 6);
 	mapper->addMapping(TBW_invulnerable->button(), 7);
 	mapper->addMapping(TBW_mines->button(), 8);
-	mapper->addMapping(SB_DamageModifier, 9);
-	mapper->addMapping(SB_TurnTime, 10);
-	mapper->addMapping(SB_InitHealth, 11);
-	mapper->addMapping(SB_SuddenDeath, 12);
-	mapper->addMapping(SB_CaseProb, 13);
+	mapper->addMapping(TBW_vampiric->button(), 9);
+	mapper->addMapping(TBW_karma->button(), 10);
+	mapper->addMapping(SB_DamageModifier, 11);
+	mapper->addMapping(SB_TurnTime, 12);
+	mapper->addMapping(SB_InitHealth, 13);
+	mapper->addMapping(SB_SuddenDeath, 14);
+	mapper->addMapping(SB_CaseProb, 15);
 
 	mapper->toFirst();
 }
diff -r 73b0bcc4396d -r 7845c77c8d31 QTfrontend/pages.h
--- a/QTfrontend/pages.h	Sun Apr 26 15:47:03 2009 +0000
+++ b/QTfrontend/pages.h	Thu Apr 30 20:13:44 2009 +0000
@@ -424,6 +424,8 @@
 	ToggleButtonWidget * TBW_laserSight;
 	ToggleButtonWidget * TBW_invulnerable;
 	ToggleButtonWidget * TBW_mines;
+	ToggleButtonWidget * TBW_vampiric;
+	ToggleButtonWidget * TBW_karma;
 
 	QSpinBox * SB_DamageModifier;
 	QSpinBox * SB_TurnTime;
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/CCHandlers.inc
--- a/hedgewars/CCHandlers.inc	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/CCHandlers.inc	Thu Apr 30 20:13:44 2009 +0000
@@ -390,6 +390,35 @@
      end
 end;
 
+procedure chHogSay(var s: shortstring);
+var Gear: PGear;
+    text: shortstring;
+begin
+text:= copy(s, 2, Length(s)-1);
+if CheckNoTeamOrHH or ((CurrentHedgehog^.Gear^.State and gstHHDriven) = 0) then
+    begin
+    chSay(text);
+    exit
+    end;
+
+if not CurrentTeam^.ExtDriven then SendIPC('h' + s);
+if byte(s[1]) < 4 then
+    begin
+    Gear:= AddGear(0, 0, gtSpeechBubble, 0, _0, _0, 0);
+    Gear^.Text:= text;
+    Gear^.Hedgehog:= CurrentHedgehog;
+    Gear^.State:= byte(s[1]);
+    end
+else
+    begin
+    // If I knew how to add a gear without it becoming immediately active, I'd
+    // just create/attach the hedgehog SpeechGear here, then activate it where
+    // SpeechType/SpeechText are activated
+    SpeechType:= byte(s[1]);
+    SpeechText:= text
+    end;
+end;
+
 procedure chNewGrave;
 begin
 if CheckNoTeamOrHH then exit;
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/GSHandlers.inc
--- a/hedgewars/GSHandlers.inc	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/GSHandlers.inc	Thu Apr 30 20:13:44 2009 +0000
@@ -67,8 +67,7 @@
 		PlaySound(sndOw1, false, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack);
 
 	dmg:= modifyDamage(1 + hwRound((hwAbs(Gear^.dY) - _0_4) * 70));
-	inc(Gear^.Damage, dmg);
-	AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y) + cHHRadius, dmg, PHedgehog(Gear^.Hedgehog)^.Team^.Clan^.Color);
+    ApplyDamage(Gear, dmg);
 	end
 end;
 
@@ -255,7 +254,7 @@
 
 if Gear^.Timer = 0 then
 	begin
-	if Gear^.Kind = gtHealthTag then
+	if (Gear^.Kind = gtHealthTag) and (PHedgehog(Gear^.Hedgehog)^.Gear <> nil) then
 		PHedgehog(Gear^.Hedgehog)^.Gear^.Active:= true; // to let current hh die
 	DeleteGear(Gear)
 	end
@@ -288,6 +287,44 @@
 Gear^.Y:= Gear^.Y - int2hwFloat(Gear^.Tex^.h)
 end;
 
+procedure doStepSpeechBubbleWork(Gear: PGear);
+begin
+dec(Gear^.Timer);
+
+if (PHedgehog(Gear^.Hedgehog)^.Gear <> nil) then
+    begin
+    Gear^.X:= PHedgehog(Gear^.Hedgehog)^.Gear^.X+int2hwFloat(Gear^.Tex^.w div 2  - Gear^.State);
+    Gear^.Y:= PHedgehog(Gear^.Hedgehog)^.Gear^.Y-int2hwFloat(16+Gear^.Tex^.h);
+    end;
+
+if Gear^.Timer = 0 then
+    begin
+    CurrentHedgehog^.SpeechGear:= nil;
+	DeleteGear(Gear)
+    end;
+end;
+
+procedure doStepSpeechBubble(Gear: PGear);
+begin
+if (CurrentHedgehog^.SpeechGear <> nil) then DeleteGear(CurrentHedgehog^.SpeechGear);
+CurrentHedgehog^.SpeechGear:= Gear;
+
+Gear^.Timer:= max(Length(Gear^.Text)*150,3000);
+
+Gear^.Tex:= RenderSpeechBubbleTex(Gear^.Text, Gear^.State, fnt16);
+
+// Arbitrary offsets added to the widths based on shape of current tails
+case Gear^.State of
+    1: Gear^.State:= SpritesData[sprSpeechTail].Width-28;
+    2: Gear^.State:= SpritesData[sprThoughtTail].Width-20;
+    3: Gear^.State:= SpritesData[sprShoutTail].Width-10;
+    end;
+
+Gear^.doStep:= @doStepSpeechBubbleWork;
+
+Gear^.Y:= Gear^.Y - int2hwFloat(Gear^.Tex^.h)
+end;
+
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepGrave(Gear: PGear);
 begin
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/HHHandlers.inc
--- a/hedgewars/HHHandlers.inc	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/HHHandlers.inc	Thu Apr 30 20:13:44 2009 +0000
@@ -100,6 +100,7 @@
 
 procedure Attack(Gear: PGear);
 var xx, yy: hwFloat;
+    tmpGear: PGear;
 begin
 with Gear^,
      PHedgehog(Gear^.Hedgehog)^ do
@@ -189,10 +190,20 @@
                       amInvulnerable: Invulnerable:= true;
                       amExtraTime: TurnTimeLeft:= TurnTimeLeft + 30000;
                       amLaserSight: cLaserSighting:= true;
+                      amVampiric: cVampiric:= true;
                   end;
 
         uStats.AmmoUsed(Ammo^[CurSlot, CurAmmo].AmmoType);
 
+        if not (SpeechText = '') then
+            begin
+            tmpGear:= AddGear(0, 0, gtSpeechBubble, 0, _0, _0, 0);
+            tmpGear^.Text:= SpeechText;
+            tmpGear^.Hedgehog:= CurrentHedgehog;
+            tmpGear^.State:= SpeechType;
+            SpeechText:= ''
+            end;
+
         Power:= 0;
         if (CurAmmoGear <> nil)
            and (((Ammo^[CurSlot, CurAmmo].Propz) and ammoprop_AltUse) = 0){check for dropping ammo from rope} then
@@ -386,6 +397,7 @@
          or TestCollisionYwithGear(Gear, -1)) then Gear^.Y:= Gear^.Y - _1;
       end;
 
+   // ARTILLERY HERE
    if not TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then Gear^.X:= Gear^.X + SignAs(_1, Gear^.dX);
    SetAllHHToActive;
 
@@ -504,6 +516,7 @@
 if (Gear^.State and gstMoving) <> 0 then
    begin
    Gear^.State:= Gear^.State and not gstAnimation;
+// ARTILLERY but not being moved by explosions
    Gear^.X:= Gear^.X + Gear^.dX;
    Gear^.Y:= Gear^.Y + Gear^.dY;
    if (not Gear^.dY.isNegative) and
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/SDLh.pas
--- a/hedgewars/SDLh.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/SDLh.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -73,6 +73,7 @@
       SDL_HWACCEL     = $00000100;
       SDL_SRCCOLORKEY = $00001000;
       SDL_RLEACCEL    = $00004000;
+      SDL_SRCALPHA    = $00010000;
 
       SDL_NOEVENT     = 0;
       SDL_ACTIVEEVENT = 1;
@@ -247,6 +248,7 @@
 function  SDL_CreateRGBSurfaceFrom(pixels: Pointer; width, height, depth, pitch: LongInt; RMask, GMask, BMask, AMask: Longword): PSDL_Surface; cdecl; external SDLLibName;
 procedure SDL_FreeSurface(Surface: PSDL_Surface); cdecl; external SDLLibName;
 function  SDL_SetColorKey(surface: PSDL_Surface; flag, key: Longword): LongInt; cdecl; external SDLLibName;
+function  SDL_SetAlpha(surface: PSDL_Surface; flag, key: Longword): LongInt; cdecl; external SDLLibName;
 
 function  SDL_UpperBlit(src: PSDL_Surface; srcrect: PSDL_Rect; dst: PSDL_Surface; dstrect: PSDL_Rect): LongInt; cdecl; external SDLLibName;
 function  SDL_FillRect(dst: PSDL_Surface; dstrect: PSDL_Rect; color: Longword): LongInt; cdecl; external SDLLibName;
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uAIAmmoTests.pas
--- a/hedgewars/uAIAmmoTests.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uAIAmmoTests.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -73,14 +73,15 @@
 			(proc: nil;              flags: 0), // amBanana
 			(proc: nil;              flags: 0), // amHellishBomb
 			(proc: nil;              flags: 0), // amNapalm
-			(proc: nil;              flags: 0),  // amDrill
-			(proc: nil;              flags: 0),  // amBallgun
+			(proc: nil;              flags: 0), // amDrill
+			(proc: nil;              flags: 0), // amBallgun
 			(proc: nil;              flags: 0), // amRCPlane
 			(proc: nil;              flags: 0), // amLowGravity
 			(proc: nil;              flags: 0), // amExtraDamage
 			(proc: nil;              flags: 0), // amInvulnerable
 			(proc: nil;              flags: 0), // amExtraTime
-			(proc: nil;              flags: 0) // amLaserSight
+			(proc: nil;              flags: 0), // amLaserSight
+			(proc: nil;              flags: 0)  // amVampiric
 			);
 
 const BadTurn = Low(LongInt) div 4;
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uAmmos.pas
--- a/hedgewars/uAmmos.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uAmmos.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -61,12 +61,17 @@
 var cnt: Longword;
     a: TAmmoType;
     ammos: TAmmoCounts;
+    substr: shortstring; // TEMPORARY
 begin
 TryDo(byte(s[0]) = byte(ord(High(TAmmoType)) + 1), 'Invalid ammo scheme (incompatible frontend)', true);
 
 // TEMPORARY hardcoded check on shoppa pending creation of probability editor
-if (s = '000000990000009000000000000000000000') or (s = '000000990000000000000000000000000000') then
+substr:= Copy(s,1,15);
+if (substr = '000000990000009') or 
+   (substr = '000000990000000') then
     shoppa:= true;
+for a:= Low(TAmmoType) to High(TAmmoType) do
+    if (ord(a) > 14) and (s[ord(a)+1] <> '0') then shoppa:= false;  // TEMPORARY etc - this just avoids updating every time new wep is added
 inc(StoreCnt);
 TryDo(StoreCnt <= cMaxHHs, 'Ammo stores overflow', true);
 
@@ -82,7 +87,8 @@
         end;
     if ((a = amLowGravity) and ((GameFlags and gfLowGravity) <> 0)) or
        ((a = amInvulnerable) and ((GameFlags and gfInvulnerable) <> 0)) or
-       ((a = amLaserSight) and ((GameFlags and gfLaserSight) <> 0)) then
+       ((a = amLaserSight) and ((GameFlags and gfLaserSight) <> 0)) or
+       ((a = amVampiric) and ((GameFlags and gfVampiric) <> 0)) then
         begin
         cnt:= 0;
         Ammoz[a].Probability:= 0 
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uChat.pas
--- a/hedgewars/uChat.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uChat.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -132,6 +132,40 @@
 procedure AcceptChatString(s: shortstring);
 var i: TWave;
 begin
+// "Make hedgehog say something"
+if (s[1] = '"') and (s[Length(s)] = '"') then 
+    begin
+    ParseCommand('/hogsay '+#1+copy(s, 2, Length(s)-2), true);
+    exit
+    end;
+// 'Make hedgehog think something'
+if (s[1] = '''') and (s[Length(s)] = '''') then 
+    begin
+    ParseCommand('/hogsay '+#2+copy(s, 2, Length(s)-2), true);
+    exit
+    end;
+// -Make hedgehog yell something-
+if (s[1] = '-') and (s[Length(s)] = '-') then 
+    begin
+    ParseCommand('/hogsay '+#3+copy(s, 2, Length(s)-2), true);
+    exit
+    end;
+// These 3 are same as above, only are to make the hedgehog say it on next attack
+if (s[1] = '/') and (copy(s, 1, 5) = '/hsa ') then
+    begin
+    ParseCommand('/hogsay '+#4+copy(s, 6, Length(s)-5), true);
+    exit
+    end;
+if (s[1] = '/') and (copy(s, 1, 5) = '/hta ') then
+    begin
+    ParseCommand('/hogsay '+#5+copy(s, 6, Length(s)-5), true);
+    exit
+    end;
+if (s[1] = '/') and (copy(s, 1, 5) = '/hya ') then
+    begin
+    ParseCommand('/hogsay '+#6+copy(s, 6, Length(s)-5), true);
+    exit
+    end;
 if (s[1] = '/') and (copy(s, 1, 4) <> '/me ') then
 	begin
 	if CurrentTeam^.ExtDriven then exit;
@@ -143,8 +177,11 @@
 			exit
 			end;
 	if (s = '/newgrave') then
+        begin
 	    ParseCommand('/newgrave', true);
-	end
+        exit
+        end;
+    end
 	else
 		ParseCommand('/say ' + s, true);
 end;
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uConsole.pas
--- a/hedgewars/uConsole.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uConsole.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -262,6 +262,7 @@
 RegisterVariable('chat'    , vtCommand, @chChat         , true );
 RegisterVariable('newgrave', vtCommand, @chNewGrave     , false);
 RegisterVariable('say'     , vtCommand, @chSay          , true );
+RegisterVariable('hogsay'  , vtCommand, @chHogSay       , true );
 RegisterVariable('ammomenu', vtCommand, @chAmmoMenu     , false);
 RegisterVariable('+precise', vtCommand, @chPrecise_p    , false);
 RegisterVariable('-precise', vtCommand, @chPrecise_m    , false);
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uConsts.pas
--- a/hedgewars/uConsts.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uConsts.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -59,7 +59,10 @@
 			sprCakeWalk, sprCakeDown, sprAMAmmosBW, sprWatermelon,
 			sprEvilTrace, sprHellishBomb, sprSeduction, sprDress,
 			sprCensored, sprDrill, sprHandDrill, sprHandBallgun, sprBalls,
-			sprPlane, sprHandPlane, sprUtility, sprInvulnerable, sprGirder);
+			sprPlane, sprHandPlane, sprUtility, sprInvulnerable, sprVampiric, sprGirder, 
+            sprSpeechCorner, sprSpeechEdge, sprSpeechTail, 
+            sprThoughtCorner, sprThoughtEdge, sprThoughtTail, 
+            sprShoutCorner, sprShoutEdge, sprShoutTail);
 
 	TGearType = (gtAmmo_Bomb, gtHedgehog, gtAmmo_Grenade, gtHealthTag, // 3
 			gtGrave, gtUFO, gtShotgunShot, gtPickHammer, gtRope, // 8
@@ -69,7 +72,7 @@
 			gtParachute, gtAirAttack, gtAirBomb, gtBlowTorch, gtGirder, // 27
 			gtTeleport, gtSwitcher, gtTarget, gtMortar, // 31
 			gtWhip, gtKamikaze, gtCake, gtSeduction, gtWatermelon, gtMelonPiece, // 37
-			gtHellishBomb, gtEvilTrace, gtWaterUp, gtDrill, gtBallGun, gtBall,gtRCPlane);
+			gtHellishBomb, gtEvilTrace, gtWaterUp, gtDrill, gtBallGun, gtBall,gtRCPlane, gtSpeechBubble);
 
 	TVisualGearType = (vgtFlake, vgtCloud, vgtExplPart, vgtExplPart2, vgtFire,
 			vgtSmallDamageTag, vgtTeamHealthSorter);
@@ -93,7 +96,7 @@
 			amBaseballBat, amParachute, amAirAttack, amMineStrike, amBlowTorch,
 			amGirder, amTeleport, amSwitch, amMortar, amKamikaze, amCake,
 			amSeduction, amWatermelon, amHellishBomb, amNapalm, amDrill, amBallgun, 
-            amRCPlane, amLowGravity, amExtraDamage, amInvulnerable, amExtraTime, amLaserSight);
+            amRCPlane, amLowGravity, amExtraDamage, amInvulnerable, amExtraTime, amLaserSight, amVampiric);
 
 	THWFont = (fnt16, fntBig, fntSmall);
 
@@ -203,6 +206,8 @@
 	gfLaserSight   = $00000040;
 	gfInvulnerable = $00000080;
 	gfMines        = $00000100;
+	gfVampiric     = $00000200;
+	gfKarma        = $00000400;
 	gfOneClanMode  = $10000000;
 
 	gstDrowning       = $00000001;
@@ -497,8 +502,28 @@
 			Width:  48; Height: 48; saveSurf: false), // sprUtility
 			(FileName:'Invulnerable';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
 			Width:  48; Height: 48; saveSurf: false), // sprInvulnerable
+			(FileName:'Vampiric';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  48; Height: 48; saveSurf: false), // sprVampiric
 			(FileName:    'amGirder'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-			Width: 512; Height:512; saveSurf: false) // sprGirder
+			Width: 512; Height:512; saveSurf: false), // sprGirder
+			(FileName:'SpeechCorner';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  12; Height: 9; saveSurf: true), // sprSpeechCorner
+			(FileName:'SpeechEdge';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  25; Height: 9; saveSurf: true), // sprSpeechEdge
+			(FileName:'SpeechTail';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  25; Height: 26; saveSurf: true), // sprSpeechTail
+			(FileName:'ThoughtCorner';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  49; Height: 37; saveSurf: true), // sprThoughtCorner
+			(FileName:'ThoughtEdge';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  23; Height: 16; saveSurf: true), // sprThoughtEdge
+			(FileName:'ThoughtTail';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  45; Height: 65; saveSurf: true), // sprThoughtTail
+			(FileName:'ShoutCorner';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  34; Height: 23; saveSurf: true), // sprShoutCorner
+			(FileName:'ShoutEdge';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  30; Height: 20; saveSurf: true), // sprShoutEdge
+			(FileName:'ShoutTail';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+			Width:  30; Height: 37; saveSurf: true) // sprShoutTail
 			);
 
 	Wavez: array [TWave] of record
@@ -1300,6 +1325,27 @@
 			isDamaging: false;
 			SkipTurns: 0;
 			PosCount: 1;
+			PosSprite: sprWater),
+			(NameId: sidVampiric;
+			NameTex: nil;
+			Probability: 15;
+			NumberInCase: 1;
+			Ammo: (Propz: ammoprop_NoCrosshair or
+						  ammoprop_DontHold or
+						  ammoprop_AltUse or
+                          ammoprop_Utility;
+					Count: 1;
+					NumPerTurn: 0;
+					Timer: 0;
+					Pos: 0;
+					AmmoType: amVampiric);
+			Slot: 6;
+			TimeAfterTurn: 0;
+			minAngle: 0;
+			maxAngle: 0;
+			isDamaging: false;
+			SkipTurns: 0;
+			PosCount: 1;
 			PosSprite: sprWater)
 			);
 
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uGears.pas
--- a/hedgewars/uGears.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uGears.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -54,11 +54,13 @@
 			IntersectGear: PGear;
 			TriggerId: Longword;
 			uid: Longword;
+            Text: shortstring;
 			end;
 
 function  AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
 procedure ProcessGears;
 procedure ResetUtilities;
+procedure ApplyDamage(Gear: PGear; Damage: Longword);
 procedure SetAllToActive;
 procedure SetAllHHToActive;
 procedure DrawGears;
@@ -71,6 +73,9 @@
 var CurAmmoGear: PGear = nil;
     GearsList: PGear = nil;
     KilledHHs: Longword = 0;
+    SuddenDeathDmg: Boolean = false;
+    SpeechType: Longword = 1;
+    SpeechText: shortstring;
 
 implementation
 uses uWorld, uMisc, uStore, uConsole, uSound, uTeams, uRandom, uCollisions,
@@ -157,7 +162,8 @@
 			@doStepDrill,
 			@doStepBallgun,
 			@doStepBomb,
-			@doStepRCPlane
+			@doStepRCPlane,
+			@doStepSpeechBubble
 			);
 
 procedure InsertGearToList(Gear: PGear);
@@ -248,7 +254,10 @@
                 end;
    gtHealthTag: begin
                 Result^.Timer:= 1500;
-                Result^.Z:= 2001;
+                Result^.Z:= 2002;
+                end;
+   gtSpeechBubble: begin
+                Result^.Z:= 2003;
                 end;
        gtGrave: begin
                 Result^.Radius:= 10;
@@ -440,9 +449,6 @@
 		if (Gear^.Damage <> 0) and
 		(not Gear^.Invulnerable) then
 			begin
-            if (PHedgehog(Gear^.Hedgehog)^.Team = CurrentTeam) and
-               (Gear^.Damage <> int(cHealthDecrease)) then
-                Gear^.State:= Gear^.State or gstLoser;
 			CheckNoDamage:= false;
 			uStats.HedgehogDamaged(Gear);
 			dmg:= Gear^.Damage;
@@ -451,6 +457,10 @@
 			else
 				dec(Gear^.Health, dmg);
 
+            if (PHedgehog(Gear^.Hedgehog)^.Team = CurrentTeam) and
+                not SuddenDeathDmg then
+                Gear^.State:= Gear^.State or gstLoser;
+
 			AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) - cHHRadius - 12,
 					gtHealthTag, dmg, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
 
@@ -462,6 +472,7 @@
 		end;
 	Gear:= Gear^.NextGear
 	end;
+SuddenDeathDmg:= false;
 end;
 
 procedure HealthMachine;
@@ -562,6 +573,7 @@
 			else begin
 				bBetweenTurns:= true;
 				HealthMachine;
+                SuddenDeathDmg:= true;
 				step:= stChDmg
 				end
 			end;
@@ -612,9 +624,14 @@
 procedure ResetUtilities;
 var  i: LongInt;
 begin
+    SpeechText:= ''; // in case it hasn't been consumed
+
     if (GameFlags and gfLowGravity) = 0 then
         cGravity:= cMaxWindSpeed;
 
+    if (GameFlags and gfVampiric) = 0 then
+        cVampiric:= false;
+
     cDamageModifier:= _1;
 
     if (GameFlags and gfLaserSight) = 0 then
@@ -629,6 +646,50 @@
                      if (Gear <> nil) then
                          Gear^.Invulnerable:= false;
 end;
+procedure ApplyDamage(Gear: PGear; Damage: Longword);
+var s: shortstring;
+    vampDmg: Longword;
+begin
+	inc(Gear^.Damage, Damage);
+	if Gear^.Kind = gtHedgehog then
+    begin
+	AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), Damage, PHedgehog(Gear^.Hedgehog)^.Team^.Clan^.Color);
+    Damage:= min(Damage, Gear^.Health);
+    if (Gear <> CurrentHedgehog^.Gear) and (CurrentHedgehog^.Gear <> nil) and (Damage >= 1) then
+        begin
+        if cVampiric then
+            begin
+            vampDmg:= hwRound(int2hwFloat(Damage)*_0_8);
+            // was considering pulsing on attack, Tiy thinks it should be permanent while in play
+            //CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstVampiric;
+            inc(CurrentHedgehog^.Gear^.Health,vampDmg);
+            str(vampDmg, s);
+            s:= '+' + s;
+            AddCaption(s, CurrentHedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
+            RenderHealth(CurrentHedgehog^);
+            RecountTeamHealth(CurrentHedgehog^.Team);
+            end;
+        if ((GameFlags and gfKarma) <> 0) and 
+           ((GameFlags and gfInvulnerable) = 0) and
+           not CurrentHedgehog^.Gear^.Invulnerable then
+           begin // this cannot just use Damage or it interrupts shotgun and gets you called stupid
+           if CurrentHedgehog^.Gear^.Health < int(Damage) then
+           begin
+               // Add damage to trigger normal resolution
+               CurrentHedgehog^.Gear^.Health := 0;
+               inc(CurrentHedgehog^.Gear^.Damage);
+           end
+           else
+               dec(CurrentHedgehog^.Gear^.Health, Damage);
+           AddGear(hwRound(Gear^.X), 
+                   hwRound(Gear^.Y), 
+                   gtHealthTag, Damage, _0, _0, 0)^.Hedgehog:= CurrentHedgehog;
+           RenderHealth(CurrentHedgehog^);
+           RecountTeamHealth(CurrentHedgehog^.Team);
+           end;
+        end;
+    end;
+end;
 
 procedure SetAllToActive;
 var t: PGear;
@@ -841,6 +902,7 @@
 			1,
 			1,
 			0);
+	HatVisible:= true;
 	defaultPos:= false
 	end else
 
@@ -950,7 +1012,10 @@
 end else // not gstHHDriven
 	begin
 	if (Gear^.Damage > 0)
-	and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then
+	and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) 
+// ARTILLERY
+//and (1=0)
+then
 		begin
 		DrawHedgehog(sx, sy,
 			hwSign(Gear^.dX),
@@ -1123,9 +1188,15 @@
 	end;
 
 if Gear^.Invulnerable then
-     begin
-     DrawSprite(sprInvulnerable, sx - 24, sy - 24, 0);
-     end;
+    begin
+    DrawSprite(sprInvulnerable, sx - 24, sy - 24, 0);
+    end;
+if cVampiric and
+   (CurrentHedgehog^.Gear <> nil) and 
+   (CurrentHedgehog^.Gear = Gear) then
+    begin
+    DrawSprite(sprVampiric, sx - 24, sy - 24, 0);
+    end;
 end;
 
 procedure DrawGears;
@@ -1216,6 +1287,8 @@
     gtAmmo_Grenade: DrawRotated(sprGrenade, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
        
        gtHealthTag: if Gear^.Tex <> nil then DrawCentered(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.Tex);
+
+       gtSpeechBubble: if Gear^.Tex <> nil then DrawCentered(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.Tex);
            
            gtGrave: DrawSurfSprite(hwRound(Gear^.X) + WorldDx - 16, hwRound(Gear^.Y) + WorldDy - 16, 32, (GameTicks shr 7) and 7, PHedgehog(Gear^.Hedgehog)^.Team^.GraveTex);
              
@@ -1328,6 +1401,9 @@
 if (GameFlags and gfLowGravity) <> 0 then
     cGravity:= cMaxWindSpeed / 2;
 
+if (GameFlags and gfVampiric) <> 0 then
+    cVampiric:= true;
+
 Gear:= GearsList;
 if (GameFlags and gfInvulnerable) <> 0 then
    while Gear <> nil do
@@ -1372,11 +1448,7 @@
 						if (Mask and EXPLNoDamage) = 0 then
 							begin
 							if not Gear^.Invulnerable then
-								begin
-							    inc(Gear^.Damage, dmg);
-							    if Gear^.Kind = gtHedgehog then
-								    AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), dmg, PHedgehog(Gear^.Hedgehog)^.Team^.Clan^.Color);
-                                end
+                                ApplyDamage(Gear, dmg)
                             else
                                 Gear^.State:= Gear^.State or gstWinner;
 							end;
@@ -1423,11 +1495,7 @@
 			gtCase,
 			gtTarget: begin
                     if (not t^.Invulnerable) then
-                        begin
-					    inc(t^.Damage, dmg);
-					    if t^.Kind = gtHedgehog then
-						    AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), dmg, PHedgehog(t^.Hedgehog)^.Team^.Clan^.Color);
-                        end
+                        ApplyDamage(t, dmg)
                     else
                         Gear^.State:= Gear^.State or gstWinner;
 
@@ -1468,12 +1536,7 @@
 			gtCase: begin
 					if (Ammo^.Kind = gtDrill) then begin Ammo^.Timer:= 0; exit; end;
                     if (not t^.ar[i]^.Invulnerable) then
-                        begin
-					    inc(t^.ar[i]^.Damage, Damage);
-
-                        if (t^.ar[i]^.Kind = gtHedgehog) and (Damage > 0) then
-                            AddDamageTag(hwRound(t^.ar[i]^.X), hwRound(t^.ar[i]^.Y), Damage, PHedgehog(t^.ar[i]^.Hedgehog)^.Team^.Clan^.Color);
-                        end
+                        ApplyDamage(t^.ar[i], Damage)
                     else
                         t^.ar[i]^.State:= t^.ar[i]^.State or gstWinner;
 
@@ -1587,7 +1650,7 @@
 	if (t^.Kind = gtHedgehog) and (t^.Y < Ammo^.Y) then
 		if not (hwSqr(Ammo^.X - t^.X) + hwSqr(Ammo^.Y - t^.Y - int2hwFloat(cHHRadius)) * 2 > _2) then
 			begin
-			inc(t^.Damage, 5);
+            ApplyDamage(t, 5);
 			t^.dX:= t^.dX + (t^.X - Ammo^.X) * _0_02;
 			t^.dY:= - _0_25;
 			t^.Active:= true;
@@ -1638,8 +1701,7 @@
 
 FollowGear:= nil;
 
-t:= getrandom(20);  // TEMPORARY  REMOVE WHEN CRATE PROBABILITY IS ADDED
-if shoppa then
+if shoppa then  // TEMPORARY  REMOVE WHEN CRATE PROBABILITY IS ADDED
     t:= 7
 else
     t:= getrandom(20);
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uIO.pas
--- a/hedgewars/uIO.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uIO.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -293,6 +293,7 @@
 		'w': ParseCommand('setweap ' + headcmd^.str[2], true);
 		't': ParseCommand('taunt ' + headcmd^.str[2], true);
 		'g': ParseCommand('newgrave', true);
+		'h': ParseCommand('hogsay ' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
 		'1'..'5': ParseCommand('timer ' + headcmd^.cmd, true);
 		#128..char(128 + cMaxSlotIndex): ParseCommand('slot ' + char(byte(headcmd^.cmd) - 79), true)
 		else
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uLocale.pas
--- a/hedgewars/uLocale.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uLocale.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -25,7 +25,7 @@
 			sidGirder, sidTeleport, sidSwitch, sidMortar, sidWhip,
 			sidKamikaze, sidCake, sidSeduction, sidWatermelon,
 			sidHellishBomb, sidDrill, sidBallgun, sidNapalm, sidRCPlane, 
-            sidLowGravity, sidExtraDamage, sidInvulnerable, sidExtraTime, sidLaserSight);
+            sidLowGravity, sidExtraDamage, sidInvulnerable, sidExtraTime, sidLaserSight, sidVampiric);
 
 	TMsgStrId = (sidStartFight, sidDraw, sidWinner, sidVolume, sidPaused,
 			sidConfirm, sidSuddenDeath);
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uMisc.pas
--- a/hedgewars/uMisc.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uMisc.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -105,6 +105,7 @@
 	cGravity: hwFloat;
 	cDamageModifier: hwFloat;
 	cLaserSighting: boolean;
+	cVampiric: boolean;
 
 	flagMakeCapture: boolean = false;
 
@@ -382,7 +383,7 @@
 			for x:= Surf^.w to Pred(tw) do
 				toP4^[x]:= 0;
 			toP4:= @(toP4^[tw]);
-			fromP4:= @(fromP4^[Surf^.w]);
+			fromP4:= @(fromP4^[Surf^.pitch div 4]);
 			end;
 
 		for y:= Surf^.h to Pred(th) do
@@ -548,6 +549,7 @@
 cGravity:= cMaxWindSpeed;
 cDamageModifier:= _1;
 cLaserSighting:= false;
+cVampiric:= false;
 
 {$IFDEF DEBUGFILE}
 {$I-}
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uStore.pas
--- a/hedgewars/uStore.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uStore.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -18,7 +18,7 @@
 
 unit uStore;
 interface
-uses uConsts, uTeams, SDLh,
+uses sysutils, uConsts, uTeams, SDLh,
 {$IFDEF IPHONE}
 	gles11,
 {$ELSE}
@@ -45,6 +45,11 @@
 procedure DrawHedgehog(X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real);
 procedure DrawFillRect(r: TSDL_Rect);
 function  RenderStringTex(s: string; Color: Longword; font: THWFont): PTexture;
+function  RenderSpeechBubbleTex(s: string; SpeechType: Longword; font: THWFont): PTexture;
+procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean);
+//procedure rotateSurface(Surface: PSDL_Surface);
+procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL
+procedure copyToXY(src, dest: PSDL_Surface; destX, destY: Integer);
 procedure RenderHealth(var Hedgehog: THedgehog);
 procedure AddProgress;
 procedure FinishProgress;
@@ -672,6 +677,176 @@
 SDL_FreeSurface(Result)
 end;
 
+function RenderSpeechBubbleTex(s: string; SpeechType: Longword; font: THWFont): PTexture;
+var textWidth, textHeight, x, y, w, h, i, pos, prevpos, line, numLines, edgeWidth, edgeHeight, cornerWidth, cornerHeight: LongInt;
+    Result, tmpsurf, rotatedEdge: PSDL_Surface;
+    rect: TSDL_Rect;
+    chars: TSysCharSet = [#9,' ','.',';',':','?','!',','];
+    substr: shortstring;
+    edge, corner, tail: TSPrite;
+begin
+
+case SpeechType of
+    1: begin; 
+       edge:= sprSpeechEdge; 
+       corner:= sprSpeechCorner;
+       tail:= sprSpeechTail;
+       end;
+    2: begin; 
+       edge:= sprThoughtEdge;
+       corner:= sprThoughtCorner; 
+       tail:= sprThoughtTail;
+       end;
+    3: begin; 
+       edge:= sprShoutEdge;
+       corner:= sprShoutCorner;
+       tail:= sprShoutTail;
+       end;
+    end;
+edgeHeight:= SpritesData[edge].Height;
+edgeWidth:= SpritesData[edge].Width;
+cornerWidth:= SpritesData[corner].Width;
+cornerHeight:= SpritesData[corner].Height;
+// This one screws it up
+s:= 'This is the song that never ends.  ''cause it goes on and on my friends. Some people, started singing it not knowing what it was. And they''ll just go on singing it forever just because... This is the song that never ends...';
+// This one doesn't
+//s:= 'This is the song that never ends.  cause it goes on and on my friends. Some people, started singing it not knowing what it was. And theyll just go on singing it forever just because... This is the song that never ends... ';
+// Also screws up, but only action
+//s:= 'This is the song that never ends.  cause it goes on and on .';
+// ok in all
+// s:= 'This is the song that never ends.  cause it goes on .';
+numLines:= 1;
+
+if length(s) = 0 then s:= '...';
+
+TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h);
+textWidth:= w;
+textHeight:= h;
+if (length(s) > 20) then
+    begin
+    i:= round(Sqrt(length(s)) * 2);
+    s:= WrapText(s, #1, chars, i);
+    for pos:= 1 to length(s) do
+        if (s[pos] = #1) or (pos = length(s)) then
+            inc(numLines);
+
+    // TODO - find out why this calc doesn't do what I expect
+    if numLines = 2 then textWidth:= w div 2
+    else if numlines > 2 then textWidth:= w div (numLines-1);
+    end;
+
+textWidth:=((textWidth-(cornerWidth-edgeWidth)*2) div edgeWidth)*edgeWidth+edgeWidth;
+textHeight:=(((numlines * h)-((cornerHeight-edgeWidth)*2)) div edgeWidth)*edgeWidth+edgeWidth;
+addfilelog(inttostr(textHeight)+'=========== '+inttostr(numlines)+' x '+inttostr(h));
+//textWidth:=max(textWidth,SpritesData[tail].Width);
+rect.x:= 0;
+rect.y:= 0;
+rect.w:= textWidth + cornerWidth * 2;
+rect.h:= textHeight + cornerHeight * 2 - edgeHeight + SpritesData[tail].Height;
+//s:= inttostr(h) + ' ' + inttostr(numlines) + ' ' + inttostr(rect.x) + ' '+inttostr(rect.y) + ' ' + inttostr(rect.w) + ' ' + inttostr(rect.h) + ' ' + s;
+
+Result:= SDL_CreateRGBSurface(SDL_SWSURFACE, rect.w, rect.h, 32, RMask, GMask, BMask, AMask);
+
+TryDo(Result <> nil, 'RenderString: fail to create surface', true);
+
+//////////////////////////////// CORNERS ///////////////////////////////
+copyToXY(SpritesData[corner].Surface, Result, 0, 0); /////////////////// NW
+
+flipSurface(SpritesData[corner].Surface, true); // store all 4 versions in memory to avoid repeated flips?
+x:= 0;
+y:= textHeight + cornerHeight -1;
+copyToXY(SpritesData[corner].Surface, Result, x, y); /////////////////// SW
+
+flipSurface(SpritesData[corner].Surface, false);
+x:= rect.w-cornerWidth-1;
+y:= textHeight + cornerHeight -1;
+copyToXY(SpritesData[corner].Surface, Result, x, y); /////////////////// SE
+
+flipSurface(SpritesData[corner].Surface, true);
+x:= rect.w-cornerWidth-1;
+y:= 0;
+copyToXY(SpritesData[corner].Surface, Result, x, y); /////////////////// NE
+flipSurface(SpritesData[corner].Surface, false); // restore original position
+//////////////////////////////// END CORNERS ///////////////////////////////
+
+//////////////////////////////// EDGES //////////////////////////////////////
+x:= cornerWidth;
+y:= 0;
+while x < rect.w-cornerWidth-1 do
+    begin
+    copyToXY(SpritesData[edge].Surface, Result, x, y); ///////////////// top edge
+    inc(x,edgeWidth);
+    end;
+flipSurface(SpritesData[edge].Surface, true);
+x:= cornerWidth;
+y:= textHeight + cornerHeight*2 - edgeHeight-1;
+while x < rect.w-cornerWidth-1 do
+    begin
+    copyToXY(SpritesData[edge].Surface, Result, x, y); ///////////////// bottom edge
+    inc(x,edgeWidth);
+    end;
+flipSurface(SpritesData[edge].Surface, true); // restore original position
+
+rotatedEdge:= SDL_CreateRGBSurface(SDL_SWSURFACE, edgeHeight, edgeWidth, 32, RMask, GMask, BMask, AMask);
+x:= rect.w - edgeHeight - 1;
+y:= cornerHeight;
+//// initially was going to rotate in place, but the SDL spec claims width/height are read only
+copyRotatedSurface(SpritesData[edge].Surface,rotatedEdge);
+while y < textHeight + cornerHeight do
+    begin
+    copyToXY(rotatedEdge, Result, x, y);
+    inc(y,edgeWidth);
+    end;
+flipSurface(rotatedEdge, false); // restore original position
+x:= 0;
+y:= cornerHeight;
+while y < textHeight + cornerHeight do
+    begin
+    copyToXY(rotatedEdge, Result, x, y);
+    inc(y,edgeWidth);
+    end;
+//////////////////////////////// END EDGES //////////////////////////////////////
+
+x:= cornerWidth;
+y:= textHeight + cornerHeight * 2 - edgeHeight - 1;
+copyToXY(SpritesData[tail].Surface, Result, x, y);
+
+rect.x:= edgeHeight;
+rect.y:= edgeHeight;
+rect.w:= rect.w - edgeHeight * 2;
+rect.h:= textHeight + cornerHeight * 2 - edgeHeight * 2;
+SDL_FillRect(Result, @rect, cWhiteColor);
+
+pos:= 1; prevpos:= 0; line:= 0;
+while pos <= length(s) do
+    begin
+    if (s[pos] = #1) or (pos = length(s)) then
+        begin
+        if s[pos] <> #1 then inc(pos);
+        while s[prevpos+1] = ' 'do inc(prevpos);
+        substr:= copy(s, prevpos+1, pos-prevpos-1);
+        if Length(substr) <> 0 then
+           begin
+           tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(substr), cColorNearBlack);
+           rect.x:= edgeHeight;
+           rect.y:= edgeHeight + line * h;
+           SDLTry(tmpsurf <> nil, true);
+           SDL_UpperBlit(tmpsurf, nil, Result, @rect);
+           SDL_FreeSurface(tmpsurf);
+           inc(line);
+           prevpos:= pos;
+           end;
+        end;
+    inc(pos);
+    end;
+
+//TryDo(SDL_SetColorKey(Result, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
+RenderSpeechBubbleTex:= Surface2Tex(Result);
+
+SDL_FreeSurface(rotatedEdge);
+SDL_FreeSurface(Result)
+end;
+
 procedure RenderHealth(var Hedgehog: THedgehog);
 var s: shortstring;
 begin
@@ -755,4 +930,73 @@
 FreeTexture(ProgrTex)
 end;
 
+procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean);
+var y, x, i, j: LongInt;
+    tmpPixel: Longword;
+    pixels: PLongWordArray;
+begin
+TryDo(Surface^.format^.BytesPerPixel = 4, 'flipSurface failed, expecting 32 bit surface', true);
+pixels:= Surface^.pixels;
+if Vertical then
+   for y := 0 to (Surface^.h div 2) - 1 do
+       for x := 0 to Surface^.w - 1 do
+           begin
+           i:= y * Surface^.w + x;
+           j:= (Surface^.h - y - 1) * Surface^.w + x;
+           tmpPixel:= pixels^[i];
+           pixels^[i]:= pixels^[j];
+           pixels^[j]:= tmpPixel;
+           end
+else
+   for x := 0 to (Surface^.w div 2) - 1 do
+       for y := 0 to Surface^.h -1 do
+           begin
+           i:= y*Surface^.w + x;
+           j:= y*Surface^.w + (Surface^.w - x - 1);
+           tmpPixel:= pixels^[i];
+           pixels^[i]:= pixels^[j];
+           pixels^[j]:= tmpPixel;
+           end;
+end;
+
+procedure copyToXY(src, dest: PSDL_Surface; destX, destY: Integer);
+var srcX, srcY, i, j, maxDest: LongInt;
+    srcPixels, destPixels: PLongWordArray;
+begin
+addfilelog('copyToXY: src surf (w, h) = ('+inttostr(src^.w)+', '+inttostr(src^.h)+')');
+addfilelog('copyToXY: dest(X, Y) = ('+inttostr(destX)+', '+inttostr(destY)+')');
+maxDest:= (dest^.pitch div 4) * dest^.h;
+srcPixels:= src^.pixels;
+destPixels:= dest^.pixels;
+
+for srcX:= 0 to src^.w - 1 do
+   for srcY:= 0 to src^.h - 1 do
+      begin
+      i:= (destY + srcY) * (dest^.pitch div 4) + destX + srcX;
+      j:= srcY * (src^.pitch div 4) + srcX;
+      // basic skip of transparent pixels - cleverness would be to do true alpha
+      if (i < maxDest) and ($FF000000 and srcPixels^[j] <> 0) then destPixels^[i]:= srcPixels^[j];
+      end;
+end;
+
+procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL, apparently
+var y, x, i, j: LongInt;
+    srcPixels, destPixels: PLongWordArray;
+begin
+TryDo(src^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true);
+TryDo(dest^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true);
+
+srcPixels:= src^.pixels;
+destPixels:= dest^.pixels;
+
+j:= 0;
+for x := 0 to src^.w - 1 do
+    for y := 0 to src^.h - 1 do
+        begin
+        i:= (src^.h - 1 - y) * (src^.pitch div 4) + x;
+        destPixels^[j]:= srcPixels^[i];
+        inc(j)
+        end;
+end;
+
 end.
diff -r 73b0bcc4396d -r 7845c77c8d31 hedgewars/uTeams.pas
--- a/hedgewars/uTeams.pas	Sun Apr 26 15:47:03 2009 +0000
+++ b/hedgewars/uTeams.pas	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 	THedgehog = record
 			Name: string[MAXNAMELEN];
 			Gear: PGear;
+			SpeechGear: PGear;
 			NameTagTex,
 			HealthTagTex,
 			HatTex: PTexture;
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/bg.txt
--- a/share/hedgewars/Data/Locale/bg.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/bg.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerable
 00:35=Extra Time
 00:36=Laser Sight
+00:37=Vampirism
 
 01:00=Бой!
 01:01=Равен рунд
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/cs.txt
--- a/share/hedgewars/Data/Locale/cs.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/cs.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerable
 00:35=Extra Time
 00:36=Laser Sight
+00:37=Vampirism
 
 01:00=Do boje!
 01:01=Kolo nerozhodně
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/de.txt
--- a/share/hedgewars/Data/Locale/de.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/de.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Unverwundbarkeit
 00:35=Zusatzzeit
 00:36=Laservisier
+00:37=Vampirism
 
 01:00=Auf in die Schlacht!
 01:01=Unentschieden
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/en.txt
--- a/share/hedgewars/Data/Locale/en.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/en.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerable
 00:35=Extra Time
 00:36=Laser Sight
+00:37=Vampirism
 
 01:00=Let's fight!
 01:01=Round draw
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/es.txt
--- a/share/hedgewars/Data/Locale/es.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/es.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerable
 00:35=Tiempo extra
 00:36=Mira láser
+00:37=Vampirism
 
 01:00=Luchad!
 01:01=Empate
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/fi.txt
--- a/share/hedgewars/Data/Locale/fi.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/fi.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerable
 00:35=Extra Time
 00:36=Laser Sight
+00:37=Vampirism
 
 01:00=Taistelu alkakoon!
 01:01=Tasapeli
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/fr.txt
--- a/share/hedgewars/Data/Locale/fr.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/fr.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnérable
 00:35=Extra Time
 00:36=Laser Sight
+00:37=Vampirism
 
 01:00=C'est parti!
 01:01=Round ex aequo
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/it.txt
--- a/share/hedgewars/Data/Locale/it.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/it.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerabilità
 00:35=Tempo Extra
 00:36=Mirino Laser
+00:37=Vampirism
 
 01:00=Combattiamo!
 01:01=Round in parità
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/pl.txt
--- a/share/hedgewars/Data/Locale/pl.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/pl.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Niezniszczalność
 00:35=Dodatkowy czas
 00:36=Celownik laserowy
+00:37=Vampirism
 
 01:00=Walczmy!
 01:01=Remis
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/pt-br.txt
--- a/share/hedgewars/Data/Locale/pt-br.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/pt-br.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerable
 00:35=Extra Time
 00:36=Laser Sight
+00:37=Vampirism
 
 01:00=Hora de lutar!
 01:01=Partida empatou
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/ru.txt
--- a/share/hedgewars/Data/Locale/ru.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/ru.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Неуязвимость
 00:35=30 секунд
 00:36=Лазерный прицел
+00:37=Vampirism
 
 01:00=Вперёд к победе!
 01:01=Ничья
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/sk.txt
--- a/share/hedgewars/Data/Locale/sk.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/sk.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Nesmrteľnosť
 00:35=Extra čas
 00:36=Laserové zameriavanie
+00:37=Vampirism
 
 01:00=Do boja!
 01:01=Remíza
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/tr.txt
--- a/share/hedgewars/Data/Locale/tr.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/tr.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Ölümsüzlük
 00:35=Arttırılmış Zaman
 00:36=Lazer Görüşü
+00:37=Vampirism
 
 01:00=Savaş başlasın!
 01:01=Beraberlik
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/uk.txt
--- a/share/hedgewars/Data/Locale/uk.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/uk.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=Invulnerable
 00:35=Extra Time
 00:36=Laser Sight
+00:37=Vampirism
 
 01:00=Уперед до перемоги!
 01:01=Нічия
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/zh_CN.txt
--- a/share/hedgewars/Data/Locale/zh_CN.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/zh_CN.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -37,6 +37,7 @@
 00:34=刀枪不入
 00:35=加时
 00:36=激光瞄准
+00:37=Vampirism
 
 01:00=战斗啦!
 01:01=平手
diff -r 73b0bcc4396d -r 7845c77c8d31 share/hedgewars/Data/Locale/zh_TW.txt
--- a/share/hedgewars/Data/Locale/zh_TW.txt	Sun Apr 26 15:47:03 2009 +0000
+++ b/share/hedgewars/Data/Locale/zh_TW.txt	Thu Apr 30 20:13:44 2009 +0000
@@ -35,6 +35,7 @@
 00:34=刀槍不入
 00:35=附加時間
 00:36=雷射瞄准
+00:37=Vampirism
 
 01:00=戰鬥開始!
 01:01=平手