Add Drawing mode, which allows drawing some graphics private to the members of a clan
--- a/QTfrontend/binds.cpp Thu Oct 06 20:58:54 2022 +0300
+++ b/QTfrontend/binds.cpp Sun Oct 16 13:14:16 2022 +0300
@@ -85,6 +85,7 @@
{"!MULTI", QT_TRANSLATE_NOOP("binds (combination)", "switch + toggle hedgehog tags"), QT_TRANSLATE_NOOP("binds", "toggle hedgehog tag translucency"), NULL, NULL},
{"!MULTI", QT_TRANSLATE_NOOP("binds (combination)", "precise + switch + toggle team bars"), QT_TRANSLATE_NOOP("binds", "toggle HUD"), NULL, NULL},
+ {"+teamdraw", "d", QT_TRANSLATE_NOOP("binds", "enter drawing mode"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Drawing mode")},
#ifdef VIDEOREC
{"record", "r", QT_TRANSLATE_NOOP("binds", "record"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Record video:")}
#endif
--- a/QTfrontend/binds.h Thu Oct 06 20:58:54 2022 +0300
+++ b/QTfrontend/binds.h Sun Oct 16 13:14:16 2022 +0300
@@ -22,9 +22,9 @@
#include <QString>
#ifdef VIDEOREC
-#define BINDS_NUMBER 63
+#define BINDS_NUMBER 64
#else
-#define BINDS_NUMBER 62
+#define BINDS_NUMBER 63
#endif
struct BindAction
--- a/QTfrontend/game.cpp Thu Oct 06 20:58:54 2022 +0300
+++ b/QTfrontend/game.cpp Sun Oct 16 13:14:16 2022 +0300
@@ -486,6 +486,12 @@
emit SendTeamMessage(msgbody);
break;
}
+ case 'O':
+ {
+ QByteArray msgbody = msg.mid(2);
+ emit SendDrawCmd(msgbody);
+ break;
+ }
case 'V':
{
if (msg.at(2) == '?')
--- a/QTfrontend/game.h Thu Oct 06 20:58:54 2022 +0300
+++ b/QTfrontend/game.h Sun Oct 16 13:14:16 2022 +0300
@@ -95,6 +95,7 @@
void SendNet(const QByteArray & msg);
void SendChat(const QString & msg);
void SendTeamMessage(const QString & msg);
+ void SendDrawCmd(const QByteArray & msg);
void GameStateChanged(GameState gameState);
void DemoPresenceChanged(bool hasDemo);
void GameStats(char type, const QString & info);
--- a/QTfrontend/hwform.cpp Thu Oct 06 20:58:54 2022 +0300
+++ b/QTfrontend/hwform.cpp Sun Oct 16 13:14:16 2022 +0300
@@ -1943,6 +1943,7 @@
connect(game, SIGNAL(SendChat(const QString &)), hwnet, SLOT(chatLineToNet(const QString &)));
connect(game, SIGNAL(SendConsoleCommand(const QString&)), hwnet, SLOT(consoleCommand(const QString&)));
connect(game, SIGNAL(SendTeamMessage(const QString &)), hwnet, SLOT(SendTeamMessage(const QString &)));
+ connect(game, SIGNAL(SendDrawCmd(const QByteArray &)), hwnet, SLOT(SendDrawCmd(const QByteArray &)));
connect(hwnet, SIGNAL(chatStringFromNet(const QString &)), game, SLOT(FromNetChat(const QString &)), Qt::QueuedConnection);
connect(hwnet, SIGNAL(Warning(const QString&)), game, SLOT(FromNetWarning(const QString&)), Qt::QueuedConnection);
connect(hwnet, SIGNAL(Error(const QString&)), game, SLOT(FromNetError(const QString&)), Qt::QueuedConnection);
--- a/QTfrontend/net/newnetclient.cpp Thu Oct 06 20:58:54 2022 +0300
+++ b/QTfrontend/net/newnetclient.cpp Sun Oct 16 13:14:16 2022 +0300
@@ -474,6 +474,29 @@
return;
}
+ if (netClientState == InRoom || netClientState == InGame || netClientState == InDemo)
+ {
+ if (lst[0] == "TEAMDRAW")
+ {
+ if(lst.size() < 3)
+ {
+ qWarning("Net: Empty CHAT message");
+ return;
+ }
+
+ QString action;
+ QString message = lst[2];
+ QByteArray sender = lst[1].toUtf8();
+
+ QByteArray em("Ou");
+ em.append(sender.size());
+ em.append(sender);
+ em.append(QByteArray::fromBase64(message.toLatin1()));
+ emit FromNet(em.prepend(em.size()));
+ return;
+ }
+ }
+
if (lst[0] == "INFO")
{
if(lst.size() < 5)
@@ -1046,6 +1069,12 @@
RawSendNet(QString("TEAMCHAT") + delimiter + str);
}
+void HWNewNet::SendDrawCmd(const QByteArray& msg)
+{
+ QString str = QString(msg.toBase64());
+ RawSendNet(QString("TEAMDRAW") + delimiter + str);
+}
+
void HWNewNet::askRoomsList()
{
if(netClientState != InLobby)
--- a/QTfrontend/net/newnetclient.h Thu Oct 06 20:58:54 2022 +0300
+++ b/QTfrontend/net/newnetclient.h Sun Oct 16 13:14:16 2022 +0300
@@ -153,6 +153,7 @@
void chatLineToNetWithEcho(const QString&);
void chatLineToLobby(const QString& str);
void SendTeamMessage(const QString& str);
+ void SendDrawCmd(const QByteArray& msg);
void SendNet(const QByteArray & buf);
void AddTeam(const HWTeam & team);
void RemoveTeam(const HWTeam& team);
--- a/doc/protocol.txt Thu Oct 06 20:58:54 2022 +0300
+++ b/doc/protocol.txt Sun Oct 16 13:14:16 2022 +0300
@@ -20,6 +20,7 @@
't' + № /taunt №
'f' + <team> 'team' is uncontrolled
'g' + <team> 'team' is controlled again (synced msg)
+ 'O' Drawing mode data
фронтенд клиенту:
'e' + <команда> выполнить "/<команда>"
--- a/gameServer/HWProtoInRoomState.hs Thu Oct 06 20:58:54 2022 +0300
+++ b/gameServer/HWProtoInRoomState.hs Sun Oct 16 13:14:16 2022 +0300
@@ -387,6 +387,12 @@
engineMsg cl = toEngineMsg $ B.concat ["b", nick cl, "]", msg, "\x20\x20"]
+handleCmd_inRoom ["TEAMDRAW", msg] = do
+ cl <- thisClient
+ chans <- roomSameClanChans
+ return [AnswerClients chans ["TEAMDRAW", nick cl, msg]]
+
+
handleCmd_inRoom ["BAN", banNick] = do
(thisClientId, rnc) <- ask
maybeClientId <- clientByNick banNick
--- a/hedgewars/CMakeLists.txt Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/CMakeLists.txt Sun Oct 16 13:14:16 2022 +0300
@@ -95,6 +95,7 @@
uVisualGearsList.pas
uVisualGearsHandlers.pas
uVisualGears.pas
+ uDrawing.pas
uGears.pas
uGame.pas
--- a/hedgewars/hwengine.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/hwengine.pas Sun Oct 16 13:14:16 2022 +0300
@@ -32,7 +32,7 @@
uses {$IFDEF IPHONEOS}cmem, {$ENDIF} SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler
, uSound, uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uAILandMarks, uLandTexture, uCollisions
, SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted
- , uPhysFSLayer, uCursor, uRandom, ArgParsers, uVisualGearsHandlers, uTextures, uRender
+ , uPhysFSLayer, uCursor, uRandom, ArgParsers, uVisualGearsHandlers, uTextures, uRender, uDrawing
{$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF}
{$IFDEF USE_TOUCH_INTERFACE}, uTouch {$ENDIF}
{$IFDEF ANDROID}, GLUnit{$ENDIF}
@@ -207,12 +207,14 @@
SDL_WINDOWEVENT_FOCUS_GAINED:
begin
cHasFocus:= true;
- onFocusStateChanged();
+ uWorld.onFocusStateChanged();
+ uDrawing.onFocusStateChanged();
end;
SDL_WINDOWEVENT_FOCUS_LOST:
begin
cHasFocus:= false;
- onFocusStateChanged();
+ uWorld.onFocusStateChanged();
+ uDrawing.onFocusStateChanged();
end;
{$IFDEF MOBILE}
(* Suspend game if minimized on mobile.
@@ -541,6 +543,7 @@
uTeams.initModule;
uVisualGears.initModule;
uVisualGearsHandlers.initModule;
+ uDrawing.initModule;
uWorld.initModule;
end;
end;
@@ -555,6 +558,7 @@
uAILandMarks.freeModule;
uCaptions.freeModule;
uWorld.freeModule;
+ uDrawing.freeModule;
uVisualGears.freeModule;
uTeams.freeModule;
uInputHandler.freeModule;
--- a/hedgewars/uAIActions.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uAIActions.pas Sun Oct 16 13:14:16 2022 +0300
@@ -246,7 +246,7 @@
ParseCommand('skip', true);
aia_Put:
- doPut(X, Y, true);
+ doPut(X, Y, true, false);
aia_waitAngle:
if LongInt(Me^.Angle) <> Abs(Param) then exit;
--- a/hedgewars/uCommandHandlers.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uCommandHandlers.pas Sun Oct 16 13:14:16 2022 +0300
@@ -27,7 +27,7 @@
implementation
uses uCommands, uTypes, uVariables, uIO, uDebug, uConsts, uScript, uUtils, SDLh, uWorld, uRandom, uCaptions
- , uVisualGearsList, uGearsHedgehog
+ , uVisualGearsList, uGearsHedgehog, uDrawing
{$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF};
var cTagsMasks : array[0..15] of byte = (7, 0, 0, 0, 0, 4, 5, 6, 15, 8, 8, 8, 8, 12, 13, 14);
@@ -514,10 +514,20 @@
PlayTaunt(byte(s[1]))
end;
-procedure chPut(var s: shortstring);
+procedure chPut_p(var s: shortstring);
begin
s:= s; // avoid compiler hint
- doPut(0, 0, false);
+ if uDrawing.isDrawingModeActive() then
+ uDrawing.onLeftMouseButtonPressed()
+ else
+ doPut(0, 0, false, false);
+end;
+
+procedure chPut_m(var s: shortstring);
+begin
+ s:= s; // avoid compiler hint
+ if uDrawing.isDrawingModeActive() then
+ uDrawing.onLeftMouseButtonReleased()
end;
procedure chCapture(var s: shortstring);
@@ -575,6 +585,13 @@
procedure chAmmoMenu(var s: shortstring);
begin
s:= s; // avoid compiler hint
+
+if uDrawing.isDrawingModeActive() then
+begin
+ uDrawing.onRightMouseButtonPressed();
+ exit;
+end;
+
if CheckNoTeamOrHH then
bShowAmmoMenu:= (not bShowAmmoMenu)
else
@@ -760,6 +777,11 @@
procedure chZoomReset(var s: shortstring);
begin
s:= s; // avoid compiler hint
+ if uDrawing.isDrawingModeActive() then
+ begin
+ uDrawing.onMiddleMouseButtonPressed();
+ exit;
+ end;
if (LocalMessage and gmPrecise <> 0) then
ZoomValue:= cDefaultZoomLevel
else
@@ -895,6 +917,18 @@
end
end;
+procedure chTeamDraw_p(var s: shortstring);
+begin
+ s:= s;
+ uDrawing.onModeButtonPressed();
+end;
+
+procedure chTeamDraw_m(var s: shortstring);
+begin
+ s:= s;
+ uDrawing.onModeButtonReleased();
+end;
+
procedure chFastForward(var cmd: shortstring);
var str0, str1, str2 : shortstring;
h, m, s : integer;
@@ -1002,6 +1036,8 @@
RegisterVariable('slot' , @chSlot , false);
RegisterVariable('setweap' , @chSetWeapon , false, true);
//////// End top by freq analysis
+ RegisterVariable('+teamdraw', @chTeamDraw_p , true);
+ RegisterVariable('-teamdraw', @chTeamDraw_m , true);
RegisterVariable('gencmd' , @chGenCmd , false);
RegisterVariable('script' , @chScript , false);
RegisterVariable('scriptparam', @chScriptParam, false);
@@ -1053,7 +1089,8 @@
RegisterVariable('switch' , @chSwitch , false);
RegisterVariable('timer' , @chTimer , false, true);
RegisterVariable('taunt' , @chTaunt , false);
- RegisterVariable('put' , @chPut , false);
+ RegisterVariable('+put' , @chPut_p , true);
+ RegisterVariable('-put' , @chPut_m , true);
RegisterVariable('+volup' , @chVolUp_p , true );
RegisterVariable('-volup' , @chVolUp_m , true );
RegisterVariable('+voldown', @chVolDown_p , true );
--- a/hedgewars/uCursor.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uCursor.pas Sun Oct 16 13:14:16 2022 +0300
@@ -13,7 +13,7 @@
implementation
-uses SDLh, uVariables, uTypes;
+uses SDLh, uVariables, uTypes, uDrawing;
procedure init;
begin
@@ -54,6 +54,7 @@
begin
CursorPoint.X:= CursorPoint.X + x;
CursorPoint.Y:= CursorPoint.Y - y;
+ uDrawing.onCursorMoved();
end;
end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uDrawing.pas Sun Oct 16 13:14:16 2022 +0300
@@ -0,0 +1,525 @@
+(*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+{$INCLUDE "options.inc"}
+
+unit uDrawing;
+(*
+ * This unit defines the Drawing mode, which allows drawing some graphics
+ * private to the members of a clan.
+ *)
+interface
+
+procedure initModule;
+procedure freeModule;
+
+function isDrawingModeActive(): boolean;
+procedure onModeButtonPressed();
+procedure onModeButtonReleased();
+procedure onFocusStateChanged();
+procedure onCursorMoved();
+procedure onLeftMouseButtonPressed();
+procedure onLeftMouseButtonReleased();
+procedure onRightMouseButtonPressed();
+procedure onMiddleMouseButtonPressed();
+procedure handleIPCInput(cmd : shortstring);
+
+implementation
+uses uTypes, uConsts, uVariables, uVisualGearsList, uUtils, uDebug, uIO, SDLh, Math;
+
+const
+ cColorsCount = 9;
+ cColors : array [0..cColorsCount - 1] of LongWord = (
+ $ff020400,
+ $4980c100,
+ $1de6ba00,
+ $b541ef00,
+ $e55bb000,
+ $20bf0000,
+ $fe8b0e00,
+ $8f590200,
+ $ffff0100
+ );
+ // Reserve one color for the local user
+ cKnownUsersMax = cColorsCount - 1;
+ cPointRadius = 25;
+ cBeaconDuration = 125;
+ cEffectDuration = 500;
+ cMaxDrawingRadius = 150;
+ cEffectGearsCountMax = 4;
+
+type
+ TDrawingState = (drwDisabled, drwStart, drwPoint, drwArrow);
+
+ TVisualEffect = record
+ vGears : array [0..cEffectGearsCountMax - 1] of PVisualGear;
+ gearsCount : integer;
+ end;
+ TDrawingContext = record
+ state : TDrawingState;
+ currVEffect : TVisualEffect;
+ prevAutoCameraOn : boolean;
+ startCursorX : LongInt;
+ startCursorY : LongInt;
+ knownUsers : array [0..cKnownUsersMax - 1] of shortstring;
+ knownUsersCount : integer;
+ lastReplacedUserIdx : integer;
+ end;
+
+var drawingCtx : TDrawingContext;
+
+procedure AddKnownUser(user : shortstring);
+var i : integer;
+begin
+ with drawingCtx do
+ begin
+ for i:= 0 to knownUsersCount - 1 do
+ begin
+ if knownUsers[i] = user then
+ exit;
+ end;
+ if knownUsersCount < cKnownUsersMax then
+ begin
+ knownUsers[knownUsersCount]:= user;
+ Inc(knownUsersCount);
+ end
+ else
+ begin
+ lastReplacedUserIdx:= (lastReplacedUserIdx + 1) mod cKnownUsersMax;
+ knownUsers[lastReplacedUserIdx]:= user;
+ end;
+ end;
+end;
+
+function GetUserColor(user : shortstring) : LongWord;
+var i : integer;
+begin
+ if user = '' then // local user
+ exit(cColors[0]);
+ with drawingCtx do
+ begin
+ for i:= 0 to knownUsersCount - 1 do
+ begin
+ if knownUsers[i] = user then
+ begin
+ exit(cColors[i + 1]);
+ end;
+ end;
+ exit(cColors[0]);
+ end;
+end;
+
+procedure recalcArrowParams(var arrow: TVisualEffect; X1, Y1, X2, Y2 : real);
+var tmp, tmpSin, tmpCos : real;
+begin
+ with arrow.vGears[0]^ do
+ begin
+ X:= X1;
+ Y:= Y1;
+ dX:= X2;
+ dY:= Y2;
+ end;
+ // Compute arrow pointer coordinates
+ if X2 = X1 then
+ if Y2 > Y1 then
+ tmp:= PI / 2
+ else
+ tmp:= -PI / 2
+ else
+ tmp:= arctan2(Y2 - Y1, X2 - X1);
+ tmpSin:= sin(tmp - PI / 4);
+ tmpCos:= cos(tmp - PI / 4);
+ with arrow.vGears[1]^ do
+ begin
+ X:= X2;
+ Y:= Y2;
+ dX:= X2 - 50 * tmpCos;
+ dY:= Y2 - 50 * tmpSin;
+ end;
+ tmpSin:= sin(tmp + PI / 4);
+ tmpCos:= cos(tmp + PI / 4);
+ with arrow.vGears[2]^ do
+ begin
+ X:= X2;
+ Y:= Y2;
+ dX:= X2 - 50 * tmpCos;
+ dY:= Y2 - 50 * tmpSin;
+ end;
+ // Compute circle center
+ with arrow.vGears[3]^ do
+ begin
+ X:= (X1 + X2) / 2;
+ Y:= (Y1 + Y2) / 2;
+ end;
+end;
+
+procedure doStepPoint(Gear: PVisualGear; Steps: Longword);
+var tmp: LongInt;
+begin
+if Gear^.FrameTicks <= Steps then
+ DeleteVisualGear(Gear)
+else
+begin
+ dec(Gear^.FrameTicks, Steps);
+ if Gear^.Tag = 0 then
+ begin
+ tmp:= round(Gear^.FrameTicks * $FF / cEffectDuration);
+ if tmp > $FF then
+ tmp:= $FF;
+ if tmp >= 0 then
+ Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or Longword(tmp);
+ end
+ else if Gear^.Tag = 1 then
+ begin
+ Gear^.State:= round(Gear^.FrameTicks * 2048 / cBeaconDuration);
+ end;
+end;
+end;
+
+procedure doStepArrow(Gear: PVisualGear; Steps: Longword);
+var tmp: LongInt;
+begin
+if Gear^.Tag = 100 then
+ exit;
+if Gear^.FrameTicks <= Steps then
+ DeleteVisualGear(Gear)
+else
+begin
+ dec(Gear^.FrameTicks, Steps);
+ if Gear^.Tag < 3 then
+ begin
+ tmp:= round(Gear^.FrameTicks * $FF / cEffectDuration);
+ if tmp > $FF then
+ tmp:= $FF;
+ if tmp >= 0 then
+ Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or Longword(tmp);
+ end
+ else if Gear^.Tag = 3 then
+ begin
+ Gear^.State:= round(Gear^.FrameTicks * 2048 / cBeaconDuration);
+ end;
+end;
+end;
+
+function isEffectEmpty(var vEffect : TVisualEffect) : boolean;
+begin
+ isEffectEmpty:= vEffect.gearsCount = 0;
+end;
+
+function AddVEffectCircle(user : shortstring; X, Y : LongInt) : TVisualEffect;
+var vGear : PVisualGear;
+ vEffect : TVisualEffect;
+ color : LongWord;
+ i : integer;
+begin
+ color:= GetUserColor(user);
+ for i:= 0 to 1 do
+ begin
+ vGear := AddVisualGear(X, Y, vgtCircle, cPointRadius, true, 1);
+ if vGear = nil then
+ begin
+ OutError('uDrawing: AddVisualGear returned nil', false);
+ vEffect.gearsCount:= 0;
+ exit(vEffect);
+ end;
+ vGear^.Tint:= color or $FF;
+ vGear^.Angle:= 0;
+ vGear^.Timer:= 10;
+ vGear^.Tag:= i;
+ vGear^.doStep:= @doStepPoint;
+ vEffect.vGears[i]:= vGear;
+ end;
+ vEffect.vGears[0]^.Tint:= color or $FF;
+ vEffect.vGears[0]^.FrameTicks:= cEffectDuration;
+ vEffect.vGears[1]^.Tint:= color or $3F;
+ vEffect.vGears[1]^.FrameTicks:= cBeaconDuration;
+
+ vEffect.gearsCount:= 2;
+ AddVEffectCircle:= vEffect;
+end;
+
+function AddVEffectArrow(user : shortstring; X1, Y1, X2, Y2 : LongInt) : TVisualEffect;
+var vGear : PVisualGear;
+ vEffect : TVisualEffect;
+ color : LongWord;
+ i : integer;
+begin
+ color:= GetUserColor(user);
+ for i:= 0 to 2 do
+ begin
+ vGear := AddVisualGear(0, 0, vgtLine, 10, true, 1);
+ if vGear = nil then
+ begin
+ OutError('uDrawing: AddVisualGear returned nil', false);
+ vEffect.gearsCount:= 0;
+ exit(vEffect);
+ end;
+ vGear^.Tint:= color or $FF;
+ vGear^.FrameTicks:= cEffectDuration;
+ vGear^.Tag:= 100;
+ vGear^.doStep:= @doStepArrow;
+ vEffect.vGears[i]:= vGear;
+ end;
+
+ vGear := AddVisualGear(0, 0, vgtCircle, 2048, true, 1);
+ if vGear = nil then
+ begin
+ OutError('uDrawing: AddVisualGear returned nil', false);
+ vEffect.gearsCount:= 0;
+ exit(vEffect);
+ end;
+ vGear^.Tint:= color;
+ vGear^.Angle:= 0;
+ vGear^.FrameTicks:= cBeaconDuration;
+ vGear^.Timer:= 10;
+ vGear^.Tag:= 100;
+ vGear^.doStep:= @doStepArrow;
+ vEffect.vGears[3]:= vGear;
+
+ vEffect.gearsCount:= 4;
+ recalcArrowParams(vEffect, X1, Y1, X2, Y2);
+
+ AddVEffectArrow:= vEffect;
+end;
+
+procedure VEffectArrowStart(var vEffect : TVisualEffect);
+var i : integer;
+begin
+ for i:= 0 to vEffect.gearsCount - 1 do
+ vEffect.vGears[i]^.Tag:= i;
+ vEffect.vGears[3]^.Tint:= (vEffect.vGears[3]^.Tint and $FFFFFF00) or $3F;
+end;
+
+procedure DeleteVEffect(var vEffect : TVisualEffect);
+var i : integer;
+begin
+ for i:= 0 to vEffect.gearsCount - 1 do
+ DeleteVisualGear(vEffect.vGears[i]);
+ vEffect.gearsCount:= 0;
+end;
+
+function isDrawingModeActive() : boolean;
+begin
+ isDrawingModeActive:= drawingCtx.state <> drwDisabled;
+end;
+
+procedure SendIPCArrow(X1, Y1, X2, Y2: LongInt);
+var s: shortstring;
+begin
+s[0]:= #18;
+s[1]:= 'O';
+s[2]:= 'a';
+SDLNet_Write32(X1, @s[3]);
+SDLNet_Write32(Y1, @s[7]);
+SDLNet_Write32(X2, @s[11]);
+SDLNet_Write32(Y2, @s[15]);
+SendIPC(s)
+end;
+
+procedure SendIPCCircle(X1, Y1: LongInt);
+var s: shortstring;
+begin
+s[0]:= #10;
+s[1]:= 'O';
+s[2]:= 'c';
+SDLNet_Write32(X1, @s[3]);
+SDLNet_Write32(Y1, @s[7]);
+SendIPC(s)
+end;
+
+procedure handleIPCInput(cmd: shortstring);
+var i, drwCmdOffset : integer;
+ userNameLen : Byte;
+ user : shortstring;
+ X1, Y1, X2, Y2 : LongInt;
+ VEffect : TVisualEffect;
+begin
+case cmd[1] of
+ 'u' : begin
+ userNameLen:= Byte(cmd[2]);
+ for i:= 0 to userNameLen do
+ user[i]:= cmd[2 + i];
+ drwCmdOffset:= 2 + userNameLen + 1;
+ if Length(cmd) < drwCmdOffset then
+ exit;
+ AddKnownUser(user);
+ case cmd[drwCmdOffset] of
+ 'a' : begin
+ if Length(cmd) < drwCmdOffset + 4 * 4 then
+ exit;
+ X1:= SDLNet_Read32(@cmd[drwCmdOffset + 1]);
+ Y1:= SDLNet_Read32(@cmd[drwCmdOffset + 1 + 4]);
+ X2:= SDLNet_Read32(@cmd[drwCmdOffset + 1 + 8]);
+ Y2:= SDLNet_Read32(@cmd[drwCmdOffset + 1 + 12]);
+ VEffect:= AddVEffectArrow(user, X1, Y1, X2, Y2);
+ if not isEffectEmpty(VEffect) then
+ VEffectArrowStart(VEffect);
+ end;
+ 'c' : begin
+ if Length(cmd) < drwCmdOffset + 4 * 2 then
+ exit;
+ X1:= SDLNet_Read32(@cmd[drwCmdOffset + 1]);
+ Y1:= SDLNet_Read32(@cmd[drwCmdOffset + 1 + 4]);
+ VEffect:= AddVEffectCircle(user, X1, Y1);
+ end;
+ end;
+ end;
+end;
+end;
+
+procedure onModeButtonPressed();
+begin
+ drawingCtx.state:= drwStart;
+ drawingCtx.prevAutoCameraOn:= autoCameraOn;
+ autoCameraOn:= false;
+end;
+
+procedure onModeButtonReleased();
+begin
+ DeleteVEffect(drawingCtx.currVEffect);
+ if drawingCtx.state <> drwDisabled then
+ begin
+ drawingCtx.state:= drwDisabled;
+ autoCameraOn:= drawingCtx.prevAutoCameraOn;
+ end;
+end;
+
+procedure onFocusStateChanged();
+begin
+ if not cHasFocus then
+ onModeButtonReleased();
+end;
+
+procedure onLeftMouseButtonPressed();
+begin
+if not isDrawingModeActive() then
+ exit;
+case drawingCtx.state of
+ drwStart: begin
+ drawingCtx.startCursorX:= CursorPoint.X;
+ drawingCtx.startCursorY:= CursorPoint.Y;
+ drawingCtx.state:= drwPoint;
+ end;
+end;
+end;
+
+procedure onLeftMouseButtonReleased();
+var tmpX, tmpY, tmpX2, tmpY2 : LongInt;
+ vEffect : TVisualEffect;
+begin
+if not isDrawingModeActive() then
+ exit;
+case drawingCtx.state of
+ drwPoint: begin
+ tmpX:= drawingCtx.startCursorX - WorldDx;
+ tmpY:= cScreenHeight - drawingCtx.startCursorY - WorldDy;
+ vEffect:= AddVEffectCircle('', tmpX, tmpY);
+ if not isEffectEmpty(vEffect) then
+ SendIPCCircle(tmpX, tmpY);
+ drawingCtx.state:= drwStart;
+ end;
+ drwArrow: begin
+ tmpX2:= CursorPoint.X - WorldDx;
+ tmpY2:= cScreenHeight - CursorPoint.Y - WorldDy;
+ with drawingCtx do
+ begin
+ tmpX:= startCursorX - WorldDx;
+ tmpY:= cScreenHeight - startCursorY - WorldDy;
+ recalcArrowParams(currVEffect, tmpX, tmpY, tmpX2, tmpY2);
+ VEffectArrowStart(currVEffect);
+ SendIPCArrow(tmpX, tmpY, tmpX2, tmpY2);
+ currVEffect.gearsCount:= 0;
+ end;
+ drawingCtx.state:= drwStart;
+ end;
+end;
+end;
+
+procedure onRightMouseButtonPressed();
+begin
+ if not isDrawingModeActive() then
+ exit;
+ DeleteVEffect(drawingCtx.currVEffect);
+ drawingCtx.state:= drwStart;
+end;
+
+procedure onMiddleMouseButtonPressed();
+begin
+end;
+
+procedure onCursorMoved();
+var tmpX, tmpY, tmpX2, tmpY2, dX, dY : LongInt;
+ h : real;
+begin
+if not isDrawingModeActive() then
+ exit;
+autoCameraOn:= false;
+dX:= CursorPoint.X - drawingCtx.startCursorX;
+dY:= CursorPoint.Y - drawingCtx.startCursorY;
+h:= sqrt(dX * dX + dY * dY);
+if (drawingCtx.state <> drwStart) and (h > cMaxDrawingRadius) then
+begin
+ CursorPoint.X:= drawingCtx.startCursorX + round(dX * cMaxDrawingRadius / h);
+ CursorPoint.Y:= drawingCtx.startCursorY + round(dY * cMaxDrawingRadius / h);
+end;
+case drawingCtx.state of
+ drwPoint : begin
+ if h > cPointRadius then
+ begin
+ tmpX:= drawingCtx.startCursorX - WorldDx;
+ tmpY:= cScreenHeight - drawingCtx.startCursorY - WorldDy;
+ tmpX2:= CursorPoint.X - WorldDx;
+ tmpY2:= cScreenHeight - CursorPoint.Y - WorldDy;
+ drawingCtx.currVEffect:= AddVEffectArrow('', tmpX, tmpY, tmpX2, tmpY2);
+ if not isEffectEmpty(drawingCtx.currVEffect) then
+ drawingCtx.state:= drwArrow
+ else
+ drawingCtx.state:= drwStart;
+ end;
+ end;
+ drwArrow : begin
+ tmpX:= drawingCtx.startCursorX - WorldDx;
+ tmpY:= cScreenHeight - drawingCtx.startCursorY - WorldDy;
+ tmpX2:= CursorPoint.X - WorldDx;
+ tmpY2:= cScreenHeight - CursorPoint.Y - WorldDy;
+ with drawingCtx do
+ begin
+ recalcArrowParams(currVEffect, tmpX, tmpY, tmpX2, tmpY2);
+ end;
+ end;
+end;
+end;
+
+procedure initModule;
+begin
+ with drawingCtx do
+ begin
+ state:= drwDisabled;
+ currVEffect.gearsCount:= 0;
+ startCursorX:= 0;
+ startCursorY:= 0;
+ knownUsersCount:= 0;
+ lastReplacedUserIdx:= 0;
+ end;
+end;
+
+procedure freeModule;
+begin
+end;
+
+end.
--- a/hedgewars/uIO.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uIO.pas Sun Oct 16 13:14:16 2022 +0300
@@ -36,10 +36,10 @@
procedure IPCWaitPongEvent;
procedure IPCCheckSock;
procedure NetGetNextCmd;
-procedure doPut(putX, putY: LongInt; fromAI: boolean);
+procedure doPut(putX, putY: LongInt; fromAI, extSource: boolean);
implementation
-uses uConsole, uConsts, uVariables, uCommands, uUtils, uDebug, uLocale, uSound;
+uses uConsole, uConsts, uVariables, uCommands, uUtils, uDebug, uLocale, uSound, uDrawing;
const
cSendEmptyPacketTime = 1000;
@@ -207,6 +207,10 @@
end
else
isProcessed:= false;
+ 'O': begin
+ s:= copy(s, 2, Length(s) - 1);
+ uDrawing.handleIPCInput(s);
+ end;
else
isProcessed:= false;
end;
@@ -443,7 +447,7 @@
'p': begin
x32:= SDLNet_Read32(@(headcmd^.str[2]));
y32:= SDLNet_Read32(@(headcmd^.str[6]));
- doPut(x32, y32, false)
+ doPut(x32, y32, false, true)
end;
'P': begin
// these are equations solved for CursorPoint
@@ -498,9 +502,10 @@
halt(HaltFatalErrorNoIPC);
end;
-procedure doPut(putX, putY: LongInt; fromAI: boolean);
+procedure doPut(putX, putY: LongInt; fromAI, extSource: boolean);
begin
-if CheckNoTeamOrHH or isPaused then
+if CheckNoTeamOrHH or isPaused or (CurrentTeam^.ExtDriven and (not extSource)) or
+ (CurrentHedgehog = nil) or ((CurrentHedgehog^.BotLevel <> 0) and (not fromAI)) then
exit;
bShowFinger:= false;
if (not CurrentTeam^.ExtDriven) and bShowAmmoMenu then
--- a/hedgewars/uInputHandler.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uInputHandler.pas Sun Oct 16 13:14:16 2022 +0300
@@ -415,6 +415,7 @@
RegisterBind(DefaultBinds, _S't', 'chat');
RegisterBind(DefaultBinds, _S'u', 'chat team');
RegisterBind(DefaultBinds, _S'y', 'confirm');
+ RegisterBind(DefaultBinds, _S'd', '+teamdraw');
RegisterBind(DefaultBinds, 'mousem', 'zoomreset');
RegisterBind(DefaultBinds, 'wheelup', 'zoomin');
@@ -426,7 +427,7 @@
for i:= 1 to 5 do RegisterBind(DefaultBinds, IntToStr(i), 'timer '+IntToStr(i));
RegisterBind(DefaultBinds, _S'n', 'timer_u');
- RegisterBind(DefaultBinds, 'mousel', '/put');
+ RegisterBind(DefaultBinds, 'mousel', '+put');
RegisterBind(DefaultBinds, 'mouser', 'ammomenu');
RegisterBind(DefaultBinds, 'backspace', 'hjump');
RegisterBind(DefaultBinds, 'tab', 'switch');
--- a/hedgewars/uTouch.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uTouch.pas Sun Oct 16 13:14:16 2022 +0300
@@ -309,7 +309,7 @@
if (CurrentHedgehog <> nil)then
if(Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0)then
begin
- ParseTeamCommand('put');
+ ParseTeamCommand('+put');
targetted:= true;
end
else if (CurAmmoGear <> nil) and (CurAmmoGear^.AmmoType = amSwitch) then
@@ -346,7 +346,7 @@
begin
CursorPoint.X:= finger.x;
CursorPoint.Y:= finger.y;
- ParseTeamCommand('put');
+ ParseTeamCommand('+put');
end
else
bShowAmmoMenu:= false;
--- a/hedgewars/uTypes.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uTypes.pas Sun Oct 16 13:14:16 2022 +0300
@@ -121,7 +121,7 @@
vgtDust, vgtSplash, vgtDroplet, vgtSmokeRing, vgtBeeTrace, vgtEgg,
vgtFeather, vgtHealthTag, vgtSmokeTrace, vgtEvilTrace, vgtExplosion,
vgtBigExplosion, vgtChunk, vgtNote, vgtLineTrail, vgtBulletHit, vgtCircle,
- vgtSmoothWindBar, vgtStraightShot, vgtNoPlaceWarn);
+ vgtSmoothWindBar, vgtStraightShot, vgtNoPlaceWarn, vgtLine);
// Damage can be caused by different sources
TDamageSource = (dsUnknown, dsFall, dsBullet, dsExplosion, dsShove, dsPoison, dsHammer);
--- a/hedgewars/uVisualGears.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uVisualGears.pas Sun Oct 16 13:14:16 2022 +0300
@@ -138,6 +138,18 @@
exit(@SpritesData[GetSprite(sprite, SDsprite)]);
end;
+procedure DrawCircleGear(gear : PVisualGear);
+var tmp: real;
+begin
+ if gear^.Angle = 1 then
+ begin
+ tmp:= Gear^.State / 100;
+ DrawTexture(round(Gear^.X-24*tmp) + WorldDx, round(Gear^.Y-24*tmp) + WorldDy, SpritesData[sprVampiric].Texture, tmp)
+ end
+ else
+ DrawCircle(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State, Gear^.Timer);
+end;
+
procedure DrawVisualGears(Layer: LongWord; worldIsShifted: boolean);
var Gear: PVisualGear;
tinted, speedlessFlakes: boolean;
@@ -208,6 +220,8 @@
vgtEvilTrace: if Gear^.State < 8 then
DrawSprite(sprEvilTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State);
vgtLineTrail: DrawLine(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 1.0, $FF, min(Gear^.Timer, $C0), min(Gear^.Timer, $80), min(Gear^.Timer, (Gear^.Tint and $FF)));
+ vgtLine: DrawLine(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, Gear^.State, Gear^.Tint);
+ vgtCircle: DrawCircleGear(gear);
end;
if (cReducedQuality and rqAntiBoom) = 0 then
case Gear^.Kind of
@@ -377,13 +391,7 @@
else
DrawTextureRotatedF(spriteData^.Texture, Gear^.Scale, 0, 0, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy + SkyOffset, Gear^.Frame, 1, spriteData^.Width, spriteData^.Height, Gear^.Angle);
end;
- vgtCircle: if gear^.Angle = 1 then
- begin
- tmp:= Gear^.State / 100;
- DrawTexture(round(Gear^.X-24*tmp) + WorldDx, round(Gear^.Y-24*tmp) + WorldDy, SpritesData[sprVampiric].Texture, tmp)
- end
- else
- DrawCircle(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State, Gear^.Timer);
+ vgtCircle: DrawCircleGear(gear);
end;
if (Gear^.Tint <> $FFFFFFFF) or tinted then
untint;
--- a/hedgewars/uVisualGearsHandlers.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uVisualGearsHandlers.pas Sun Oct 16 13:14:16 2022 +0300
@@ -335,6 +335,18 @@
end;
////////////////////////////////////////////////////////////////////////////////
+procedure doStepLine(Gear: PVisualGear; Steps: Longword);
+begin
+{$IFNDEF PAS2C}
+Steps := Steps;
+{$ENDIF}
+if Gear^.Timer <= Steps then
+ DeleteVisualGear(Gear)
+else
+ dec(Gear^.Timer, Steps)
+end;
+
+////////////////////////////////////////////////////////////////////////////////
procedure doStepEgg(Gear: PVisualGear; Steps: Longword);
begin
Gear^.X:= Gear^.X + Gear^.dX * Steps;
@@ -1072,7 +1084,8 @@
@doStepCircle,
@doStepSmoothWindBar,
@doStepStraightShot,
- @doStepNoPlaceWarn
+ @doStepNoPlaceWarn,
+ @doStepLine
);
procedure initModule;
--- a/hedgewars/uVisualGearsList.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uVisualGearsList.pas Sun Oct 16 13:14:16 2022 +0300
@@ -403,6 +403,7 @@
vgtSmokeTrace,
vgtEvilTrace,
vgtLineTrail,
+ vgtLine,
vgtSmoke,
vgtSmokeWhite,
vgtDust,
--- a/hedgewars/uWorld.pas Thu Oct 06 20:58:54 2022 +0300
+++ b/hedgewars/uWorld.pas Sun Oct 16 13:14:16 2022 +0300
@@ -65,6 +65,7 @@
, uTeams
, uDebug
, uInputHandler
+ , uDrawing
{$IFDEF USE_VIDEO_RECORDING}
, uVideoRec
{$ENDIF}
@@ -1925,6 +1926,9 @@
DrawTextureF(SpritesData[sprArrow].Texture, cDefaultZoomLevel / cScaleFactor, TargetCursorPoint.X + round(SpritesData[sprArrow].Width / cScaleFactor), cScreenHeight + round(SpritesData[sprArrow].Height / cScaleFactor) - TargetCursorPoint.Y, (RealTicks shr 6) mod 8, 1, SpritesData[sprArrow].Width, SpritesData[sprArrow].Height);
end;
+if uDrawing.isDrawingModeActive() then
+ DrawTextureF(SpritesData[sprArrow].Texture, cDefaultZoomLevel / cScaleFactor, CursorPoint.X + round(SpritesData[sprArrow].Width / cScaleFactor), cScreenHeight + round(SpritesData[sprArrow].Height / cScaleFactor) - CursorPoint.Y, (RealTicks shr 6) mod 8, 1, SpritesData[sprArrow].Width, SpritesData[sprArrow].Height);
+
// debug stuff
if cViewLimitsDebug then
begin
@@ -2010,9 +2014,9 @@
exit
end;
-if isCursorVisible then
+if isCursorVisible or uDrawing.isDrawingModeActive() then
begin
- if (not CurrentTeam^.ExtDriven) and (GameTicks >= PrevSentPointTime + cSendCursorPosTime) then
+ if isCursorVisible and (not CurrentTeam^.ExtDriven) and (GameTicks >= PrevSentPointTime + cSendCursorPosTime) then
begin
SendIPCXY('P', CursorPoint.X - WorldDx, cScreenHeight - CursorPoint.Y - WorldDy);
PrevSentPointTime:= GameTicks
@@ -2024,7 +2028,8 @@
// this generates the border around the screen that moves the camera when cursor is near it
if (CurrentTeam^.ExtDriven and isCursorVisible and autoCameraOn) or
- (not CurrentTeam^.ExtDriven and isCursorVisible) or ((FollowGear <> nil) and autoCameraOn) then
+ (not CurrentTeam^.ExtDriven and isCursorVisible) or
+ ((FollowGear <> nil) and autoCameraOn) or uDrawing.isDrawingModeActive() then
begin
if CursorPoint.X < - trunc(cScreenWidth / cScaleFactor) + EdgesDist then
begin