Jump to content
dupa1

Save/restore map position by script

Recommended Posts

By default, when you open the map, it's centered around the player. I would like the map to stay at the position where it was when it was closed. I've set up a mission event handler to fire when the map is opened and closed, but I need some help finding out out how to save/restore the coordinates. Thanks in advance.

Share this post


Link to post
Share on other sites

Hi, if you want to restore last map position you can do it like this. This code will automatically update map position and when you open the map again, map will be set to latest position. You can of course change the variable names (MapCurrentPos and MapCurrentZoom) to whatever you like. I hope this will help you 🙂.

addMissionEventHandler ["Map", { 
 params ["_mapIsOpened", "_mapIsForced"];
if (_mapIsOpened) then {
 private _map = ((uiNamespace getVariable "RscDiary") displayCtrl 51);
mapAnimAdd [0,missionNamespace getVariable ["MapCurrentZoom",ctrlMapScale _map],missionNamespace getVariable ["MapCurrentPos",_map ctrlMapScreenToWorld [0.5, 0.5]]];
mapAnimCommit;
_map ctrlAddEventhandler ["Draw",{
params ["_map"];
MapCurrentPos = _map ctrlMapScreenToWorld [0.5, 0.5];
MapCurrentZoom = ctrlMapScale _map;
hintSilent format ["%1\nzoom:%2",MapCurrentPos,MapCurrentZoom];
}];
};
}];
  • Like 3

Share this post


Link to post
Share on other sites

Thank you for replying, I can not test it right now, but it looks like exactly what I need.

Share this post


Link to post
Share on other sites

Tested it, works amazingly. Thank you for helping!

Share this post


Link to post
Share on other sites

Here comes a blast from the past:

 

I've modified the code slightly. Now, pressing Insert key should enable/disable "Map Lock" - i.e. saving the map position. There is now a global variable called "mapLock", if it's true - map position is saved, if it is false - the map animation should be skipped and the map should be opened normally.

 

The problem: When I start a game locally (MP), everything works fine. When I upload to a dedicated server (it's a laptop right next to me) and connect to it, the map position saves even when mapLock is false. Even if I completely remove the map animation code, map position still saves. I suspect that the default map animation that's supposed to bring the map to player's position is interrupted somehow. From my experiments, just mentioning "ctrlMapScreenToWorld" or "ctrlMapScale" inside map "draw" EH causes this.

 

Also, is it necessary to add a new "draw" EH every time the map is opened or is one time enough?

 


mapLock = false;

waituntil {!(isNull (findDisplay 46))};
(FindDisplay 46) displayAddEventHandler ["keyDown",
{
  systemChat format ["Key: %1", _this select 1];
  //Ins key
  if ((_this select 1) == 210) then
  {
    mapLock = !mapLock;
    systemChat format ["Map lock : %1", mapLock];
    playSoundUI ["Orange_PhoneCall_Accept"];
  };
}];


addMissionEventHandler ["Map",
{
 params ["_mapIsOpened", "_mapIsForced"];
 if (_mapIsOpened) then
 {
   private _map = ((uiNamespace getVariable "RscDiary") displayCtrl 51);
   if (mapLock) then
   {
     mapAnimAdd [0,missionNamespace getVariable ["MapCurrentZoom",ctrlMapScale _map],missionNamespace getVariable ["MapCurrentPos",_map ctrlMapScreenToWorld [0.5, 0.5]]];
     mapAnimCommit;
   };
   _map ctrlAddEventhandler ["Draw",
   {
      params ["_map"];
      if (mapLock) then
      {
        MapCurrentPos = _map ctrlMapScreenToWorld [0.5, 0.5];
        MapCurrentZoom = ctrlMapScale _map;
      };
      hintSilent format ["%1\nzoom:%2",MapCurrentPos,MapCurrentZoom];
   }];
  };
}];

Share this post


Link to post
Share on other sites
Posted (edited)

Hi again.
You were right. When I looked at the code, and tested it now (a few months later) I noticed, when you wanted to switch the map on and off it behaved just as you described even in singleplayer.
Originally I wrote the code for just one execution so it would be consistent during whole mission.

10 hours ago, dupa1 said:

Also, is it necessary to add a new "draw" EH every time the map is opened or is one time enough?

I noticed draw eventhandler began to stack as it was created everytime map opened what must've created some performance issues later in the game. I tought when map would be closed, the event will stop firing, because control event is destroyed when display is closed and control returns "No control" (or controlNull which is the same). But 

((uiNamespace getVariable "RscDiary") displayCtrl 51);

still returns control so I guess RscDiary is not destroyed but instead is hidden and that caused the eventhandler to be added again and again to the same control. I guess map display is still opened but hidden through whole mission, and only shown when map key is pressed. Creating and destroying whole map display would not make sense from optimization perspective and that's completely understandable. So you were right, it was not neccessary to add it everytime and it SHOULD be added just one time.

I rewrote the code so now it should behave correctly. I separated the code to two functions Add and Remove. I believe now the code should behave as you originally wanted. Let me know if you need further help.

Spoiler

//Switchable version-press insert to see the result
soldierXXXX_fnc_MapRemPos_Add = {
scriptName "soldierXXXX_fnc_MapRemPos_Add";
MapEH_RemPos = addMissionEventHandler ["Map", {
params ["_mapIsOpened", "_mapIsForced"];
if (_mapIsOpened) then {
private _map = ((uiNamespace getVariable "RscDiary") displayCtrl 51);
//This will cause the map to move to latest saved position when map is opened
mapAnimAdd [0,missionNamespace getVariable ["MapCurrentZoom",ctrlMapScale _map],missionNamespace getVariable ["MapCurrentPos",_map ctrlMapScreenToWorld [0.5, 0.5]]];
mapAnimCommit;
};
}];

private _map = ((uiNamespace getVariable "RscDiary") displayCtrl 51);
DrawEH_RemPos = _map ctrlAddEventhandler ["Draw",{
params ["_map"];
//This will update variables each time map is rendered-basically each frame
MapCurrentPos = _map ctrlMapScreenToWorld [0.5, 0.5];
MapCurrentZoom = ctrlMapScale _map;
hintSilent format ["%1\nzoom:%2",MapCurrentPos,MapCurrentZoom];
}];
};

soldierXXXX_fnc_MapRemPos_Remove = {
scriptName "soldierXXXX_fnc_MapRemPos_Remove";
removeMissionEventHandler ["Map",MapEH_RemPos];
private _map = ((uiNamespace getVariable "RscDiary") displayCtrl 51);
_map ctrlRemoveEventHandler ["Draw",DrawEH_RemPos];
MapEH_RemPos = nil;
DrawEH_RemPos = nil;
MapCurrentPos = nil;
MapCurrentZoom = nil;
hintSilent "";
};

mapLock = false;//Initial state of map remembering state

waituntil {!(isNull (findDisplay 46))};
(FindDisplay 46) displayAddEventHandler ["keyDown",
{
  systemChat format ["Key: %1", _this select 1];
  //Ins key
  if ((_this select 1) == 210) then
  {
    mapLock = !mapLock;
    systemChat format ["Map lock : %1", mapLock];
    playSoundUI ["Orange_PhoneCall_Accept"];
	if (mapLock) then {[] spawn soldierXXXX_fnc_MapRemPos_Add;}
	             else {[] spawn soldierXXXX_fnc_MapRemPos_Remove;};
  };
}];
if (mapLock) then {[] spawn soldierXXXX_fnc_MapRemPos_Add;};//initialize the function if the script is started with mapLock = true;

 

 

Edited by soldierXXXX
Updated the code when initial mapLock is set to TRUE;

Share this post


Link to post
Share on other sites

Big thanks for the quick answer! I did this:


waitUntil {!isNull (findDisplay 12)};
(findDisplay 12 displayCtrl 51) ctrlAddEventhandler ["Draw",
{
  params ["_map"];
  systemChat format ["scale: %1", ctrlMapScale _map];
}];

 

Locally, it works fine - the scale displays and the map works as usual. When I run it on a dediceted server and connect as client, the map scale is displayed, but when the map is reopened, the position is NOT returned to the players area - it remains where it was closed - basically what I was trying to achieve lol. But I'm trying to figure out what the hell is going on, or at least find a way to achieve the goal without voodoo stuff. Like I said before, just mentioning ctrlMapPosition or ctrlMapScale inside "draw" EH causes this to happen.

 

So I assume that we would need to move the map pos/scale saving away from draw EH. 

Share this post


Link to post
Share on other sites

The code should run locally to each machine separately so if you already didn't, put the latest code to initPlayerLocal.sqf.  

2 hours ago, dupa1 said:

Like I said before, just mentioning ctrlMapPosition or ctrlMapScale inside "draw" EH causes this to happen.

Doubt it, Draw eventhandler is there just to update the position where you are looking before closing the map. It updates each time the map is rendered (so basically each frame). What actually causes the map to move is Map eventhandler, especially this part of the code:

mapAnimAdd [0,missionNamespace getVariable ["MapCurrentZoom",ctrlMapScale _map],missionNamespace getVariable ["MapCurrentPos",_map ctrlMapScreenToWorld [0.5, 0.5]]];
mapAnimCommit;

 

2 hours ago, dupa1 said:

So I assume that we would need to move the map pos/scale saving away from draw EH. 

That won't help at all. Actually this is the most efficient way i cound've come with. Putting variable updating like in each frame event would do basically the same thing except then the code would run even if map wouldn't be opened, so you would need to check each frame if map is opened or not. Creating separate function that would run in scheduler would take resources from other scripts and would also not be as efficient.
Please try my latest code and put it into initPlayerLocal.sqf, if the problem still persists please make a video of the issue, so we can see what is actually happening. Be aware that map zoom remains the same with or without the function running. Best case to test how it works is Altis map. Move the map to completely different area. When function is running it will return there, if not map returns near the player (never centers directly on the player).

Share this post


Link to post
Share on other sites

I understand the code. I understand it's difficult to believe, but again:

 

initPlayerLocal.sqf:


waitUntil {!isNull (findDisplay 12)};
(findDisplay 12 displayCtrl 51) ctrlAddEventhandler ["Draw",
{
  params ["_map"];
//  systemChat str (_map ctrlMapScreenToWorld [0.5, 0.5]);
  systemChat format ["scale: %1", ctrlMapScale _map];
}];

in editor I just put a player, exported to MP missions and uploaded to server.

 

Running it locally works fine.

Running it on dedicated server saves map position when reopening it. NO MAP ANIMATION CODE, just systemChat ctrlMapScale.  As I said in my post yesterday, I suspect mentioning it in "draw" EH somehow interferes with the default map animation that is supposed to move the map to the player area. This is some voodoo, I would really like to understand wtf is going on.

 

Edit: I've tried your latest code first thing when you replied

Share this post


Link to post
Share on other sites

I'll make a video. It'll take some time because I need to charge my phone, but it will show exactly what I described in words.

Share this post


Link to post
Share on other sites

I made a video. It's janky and the sound is crappy, but it demonstrates the problem.

 

Share this post


Link to post
Share on other sites

Hmmmm. That actually seems like a bug honestly. This code should not actually do anything like that. If I would be you, I would report it to feedback tracker. Seems like on dedicated server internal function that should return the map to player's position doesn't run. It's weird that it does that only when you write that code. Great video, that should help even developers to understand what's going on. I'm afraid i can't help you more, as I cannot see what's happening inside the engine. But definitely report it.

Share this post


Link to post
Share on other sites

The thing is, initPlayerLocal.sqf is executed on the client and not on the server. So I am really clueless about what's going on. However I am determined on making the "Map Lock" thingie work, so I will look for alternative methods of saving the map position and scale. If you have any suggestions, I'm all ears.

Share this post


Link to post
Share on other sites

Yes I'm aware that the code runs runs on client, but somehow when in multiplayer environment internal map function seems to act differently when you write that code. I don't really see how it might interfere like that, and if even is possible to write the code differently. You demonstrated it enough, and the best thing to do now is report it to feedback tracker. Seriously can't wrap my head around how can such simple code broke default function like that, makes no sense.

Share this post


Link to post
Share on other sites

Update: The code below had the same result, so it's not related to the "draw" EH. I reported the issue on the tracker like you recommended.

I really really appreciate all your help. Big thanks!!! I think I'll leave it for now.


mapLock = false;

waituntil {!(isNull (findDisplay 46))};
(FindDisplay 46) displayAddEventHandler ["keyDown",
{
  //Ins key
  if (_this select 1 == 210) then
  {
    mapLock = !mapLock;
    systemChat format ["Map Lock: %1", mapLock];
    playSoundUI ["Orange_PhoneCall_Accept"];
  };

  if (mapLock) then
  {
    [] spawn
    {
      while {mapLock} do
      {
        if visibleMap then
        {
          mapPos = (findDisplay 12 displayCtrl 51) ctrlMapScreenToWorld  [0.5,0.5];
          mapScale = ctrlMapScale (findDisplay 12 displayCtrl 51);
        };
        sleep .1;
      };
    }
  };
}];

addMissionEventHandler ["Map",
{
  params ["_mapIsOpened", "_mapIsForced"];
  if _mapIsOpened then
  {
    if (mapLock) then
    {
      //private _map = ((uiNamespace getVariable "RscDiary") displayCtrl 51);
      private _map = findDisplay 12 displayCtrl 51;
      mapAnimAdd
      [
        0,
        missionNamespace getVariable ["mapScale", ctrlMapScale _map],
        missionNamespace getVariable
        [
          "mapPos",
          _map ctrlMapScreenToWorld [0.5, 0.5]
        ]
      ];
      mapAnimCommit;
    };
  }
  else  //If map is closed
  {
  };
}];

  • Like 1

Share this post


Link to post
Share on other sites

So, I finally managed to get it working.... The end result is pretty simple actually.. well almost end result. There is one slight issue. When map lock is enabled, if you spam M, the map moves a bit down every time it's closed and opened. It's probably related to ctrlMapScreenToWorld[0.5, 0.5]. I think the map in ArmA 3 is not full screen, and the value of 0.5 needs to be adjusted. I think I'll figure it out, just wanted to say I got it working.

 

EDIT: Value of 0.53 for vertical offset seems to work for me. Code below updated.

 

mapLock = false;
mapPos = [1,1,1];
mapScale = 0.1;

waituntil {!(isNull (findDisplay 46))};
(FindDisplay 46) displayAddEventHandler ["keyDown",
{
  //Ins key
  if (_this select 1 == 210) then
  {
    mapLock = !mapLock;
    systemChat format ["Map Lock: %1", mapLock];
    playSoundUI ["Orange_PhoneCall_Accept"];
  };
}];

waituntil {!(isNull (findDisplay 12))};
findDisplay 12 displayCtrl 51 ctrlAddEventHandler ["Draw",
{
  params ["_map"];

  if (visibleMap) then
  {
    mapScale = ctrlMapScale _map;
    mapPos = _map ctrlMapScreenToWorld [0.5, 0.53];
  };
}];

addMissionEventHandler ["Map",
{
  params ["_mapIsOpened", "_mapIsForced"];
  if _mapIsOpened then
  {
    _pos = getPos player;
    _scale = missionNamespace getVariable ["mapScale", 0.1]; //Default value of 0.1 in case mapScale is nil
    if (mapLock) then {_pos = missionNamespace getVariable ["mapPos", getPos player]}; //Default value of player pos if mapPos is nil
    mapAnimAdd [0, _scale, _pos];
    mapAnimCommit;
  };
}];

 

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×