diff --git a/Pongbot/Bot/Bot.cpp b/Pongbot/Bot/Bot.cpp index 8626b93..d5ce229 100644 --- a/Pongbot/Bot/Bot.cpp +++ b/Pongbot/Bot/Bot.cpp @@ -75,31 +75,6 @@ void Bot::Think() _IBotController->RunPlayerMove(&cmd); } -Player Bot::GetPlayer() const -{ - return _Player; -} - -edict_t *Bot::GetEdict() const -{ - return _Edict; -} - -bool Bot::Exists() const -{ - return _Player.Exists(); -} - -Vector Bot::GetPos() const -{ - return _Player.GetPos(); -} - -Vector Bot::GetEarPos() const -{ - return _Player.GetHeadPos(); -} - QAngle Bot::GetViewAngle() const { return _IBotController->GetLocalAngles(); @@ -116,16 +91,6 @@ void Bot::SetViewAngle(QAngle angle) _TargetViewAngle = angle; } -TFClass Bot::GetClass() const -{ - return _CurrentClass; -} - -TFTeam Bot::GetTeam() const -{ - return _Player.GetTeam(); -} - BotVisibles* Bot::GetBotVisibles() const { return _BotVisibles; @@ -142,26 +107,11 @@ void Bot::SetMovement(Vector2D movement) _Movement = movement; } -Vector2D Bot::GetMovement() const -{ - return _Movement; -} - void Bot::SetPressedButtons(int pressedButtons) { _PressedButtons = pressedButtons; } -int Bot::GetPressedButtons() const -{ - return _PressedButtons; -} - -WeaponSlot Bot::GetSelectedWeaponSlot() const -{ - return _SelectedWeaponSlot; -} - void Bot::SetSelectedWeapon(WeaponSlot weapon) { const char *weaponName = nullptr; @@ -185,11 +135,6 @@ void Bot::SetSelectedWeapon(WeaponSlot weapon) } } -bool Bot::IsDead() const -{ - return _Player.IsDead(); -} - void Bot::ChangeClass(TFClass tfClass) { ExecClientCommand("joinclass %s", _TFClassToJoinName(tfClass)); @@ -254,11 +199,6 @@ WeaponSlot Bot::GetIdealWeaponForRange(float range) const } } -IServerEntity* Bot::GetIServerEntity() const -{ - return _Edict->GetIServerEntity(); -} - void Bot::_SwitchToFittingTeam() { uint8_t red = 0; diff --git a/Pongbot/Bot/Bot.h b/Pongbot/Bot/Bot.h index 2053587..8f1fe1c 100644 --- a/Pongbot/Bot/Bot.h +++ b/Pongbot/Bot/Bot.h @@ -22,27 +22,63 @@ class Bot const char* Name; void Think(); - Player GetPlayer() const; - edict_t* GetEdict() const; - bool Exists() const; - Vector GetPos() const; - Vector GetEarPos() const; + inline Player GetPlayer() const + { + return _Player; + } + inline edict_t* GetEdict() const + { + return _Edict; + } + inline bool Exists() const + { + return _Player.Exists(); + } + inline Vector GetPos() const + { + return _Player.GetPos(); + } + inline Vector GetEarPos() const + { + return _Player.GetHeadPos(); + } QAngle GetViewAngle() const; void SetViewAngle(QAngle angle); - TFClass GetClass() const; - TFTeam GetTeam() const; + inline TFClass GetClass() const + { + return _CurrentClass; + } + inline TFTeam GetTeam() const + { + return _Player.GetTeam(); + } void SetMovement(Vector2D movement); - Vector2D GetMovement() const; + inline Vector2D GetMovement() const + { + return _Movement; + } void SetPressedButtons(int pressedButtons); - int GetPressedButtons() const; - WeaponSlot GetSelectedWeaponSlot() const; + inline int GetPressedButtons() const + { + return _PressedButtons; + } + inline WeaponSlot GetSelectedWeaponSlot() const + { + return _SelectedWeaponSlot; + } void SetSelectedWeapon(WeaponSlot weapon); BotVisibles* GetBotVisibles() const; - bool IsDead() const; + inline bool IsDead() const + { + return _Player.IsDead(); + } void ChangeClass(TFClass tfClass); void ExecClientCommand(const char* command, ...) const; WeaponSlot GetIdealWeaponForRange(float range) const; - IServerEntity* GetIServerEntity() const; + inline IServerEntity* GetIServerEntity() const + { + return _Edict->GetIServerEntity(); + } private: Player _Player; diff --git a/Pongbot/Bot/Brain/BotBrain.cpp b/Pongbot/Bot/Brain/BotBrain.cpp index e0e0ead..5b1a50c 100644 --- a/Pongbot/Bot/Brain/BotBrain.cpp +++ b/Pongbot/Bot/Brain/BotBrain.cpp @@ -120,11 +120,6 @@ void BotBrain::OnObjectiveUpdate() _ClearTask(); } -Bot *BotBrain::_GetBot() const -{ - return _ABot; -} - void BotBrain::_SetBotTask(BotTask* task) { _ClearTask(); diff --git a/Pongbot/Bot/Brain/BotBrain.h b/Pongbot/Bot/Brain/BotBrain.h index d3cac90..48eb779 100644 --- a/Pongbot/Bot/Brain/BotBrain.h +++ b/Pongbot/Bot/Brain/BotBrain.h @@ -22,7 +22,10 @@ class BotBrain : public IEventHooker virtual void OnObjectiveUpdate(); protected: - Bot* _GetBot() const; + inline Bot* _GetBot() const + { + return _ABot; + } void _SetBotTask(BotTask* task); bool _HasBotTask() const; bool _IsCurrentBotTaskOfType(const std::type_info& type) const; diff --git a/Pongbot/Bot/Brain/BotBrainSniper.cpp b/Pongbot/Bot/Brain/BotBrainSniper.cpp index 226f68f..a98e940 100644 --- a/Pongbot/Bot/Brain/BotBrainSniper.cpp +++ b/Pongbot/Bot/Brain/BotBrainSniper.cpp @@ -4,10 +4,17 @@ #include "../../ConVarHolder.h" #include "Tasks/BotTaskSniperSnipe.h" #include "../../Util.h" +#include "../../Waypoint/WaypointManager.h" +#include "../../Waypoint/WaypointNodeFlagTypes.h" +#include "Tasks/BotTaskGoto.h" + +extern WaypointManager* _WaypointManager; void BotBrainSniper::_OnThink() { Bot* bot = _GetBot(); + Vector botPos = bot->GetPos(); + TFTeam botTeam = bot->GetTeam(); if (!_IsCurrentBotTaskOfType(typeid(BotTaskSniperSnipe)) && bot->GetPlayer().IsSniperZoomedIn()) { @@ -17,9 +24,27 @@ void BotBrainSniper::_OnThink() BotVisibleTarget visibleTarget = bot->GetBotVisibles()->GetMostImportantTarget(); if (visibleTarget.IsValid() - && Util::DistanceToNoZ(bot->GetPos(), visibleTarget.GetPos()) > _ConVarHolder->CVarBotWeaponLongRangeDist->GetFloat() + && Util::DistanceToNoZ(botPos, visibleTarget.GetPos()) > _ConVarHolder->CVarBotWeaponLongRangeDist->GetFloat() && !_IsCurrentBotTaskOfType(typeid(BotTaskSniperSnipe))) { _SetBotTask(new BotTaskSniperSnipe(bot)); } + + if (!_HasBotTask()) + { + WaypointNode* node = _WaypointManager->GetClosestWaypointNode(botPos, -1, botTeam == TEAM_RED ? NODE_SNIPER_CAMP_RED + : NODE_SNIPER_CAMP_BLUE); + if (node) + { + _SetBotTask(new BotTaskSniperSnipe(bot, _ConVarHolder->CVarBotSniperCampTime->GetInt(), node->OptimalViewAngle)); + } + else + { + node = _WaypointManager->GetRandomWaypointNode(botTeam == TEAM_RED ? ~NODE_SNIPER_CAMP_RED : ~NODE_SNIPER_CAMP_BLUE); + if (node) + { + _SetBotTask(new BotTaskGoto(bot, node->GetPos())); + } + } + } } \ No newline at end of file diff --git a/Pongbot/Bot/Brain/Tasks/BotTask.cpp b/Pongbot/Bot/Brain/Tasks/BotTask.cpp index 0982585..3ed047a 100644 --- a/Pongbot/Bot/Brain/Tasks/BotTask.cpp +++ b/Pongbot/Bot/Brain/Tasks/BotTask.cpp @@ -8,6 +8,7 @@ bool BotTask::OnThink() { _BotTargetPos.Zero(); + _BotTargetAngle = {0.f, 0.f, 0.f}; _IsBotViewAngleOverriden = false; _BotPressedButtons = 0; @@ -41,7 +42,20 @@ bool BotTask::OnThink() { _Bot->SetMovement(Util::GetIdealMoveSpeedsToPos(_Bot, _BotTargetPos)); } - _Bot->SetViewAngle(Util::GetLookAtAngleForPos(_Bot, _BotTargetLookAt)); + + if (!_BotTargetLookAt.IsZero()) + { + _Bot->SetViewAngle(Util::GetLookAtAngleForPos(_Bot, _BotTargetLookAt)); + } + else if (_BotTargetAngle != QAngle(0.f, 0.f, 0.f)) + { + _Bot->SetViewAngle(_BotTargetAngle); + } + else + { + _Bot->SetViewAngle({0.f, 0.f, 0.f}); + } + _Bot->SetPressedButtons(_BotPressedButtons); _Bot->SetSelectedWeapon(_WeaponSlot); @@ -99,34 +113,4 @@ void BotTask::_CheckIfStuckInPos() _PosStuckTries = 0; _LastPos = currentPos; } -} - -void BotTask::_BotMoveTo(Vector pos) -{ - _BotTargetPos = pos; -} - -void BotTask::_SetBotLookAt(Vector pos) -{ - _BotTargetLookAt = pos; -} - -void BotTask::_OverrideBotViewAngle() -{ - _IsBotViewAngleOverriden = true; -} - -void BotTask::_AddBotPressedButton(int button) -{ - _BotPressedButtons |= button; -} - -void BotTask::_SetBotWeaponSlot(WeaponSlot weaponSlot) -{ - _WeaponSlot = weaponSlot; -} - -Bot* BotTask::_GetBot() const -{ - return _Bot; } \ No newline at end of file diff --git a/Pongbot/Bot/Brain/Tasks/BotTask.h b/Pongbot/Bot/Brain/Tasks/BotTask.h index 4206251..a745c05 100644 --- a/Pongbot/Bot/Brain/Tasks/BotTask.h +++ b/Pongbot/Bot/Brain/Tasks/BotTask.h @@ -17,17 +17,42 @@ class BotTask bool _AbortTask; bool _DoStuckPosPanicHandling; - void _BotMoveTo(Vector pos); - void _SetBotLookAt(Vector pos); - void _OverrideBotViewAngle(); - void _AddBotPressedButton(int button); - void _SetBotWeaponSlot(WeaponSlot weaponSlot); - Bot* _GetBot() const; + inline void _BotMoveTo(Vector pos) + { + _BotTargetPos = pos; + } + inline void _SetBotLookAt(Vector pos) + { + _BotTargetAngle = {0.f, 0.f, 0.f}; + _BotTargetLookAt = pos; + } + inline void _SetBotAngle(QAngle angle) + { + _BotTargetLookAt = {0.f, 0.f, 0.f}; + _BotTargetAngle = angle; + } + inline void _OverrideBotViewAngle() + { + _IsBotViewAngleOverriden = true; + } + inline void _AddBotPressedButton(int button) + { + _BotPressedButtons |= button; + } + inline void _SetBotWeaponSlot(WeaponSlot weaponSlot) + { + _WeaponSlot = weaponSlot; + } + inline Bot* _GetBot() const + { + return _Bot; + } private: Bot* _Bot; Vector _BotTargetPos; Vector _BotTargetLookAt; + QAngle _BotTargetAngle; bool _IsBotViewAngleOverriden; int _BotPressedButtons; WeaponSlot _WeaponSlot; diff --git a/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.cpp b/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.cpp index 9fff91f..b6be25c 100644 --- a/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.cpp +++ b/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.cpp @@ -4,31 +4,46 @@ #include "../../../Util.h" #include "../../../ConVarHolder.h" #include "../../../TF2/Entity/Player.h" +#include "../../../Waypoint/WaypointManager.h" #include #include -extern IVEngineServer* Engine; +extern WaypointManager* _WaypointManager; bool BotTaskSniperSnipe::_OnThink() { // TODO: Check for ammo!!!! Bot* bot = _GetBot(); + Vector botPos = bot->GetPos(); Player botInfo(bot->GetEdict()); BotVisibleTarget visibleTarget = bot->GetBotVisibles()->GetMostImportantTarget(); Vector visibleTargetPos; + // Zoom in if not already done so + if (!botInfo.IsSniperZoomedIn()) + { + _AddBotPressedButton(IN_ATTACK2); + } + if (!visibleTarget.IsValid()) { - return true; + if (Engine->Time() > _CampTime) + { + return true; + } + else if (_PrefAngle != QAngle(0.f, 0.f, 0.f)) + { + _SetBotAngle(_PrefAngle); + } } else { visibleTargetPos = visibleTarget.GetPos(); // Abort if enemy too near - if (/*visibleTarget &&*/ Util::DistanceToNoZ(bot->GetPos(), visibleTargetPos) < _ConVarHolder->CVarBotWeaponLongRangeDist->GetFloat()) + if (Util::DistanceToNoZ(botPos, visibleTargetPos) < _ConVarHolder->CVarBotWeaponLongRangeDist->GetFloat()) { return true; } @@ -49,12 +64,6 @@ bool BotTaskSniperSnipe::_OnThink() } } - // Zoom in if not already done so - if (!botInfo.IsSniperZoomedIn()) - { - _AddBotPressedButton(IN_ATTACK2); - } - _OverrideBotViewAngle(); if (visibleTarget.IsValid()) { diff --git a/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.h b/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.h index efa3a0b..0be8910 100644 --- a/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.h +++ b/Pongbot/Bot/Brain/Tasks/BotTaskSniperSnipe.h @@ -1,15 +1,20 @@ #pragma once #include "BotTask.h" +extern IVEngineServer* Engine; + class BotTaskSniperSnipe : public BotTask { public: - BotTaskSniperSnipe(Bot *bot) : BotTask(bot), _ShootTime(0.f) + BotTaskSniperSnipe(Bot *bot, int campTime = -1, QAngle prefAngle = {0.f, 0.f, 0.f}) : BotTask(bot), + _CampTime(Engine->Time() + campTime), _PrefAngle(prefAngle), _ShootTime(0.f) { _DoStuckPosPanicHandling = false; } private: + int _CampTime; + QAngle _PrefAngle; float _ShootTime; virtual bool _OnThink(); diff --git a/Pongbot/Bot/Visibles/BotVisibles.h b/Pongbot/Bot/Visibles/BotVisibles.h index 630d8be..dca0f03 100644 --- a/Pongbot/Bot/Visibles/BotVisibles.h +++ b/Pongbot/Bot/Visibles/BotVisibles.h @@ -37,22 +37,22 @@ struct BotVisibleTarget _Valid = target.IsValid(); } - Vector GetPos() const + inline Vector GetPos() const { return _Pos; } - BotTargetPriority GetPriority() const + inline BotTargetPriority GetPriority() const { return _Priority; } - Entity GetEntity() const + inline Entity GetEntity() const { return _Entity; } - bool IsValid() const + inline bool IsValid() const { return _Valid; } diff --git a/Pongbot/ConVarHolder.cpp b/Pongbot/ConVarHolder.cpp index bd02ee6..7514e03 100644 --- a/Pongbot/ConVarHolder.cpp +++ b/Pongbot/ConVarHolder.cpp @@ -32,6 +32,7 @@ void ConVarHolder::Init() _ConVarHolder->CVarBotEnableBrain = new ConVar("pongbot_bot_brain_enabled", "1", 0, "Bots should be able to think"); _ConVarHolder->CVarBotMedTargetDistance = new ConVar("pongbot_bot_med_targetdistance", "100.0", 0, "Distance med bots should have to heal target"); _ConVarHolder->CVarBotMovementIgnoreRadius = new ConVar("pongbot_bot_movement_ignoreradius", "5.0", 0, "Radius around bot where movement tasks should be ignored"); + _ConVarHolder->CVarBotSniperCampTime = new ConVar("pongbot_bot_sniper_camp_time", "60", 0, "Time in seconds after which snipers might consider moving to another camping spot (if one exists)"); } } @@ -58,6 +59,7 @@ void ConVarHolder::Destroy() delete _ConVarHolder->CVarBotEnableBrain; delete _ConVarHolder->CVarBotMedTargetDistance; delete _ConVarHolder->CVarBotMovementIgnoreRadius; + delete _ConVarHolder->CVarBotSniperCampTime; delete _ConVarHolder; _ConVarHolder = nullptr; diff --git a/Pongbot/ConVarHolder.h b/Pongbot/ConVarHolder.h index 88720a3..95fc64c 100644 --- a/Pongbot/ConVarHolder.h +++ b/Pongbot/ConVarHolder.h @@ -31,6 +31,7 @@ class ConVarHolder ConVar* CVarBotEnableBrain = nullptr; ConVar* CVarBotMedTargetDistance = nullptr; ConVar* CVarBotMovementIgnoreRadius = nullptr; + ConVar* CVarBotSniperCampTime = nullptr; }; extern ConVarHolder* _ConVarHolder; \ No newline at end of file diff --git a/Pongbot/Main.cpp b/Pongbot/Main.cpp index 98066b4..39bc1eb 100644 --- a/Pongbot/Main.cpp +++ b/Pongbot/Main.cpp @@ -61,7 +61,7 @@ bool Main::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool lat WaypointManager::Init(); BotManager::Init(); - Util::Log("Initialization done! (Took %.2f seconds)", Engine->Time() - initStartTime); + Util::Log("Initialization done! (Took %.0f ms)", (Engine->Time() - initStartTime) * 1000); Util::Log("!!! Don't forget to set sv_quota_stringcmdspersecond to some high value (e.g. 999999) to prevent crashes !!!"); return true; @@ -82,7 +82,7 @@ bool Main::Unload(char* error, size_t len) SH_REMOVE_HOOK(IServerGameDLL, GameFrame, Server, SH_MEMBER(this, &Main::_OnGameFrame), true); - Util::Log("Shutdown successful! (Took %.2f seconds)", Engine->Time() - destroyStartTime); + Util::Log("Shutdown successful! (Took %.0f ms)", (Engine->Time() - destroyStartTime) * 1000); return true; } diff --git a/Pongbot/TF2/Entity/Player.cpp b/Pongbot/TF2/Entity/Player.cpp index 805d363..babde7d 100644 --- a/Pongbot/TF2/Entity/Player.cpp +++ b/Pongbot/TF2/Entity/Player.cpp @@ -12,32 +12,17 @@ Player::Player(Entity entity) : Entity(entity), _IIPlayerInfo(IIPlayerInfoManage float Player::GetHealth() const { - if (!Exists()) - { - return -1; - } - - return _IIPlayerInfo->GetHealth(); + return Exists() ? _IIPlayerInfo->GetHealth() : -1; } float Player::GetFOV() const { - if (!Exists()) - { - return -1; - } - - return _EntityDataProvider->GetDataFromEntity(*this, DATA_PLAYER_FOV); + return Exists() ? _EntityDataProvider->GetDataFromEntity(*this, DATA_PLAYER_FOV) : -1; } bool Player::IsSniperZoomedIn() const { - if (!Exists()) - { - return false; - } - - return GetFOV() == 20.f; + return Exists() ? GetFOV() <= 21.f : false; } Vector Player::GetHeadPos() const diff --git a/Pongbot/Waypoint/WaypointNodeFlagTypes.h b/Pongbot/Waypoint/WaypointNodeFlagTypes.h index 056c82f..f9ef7f1 100644 --- a/Pongbot/Waypoint/WaypointNodeFlagTypes.h +++ b/Pongbot/Waypoint/WaypointNodeFlagTypes.h @@ -2,12 +2,14 @@ enum WaypointNodeFlagType { - NODE_SPAWN_RED = 1, - NODE_SPAWN_BLUE = 2, - NODE_ITEMFLAG_RED = 4, - NODE_ITEMFLAG_BLUE = 8, - NODE_HEALTH = 16, - NODE_AMMO = 32, - NODE_SENTRY_RED = 64, - NODE_SENTRY_BLUE = 128 + NODE_SPAWN_RED = (1 << 0), + NODE_SPAWN_BLUE = (1 << 1), + NODE_ITEMFLAG_RED = (1 << 2), + NODE_ITEMFLAG_BLUE = (1 << 3), + NODE_HEALTH = (1 << 4), + NODE_AMMO = (1 << 5), + NODE_SENTRY_RED = (1 << 6), + NODE_SENTRY_BLUE = (1 << 7), + NODE_SNIPER_CAMP_RED = (1 << 8), + NODE_SNIPER_CAMP_BLUE = (1 << 9) }; \ No newline at end of file diff --git a/Pongbot/Waypoint/WaypointNodeFlagsProvider.h b/Pongbot/Waypoint/WaypointNodeFlagsProvider.h index 760f790..73aa870 100644 --- a/Pongbot/Waypoint/WaypointNodeFlagsProvider.h +++ b/Pongbot/Waypoint/WaypointNodeFlagsProvider.h @@ -29,14 +29,16 @@ class WaypointNodeFlagsProvider private: std::map _WaypointNodeFlags = { - {NODE_SPAWN_RED, WaypointNodeFlagInfo("SPAWN_RED", "Inside red spawn zone")}, - {NODE_SPAWN_BLUE, WaypointNodeFlagInfo("SPAWN_BLUE", "Inside blue spawn zone")}, - {NODE_ITEMFLAG_RED, WaypointNodeFlagInfo("ITEMFLAG_RED", "Red CTF Flag spawns here")}, - {NODE_ITEMFLAG_BLUE, WaypointNodeFlagInfo("ITEMFLAG_BLUE", "Blue CTF Flag spawns here")}, - {NODE_HEALTH, WaypointNodeFlagInfo("HEALTH", "Health Pack spawns here")}, - {NODE_AMMO, WaypointNodeFlagInfo("AMMO", "Ammo Pack spawns here")}, - {NODE_SENTRY_RED, WaypointNodeFlagInfo("SENTRY_RED", "Red Engineers build sentries here")}, - {NODE_SENTRY_BLUE, WaypointNodeFlagInfo("SENTRY_BLUE", "Blue Engineers build sentries here")} + {NODE_SPAWN_RED, {"SPAWN_RED", "Inside red spawn zone"}}, + {NODE_SPAWN_BLUE, {"SPAWN_BLUE", "Inside blue spawn zone"}}, + {NODE_ITEMFLAG_RED, {"ITEMFLAG_RED", "Red CTF Flag spawns here"}}, + {NODE_ITEMFLAG_BLUE, {"ITEMFLAG_BLUE", "Blue CTF Flag spawns here"}}, + {NODE_HEALTH, {"HEALTH", "Health Pack spawns here"}}, + {NODE_AMMO, {"AMMO", "Ammo Pack spawns here"}}, + {NODE_SENTRY_RED, {"SENTRY_RED", "Red Engineers build sentries here"}}, + {NODE_SENTRY_BLUE, {"SENTRY_BLUE", "Blue Engineers build sentries here"}}, + {NODE_SNIPER_CAMP_RED, {"SNIPER_CAMP_RED", "Red Snipers camp here"}}, + {NODE_SNIPER_CAMP_BLUE, {"SNIPER_CAMP_BLUE", "Blue Snipers camp here"}} }; }; diff --git a/waypoints/ctf_turbine.pbw b/waypoints/ctf_turbine.pbw index c40ee30..8314738 100644 --- a/waypoints/ctf_turbine.pbw +++ b/waypoints/ctf_turbine.pbw @@ -21,10 +21,10 @@ 17:1006.95:102.074:56.0312:0:30:0:0:0:16:18:\ 18:861.34:94.8477:56.0312:0:30:0:0:0:17:19:\ 19:856.729:-85.122:56.0312:0:30:0:0:0:18:20:\ -20:709.84:-89.1551:48.0312:0:30:0:0:0:19:21:\ -21:681.026:39.1309:48.0312:0:40:0:0:0:20:22:\ +20:709.84:-89.1551:48.0312:0:30:0:0:0:19:21:115:\ +21:681.026:39.1309:48.0312:0:40:0:0:0:20:22:115:\ 22:680.148:181.022:48.0312:0:30:0:0:0:21:23:\ -23:677.909:471.126:48.0312:0:40:0:0:0:22:24:\ +23:677.909:471.126:48.0312:256:40:0:0:0:22:24:\ 24:671.323:643.121:48.0312:0:40:0:0:0:23:25:\ 25:342.4:643.338:-72.7648:0:40:0:0:0:24:26:\ 26:-26.427:639.328:-255.969:0:50:0:0:0:25:28:110:\ @@ -87,9 +87,9 @@ 83:-1138.45:-101.459:56.0312:0:30:0:0:0:82:84:\ 84:-857.537:-104.315:56.0312:0:30:0:0:0:83:85:\ 85:-864.726:93.8542:56.0312:0:20:0:0:0:84:86:\ -86:-679.366:84.4599:48.0312:0:30:0:0:0:85:87:\ +86:-679.366:84.4599:48.0312:0:30:0:0:0:85:87:118:\ 87:-681.165:-193.825:48.0312:0:30:0:0:0:86:88:\ -88:-681.823:-438.13:48.0312:0:40:0:0:0:87:89:\ +88:-681.823:-438.13:48.0312:512:40:0:0:0:87:89:\ 89:-673.528:-641.093:48.0312:0:30:0:0:0:88:90:\ 90:-416.271:-637.422:-35.8298:0:40:0:0:0:89:91:54:\ 91:5.70471:-636.186:-246.817:0:40:0:0:0:90:53:92:\ @@ -116,3 +116,9 @@ 112:572.144:75.1104:-255.969:0:100:0:0:0:108:113:\ 113:564.103:-163.888:-255.969:0:100:0:0:0:112:52:114:\ 114:473.963:-329.754:-255.969:0:70:0:0:0:113:53:52:\ +115:681.104:-195.527:48.0312:0:30:0:0:0:116:21:20:\ +116:682.984:-415.945:48.0312:256:30:0:0:0:117:115:\ +117:678.294:-629.805:48.0312:256:30:0:0:0:116:\ +118:-681.103:192.418:48.0312:0:30:0:0:0:86:119:\ +119:-682.241:369.452:48.0312:512:30:0:0:0:120:118:\ +120:-683.652:582.438:48.0312:512:30:0:0:0:119:\