TODO: - Make boat map pins only become visible after you've visited them instead of just disabling them. - Make the big World Map Overworld region icons for the different regions only become visible after you've visited those regions.
I found your mod by searching for a mod that would make points of interest visible on the map when visiting them or by picking up posters on noticeboards that add points of interest. I haven't installed your mod, but from the description the second possibility hasn't been implemented. It would be awesome if you could add this, even more so if there is some distinction between these points of interest and the ones added when visiting them.
Sorry if the text seemed strange. I wrote it with the help of an automatic translator.
Hi, love the mod! Though annoyed with the boat pin situation. So... I made boat pins become visible upon visiting ^w^ Feel free to use this for an update to the mod if you wish!
They become visible when you first visit the boat, or, if you've already visited the boat before, once you get close enough for the interaction prompt to show up. Only works when the boat is within a 1m radius of the map pin, to avoid false positives in case there's boats without map pins. Connection between the boat & pin is purely through world position.
Have not tested with all boats, only a handful.
Add boat script to the mod (content\scripts\game\vehicles\boat\boat.ws). Inside OnAreaEnter event, add a variable: var mapPin : SCommonMapPinInstance;
And set the map pin under the if( player ) conditional: if ( TryGetMapPin( mapPin ) ) { FactsAdd('modTrueExploration'+(string)mapPin.id); }
And add this new function to the class (location doesn't matter, I added mine down at the bottom after GetCanBeDestroyed): function TryGetMapPin( out mapPin : SCommonMapPinInstance ) : bool { var i : int; var allPins : array< SCommonMapPinInstance >; var pin : SCommonMapPinInstance; var boatPins : array< SCommonMapPinInstance >; var position : Vector; var closestPin : SCommonMapPinInstance; var closestPinDistance : float;
allPins = theGame.GetCommonMapManager().GetMapPinInstances(theGame.GetWorld().GetPath()); for ( i = 0; i < allPins.Size(); i += 1 ) { pin = allPins[ i ];
if ( pin.type == 'Boat' ) { boatPins.PushBack(pin); } }
position = GetWorldPosition(); closestPinDistance = -1; for ( i = 0; i < boatPins.Size(); i += 1 ) { pin = boatPins[ i ];
if ( closestPinDistance < 0 || VecDistanceSquared2D( pin.position, position ) < closestPinDistance ) { closestPinDistance = VecDistanceSquared2D( pin.position, position ); closestPin = pin; } }
event OnAreaEnter( area : CTriggerAreaComponent, activator : CComponent ) { var player : CR4Player; var mapPin : SCommonMapPinInstance;//modtrueexploration
player = (CR4Player)activator.GetEntity();
if( player ) { //modtrueexploration++ if ( TryGetMapPin( mapPin ) ) { FactsAdd('modTrueExploration'+(string)mapPin.id); } //modtrueexploration--
timer function DrowningDismount( dt : float, id : int ) { if( !boatComp ) { LogBoatFatal( "Entity doesn't have boat component." ); return; }
boatComp.IssueCommandToDismount( DT_normal ); }
import final function HasDrowned() : bool; import final function SetHasDrowned( val : bool );
event OnStreamIn() { }
import final function SetTeleportedFromOtherHUB( val : bool );
public function ToggleInteraction( enable : bool ) { var components: array< CComponent >; var i : int; var inter : CInteractionComponent; components = GetComponentsByClassName( 'CInteractionComponent' );
for( i=0; i<components.Size(); i+=1 ) { inter = (CInteractionComponent)components[i];
if( inter ) { inter.SetEnabled( enable ); } } }
public function GetBoatComponent() : CBoatComponent { return boatComp; }
public function GetMountInteractionComponent( optional forPassenger : bool ) : CInteractionComponent { if( forPassenger ) return mountInteractionCompPassenger; else return mountInteractionComp; }
public function SetCanBeDestroyed( val : bool ) { canBeDestroyed = val; } public function GetCanBeDestroyed() : bool { return canBeDestroyed; }
//modtrueexploration++ function TryGetMapPin( out mapPin : SCommonMapPinInstance ) : bool { var i : int; var allPins : array< SCommonMapPinInstance >; var pin : SCommonMapPinInstance; var boatPins : array< SCommonMapPinInstance >; var position : Vector; var closestPin : SCommonMapPinInstance; var closestPinDistance : float;
allPins = theGame.GetCommonMapManager().GetMapPinInstances(theGame.GetWorld().GetPath()); for ( i = 0; i < allPins.Size(); i += 1 ) { pin = allPins[ i ];
if ( pin.type == 'Boat' ) { boatPins.PushBack(pin); } }
position = GetWorldPosition(); closestPinDistance = -1; for ( i = 0; i < boatPins.Size(); i += 1 ) { pin = boatPins[ i ];
if ( closestPinDistance < 0 || VecDistanceSquared2D( pin.position, position ) < closestPinDistance ) { closestPinDistance = VecDistanceSquared2D( pin.position, position ); closestPin = pin; } }
In content/script/local/TrueExploration.ws, add an if check for boat pins inside ModTrueExplorationPinShouldBeVisible: else if(pin.type == 'Boat') { return FactsDoesExist('modTrueExploration'+(string)pin.id); } The resulting file should look like this:
Spoiler:
Show
/** * Given a mappin returns false if it corresponds a noticeboard you haven't visited yet, * , an unvisited point of interest (POI)/question mark icon, a usable boat, a player stash. Otherwise returns true. * If the map pin belongs to a noticeboard its map icon is changed to the basic white/questless version. * * @param pin the map pin to be tested and updated. * @return false if the map pin shouldn't be displayed on the map, true otherwise. */ function ModTrueExplorationPinShouldBeVisible(out pin : SCommonMapPinInstance): bool { var board : W3NoticeBoard;
Knew nothing going in other than general coding things. Looked at how the mod does things, how the vanilla game does things, figured things out as I went.
My process was something like
How does the mod do it for other things? Uses this FactsAdd & FactsDoesExist stuff for storage for PlayerStash, I'll use that.
It uses entityName & pin.tag to link stash to its pin, is that a thing I can use? *tests* Nope, boat map pins don't seem to have a tag, and nor do boats have an entityName... (or if they did it was the same for all, I don't remember anymore.)
Since boat map pins don't have a tag, how do I uniquely identify the boat map pins? *finds that map pins have an id, problem solved*
How do vanilla discoverable things do it? Signposts and whatnot? Oh, they inherit from an entirely different class, nope this doesn't work... Boats do have OnAreaEnter just like they do though, maybe I can use that somehow. *adds a notification upon entering the boat's area*
Maybe I can match them by coordinates? *adds the boat's coordinates to the notification & map pins' coordinates to their description on the map, checks ingame* Cool, the boat & the map pin have almost the exact same coordinates.
Are coordinates global, or per worldspace? *adds coordinates to some other map pins & compares White Orchard and Velen* Per worldspace, middle seems to be 0 0...
I need the boat's worldspace, how do I get it? *can't find out, but does find how to get current world which should work fine too*
How do I get all the map pins of a worlspace? I'll need to loop through them to figure out a match. *looks around in vanilla code & finds out*
How do I make a filtered list in this programming language? *looks around in vanilla code & finds out* Alright now I'll use that to make a list of boat pins in the current world.
Can I calculate the distance between 2 vectors easily? (Vectors are what's used for coordinates.) *finds functions in a math class* Cool, I'll use the 2D squared one to find the closest map pin to the boat. (I know from past experience squared is way more performant when it comes to vectors, you don't need exact distance for finding the closest match.)
Let's implement this with the FactsAdd & FactsDoesExist stuff... *tests* Nice, it's working!
I'm not sure each boat has a map pin. If one doesn't, entering its area would "discover" the map pin of whatever other boat map pin happens to be the closest. Should probably restrict the closest pin to have to be very close so that can't happen... *tests ingame to see how far boat map pins generally are from the boat* These are all less than 1mm away, 1m should be more than enough for outliers that might be a bit further.
Thanks for this mod, it is a small improvement but essential for us "old gamers" who enjoy a more "Morrowind-like" exploration if you like the comparison.
Hopefully, the bug of never appearing boats could be fixed in a future update, or at least give the option to not disable them entirely (a little immersion-breaking, but a little better than not having them at all even after being discovered. The boat is a "minor" thing on the map and not really a POI that gives you role-playing and exploration value)
Also, I see that it is marked as not compatible with "Additional Stash Locations", what does this mean in practice? How will they interact?
Anyway, thank for all of your mods, they are improving the game way more than all the amazing ultra HD graphical stuff ;)
The map pins for the additional stashes introduced by that mod simply don't show up on the map, even after "unlocking" or visiting those stashes. I haven't looked into why that is though I suspect they simply didn't follow the same stash entity name-> map pin name rule as vanilla which this mod relies on.
Please help. I installed this mod to remove visited POIs, but now, even after removing the mod, the POIs don't stay after I revisit them. Even verifying the game files didn't help
So, I used this mod for a bit, then decided to uninstall it (because I'm a completionist). However, map markers are not reverting to vanilla. They are still hidden! Now what?
EDIT: Mhh, this is weird. At first I thought harbors weren't working at all. I discovered one and it wasn't saved on the map. Then I tried again, and now the major harbors in Skellige and Valen (plus I think the ones I discovered on my own) are showing. But only when I look at the map when at the helm of a boat. And I have to open, close and open the map a second time. So something is definitely screwy. And it isn't really in the spirit of the mod, but fine with me. Certainly a lot better than not being able to fast travel at all.
These books, and they're not the only ones that add information and map markers for the skellige isles. Are you sure it's really all harbors unlocked and not just a few?
I just tested it out with someone's save on nexus from right before skellige and I can't reproduce the issue you're having: there were only a handful of already unlocked harbors when I got to skellige (out of 17 total) presumably from information gathered and books read before getting there - certainly not all of them.
I can look into disabling map pin info gathering from books - but it probably wouldn't be retroactive and wouldn't solve some unrelated issue with all harbors being unlocked.
These books unlock harbors indeed, but the major islands are still get unlocked without any reading, just tested with my first time arriving save file without reading any books about Skellige. Maybe some other books or dialogues unlock them, but I'd rather do that by exploring anyway, otherwise it feels like cheating.
50 comments
TODO:
- Make boat map pins only become visible after you've visited them instead of just disabling them.
- Make the big World Map Overworld region icons for the different regions only become visible after you've visited those regions.
Sorry if the text seemed strange. I wrote it with the help of an automatic translator.
I made boat pins become visible upon visiting ^w^ Feel free to use this for an update to the mod if you wish!
They become visible when you first visit the boat, or, if you've already visited the boat before, once you get close enough for the interaction prompt to show up.
Only works when the boat is within a 1m radius of the map pin, to avoid false positives in case there's boats without map pins. Connection between the boat & pin is purely through world position.
Have not tested with all boats, only a handful.
Inside OnAreaEnter event, add a variable:
var mapPin : SCommonMapPinInstance;
And set the map pin under the if( player ) conditional:
if ( TryGetMapPin( mapPin ) )
{
FactsAdd('modTrueExploration'+(string)mapPin.id);
}
And add this new function to the class (location doesn't matter, I added mine down at the bottom after GetCanBeDestroyed):
function TryGetMapPin( out mapPin : SCommonMapPinInstance ) : bool
{
var i : int;
var allPins : array< SCommonMapPinInstance >;
var pin : SCommonMapPinInstance;
var boatPins : array< SCommonMapPinInstance >;
var position : Vector;
var closestPin : SCommonMapPinInstance;
var closestPinDistance : float;
allPins = theGame.GetCommonMapManager().GetMapPinInstances(theGame.GetWorld().GetPath());
for ( i = 0; i < allPins.Size(); i += 1 )
{
pin = allPins[ i ];
if ( pin.type == 'Boat' )
{
boatPins.PushBack(pin);
}
}
position = GetWorldPosition();
closestPinDistance = -1;
for ( i = 0; i < boatPins.Size(); i += 1 )
{
pin = boatPins[ i ];
if ( closestPinDistance < 0 || VecDistanceSquared2D( pin.position, position ) < closestPinDistance )
{
closestPinDistance = VecDistanceSquared2D( pin.position, position );
closestPin = pin;
}
}
closestPinDistance = VecDistance2D(closestPin.position, position);
if(closestPinDistance < 1.0f)
{
mapPin = closestPin;
return true;
}
else
{
return false;
}
}
The resulting file should look something like this:
/***********************************************************************/
/** © 2015 CD PROJEKT S.A. All rights reserved.
/** THE WITCHER® is a trademark of CD PROJEKT S. A.
/** The Witcher game is based on the prose of Andrzej Sapkowski.
/***********************************************************************/
import class W3Boat extends CGameplayEntity
{
private autobind boatComp : CBoatComponent = single;
private autobind mountInteractionComp : CInteractionComponent = "mountExplorationInteraction";
private autobind mountInteractionCompPassenger : CInteractionComponent = "mountExplorationInteractionPassenger";
private saved var canBeDestroyed : bool; default canBeDestroyed = true;
private var needEnableInteractions: bool; default needEnableInteractions = false;
event OnStreamOut()
{
if(theGame.IsBoatMarkedForDestroy(this))
{
AddTimer( 'DelayedDestroyBoat', 0.1f );
}
}
timer function DelayedDestroyBoat( td : float , id : int)
{
RemoveTimer( 'DelayedDestroyBoat' );
Destroy();
}
event OnAreaEnter( area : CTriggerAreaComponent, activator : CComponent )
{
var player : CR4Player;
var mapPin : SCommonMapPinInstance;//modtrueexploration
player = (CR4Player)activator.GetEntity();
if( player )
{
//modtrueexploration++
if ( TryGetMapPin( mapPin ) )
{
FactsAdd('modTrueExploration'+(string)mapPin.id);
}
//modtrueexploration--
if( area.GetName() == "FirstDiscoveryTrigger" )
{
area.SetEnabled( false );
}
else if( area.GetName() == "OnBoatTrigger" )
{
player.SetIsOnBoat( true );
player.BlockAction( EIAB_RunAndSprint, 'OnBoatTrigger', false, false, true );
player.BlockAction( EIAB_CallHorse, 'OnBoatTrigger', false, false, true );
if( !HasDrowned() )
{
needEnableInteractions = true;
if( mountInteractionComp )
mountInteractionComp.SetEnabled( true );
if( mountInteractionCompPassenger && boatComp.user )
mountInteractionCompPassenger.SetEnabled( true );
}
}
}
}
event OnAreaExit( area : CTriggerAreaComponent, activator : CComponent )
{
var player : CR4Player;
player = (CR4Player)activator.GetEntity();
if( player )
{
if( area.GetName() == "OnBoatTrigger" )
{
player.SetIsOnBoat( false );
player.UnblockAction( EIAB_RunAndSprint, 'OnBoatTrigger' );
player.UnblockAction( EIAB_CallHorse, 'OnBoatTrigger' );
player.SetBehaviorVariable( 'bRainStormIdleAnim', 0.0 );
needEnableInteractions = false;
if( mountInteractionComp )
mountInteractionComp.SetEnabled( false );
if( mountInteractionCompPassenger )
mountInteractionCompPassenger.SetEnabled( false );
}
}
}
event OnInteractionActivationTest( interactionComponentName : string, activator : CEntity )
{
if( ( interactionComponentName == "mountExplorationInteraction" || interactionComponentName == "mountExplorationInteractionPassenger" ) && activator == thePlayer && !HasDrowned() )
{
if( !thePlayer.IsActionAllowed( EIAB_MountVehicle ) )
return false;
return true;
}
}
event OnInteractionAttached( interaction : CInteractionComponent )
{
interaction.SetEnabled( needEnableInteractions );
}
timer function DrowningDismount( dt : float, id : int )
{
if( !boatComp )
{
LogBoatFatal( "Entity doesn't have boat component." );
return;
}
boatComp.IssueCommandToDismount( DT_normal );
}
import final function HasDrowned() : bool;
import final function SetHasDrowned( val : bool );
event OnStreamIn()
{
}
import final function SetTeleportedFromOtherHUB( val : bool );
public function ToggleInteraction( enable : bool )
{
var components: array< CComponent >;
var i : int;
var inter : CInteractionComponent;
components = GetComponentsByClassName( 'CInteractionComponent' );
for( i=0; i<components.Size(); i+=1 )
{
inter = (CInteractionComponent)components[i];
if( inter )
{
inter.SetEnabled( enable );
}
}
}
public function GetBoatComponent() : CBoatComponent
{
return boatComp;
}
public function GetMountInteractionComponent( optional forPassenger : bool ) : CInteractionComponent
{
if( forPassenger )
return mountInteractionCompPassenger;
else
return mountInteractionComp;
}
public function SetCanBeDestroyed( val : bool ) { canBeDestroyed = val; }
public function GetCanBeDestroyed() : bool { return canBeDestroyed; }
//modtrueexploration++
function TryGetMapPin( out mapPin : SCommonMapPinInstance ) : bool
{
var i : int;
var allPins : array< SCommonMapPinInstance >;
var pin : SCommonMapPinInstance;
var boatPins : array< SCommonMapPinInstance >;
var position : Vector;
var closestPin : SCommonMapPinInstance;
var closestPinDistance : float;
allPins = theGame.GetCommonMapManager().GetMapPinInstances(theGame.GetWorld().GetPath());
for ( i = 0; i < allPins.Size(); i += 1 )
{
pin = allPins[ i ];
if ( pin.type == 'Boat' )
{
boatPins.PushBack(pin);
}
}
position = GetWorldPosition();
closestPinDistance = -1;
for ( i = 0; i < boatPins.Size(); i += 1 )
{
pin = boatPins[ i ];
if ( closestPinDistance < 0 || VecDistanceSquared2D( pin.position, position ) < closestPinDistance )
{
closestPinDistance = VecDistanceSquared2D( pin.position, position );
closestPin = pin;
}
}
closestPinDistance = VecDistance2D(closestPin.position, position);
if(closestPinDistance < 1.0f)
{
mapPin = closestPin;
return true;
}
else
{
return false;
}
}
//modtrueexploration--
}
else if(pin.type == 'Boat')
The resulting file should look like this:{
return FactsDoesExist('modTrueExploration'+(string)pin.id);
}
/**
* Given a mappin returns false if it corresponds a noticeboard you haven't visited yet,
* , an unvisited point of interest (POI)/question mark icon, a usable boat, a player stash. Otherwise returns true.
* If the map pin belongs to a noticeboard its map icon is changed to the basic white/questless version.
*
* @param pin the map pin to be tested and updated.
* @return false if the map pin shouldn't be displayed on the map, true otherwise.
*/
function ModTrueExplorationPinShouldBeVisible(out pin : SCommonMapPinInstance): bool
{
var board : W3NoticeBoard;
if(pin.type !='NoticeBoard'
&& pin.type !='Boat'
&& pin.type!='PlayerStash'
&& pin.visibleType!='NotDiscoveredPOI'
)
{
return true;
}
if(pin.type == 'NoticeBoard')
{
board = (W3NoticeBoard)theGame.GetEntityByTag( pin.tag );
if(board)
{
//if you want noticeboards to only appear white/questless just remove the "//" double slashes from the line below.
//pin.visibleType = 'NoticeBoard';
return board.WasVisited();
}
}
else if(pin.type == 'PlayerStash')
{
return FactsDoesExist('modTrueExploration'+NameToString(pin.tag));
}
else if(pin.type == 'Boat')
{
return FactsDoesExist('modTrueExploration'+(string)pin.id);
}
return false;
}
My process was something like
On encountering a boat,
Then in the mod's existing code where it checks if a map pin should be visible, add handling for boat pins, again following example of player stashes.
Hopefully, the bug of never appearing boats could be fixed in a future update, or at least give the option to not disable them entirely (a little immersion-breaking, but a little better than not having them at all even after being discovered. The boat is a "minor" thing on the map and not really a POI that gives you role-playing and exploration value)
Also, I see that it is marked as not compatible with "Additional Stash Locations", what does this mean in practice? How will they interact?
Anyway, thank for all of your mods, they are improving the game way more than all the amazing ultra HD graphical stuff ;)
Mhh, this is weird. At first I thought harbors weren't working at all. I discovered one and it wasn't saved on the map. Then I tried again, and now the major harbors in Skellige and Valen (plus I think the ones I discovered on my own) are showing. But only when I look at the map when at the helm of a boat. And I have to open, close and open the map a second time. So something is definitely screwy. And it isn't really in the spirit of the mod, but fine with me. Certainly a lot better than not being able to fast travel at all.
Is this intentional?
Velen is fine.
Just travelled there doing the main quest, got on a boat and opened a map.
I just tested it out with someone's save on nexus from right before skellige and I can't reproduce the issue you're having: there were only a handful of already unlocked harbors when I got to skellige (out of 17 total) presumably from information gathered and books read before getting there - certainly not all of them.
I can look into disabling map pin info gathering from books - but it probably wouldn't be retroactive and wouldn't solve some unrelated issue with all harbors being unlocked.
return board.WasVisited();
with
return true;