With this patch the game doesn't crash when gaming by net
authorunc0rr
Wed, 28 Dec 2005 22:18:11 +0000
changeset 32 78bff13b11c0
parent 31 99888245a4e8
child 33 d6363050cad0
With this patch the game doesn't crash when gaming by net
QTfrontend/game.cpp
QTfrontend/game.h
QTfrontend/hwform.cpp
QTfrontend/hwform.h
QTfrontend/hwform.ui
QTfrontend/netclient.cpp
QTfrontend/netclient.h
hedgewars/CCHandlers.inc
hedgewars/HHHandlers.inc
hedgewars/uConsole.pas
hedgewars/uGame.pas
hedgewars/uGears.pas
hedgewars/uMisc.pas
hedgewars/uTeams.pas
--- a/QTfrontend/game.cpp	Tue Dec 27 10:20:55 2005 +0000
+++ b/QTfrontend/game.cpp	Wed Dec 28 22:18:11 2005 +0000
@@ -70,6 +70,8 @@
 		connect(client, SIGNAL(disconnected()), this, SLOT(ClientDisconnect()));
 		connect(client, SIGNAL(readyRead()), this, SLOT(ClientRead()));
 		msgsize = 0;
+		if (toSendBuf.size() > 0)
+			SENDIPC("?");
 	} else
 	{
 		client->disconnectFromHost();
@@ -91,13 +93,6 @@
 
 void HWGame::SendConfig()
 {
-	if (gameType == gtDemo)
-	{
-		SENDIPC("TD");
-		RawSendIPC(*toSendBuf);
-		delete toSendBuf;
-		return ;
-	}
 	SENDIPC("TL");
 //	SENDIPC("e$gmflags 0");
 	SENDIPC("eaddteam");
@@ -139,7 +134,10 @@
 				emit SendNet(QByteArray("\x01""C"));
 			}
 			else
-				SendConfig();
+			{
+				if (gameType == gtLocal)
+				 	SendConfig();
+			}
 			break;
 		}
 		case '+':
@@ -148,46 +146,53 @@
 		}
 		default:
 		{
+			QByteArray tmpbuf = QByteArray::fromRawData((char *)&msgsize, 1) + QByteArray::fromRawData(msgbuf, msgsize);
 			if (gameType == gtNet)
 			{
-				emit SendNet(QByteArray::fromRawData(msgbuf, msgsize));
+				emit SendNet(tmpbuf);
 			}
-			demo->append(msgsize);
-			demo->append(QByteArray::fromRawData(msgbuf, msgsize));
+			demo->append(tmpbuf);
 		}
 	}
 }
 
 void HWGame::SendIPC(const char * msg, quint8 len)
 {
-	IPCSocket->write((char *)&len, 1);
-	IPCSocket->write(msg, len);
-	if ((len > 5) && !((msg[0] == 'e') && (msg[1] == 'b')))
-	{
-		demo->append(len);
-		demo->append(QByteArray::fromRawData(msg, len));
-	}
+	SendIPC(QByteArray::fromRawData(msg, len));
 }
 
 void HWGame::SendIPC(const QByteArray & buf)
 {
 	if (buf.size() > MAXMSGCHARS) return;
-	quint8 len = buf.size();
-	IPCSocket->write((char *)&len, 1);
-	IPCSocket->write(buf);
-	demo->append(len);
-	demo->append(buf);
+	if (!IPCSocket)
+	{
+		toSendBuf += buf;
+	} else
+	{
+		quint8 len = buf.size();
+		RawSendIPC(QByteArray::fromRawData((char *)&len, 1) + buf);
+	}
 }
 
 void HWGame::RawSendIPC(const QByteArray & buf)
 {
-	IPCSocket->write(buf);
-	demo->append(buf);
+	if (!IPCSocket)
+	{
+		toSendBuf += buf;
+	} else
+	{
+		if (toSendBuf.size() > 0)
+		{
+			IPCSocket->write(toSendBuf);
+			demo->append(toSendBuf);
+		}
+		IPCSocket->write(buf);
+		demo->append(buf);
+	}
 }
 
 void HWGame::FromNet(const QByteArray & msg)
 {
-	// ?local config?
 	RawSendIPC(msg);
 }
 
@@ -299,52 +304,43 @@
 
 	// read demo
 	QDataStream stream(&demofile);
-	toSendBuf = new QByteArray();
 	char buf[512];
 	quint32 readbytes;
 	do
 	{
 		readbytes = stream.readRawData((char *)&buf, 512);
-		toSendBuf -> append(QByteArray((char *)&buf, readbytes));
+		toSendBuf.append(QByteArray((char *)&buf, readbytes));
 
 	} while (readbytes > 0);
 	demofile.close();
 
 	// cut seed
-	quint32 index = toSendBuf->indexOf(10);
-	if (!index)
+	quint32 index = toSendBuf.indexOf(10);
+	if ((index < 3) || (index > 20))
 	{
 		QMessageBox::critical(0,
 				tr("Error"),
-				tr("Corrupted demo file %s").arg(demofilename),
+				tr("Corrupted demo file %1").arg(demofilename),
 				tr("Quit"));
 		return ;
 	}
-	seed = QString(toSendBuf->left(index++));
-	toSendBuf->remove(0, index);
+	seed = QString(toSendBuf.left(index++));
+	toSendBuf.remove(0, index);
 
+	toSendBuf = QByteArray::fromRawData("\x02TD", 3) + toSendBuf;
 	// run engine
-	QProcess * process;
-	QStringList arguments;
-	process = new QProcess;
-	arguments << resolutions[0][vid_Resolution];
-	arguments << resolutions[1][vid_Resolution];
-	arguments << GetThemeBySeed();
-	arguments << "46631";
-	arguments << seed;
-	arguments << (vid_Fullscreen ? "1" : "0");
-	process->start("hw", arguments);
 	demo = new QByteArray;
+	Start();
 }
 
 void HWGame::StartNet(const QString & netseed)
 {
 	gameType = gtNet;
 	seed = netseed;
-	Start();
 	demo = new QByteArray;
 	demo->append(seed.toLocal8Bit());
 	demo->append(10);
+	Start();
 }
 
 void HWGame::StartLocal()
@@ -352,10 +348,10 @@
 	gameType = gtLocal;
 	if (TeamCount < 2) return;
 	seedgen.GenRNDStr(seed, 10);
-	Start();
 	demo = new QByteArray;
 	demo->append(seed.toLocal8Bit());
 	demo->append(10);
+	Start();
 }
 
 void HWGame::LocalCFG(const QString & teamname)
--- a/QTfrontend/game.h	Tue Dec 27 10:20:55 2005 +0000
+++ b/QTfrontend/game.h	Wed Dec 28 22:18:11 2005 +0000
@@ -80,7 +80,7 @@
 	int TeamCount;
 	RNDStr seedgen;
 	QByteArray * demo;
-	QByteArray * toSendBuf;
+	QByteArray toSendBuf;
 	int vid_Resolution;
 	bool vid_Fullscreen;
 	GameType gameType;
--- a/QTfrontend/hwform.cpp	Tue Dec 27 10:20:55 2005 +0000
+++ b/QTfrontend/hwform.cpp	Wed Dec 28 22:18:11 2005 +0000
@@ -251,8 +251,8 @@
 void HWForm::SimpleGame()
 {
 	game = new HWGame(ui.CBResolution->currentIndex(), ui.CBFullscreen->isChecked());
-	game->AddTeam("team.cfg");
-	game->AddTeam("team.cfg");
+	game->AddTeam("team");
+	game->AddTeam("team");
 	game->StartLocal();
 }
 
@@ -310,6 +310,7 @@
 	connect(hwnet, SIGNAL(Connected()), this, SLOT(GoToNetChat()));
 	connect(hwnet, SIGNAL(AddGame(const QString &)), this, SLOT(AddGame(const QString &)));
 	connect(hwnet, SIGNAL(EnteredGame()), this, SLOT(NetGameEnter()));
+	connect(hwnet, SIGNAL(ChangeInTeams(const QStringList &)), this, SLOT(ChangeInNetTeams(const QStringList &)));
 }
 
 void HWForm::NetDisconnect()
@@ -350,3 +351,10 @@
 {
 	hwnet->StartGame();
 }
+
+void HWForm::ChangeInNetTeams(const QStringList & teams)
+{
+	ui.listNetTeams->clear();
+	ui.listNetTeams->addItems(teams);
+}
+
--- a/QTfrontend/hwform.h	Tue Dec 27 10:20:55 2005 +0000
+++ b/QTfrontend/hwform.h	Wed Dec 28 22:18:11 2005 +0000
@@ -77,6 +77,7 @@
 	void NetAddTeam();
 	void NetGameEnter();
 	void NetStartGame();
+	void ChangeInNetTeams(const QStringList & teams);
 
 public slots:
 	void CBGrave_activated(const QString & gravename);
--- a/QTfrontend/hwform.ui	Tue Dec 27 10:20:55 2005 +0000
+++ b/QTfrontend/hwform.ui	Wed Dec 28 22:18:11 2005 +0000
@@ -50,7 +50,7 @@
      </size>
     </property>
     <property name="currentIndex" >
-     <number>2</number>
+     <number>7</number>
     </property>
     <widget class="QWidget" name="page" >
      <widget class="QPushButton" name="BtnSimpleGame" >
@@ -379,8 +379,8 @@
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>99</width>
-          <height>29</height>
+          <width>181</width>
+          <height>270</height>
          </rect>
         </property>
         <attribute name="label" >
@@ -998,6 +998,16 @@
        <bool>false</bool>
       </property>
      </widget>
+     <widget class="QListWidget" name="listNetTeams" >
+      <property name="geometry" >
+       <rect>
+        <x>270</x>
+        <y>30</y>
+        <width>120</width>
+        <height>80</height>
+       </rect>
+      </property>
+     </widget>
     </widget>
     <widget class="QWidget" name="pageMain" >
      <widget class="QPushButton" name="BtnSinglePlayer" >
--- a/QTfrontend/netclient.cpp	Tue Dec 27 10:20:55 2005 +0000
+++ b/QTfrontend/netclient.cpp	Wed Dec 28 22:18:11 2005 +0000
@@ -35,10 +35,6 @@
 #include "netclient.h"
 #include "game.h"
 
-#include <QtDebug>
-
-#define chkp qDebug() << "hw chkp in " << __FILE__ << ":" << __LINE__
-
 HWNet::HWNet(int Resolution, bool Fullscreen)
 	: QObject()
 {
@@ -160,10 +156,14 @@
 	if ((state == nsGaming) || (state == nsStarting))
 	{
 		QString msg = QString(buf.toBase64());
-		if (msg == "AUM=")
+		if ((msg == "AUM=") && (mynick == opnick))
 		{
 			ConfigAsked();
 		} else
+		if (msg == "AT8=")
+		{
+			// its ping ("?")
+		} else
 		{
 			RawSendNet(QString("PRIVMSG %1 :"MAGIC_CHAR MAGIC_CHAR"%2").arg(channel, msg));
 		}
@@ -325,7 +325,9 @@
 				teamnames += MAGIC_CHAR;
 				teamnames += teams[i].hhs[0];
 			}
-			RawSendNet(QString("PRIVMSG %1 :"MAGIC_CHAR"Teams%2").arg(channel, teamnames));
+			QString tmsg = QString(MAGIC_CHAR"=%2").arg(teamnames);
+			RawSendNet(QString("PRIVMSG %1 :").arg(channel) + tmsg);
+			hwp_chanmsg(mynick, tmsg);
 		}
 	}
 }
@@ -338,7 +340,6 @@
 		quint32 color = 65535;
 		for (int i = 0; i < teamsCount; i++)
 		{
-chkp;			SENDCFGSTRNET("eaddteam");
 			QString msg;
 			msg = MAGIC_CHAR "T" MAGIC_CHAR + teams[i].nick + MAGIC_CHAR + teams[i].hhs.join(MAGIC_CHAR);
 			RawSendNet(QString("PRIVMSG %1 :%2").arg(channel, msg));
@@ -349,7 +350,8 @@
 			SENDCFGSTRNET("eadd hh2 0");
 			color <<= 8;
 		}
-chkp;		SENDCFGSTRNET("!");
+		SENDCFGSTRNET("!");
+		state = nsGaming;
 	}
 }
 
@@ -359,28 +361,43 @@
 	{
 		return ;
 	}
-	if ((state == nsJoined) && (msg.startsWith(MAGIC_CHAR"Start!"MAGIC_CHAR)) && (who == opnick))
+	if (state == nsJoined)
 	{
-		state = nsStarting;
-		RunGame(msg.mid(7));
-		return ;
+		if (msg.startsWith(MAGIC_CHAR"Start!"MAGIC_CHAR) && (who == opnick))
+		{
+			state = nsStarting;
+			RunGame(msg.mid(7));
+			return ;
+		}
+		if (msg.startsWith(MAGIC_CHAR"="MAGIC_CHAR) && (who == opnick))
+		{
+			emit ChangeInTeams(msg.mid(3).split(MAGIC_CHAR));
+		}
 	}
-	if ((state == nsStarting) && (msg == MAGIC_CHAR MAGIC_CHAR "AUM="))
+	if (state == nsStarting)
 	{
-		if (mynick == opnick) ConfigAsked();
-		return ;
+		if (msg == MAGIC_CHAR MAGIC_CHAR "AUM=")
+		{
+			if (mynick == opnick) ConfigAsked();
+			return ;
+		}
+		if (msg == MAGIC_CHAR MAGIC_CHAR "ASE=")
+		{
+			state = nsGaming;
+		}
+		if (msg.startsWith(MAGIC_CHAR"T"MAGIC_CHAR))
+		{
+			NetTeamAdded(msg.mid(3));
+		}
 	}
-	if ((state == nsStarting) && (msg.startsWith(MAGIC_CHAR"T"MAGIC_CHAR)))
-	{
-		NetTeamAdded(msg.mid(3));
-	}
-	if (state != nsGaming)
+	if ((state != nsGaming) && (state != nsStarting))
 	{
 		return;
 	}
 	if (msg.startsWith(MAGIC_CHAR MAGIC_CHAR)) // HWP message
 	{
-		emit FromNet(QByteArray::fromBase64(msg.mid(2).toLocal8Bit()));
+		QByteArray em = QByteArray::fromBase64(msg.mid(2).toLocal8Bit());
+		emit FromNet(em);
 	} else // smth other
 	{
 
@@ -392,18 +409,19 @@
 	QStringList list = msg.split(MAGIC_CHAR, QString::SkipEmptyParts);
 	if (list.size() != 10)
 		return ;
+	SENDCFGSTRLOC("eaddteam");
 	if (list[0] == mynick)
 	{
-chkp;		emit LocalCFG(list[1]);
+		emit LocalCFG(list[1]);
 	} else
 	{
-chkp;		SENDCFGSTRLOC("erdriven");
-		SENDCFGSTRLOC(QString("ename team %1").arg(list[2]));
+		SENDCFGSTRLOC("erdriven");
+		SENDCFGSTRLOC(QString("ename team %1").arg(list[1]));
 		for (int i = 0; i < 8; i++)
 		{
-			SENDCFGSTRLOC(QString("ename hh%1").arg(i) + list[i + 3]);
+			SENDCFGSTRLOC(QString("ename hh%1 ").arg(i) + list[i + 2]);
 		}
-chkp;	}
+	}
 }
 
 void HWNet::AddTeam(const HWTeam & team)
--- a/QTfrontend/netclient.h	Tue Dec 27 10:20:55 2005 +0000
+++ b/QTfrontend/netclient.h	Wed Dec 28 22:18:11 2005 +0000
@@ -67,6 +67,7 @@
 	void EnteredGame();
 	void FromNet(const QByteArray & buf);
 	void LocalCFG(const QString & team);
+	void ChangeInTeams(const QStringList & teams);
 
 public slots:
 	void SendNet(const QByteArray & buf);
--- a/hedgewars/CCHandlers.inc	Tue Dec 27 10:20:55 2005 +0000
+++ b/hedgewars/CCHandlers.inc	Wed Dec 28 22:18:11 2005 +0000
@@ -218,6 +218,8 @@
      if ((State and gstHHDriven)<>0)and((State and (gstAttacked or gstHHChooseTarget or gstMoving)) = 0) then
         begin
         FollowGear:= CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog].Gear;
+        if (State and (gstAttacked or gstHHChooseTarget) = 0)and(CurAmmoGear = nil) then
+           State:= State or gstAttacking;
         if not CurrentTeam.ExtDriven then SendIPC('A');
         Message:= Message or gm_Attack
         end
@@ -225,56 +227,14 @@
 end;
 
 procedure chAttack_m(var s: shortstring);
-var xx, yy: real;
 begin
 if CheckNoTeamOrHH then exit;
 with CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog].Gear^,
      CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog] do
      begin
      {$IFDEF DEBUGFILE}AddFileLog('/-attack: Gear.State = '+inttostr(State)+' CurAmmoGear = '+inttostr(longword(CurAmmoGear)));{$ENDIF}
-     if CurAmmoGear <> nil then
-        begin
-        Message:= Message and not gm_Attack;
-        if not CurrentTeam.ExtDriven then SendIPC('a')
-        end;
-     if (((State and (gstHHDriven or gstAttacking)) = (gstHHDriven or gstAttacking))and
-        ((State and (gstAttacked or gstMoving or gstHHChooseTarget)) = 0)and
-        (((State and gstFalling  ) = 0)or((Ammo[CurSlot, CurAmmo].Propz and ammoprop_AttackInFall) <> 0))and
-        (((State and gstHHJumping) = 0)or((Ammo[CurSlot, CurAmmo].Propz and ammoprop_AttackInJump) <> 0)))and
-        (CurAmmoGear = nil) then
-        begin
-        if (Ammo[CurSlot, CurAmmo].Propz and ammoprop_Power) <> 0 then
-           begin
-           StopTPUSound;
-           PlaySound(sndThrowRelease);
-           end;
-        xx:= Sign(dX)*Sin(Angle*pi/cMaxAngle);
-        yy:= -Cos(Angle*pi/cMaxAngle);
-             case Ammo[CurSlot, CurAmmo].AmmoType of
-                      amBazooka: FollowGear:= AddGear(round(X), round(Y), gtAmmo_Grenade, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor);
-                      amGrenade: FollowGear:= AddGear(round(X), round(Y), gtAmmo_Bomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo[CurSlot, CurAmmo].Timer);
-                          amUFO: FollowGear:= AddGear(round(X), round(Y), gtUFO,          0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor);
-                      amShotgun: begin
-                                 PlaySound(sndShotgunReload);
-                                 FollowGear:= AddGear(round(X + xx*20), round(Y + yy*20), gtShotgunShot,  0, xx * 0.5, 0.5 * yy);
-                                 end;
-                         amSkip: TurnTimeLeft:= 0;
-                   amPickHammer: CurAmmoGear:= AddGear(round(Gear.X), round(Gear.Y) + cHHHalfHeight, gtPickHammer, 0);
-                         amRope: CurAmmoGear:= AddGear(round(Gear.X), round(Gear.Y), gtRope, 0, xx, yy);
-                         amMine: AddGear(round(X) + Sign(dX) * 7, round(Y), gtMine, 0, Sign(dX) * 0.01, 0, 3000);
-                  end;
-        Power:= 0;
-        if CurAmmoGear <> nil then
-           begin
-           CurAmmoGear.Message:= Gear.Message;
-           exit
-           end else
-           begin
-           Message:= Message and not gm_Attack;
-           if not CurrentTeam.ExtDriven then SendIPC('a')
-           end;
-        AfterAttack
-        end
+      Message:= Message and not gm_Attack;
+      if not CurrentTeam.ExtDriven then SendIPC('a')
      end
 end;
 
--- a/hedgewars/HHHandlers.inc	Tue Dec 27 10:20:55 2005 +0000
+++ b/hedgewars/HHHandlers.inc	Wed Dec 28 22:18:11 2005 +0000
@@ -31,6 +31,78 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *)
 
+procedure Attack(Gear: PGear);
+var xx, yy: real;
+begin
+with Gear^,
+     CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog] do
+     begin
+     {$IFDEF DEBUGFILE}AddFileLog('Attack: Gear.State = '+inttostr(State)+' CurAmmoGear = '+inttostr(longword(CurAmmoGear)));{$ENDIF}
+     if CurAmmoGear <> nil then
+        begin
+        Message:= Message and not gm_Attack;
+        if not CurrentTeam.ExtDriven then SendIPC('a')
+        end;
+     if (((State and (gstHHDriven or gstAttacking)) = (gstHHDriven or gstAttacking))and
+        ((State and (gstAttacked or gstMoving or gstHHChooseTarget)) = 0)and
+        (((State and gstFalling  ) = 0)or((Ammo[CurSlot, CurAmmo].Propz and ammoprop_AttackInFall) <> 0))and
+        (((State and gstHHJumping) = 0)or((Ammo[CurSlot, CurAmmo].Propz and ammoprop_AttackInJump) <> 0)))and
+        (CurAmmoGear = nil) then
+        begin
+        if (Ammo[CurSlot, CurAmmo].Propz and ammoprop_Power) <> 0 then
+           begin
+           StopTPUSound;
+           PlaySound(sndThrowRelease);
+           end;
+        xx:= Sign(dX)*Sin(Angle*pi/cMaxAngle);
+        yy:= -Cos(Angle*pi/cMaxAngle);
+             case Ammo[CurSlot, CurAmmo].AmmoType of
+                      amBazooka: FollowGear:= AddGear(round(X), round(Y), gtAmmo_Grenade, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor);
+                      amGrenade: FollowGear:= AddGear(round(X), round(Y), gtAmmo_Bomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo[CurSlot, CurAmmo].Timer);
+                          amUFO: FollowGear:= AddGear(round(X), round(Y), gtUFO,          0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor);
+                      amShotgun: begin
+                                 PlaySound(sndShotgunReload);
+                                 FollowGear:= AddGear(round(X + xx*20), round(Y + yy*20), gtShotgunShot,  0, xx * 0.5, 0.5 * yy);
+                                 end;
+                         amSkip: TurnTimeLeft:= 0;
+                   amPickHammer: CurAmmoGear:= AddGear(round(Gear.X), round(Gear.Y) + cHHHalfHeight, gtPickHammer, 0);
+                         amRope: CurAmmoGear:= AddGear(round(Gear.X), round(Gear.Y), gtRope, 0, xx, yy);
+                         amMine: AddGear(round(X) + Sign(dX) * 7, round(Y), gtMine, 0, Sign(dX) * 0.01, 0, 3000);
+                  end;
+        Power:= 0;
+        if CurAmmoGear <> nil then
+           begin
+           CurAmmoGear.Message:= Gear.Message;
+           exit
+           end else
+           begin
+           Message:= Message and not gm_Attack;
+           if not CurrentTeam.ExtDriven then SendIPC('a')
+           end;
+        AfterAttack
+        end
+     end
+end;
+
+procedure AfterAttack;
+begin
+with CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog].Gear^,
+     CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog] do
+     begin
+        Inc(AttacksNum);
+        State:= State and not gstAttacking;
+        if Ammo[CurSlot, CurAmmo].NumPerTurn >= AttacksNum then isInMultiShoot:= true
+           else begin
+           TurnTimeLeft:= Ammoz[Ammo[CurSlot, CurAmmo].AmmoType].TimeAfterTurn;
+           State:= State or gstAttacked;
+           OnUsedAmmo(Ammo)
+           end;
+     AttackBar:= 0
+     end
+end;
+
+
+
 procedure doStepHedgehog(Gear: PGear); forward;
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepHedgehogDriven(Gear: PGear);
@@ -64,15 +136,13 @@
    end;
 
 if (Gear.Message and gm_Attack)<>0 then
-   if (Gear.State and (gstAttacked or gstHHChooseTarget) = 0)and(CurAmmoGear = nil) then
+   if (Gear.State and (gstAttacked or gstHHChooseTarget) = 0) then
       with PHedgehog(Gear.Hedgehog)^ do
-//        if ((Gear.State and gstFalling  ) = 0)or((Ammo[CurSlot, CurAmmo].Propz and ammoprop_AttackInFall) <> 0)
-//        and((Gear.State and gstHHJumping) = 0)or((Ammo[CurSlot, CurAmmo].Propz and ammoprop_AttackInJump) <> 0) then
             begin
             Gear.State:= Gear.State or gstAttacking;
-            if Gear.Power = cMaxPower then ParseCommand('-attack')
+            if Gear.Power = cMaxPower then Gear.Message:= Gear.Message and not gm_Attack
                else begin
-               if (Ammo[CurSlot, CurAmmo].Propz and ammoprop_Power) = 0 then Gear.Power:= cMaxPower
+               if (Ammo[CurSlot, CurAmmo].Propz and ammoprop_Power) = 0 then Attack(Gear)
                   else begin
                   if Gear.Power = 0 then
                      begin
@@ -84,6 +154,7 @@
                end;
             end else Gear.Message:= Gear.Message and not gm_Attack;
 
+if ((Gear.State and gstAttacking) <> 0) and ((Gear.Message and gm_Attack) = 0) then Attack(Gear);
 
 if (Gear.State and gstFalling) <> 0 then
    begin
--- a/hedgewars/uConsole.pas	Tue Dec 27 10:20:55 2005 +0000
+++ b/hedgewars/uConsole.pas	Wed Dec 28 22:18:11 2005 +0000
@@ -44,7 +44,6 @@
 procedure WriteLnToConsole(s: shortstring);
 procedure KeyPressConsole(Key: Longword);
 procedure ParseCommand(CmdStr: shortstring);
-procedure AfterAttack; // экспортируется только для вызова из CurrAmmoGear
 
 implementation
 {$J+}
@@ -235,23 +234,6 @@
 
 {$INCLUDE CCHandlers.inc}
 
-procedure AfterAttack;
-begin
-with CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog].Gear^,
-     CurrentTeam.Hedgehogs[CurrentTeam.CurrHedgehog] do
-     begin
-        Inc(AttacksNum);
-        State:= State and not gstAttacking;
-        if Ammo[CurSlot, CurAmmo].NumPerTurn >= AttacksNum then isInMultiShoot:= true
-           else begin
-           TurnTimeLeft:= Ammoz[Ammo[CurSlot, CurAmmo].AmmoType].TimeAfterTurn;
-           State:= State or gstAttacked;
-           OnUsedAmmo(Ammo)
-           end;
-     AttackBar:= 0
-     end
-end;
-
 initialization
 InitConsole;
 RegisterVariable('quit'    , vtCommand, @chQuit         );
--- a/hedgewars/uGame.pas	Tue Dec 27 10:20:55 2005 +0000
+++ b/hedgewars/uGame.pas	Wed Dec 28 22:18:11 2005 +0000
@@ -53,6 +53,7 @@
       ProcessKbdDemo;
    end
    else begin
+   ProcessKbd;
    NetGetNextCmd; // на случай, если что-то сказано
    if SendEmptyPacketTicks >= cSendEmptyPacketTime then
       begin
--- a/hedgewars/uGears.pas	Tue Dec 27 10:20:55 2005 +0000
+++ b/hedgewars/uGears.pas	Wed Dec 28 22:18:11 2005 +0000
@@ -74,7 +74,7 @@
 var CurAmmoGear: PGear = nil;
 
 implementation
-uses uWorld, uMisc, uStore, uConsole, uSound, uTeams, uRandom, uCollisions, uLand;
+uses uWorld, uMisc, uStore, uConsole, uSound, uTeams, uRandom, uCollisions, uLand, uIO;
 var GearsList: PGear = nil;
     RopePoints: record
                 Count: Longword;
@@ -90,6 +90,7 @@
 procedure doMakeExplosion(X, Y, Radius: integer; Mask: LongWord); forward;
 function  CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: integer): PGear; forward;
 procedure SpawnBoxOfSmth; forward;
+procedure AfterAttack; forward;
 
 {$INCLUDE GSHandlers.inc}
 {$INCLUDE HHHandlers.inc}
--- a/hedgewars/uMisc.pas	Tue Dec 27 10:20:55 2005 +0000
+++ b/hedgewars/uMisc.pas	Wed Dec 28 22:18:11 2005 +0000
@@ -70,7 +70,7 @@
     cExplosionBorderColor : LongWord = $808080;
 
     cDrownSpeed   : Real = 0.06;
-    cMaxWindSpeed : Real = 0.0003;
+    cMaxWindSpeed : Real = 0.0005;
     cWindSpeed    : Real = 0.0001;
     cGravity      : Real = 0.0005;
 
--- a/hedgewars/uTeams.pas	Tue Dec 27 10:20:55 2005 +0000
+++ b/hedgewars/uTeams.pas	Wed Dec 28 22:18:11 2005 +0000
@@ -115,7 +115,7 @@
      AttacksNum:= 0;
      with Gear^ do
           begin
-          State:= State or gstHHDriven;
+          State:= gstHHDriven;
           Active:= true
           end;
      FollowGear:= Gear