Scripters cove

Discussion and information relevant to creating special missions, new ships, skins etc.

Moderators: winston, another_commander

User avatar
Rorschachhamster
---- E L I T E ----
---- E L I T E ----
Posts: 274
Joined: Sun Aug 05, 2012 11:46 pm
Contact:

Re: Scripters cove

Post by Rorschachhamster »

I made this AI for my freighttrain. The idea is, that it flies to a random station and then searchs for another random station to fly to... but if I check the destination in the debug console, it's changing it constantly... so it just flies around and almost ever into empty space in something that looks like a very big circle. :?
Help! :P

Code: Select all

{
    GLOBAL = {		
	ENTER = ("randomPauseAI: 1.0 2.0");
	UPDATE = ("setStateTo: SETUP_FLIGHT");
	}; 
	
	"SETUP_FLIGHT" = {
	ENTER = ("setDesiredRangeTo: 30000000.0", setTargetToRandomStation);
	"STATION_FOUND" = (setDestinationToTarget, "setDesiredRangeTo: 30000.0", "setStateTo: HEAD_FOR_RANDOMSTATION");
	"NO_STATION_IN_RANGE" = (setCourseToPlanet, "setDesiredRangeTo: 30000.0", "setStateTo: HEAD_FOR_RANDOMSTATION");
	UPDATE = ();
	};
	
    	
    FLEEING = {
        ENTER = ("setDesiredRangeTo: 25600", performFlee); 
        "ESCORT_ATTACKED" = (setTargetToPrimaryAggressor, groupAttackTarget);
        "DESIRED_RANGE_ACHIEVED" = ("setStateTo: HEAD_FOR_RANDOMSTATION"); 
        "INCOMING_MISSILE" = (setTargetToPrimaryAggressor, groupAttackTarget, "setDesiredRangeTo: 25600", performFlee); 
        "REACHED_SAFETY" = ("setStateTo: HEAD_FOR_RANDOMSTATION"); 
        "ATTACKED" = (setTargetToPrimaryAggressor, broadcastDistressMessage, groupAttackTarget, "setDesiredRangeTo: 25600", performFlee);
        "GROUP_ATTACK_TARGET" = (setTargetToPrimaryAggressor, fightOrFleeHostiles, fightOrFleeMissile, broadcastDistressMessage, "setDesiredRangeTo: 25600", performFlee);
        "TARGET_LOST" = ("setStateTo: HEAD_FOR_RANDOMSTATION"); 
        "TARGET_DESTROYED" = ("setStateTo: HEAD_FOR_RANDOMSTATION");
        UPDATE = (); 
    }; 

    	
	"HEAD_FOR_RANDOMSTATION" = {
	ENTER = (performStop);
	"COURSE_OK" = ("setSpeedFactorTo: 0.5", performFlyToRangeFromDestination);
	"DESIRED_RANGE_ACHIEVED" = ("pauseAI: 30.0", "setStateTo: SETUP_FLIGHT");
    "INCOMING_MISSILE" = (setTargetToPrimaryAggressor, fightOrFleeHostiles, fightOrFleeMissile, "setStateTo: FLEEING");
	"ATTACKED" = (setTargetToPrimaryAggressor, fightOrFleeHostiles, fightOrFleeMissile, "setStateTo: FLEEING"); 
	"ESCORT_ATTACKED" = (setTargetToPrimaryAggressor, groupAttackTarget);
	"GROUP_ATTACK_TARGET" = (setTargetToPrimaryAggressor, fightOrFleeHostiles, fightOrFleeMissile, "setStateTo: FLEEING");
	UPDATE = ("setDesiredRangeTo: 30000.0", checkCourseToDestination, scanForHostiles, "pauseAI: 10.0");
	}; 
}
Please? :cry:
User avatar
Smivs
Retired Assassin
Retired Assassin
Posts: 8408
Joined: Tue Feb 09, 2010 11:31 am
Location: Lost in space
Contact:

Re: Scripters cove

Post by Smivs »

You have a loop in the AI. In "SETUP_FLIGHT" whether you find a station or not, you set the state to "HEAD_FOR_RANDOMSTATION". In this state you then "performStop" but also tell the ship to fly to range from destination, and when the desired range is achieved, you switch back to "SETUP_FLIGHT" and the whole thing starts again.
There are other issues as well, but that's probably why your ship is flying around in circles.
Commander Smivs, the friendliest Gourd this side of Riedquat.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Re: Scripters cove

Post by Eric Walch »

No direct relation to the problem but in the last UPDATE you must use:

Code: Select all

			setTargetToLastStation,
			"setDesiredRangeTo: 30000.0",
			setDestinationToTarget,
This to make sure you go to the last random station. Or you will go wrong after an attack.
User avatar
Rorschachhamster
---- E L I T E ----
---- E L I T E ----
Posts: 274
Joined: Sun Aug 05, 2012 11:46 pm
Contact:

Re: Scripters cove

Post by Rorschachhamster »

Thank you.

I'm still trying to understand this - performStop deletes the set destination? :?: (Would make sense, now that I think about it... :? )
Because it is thought to loop, after arriving at the destination, to get a new target station. The thing is, it almost always finds a station, says debug console... and if not, it is supposed to fly to the planet and then pick another station (at least the system station should always be in this 3000000 range...)

@eric - Hmm, I had setTargetToLastStation somewhere in, before trying to do it another way... :roll: :lol: Oh, and would the planet check as station to setTargetToLastStation?
I'll make that the main station for now... could make proplems if there is no other station near it... :|

I'll try it. Be right back! :wink:
User avatar
Rorschachhamster
---- E L I T E ----
---- E L I T E ----
Posts: 274
Joined: Sun Aug 05, 2012 11:46 pm
Contact:

Re: Scripters cove

Post by Rorschachhamster »

Sorry for the double post, but I can't get it right. I have changed it now and it seems to do what I want .... only it doesn't.
This is what the AI logs:

Code: Select all

19:51:59.088 [ai.takeAction]: Traction Engine 401 to take action setTargetToLastStation
19:51:59.088 [ai.takeAction]: Traction Engine 401 to take action setDesiredRangeTo: 30000.0
19:51:59.088 [ai.takeAction]: Traction Engine 401 to take action setDestinationToTarget
19:51:59.088 [ai.takeAction]: Traction Engine 401 to take action checkCourseToDestination
19:51:59.089 [ai.takeAction]: Traction Engine 401 to take action scanForHostiles
19:51:59.089 [ai.message.receive]: AI Freighttrain_AIs.plist for Traction Engine 401 in state 'HEAD_FOR_RANDOMSTATION' receives message 'NOTHING_FOUND'. Context: handling deferred message, stack depth: 0
19:51:59.089 [ai.message.receive]: AI Freighttrain_AIs.plist for Traction Engine 401 in state 'HEAD_FOR_RANDOMSTATION' receives message 'COURSE_OK'. Context: handling deferred message, stack depth: 0
19:51:59.089 [ai.takeAction]: Traction Engine 401 to take action setSpeedFactorTo: 0.5
19:51:59.089 [ai.takeAction]: Traction Engine 401 to take action setDesiredRangeTo: 30000.0
19:51:59.089 [ai.takeAction]: Traction Engine 401 to take action performFlyToRangeFromDestination
19:51:59.089 [ai.takeAction]: Traction Engine 401 to take action pauseAI: 10.0
...and repeat.

"NOTHING_FOUND" is from scanForHostiles? Because if I look at the target in the debug console it shows always some station or another. But... it doesn't go there. Does a ship not fly in a straight line towards it's destination? And "Course_OK" comes from the checkCourseToDestination, so it should work, as the target is a station, it sets the target as destination and performFlyToRangeFromDestination fires, too? :shock: :? :roll: :evil: :x :cry: :lol:
Because if I check the destination in the debug console it changes constantly and is not the target station. Wich could be perfectly normal. For all I know. :wink:
So a little help, please?
User avatar
GGShinobi
---- E L I T E ----
---- E L I T E ----
Posts: 291
Joined: Tue Dec 25, 2012 7:20 pm

Re: Scripters cove

Post by GGShinobi »

Sorry Rohrschachhamster, but I can't help you on this. :(

I've got some problem of my own, or rather some strange behaviour I want to report.

Since subentities can't be restored selectively but only all together / at once, in order to simulate the repair of only a single subentity with my External Repair System (ERS), I wrote a function which destroys all subentites that have not been repaired by the ERS after I called player.ship.restoreSubEntities(). Or at least it ought to do this (it does now in my updated version), but it didn't work properly and it took me some time to find out why.

Here is the original code:

Code: Select all

this.$setupSubentities = function() {
  var damagedSubents = $getDamagedSubentsAsString();
  $showDebugInfo("damagedSubents: " + damagedSubents, 1);

  for (var x in player.ship.subEntities) {
    var subentity = player.ship.subEntities[x];
    $showDebugInfo("setting up subEnt(" + x + "): " + subentity, 1);
    // if subentity is marked as damaged, remove it / damage it again (it might just have been restored by "restoreSubentities()")
    if (damagedSubents.indexOf("" + subentity) > -1) {
      $showDebugInfo("removing damaged subentity(" + x + "): " + subentity, 1);
      player.ship.subEntities[x].remove(true); // true: suppress death event
    } else {
      // subentity is operational => prepare subentity for ERS by setting up "subentity died" functions
      $showDebugInfo("preparing subentity for ERS repairs(" + x + "): " + subentity, 1);
      // remember original shipDied() in order to be able to start it later:
      player.ship.subEntities[x].script._shipDiedOld = player.ship.subEntities[x].script.shipDied;
      player.ship.subEntities[x].script.shipDied = function(whom, why) {
        worldScripts["GGIndustries_ERS_MainScript.js"].$notifyERS(this.ship, why); // let ERS know that subentity has been destroyed
        if (this._shipDiedOld) {
          this._shipDiedOld(whom, why);
        }
      }
    }
  }
}
The part which ought to remove all subentities that are known to be damaged didn't work. Only some of the subentities where removed, but approximately half of it was skipped. :!: Through many experiments I came to the conclusion that the reason for this is that the for-loop

Code: Select all

  for (var x in player.ship.subEntities) {
doesn't grant access to all members of player.ship.subEntities because the line

Code: Select all

player.ship.subEntities[x].remove(true);
changes the array so that player.ship.subEntities[x] varies over the loop-iterations. So, for instance, let's say there are seven subentities marked as damaged of a total of 8 subentites, what happens is that player.ship.subentites[0] to player.ship.subEntities[4] still return a subEntity, but player.ship.subEntites[5] does not because the array has already been shrinked by "subEntites[x].remove()". Therefore, approximately half of the subEntites which ought to get removed will stay alive.

:idea: My quick workaround for this is to only mark the subEntities for removal as long as I'm inside the for-loop, and to remove them after I left it. That way it works!

Here is the working version:

Code: Select all

this.$setupSubentities = function() {
  var damagedSubents = $getDamagedSubentsAsString();
  $showDebugInfo("damagedSubents: " + damagedSubents, 1);

  var subents2Remove = new Array();
  for (var x in player.ship.subEntities) {
    var subentity = player.ship.subEntities[x];
    $showDebugInfo("setting up subEnt(" + x + "): " + subentity, 1);
    // if subentity is marked as damaged, remove it / damage it again (it might just have been restored by "restoreSubentities()")
    if (damagedSubents.indexOf("" + subentity) > -1) {
      $showDebugInfo("removing damaged subentity(" + x + "): " + subentity, 1);
      // player.ship.subEntities[x].remove(true); // true: suppress death event // can't do it immediately because this will change player.ship.subEntities, preventing access to all
      subents2Remove.push(player.ship.subEntities[x]);
    } else {
      // subentity is operational => prepare subentity for ERS by setting up "subentity died" functions
      $showDebugInfo("preparing subentity for ERS repairs(" + x + "): " + subentity, 1);
      // remember original shipDied() in order to be able to start it later:
      player.ship.subEntities[x].script._shipDiedOld = player.ship.subEntities[x].script.shipDied;
      player.ship.subEntities[x].script.shipDied = function(whom, why) {
        worldScripts["GGIndustries_ERS_MainScript.js"].$notifyERS(this.ship, why); // let ERS know that subentity has been destroyed
        if (this._shipDiedOld) {
          this._shipDiedOld(whom, why);
        }
      }
    }
  }

  // remove damaged subEntities:
  for (var x in subents2Remove) {
    $showDebugInfo("subents2Remove(" + x + "): " + subents2Remove[x], 1);
    subents2Remove[x].remove(true); // true: suppress death event
  }
}
Is this behaviour of subEntities.remove() intentional? It sure makes sense, but for writers of OXP's which think they can remove all subEnts in a single for-loop this might come quite unexpected (as it was for me). :roll: :?:
忍 knowing that enough is enough, you'll always have enough.

Running Oolite 1.77 on Ubuntu Linux 12.04 LTS
User avatar
cim
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 4072
Joined: Fri Nov 11, 2011 6:19 pm

Re: Scripters cove

Post by cim »

GGShinobi wrote:
Is this behaviour of subEntities.remove() intentional? It sure makes sense, but for writers of OXP's which think they can remove all subEnts in a single for-loop this might come quite unexpected (as it was for me). :roll: :?:
As a general rule, removing elements from the array you're currently iterating over is a bad idea. In some cases you can do it safely by iterating backwards over the array.
User avatar
GGShinobi
---- E L I T E ----
---- E L I T E ----
Posts: 291
Joined: Tue Dec 25, 2012 7:20 pm

Re: Scripters cove

Post by GGShinobi »

cim wrote:
GGShinobi wrote:
Is this behaviour of subEntities.remove() intentional? It sure makes sense, but for writers of OXP's which think they can remove all subEnts in a single for-loop this might come quite unexpected (as it was for me). :roll: :?:
As a general rule, removing elements from the array you're currently iterating over is a bad idea. In some cases you can do it safely by iterating backwards over the array.
Ooooh!!! :mrgreen: And I already wondered why Solonar iterated backwards when deactivating all turrets in his Turret Toggler!! :idea: :oops:

And I also thought that "remove()" was a function of the subEntity, not of the array ship.subEntities... well! :roll:

Hey, this was my 222th post! Cool number! :mrgreen:
忍 knowing that enough is enough, you'll always have enough.

Running Oolite 1.77 on Ubuntu Linux 12.04 LTS
User avatar
cim
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 4072
Joined: Fri Nov 11, 2011 6:19 pm

Re: Scripters cove

Post by cim »

GGShinobi wrote:
And I also thought that "remove()" was a function of the subEntity, not of the array ship.subEntities... well! :roll:
It is a function of the subentity. However, the next time you query the array, the subentity, having been removed, is no longer in it, as removing the subentity requires removing it from the ship's list of subentities.
User avatar
GGShinobi
---- E L I T E ----
---- E L I T E ----
Posts: 291
Joined: Tue Dec 25, 2012 7:20 pm

Re: Scripters cove

Post by GGShinobi »

cim wrote:
GGShinobi wrote:
And I also thought that "remove()" was a function of the subEntity, not of the array ship.subEntities... well! :roll:
It is a function of the subentity. However, the next time you query the array, the subentity, having been removed, is no longer in it, as removing the subentity requires removing it from the ship's list of subentities.
I see! Makes perfect sense! Thanks again, cim! :mrgreen:
忍 knowing that enough is enough, you'll always have enough.

Running Oolite 1.77 on Ubuntu Linux 12.04 LTS
User avatar
Wildeblood
---- E L I T E ----
---- E L I T E ----
Posts: 2409
Joined: Sat Jun 11, 2011 6:07 am
Location: Western Australia

Re: Scripters cove

Post by Wildeblood »

Similarly with system.allShips which is an array, you can't use system.allShips[x] instead of a pointer to a particular ship, because as ships are being continually added and removed from the system the position of a particular ship in the array continually changes.
User avatar
GGShinobi
---- E L I T E ----
---- E L I T E ----
Posts: 291
Joined: Tue Dec 25, 2012 7:20 pm

Re: Scripters cove

Post by GGShinobi »

Wildeblood wrote:
Similarly with system.allShips which is an array, you can't use system.allShips[x] instead of a pointer to a particular ship, because as ships are being continually added and removed from the system the position of a particular ship in the array continually changes.
Thanks Wildeblood! Good to know!
忍 knowing that enough is enough, you'll always have enough.

Running Oolite 1.77 on Ubuntu Linux 12.04 LTS
User avatar
Rorschachhamster
---- E L I T E ----
---- E L I T E ----
Posts: 274
Joined: Sun Aug 05, 2012 11:46 pm
Contact:

Re: Scripters cove

Post by Rorschachhamster »

Is there an easy way to get an ship to orbit the main planet again and again with a considerable speed? :|

I tried a framecallback with a "vector correction", but it looks shaky from up close:

Code: Select all

this.OrbitSatellite = function(delta)
	{
     var targetVector = system.mainPlanet.position.subtract(this.ship.position).direction();
     this.ship.orientation = targetVector.rotationTo([0, 1, 0]);	
	}
Oh, and I still argue with my freighttrains AI, but it is rather stubborn to do it's own thing :wink:
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Re: Scripters cove

Post by Eric Walch »

A year ago I wrote a small orbiter script. Not with frameCallbacks, but just with AI:

Code: Select all

{
    GLOBAL = {ENTER = ("setStateTo: ORBIT_PLANET");
    };
    ORBIT_PLANET = {
        "COURSE_OK" = ("setSpeedFactorTo: 0.65", performFlyToRangeFromDestination);
        "WAYPOINT_SET" = ("setAITo: gotoWaypointAI.plist");
        "TARGET_FOUND" = (setTargetToFoundTarget, "setAITo: traderInterceptAI.plist", fightOrFleeHostiles);
        "INCOMING_MISSILE" = ("setAITo: traderInterceptAI.plist", "setStateTo: INCOMING_MISSILE", "randomPauseAI: 0.5 2.0"); 
        ATTACKED = ("setAITo: traderInterceptAI.plist", fightOrFleeHostiles);
        UPDATE = ("sendScriptMessage: newOrbitPoint", "setDesiredRangeTo: 500.0",
            setDestinationFromCoordinates, checkCourseToDestination, "pauseAI: 10");
    }; 
}
And than in the ship script:

Code: Select all

this.newOrbitPoint = function ()
{
    var v = this.ship.position.add(this.ship.heading.multiply(10000));
    v = v.subtract(system.mainPlanet.position).direction().multiply(2.5 * system.mainPlanet.radius);
    this.ship.savedCoordinates = system.mainPlanet.position.add(v);
}
But it is long ago, So I'm not sure if it indeed works as intended or that it needed adjustments.

In this case for an orbiting ship, that does try to avoid obstacles.
User avatar
Rorschachhamster
---- E L I T E ----
---- E L I T E ----
Posts: 274
Joined: Sun Aug 05, 2012 11:46 pm
Contact:

Re: Scripters cove

Post by Rorschachhamster »

Eric Walch wrote:
But it is long ago, So I'm not sure if it indeed works as intended or that it needed adjustments.
Thanks, I'll just try it, and we'll see... :D :wink:
Post Reply