# HG changeset patch # User S.D. # Date 1665915256 -10800 # Node ID 014f4edd042176c76e88632f3db9b082535d001b # Parent a323e1954a6f2c7f997cf86f6f6343917df224d2 Add Drawing mode, which allows drawing some graphics private to the members of a clan diff -r a323e1954a6f -r 014f4edd0421 QTfrontend/binds.cpp --- 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 diff -r a323e1954a6f -r 014f4edd0421 QTfrontend/binds.h --- 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 #ifdef VIDEOREC -#define BINDS_NUMBER 63 +#define BINDS_NUMBER 64 #else -#define BINDS_NUMBER 62 +#define BINDS_NUMBER 63 #endif struct BindAction diff -r a323e1954a6f -r 014f4edd0421 QTfrontend/game.cpp --- 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) == '?') diff -r a323e1954a6f -r 014f4edd0421 QTfrontend/game.h --- 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); diff -r a323e1954a6f -r 014f4edd0421 QTfrontend/hwform.cpp --- 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); diff -r a323e1954a6f -r 014f4edd0421 QTfrontend/net/newnetclient.cpp --- 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) diff -r a323e1954a6f -r 014f4edd0421 QTfrontend/net/newnetclient.h --- 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); diff -r a323e1954a6f -r 014f4edd0421 doc/protocol.txt --- 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' is uncontrolled 'g' + 'team' is controlled again (synced msg) + 'O' Drawing mode data фронтенд клиенту: 'e' + <команда> выполнить "/<команда>" diff -r a323e1954a6f -r 014f4edd0421 gameServer/HWProtoInRoomState.hs --- 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 diff -r a323e1954a6f -r 014f4edd0421 hedgewars/CMakeLists.txt --- 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 diff -r a323e1954a6f -r 014f4edd0421 hedgewars/hwengine.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; diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uAIActions.pas --- 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; diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uCommandHandlers.pas --- 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 ); diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uCursor.pas --- 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. diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uDrawing.pas --- /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 + * + * 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. diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uIO.pas --- 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 diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uInputHandler.pas --- 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'); diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uTouch.pas --- 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; diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uTypes.pas --- 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); diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uVisualGears.pas --- 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; diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uVisualGearsHandlers.pas --- 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; diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uVisualGearsList.pas --- 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, diff -r a323e1954a6f -r 014f4edd0421 hedgewars/uWorld.pas --- 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