Jump to content

[0.23] Im a Cupcake Wannabe (Advanced Hover System)


mpink

Recommended Posts

Im A Cupcake Wannabe (Advanced Hover System V0.41)

V0.4 in action.

The Intention of this plugin is to enable people to achieve the levels of flight control that Cupcake manages using just a joystick but using only a keyboard.

http://www.youtube.com/watch?feature=player_embedded&v=SyUpLwbWbvU]

Your probably better off just watching some example builds than reading my ramblings on how to use and setup the AHS.

Examples Of v0.41 Builds (Tutorials)

The plugin currently adds the parts Small Advanced Hover System and Large Advanced Hover System to the control section and the RCS Translation Module to all thrust tweakable engines on start-up.

You may place multiple AHS on a craft at the same time and doing so will put the later found parts into a Slave mode where they just increase the number of engines that the Master AHS can control at any one time.

The AHS Has 3 hover modes

1)Off = No hover control attempted

2)Active = Trys to output enough thrust to hover your craft no matte what direction it is facing.

3)Fixed = Uses a const thrust level that is just enough to hover but only if you point the craft in the correct direction.

None of the hover modes attempt to change your speed but just cause a zero G effect.

The hover direction can also be changed using tweakables and Actiongroups to let you change you orientation to the ground in mid flight.

There is an RCS Translation Toggle in the tweakables/Action Groups that lets you enable/disable an RCS style 3D Translation using the RCS keys.

To link engines into the AHS system you must right click on them and use the Next RCS Dimension and Toggle RCS Direction buttons to set the RCS Direction to your preferred state.

Engines default to RCS Direction=None and in this state do not take up any of the AHS control slots.

Action groups can also be used to set engine RCS Directions of all engines with one button press.

The AHS produces a lot of heat and the small version is prone to Crashing or exploding if you do not use enough heat-sinks.

All AHS use a SAS stack icon as to provide overheat warnings and show the active state using colour.

White = Off

Light Blue = Hover mode on and RCS Translation Active

Green = Hover mode on

Dark Blue = RCS Translation Active

Red = Crashed

The AHS can find and connect to an Active Gravity Sensor. Hovers are set by default at 9.82/MS but even Kerbins sea level gravity dose not match this so placing a gravity sensor and activating it on launch will greatly increase your chance of a stable hover (Especially off world)

v0.3 Mun Lander Construction and Mission acelerated to 5X game time.

Things Iv Tried But Dont Want To Continue With

An Altitude lock. This was an attempt to maintain a set altitude but i found that with my lazy programming skills and the slow thrust change rate of the engines the craft would just yo yo + i think its better to need a little skill to fly still.

Horizontal translation. This was an attempt to increase thrust to an engine on one side as to cause rotation and thus translation in response to the translation keys. The hope was that S.A.S would then correct the rotation but in reality the engines were too powerful and caused a lot of S.A.S drift completely defeating the point.

Things To Do

1) Setup a heavy energy usage. The PNK A.S.S has a mass of 1 and should use lots of power to do all the computing. I think kerbal Comps probably get bigger as they get faster.

2) Enable Master Slave system for PNK A.S.S so that you can have more than one unit on a craft. Adding More units will increase the number of engines you can control at anyone time.

3) Look into adding tweakables to all engine modules as to add an RCS translation control for certain directions (RCS Mode: left/Right/none). This is just a way to better connect engine translation into the keyboard and not an attempt to replace the RCS system. These engines are not smart and will only do translation control not rotation. As the engines are manual setup this will need a lot of skill in the construction phase that should replace lake of skill one may have in the piloting phase.

4) Providing 3 works then it should be easy to set a hover direction using Action Groups. This would let you set what translation direction to use for controlling a hover. The main reason for this would be to enable some crazy acrobatics when constructing bases. You could remove a base component horizontally from its transport craft, rotate it 90 degrease and then hit an Action button to set the new hover direction and then place the component vertically.

Attempt thrust to COM Alignment. Center of mass changes with strange payloads and so an auto balance system would be cool. while balancing trust to mass centres sounds good its a lot of work for not much of a result. Using multiple tugs at 1 time should be an easy way to balance any uneven load and force some need for skill to remain.

5) Monitor changes to the selected engine types and set the thrust on any released engines back to 100%. (probably irelevent but depends on how the RCS engine module works)

Test 0.3 making a video for the Today I Build A Rocket challenge. (Currently the leader :Dlink)

Build a Kethane Colony or 2 to test final version.

Work With Variable Gravity (Find and use an active Gravity Sensor)

Selectable Engine Type Tweakable

Show the PNK A.S.S in action

Full Change Log

0.41
Fixed stupid bugs so that the RCS Translation directions are the same as the RCS system (still dose not change perspective when using control from hear).
Fixed silly bug where slaves were controlling as well as masters.
Added 95% heat auto shutdown / crash state (wont always save you on a warp though)
Running out of power now causes a crash state (When crashed the AHS needs to be shut down and started up again).
Changed some of the max settings on sliders to try and give more precise control ( low end still seams to have a dead spot though )
Changed the gravity sensor reading so that the AHS gets its own supper precise gravity reading but only if a gravity sensor is in a working readable state.
Added RCS Translation event and actions (No longer uses the RCS toggle).
Removed the check for SAS toggle as hover mode replaced the need for this.
Added Icon colours for the SAS stack icon (Cant use custom icons yet) so you can tell what state it is in at a glance without needing to right click the part.

v0.4
Redesigned the ASS into an AHS so it now has hover direction, hover mode and some-other tweakables.
Added RCSEngineTranslation Module and added it to all engine parts on start-up.
Made 2 new models so that the AHS have there very own look (stock style).
Added resource usage so it now eats up the energy and leaks out heat.
Added Master/Slave system along with a more limiting number of controllable engines so multiple AHS should be needed for large craft.
Added the Active hover mode as suggested by Virindi (good call there )

v0.3
Added reading of active gravity sensors for off world hovering.
Added engine types to tweakable options for control over what engine types to use for a hover.
Engine Types restricted to first 5 engine types found and should work for any engines (not just stock).
Added a restriction so the ASS may only control 20 engines at one time.

v0.2
Added thrust vector analysis so hovering can be maintained if engines move or dont all point in the same direction.
Added Z translation controls and tweakable for changing your vertical speed.

v0.1
Added the PNK ASS basic hover system.

V0.41 Code with do what you want licence at the top

//M Pink provides AHS (v0.41) freely for anyone to use for anyreason they see fit.
//M Pink is not responsable for any damage caused by AHS.
//AHS Should Be Used At Your Own Risk !

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace AHS
{
public class Direction
{//Put an end to the problems of compearing strings (miss spelling a direction) by using the AHS.Direction
public const string None = "None";
public const string Left = "Left";
public const string Right = "Right";
public const string Up = "Up";
public const string Down = "Down";
public const string Forward = "Forward";
public const string Backward = "Backward";
}

[KSPAddon(KSPAddon.Startup.MainMenu,true)]
public class RCSAddonSetup :MonoBehaviour
{
void Awake ()
{
Debug.Log("(AHS.RCSAddonSetup) Awake()");

PartLoader pl = PartLoader.Instance;
if (pl==null)
{
Debug.Log("(AHS.RCSAddonSetup) Partloader was null");
}
else
{
Debug.Log("(AHS.RCSAddonSetup) Partloader Found");
Debug.Log("(AHS.RCSAddonSetup) Number of parts to scan = "+pl.parts.Count);
int a=0;
for (a=0; a<pl.parts.Count; a++)
{
bool has_engine = false;
bool has_rcs = false;
bool throtle_locked = true;//fail safe. Only use if throtleLocked is found and false
int b=0;
for (b=0; b<pl.parts[a].moduleInfos.Count; b++)
{
if ("Engine" == pl.parts[a].moduleInfos[b].moduleName) has_engine=true;
if ("RCSEngineTranslation" == pl.parts[a].moduleInfos[b].moduleName) has_rcs=true;
}

if (has_engine)
{//parts that dont have a ModuleEngines will return null for FindModulesImplementing
if (pl.parts[a].partPrefab.FindModulesImplementing<ModuleEngines>().Count!=0)
{
throtle_locked = pl.parts[a].partPrefab.FindModulesImplementing<ModuleEngines>()[0].throttleLocked;
}
}

if (has_engine && !has_rcs && !throtle_locked)
{
Debug.Log("(AHS.RCSAddonSetup) Engine module found for "+pl.parts[a].title+" adding RCSEngineTranslation module");

pl.parts[a].partPrefab.AddModule("RCSEngineTranslation");
AvailablePart.ModuleInfo info = new AvailablePart.ModuleInfo();
info.moduleName = "RCSEngineTranslation";
pl.parts[a].moduleInfos.Add(info);
}//endof if (has_engine && !has_rcs)
}//endof for (a=0; a<pl.parts.Count; a++)
}//end of else if (pl==null)
}
}

public class RCSEngineTranslation : PartModule
{

[KSPField (guiName = "dimention", isPersistant=true, guiActive=false)]
int dimention = 0;
[KSPField (guiName = "direction", isPersistant=true, guiActive=false)]
bool invert_direction = false;

[KSPField (guiName = "RCS Direction", isPersistant=false, guiActive=true, guiActiveEditor=true)]
public string state_string = "Not Set Yet";

[KSPEvent ( guiName="Next RCS Dimention", guiActive=true, guiActiveEditor=true )]
public void EventNextDirection()
{
this.dimention++;
if (this.dimention>3) this.dimention=0;
this.DisplayMode();
}

[KSPEvent ( guiName="Togle RCS Direction", guiActive=true, guiActiveEditor=true )]
public void EventTogleDirection()
{
this.invert_direction = !this.invert_direction;
this.DisplayMode();
}

[KSPAction ("Set RCS None")]
void ActionSetRCSNone ()
{
this.dimention=0;
this.DisplayMode();
}
[KSPAction ("Set RCS Left")]
void ActionSetRCSLeft ()
{
this.dimention=1;
this.invert_direction=false;
this.DisplayMode();
}
[KSPAction ("Set RCS Right")]
void ActionSetRCSRight ()
{
this.dimention=1;
this.invert_direction=true;
this.DisplayMode();
}
[KSPAction ("Set RCS Up")]
void ActionSetRCSUp ()
{
this.dimention=2;
this.invert_direction=false;
this.DisplayMode();
}
[KSPAction ("Set RCS Down")]
void ActionSetRCSDown ()
{
this.dimention=2;
this.invert_direction=true;
this.DisplayMode();
}
[KSPAction ("Set RCS Forward")]
void ActionSetRCSForward ()
{
this.dimention=3;
this.invert_direction=false;
this.DisplayMode();
}
[KSPAction ("Set RCS Backward")]
void ActionSetRCSBackward ()
{
this.dimention=3;
this.invert_direction=true;
this.DisplayMode();
}

public void DisplayMode ()
{
switch (this.dimention)
{
case 1: if (!this.invert_direction) this.state_string=Direction.Left; else this.state_string=Direction.Right; break;
case 2: if (!this.invert_direction) this.state_string=Direction.Up; else this.state_string=Direction.Down; break;
case 3: if (!this.invert_direction) this.state_string=Direction.Forward; else this.state_string=Direction.Backward; break;
default: this.state_string = Direction.None; break;
}
}

public RCSEngineTranslation ()
{
}//end of public RCSEngineTranslation ()

public override void OnLoad (ConfigNode node)
{
this.DisplayMode();
}
public override void OnStart (PartModule.StartState state)
{
this.DisplayMode();
}

}//end of public class RCSEngineTranslation : PartModule

public class AHS : PartModule
{

//energy used per seconde
[KSPField (isPersistant=true)]
public float power_consumption = 0.5f;
//heat produced per seconde
[KSPField (isPersistant=true)]
public float heat_production = 50f;

//thrust settings for all dimentions
//isPersistant is probably not needed as saves cant be done throtled up in an atmoshpher
[KSPField (guiName = "throtel left", isPersistant=true, guiActive=false)]
public float thrust_left = 100f;
[KSPField (guiName = "throtel right", isPersistant=true, guiActive=false)]
public float thrust_right = 100f;
[KSPField (guiName = "throtel up", isPersistant=true, guiActive=false)]
public float thrust_up = 100f;
[KSPField (guiName = "throtel down", isPersistant=true, guiActive=false)]
public float thrust_down = 100f;
[KSPField (guiName = "throtel forward", isPersistant=true, guiActive=false)]
public float thrust_forward = 100f;
[KSPField (guiName = "throtel backward", isPersistant=true, guiActive=false)]
public float thrust_backward = 100f;




//start of display fields
[KSPField (guiName = "Version", guiActive=true, guiActiveEditor=true)]
public string version = "0.41";

[KSPField (guiName = "Comand State", guiActive=true, guiActiveEditor=true)]
public string comand_state = "Not Set Yet";//Master,Slave,Off or Crashed. The state should be set to Off on deactivation and ony be set to Mast/Slave if its not Crashed

[KSPField (guiName = "Engine Usage", guiActive=true, guiActiveEditor=true)]
public string engine_usage = "Not Set Yet";//no / max

[KSPField (guiName="Gravity", isPersistant=false, guiActive=true, guiActiveEditor=false)]
public string gravity_state = "Not Set Yet";
public int grav_sensor_index = -1;
public bool grav_sensor_active = false;
public string grav_readout = ".....";
public float gravity = 9.82f;

[UI_Toggle (enabledText="Deactivate", disabledText="Activate"), KSPField(guiName="AHS", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool ahs_on = true;
public bool ahs_was_on = true;
[KSPAction ("Activate AHS")]
public void ActionActivateAHS (KSPActionParam param)
{
this.ahs_on=true;
}
[KSPAction ("Deactivate AHS")]
public void ActionDeactivateAHS (KSPActionParam param)
{
this.ahs_on=false;
}
[KSPAction ("Toggle AHS")]
public void ActionToggleAHS (KSPActionParam param)
{
this.ahs_on=!this.ahs_on;
}




//halfway range seporators
[UI_FloatRange (stepIncrement=0.01f, maxValue=3f, minValue=0.01f), KSPField (guiName="Translation G", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public float max_g_change = 0.25f;

[UI_FloatRange (stepIncrement=0.5f, maxValue=25f, minValue=0.5f), KSPField (guiName="Thrust Change Rate", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public float max_thrust_change = 4f;







[UI_Toggle (enabledText="Deactivate", disabledText="Activate"), KSPField(guiName="RCS Translation", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool rcs_on = true;
[KSPAction ("Activate RCS Translation")]
public void ActionActivateRCSTranslation (KSPActionParam param)
{
this.rcs_on=true;
}
[KSPAction ("Deactivate RCS Translation")]
public void ActionDeactivateRCSTranslation (KSPActionParam param)
{
this.rcs_on=false;
}
[KSPAction ("Toggle RCS Translation")]
public void ActionToggleRCSTranslation (KSPActionParam param)
{
this.rcs_on=!this.rcs_on;
}






[KSPField (guiName = "dimention", isPersistant=true, guiActive=false)]
int hover_dimention = 3;
[KSPField (guiName = "direction", isPersistant=true, guiActive=false)]
bool hover_invert_direction = false;

[KSPField (guiName = "Hover Direction", guiActive=true, guiActiveEditor=true)]
public string hover_state_string = "Not Set Yet";

//index 0
[KSPEvent ( guiName="Next Hover Dimention", guiActive=true, guiActiveEditor=true )]
public void EventNextDirection()
{
this.hover_dimention++;
if (this.hover_dimention>3) this.hover_dimention=1;
this.HoverDisplayMode();
}
//index 1
[KSPEvent ( guiName="Togle Hover Direction", guiActive=true, guiActiveEditor=true )]
public void EventTogleDirection()
{
this.hover_invert_direction = !this.hover_invert_direction;
this.HoverDisplayMode();
}
[KSPAction ("Hover Left")]
public void ActionHoverLeft (KSPActionParam param)
{
this.hover_dimention=1;
this.hover_invert_direction=false;
this.HoverDisplayMode();
}
[KSPAction ("Hover Right")]
public void ActionHoveRight (KSPActionParam param)
{
this.hover_dimention=1;
this.hover_invert_direction=true;
this.HoverDisplayMode();
}
[KSPAction ("Hover Up")]
public void ActionHoverUP (KSPActionParam param)
{
this.hover_dimention=2;
this.hover_invert_direction=false;
this.HoverDisplayMode();
}
[KSPAction ("Hover Down")]
public void ActionHoverDown (KSPActionParam param)
{
this.hover_dimention=2;
this.hover_invert_direction=true;
this.HoverDisplayMode();
}
[KSPAction ("Hover Forward")]
public void ActionHoverForward (KSPActionParam param)
{
this.hover_dimention=3;
this.hover_invert_direction=false;
this.HoverDisplayMode();
}
[KSPAction ("Hover Backward")]
public void ActionHoverBackward (KSPActionParam param)
{
this.hover_dimention=3;
this.hover_invert_direction=true;
this.HoverDisplayMode();
}
public void HoverDisplayMode ()
{
switch (this.hover_dimention)
{
case 1: if (!this.hover_invert_direction) this.hover_state_string="Left"; else this.hover_state_string="Right"; break;
case 2: if (!this.hover_invert_direction) this.hover_state_string="Up"; else this.hover_state_string="Down"; break;
case 3: if (!this.hover_invert_direction) this.hover_state_string="Forward"; else this.hover_state_string="Backward"; break;
default: this.hover_state_string = "None"; break;
}
}



[KSPField (guiName="Hover Mode: Active", guiActive=false, isPersistant=true)]
public int hover_mode = 1;
//index 2
[KSPEvent (guiName="Hover Mode: Active", guiActive=true, guiActiveEditor=true)]
public void EventNextHoverMode()
{
this.hover_mode++;
if (this.hover_mode>2) this.hover_mode=0;
this.SetHoverGUI();
}
[KSPAction ("Set Active Hover Mode")]
public void ActionSetActiveHover (KSPActionParam param)
{
this.hover_mode=1;
this.SetHoverGUI();
}
[KSPAction ("Set Fixed Hover Mode")]
public void ActionSetFixedHover (KSPActionParam param)
{
this.hover_mode=2;
this.SetHoverGUI();
}
[KSPAction ("Set Hover Mode Off")]
public void ActionSetHoverModeOff (KSPActionParam param)
{
this.hover_mode=0;
this.SetHoverGUI();
}
public void SetHoverGUI ()
{
switch(this.hover_mode)
{
case 0: base.Events.GetByIndex(2).guiName="Hover Mode: Off"; break;
case 1: base.Events.GetByIndex(2).guiName="Hover Mode: Active"; break;
case 2: base.Events.GetByIndex(2).guiName="Hover Mode: Fixed"; break;
}
}


//index 3
[KSPEvent (guiName="Force Editor Update", guiActive=true, guiActiveEditor=true)]
void EventForceEditorUpdate ()
{
this.CheckSlaveState();
this.CountEngines();
}

int no_parts_in_last_check = -1;//used to check if parts have changed from last update

[KSPField (isPersistant=true)]
public int max_engines=10;
public int controlable_engines=0;
public List<int> slave_indices = new List<int>();

public AHS ()
{
Debug.Log("(AHS) Constructor()");
}//endof public AHS ()

public override void OnStart (PartModule.StartState state)
{
base.OnStart (state);
if (this.vessel!=null)
{//vessel = null in editor
//disable editor only events
Debug.Log ("(AHS) Number of events = "+base.Events.Count);
base.Events.GetByIndex(3).active=false;
}
this.HoverDisplayMode();
this.SetHoverGUI();

this.part.stagingIcon = "SAS";//DefaultIcons.CUSTOM
}

public override void OnUpdate ()
{
base.OnUpdate();
this.CheckSlaveState();//master slave states could change every frame so needs checking

//set icon color
if (!this.ahs_on) this.part.stackIcon.SetIconColor(Color.white);
else if (this.comand_state=="Crashed") this.part.stackIcon.SetIconColor(Color.red);
else if (this.hover_mode!=0 && this.rcs_on)
{
this.part.stackIcon.SetIconColor(Color.cyan);
}
else if (this.hover_mode==0 && !this.rcs_on)
{
this.part.stackIcon.SetIconColor(Color.white);
}
else
{
if (this.hover_mode!=0) this.part.stackIcon.SetIconColor(Color.green);
if (this.rcs_on) this.part.stackIcon.SetIconColor(Color.blue);
}
if (this.comand_state!="Master" && this.comand_state!="Slave") return;

this.CheckGravSensor();
//all changes in craft must now have been detected and delt with
this.no_parts_in_last_check = this.vessel.parts.Count;


//energy useage check
double amount_needed = (double)(this.power_consumption * TimeWarp.deltaTime);
double amount_got = this.part.RequestResource("ElectricCharge" ,amount_needed);
if ((amount_needed*0.95) > amount_got )
{
this.comand_state = "Crashed";
return;
}


//temperature check
if (this.part.temperature >= (this.part.maxTemp*0.95))
{
this.comand_state = "Crashed";
return;
}
this.part.temperature += (this.heat_production * TimeWarp.deltaTime);


//check for change in control states
this.ahs_was_on = this.ahs_on;
if (!this.ahs_on) return;

this.CountEngines();//engines may be changed anytime so need rechecking

//only the master control system should do any actual work
if (this.comand_state!="Master") return;

//apply AHS stablisation
CenterOfThrustQuery cot = new CenterOfThrustQuery();
Vector3 thrust_up_vec = Vector3.zero;
Vector3 thrust_down_vec = Vector3.zero;
Vector3 thrust_left_vec = Vector3.zero;
Vector3 thrust_right_vec = Vector3.zero;
Vector3 thrust_forward_vec = Vector3.zero;
Vector3 thrust_backward_vec = Vector3.zero;
float t;
int i;
int c=0;
List<ModuleEngines> engine_modules = new List<ModuleEngines>();
List<string> engine_rcs_state = new List<string>();

//make thrust vectors
for (i=0; i<this.vessel.parts.Count; i++)
{
if (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>().Count>0)
{
if (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string!="None" && c<this.controlable_engines)
{
c++;
if (c==this.controlable_engines) Debug.Log("(AHS) Max controlable engines reached");
engine_modules.Add(this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0]);
engine_rcs_state.Add (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string);
this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0].OnCenterOfThrustQuery(cot);
t = this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0].finalThrust;
cot.dir.x *= t;
cot.dir.y *= t;
cot.dir.z *= t;
switch (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string)
{
case Direction.Up: thrust_up_vec += cot.dir; break;
case Direction.Down: thrust_down_vec += cot.dir; break;
case Direction.Left: thrust_left_vec += cot.dir; break;
case Direction.Right: thrust_right_vec += cot.dir; break;
case Direction.Forward: thrust_forward_vec += cot.dir; break;
case Direction.Backward: thrust_backward_vec += cot.dir; break;
}
}
}
}

float target_up_thrust = 0f;
float target_down_thrust = 0f;
float target_left_thrust = 0f;
float target_right_thrust = 0f;
float target_forward_thrust = 0f;
float target_backward_thrust = 0f;

if (this.rcs_on)
{//Change target thrust and thus vertical speed
float g_change_in_thrust = (this.max_g_change*9.82f)*this.vessel.GetTotalMass();
target_down_thrust -= (g_change_in_thrust*this.vessel.ctrlState.Y);
target_up_thrust += (g_change_in_thrust*this.vessel.ctrlState.Y);
target_left_thrust += (g_change_in_thrust*this.vessel.ctrlState.X);
target_right_thrust -= (g_change_in_thrust*this.vessel.ctrlState.X);
target_forward_thrust -= (g_change_in_thrust*this.vessel.ctrlState.Z);
target_backward_thrust += (g_change_in_thrust*this.vessel.ctrlState.Z);
}

//adjust target thrust for hover correction
if (this.hover_mode>0)
{
switch (this.hover_state_string)
{
case Direction.Up: target_up_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case Direction.Down: target_down_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case Direction.Left: target_left_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case Direction.Right: target_right_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case Direction.Forward: target_forward_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case Direction.Backward: target_backward_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
}

if (this.hover_mode==1)
{
Vector3 sky_vector = this.vessel.mainBody.transform.position-this.vessel.transform.position;
Vector3 sky_n = sky_vector.normalized;
Vector3 thr_n = Vector3.zero;

//rotating one normal into the other should return 0 if they are on the same plan and 1 if at 90 degrease to each other
switch (this.hover_state_string)
{
case Direction.Up: thr_n = thrust_up_vec.normalized; break;
case Direction.Down: thr_n = thrust_down_vec.normalized; break;
case Direction.Left: thr_n = thrust_left_vec.normalized; break;
case Direction.Right: thr_n = thrust_right_vec.normalized; break;
case Direction.Forward: thr_n = thrust_forward_vec.normalized; break;
case Direction.Backward: thr_n = thrust_backward_vec.normalized; break;
}

float m = Mathf.Abs( (sky_n.x* thr_n.x) + (sky_n.y*thr_n.y) + (sky_n.z* thr_n.z));
if (m==0f) m=0.00000001f;//avoid divide by 0

switch (this.hover_state_string)
{
case Direction.Up: target_up_thrust /= m; break;
case Direction.Down: target_down_thrust /= m; break;
case Direction.Left: target_left_thrust /= m; break;
case Direction.Right: target_right_thrust /= m; break;
case Direction.Forward: target_forward_thrust /= m; break;
case Direction.Backward: target_backward_thrust /= m; break;
}
}
}

//Adjust throtles slowly and hope we find a sweatspot !
float irevavent_thrust = ((9.82f*this.vessel.GetTotalMass())*0.01f);
if (target_up_thrust==thrust_up_vec.magnitude && thrust_up_vec.magnitude==0f) this.thrust_up=0f; else
{
if ((target_up_thrust-thrust_up_vec.magnitude)<0f) this.thrust_up-=this.max_thrust_change; else this.thrust_up+=this.max_thrust_change;
this.thrust_up = Mathf.Clamp(this.thrust_up,0f,100f);
}
if (target_down_thrust==thrust_down_vec.magnitude && thrust_down_vec.magnitude==0f) this.thrust_down=0f; else
{
if ((target_down_thrust-thrust_down_vec.magnitude)<0f) this.thrust_down-=this.max_thrust_change; else this.thrust_down+=this.max_thrust_change;
this.thrust_down = Mathf.Clamp(this.thrust_down,0f,100f);
}
if (target_left_thrust==thrust_left_vec.magnitude && thrust_left_vec.magnitude==0f) this.thrust_left=0f; else
{
if ((target_left_thrust-thrust_left_vec.magnitude)<0f) this.thrust_left-=this.max_thrust_change; else this.thrust_left+=this.max_thrust_change;
this.thrust_left = Mathf.Clamp(this.thrust_left,0f,100f);
}
if (target_right_thrust==thrust_right_vec.magnitude && thrust_right_vec.magnitude==0f) this.thrust_right=0f; else
{
if ((target_right_thrust-thrust_right_vec.magnitude)<0f) this.thrust_right-=this.max_thrust_change; else this.thrust_right+=this.max_thrust_change;
this.thrust_right = Mathf.Clamp(this.thrust_right,0f,100f);
}
if (target_forward_thrust==thrust_forward_vec.magnitude && thrust_forward_vec.magnitude==0f) this.thrust_forward=0f; else
{
if ((target_forward_thrust-thrust_forward_vec.magnitude)<0f) this.thrust_forward-=this.max_thrust_change; else this.thrust_forward+=this.max_thrust_change;
this.thrust_forward = Mathf.Clamp(this.thrust_forward,0f,100f);
}
if (target_backward_thrust==thrust_backward_vec.magnitude && thrust_backward_vec.magnitude==0f) this.thrust_backward=0f; else
{
if ((target_backward_thrust-thrust_backward_vec.magnitude)<0f) this.thrust_backward-=this.max_thrust_change; else this.thrust_backward+=this.max_thrust_change;
this.thrust_backward = Mathf.Clamp(this.thrust_backward,0f,100f);
}

//set engine thrusts
for (i=0; i<engine_modules.Count; i++)
{
switch (engine_rcs_state[i])
{
case Direction.Up: engine_modules[i].thrustPercentage = this.thrust_up; break;
case Direction.Down: engine_modules[i].thrustPercentage = this.thrust_down; break;
case Direction.Left: engine_modules[i].thrustPercentage = this.thrust_left; break;
case Direction.Right: engine_modules[i].thrustPercentage = this.thrust_right; break;
case Direction.Forward: engine_modules[i].thrustPercentage = this.thrust_forward; break;
case Direction.Backward: engine_modules[i].thrustPercentage = this.thrust_backward; break;
}
}
}//endof public override void OnUpdate ()


//Used in editor to update gui info. Should be done in update as a full engine scan will be done every time as to check for states
public void CountEngines ()
{
int i;
int master_index=-1;

//vessel will = null in the editor so we must cycle through parts list another way if it is not set
List <Part> part_list;
if (this.vessel!=null)
{
part_list = this.vessel.Parts;
}
else
{
part_list = new List<Part>();
Part start_part = this.part;
while (start_part.parent!=null) start_part=start_part.parent;
AddPartToList(start_part,part_list);
}

int controlable_no = 0;
int found_engines = 0;
for (i=0; i<part_list.Count; i++)
{
if (part_list[i].FindModulesImplementing<AHS>().Count>0)
{
//functional states are only Master or Slave
if (part_list[i].FindModulesImplementing<AHS>()[0].comand_state=="Master")
{
master_index=i;
}
if ( part_list[i].FindModulesImplementing<AHS>()[0].comand_state=="Master" ||
part_list[i].FindModulesImplementing<AHS>()[0].comand_state=="Slave" )
{
controlable_no += part_list[i].FindModulesImplementing<AHS>()[0].max_engines;
}
}
if (part_list[i].FindModulesImplementing<RCSEngineTranslation>().Count>0)
{
if (part_list[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string!="None") found_engines++;
}
}
if (master_index!=-1)
{
part_list[master_index].FindModulesImplementing<AHS>()[0].controlable_engines = controlable_no;
part_list[master_index].FindModulesImplementing<AHS>()[0].engine_usage = found_engines+"/"+controlable_no;
List<int> slaves = part_list[master_index].FindModulesImplementing<AHS>()[0].slave_indices;
for (i=0; i<slaves.Count; i++)
{
part_list[slaves[i]].FindModulesImplementing<AHS>()[0].engine_usage = found_engines+"/"+controlable_no;
}
}
}

//Search for working slaves and update the master's slave list
public void CheckSlaveState ()
{
int master_index = -1;
int i;

//vessel will = null in the editor so we must cycle through parts list another way if it is not set
List <Part> part_list;
if (this.vessel!=null)
{
part_list = this.vessel.Parts;
}
else
{
part_list = new List<Part>();
Part start_part = this.part;
while (start_part.parent!=null) start_part=start_part.parent;
AddPartToList(start_part,part_list);
}

for (i=0; i<part_list.Count; i++)
{
if (part_list[i].FindModulesImplementing<AHS>().Count>0)
{
//All inactive states must be checked before asigning master slave states
if (!part_list[i].FindModulesImplementing<AHS>()[0].ahs_on)
{
part_list[i].FindModulesImplementing<AHS>()[0].comand_state = "Off";
}
else if ( part_list[i].FindModulesImplementing<AHS>()[0].comand_state!="Crashed" )
{
if (master_index==-1)
{
master_index=i;
part_list[master_index].FindModulesImplementing<AHS>()[0].comand_state = "Master";
part_list[master_index].FindModulesImplementing<AHS>()[0].slave_indices.Clear();
}
else
{
part_list[i].FindModulesImplementing<AHS>()[0].comand_state = "Slave";
part_list[master_index].FindModulesImplementing<AHS>()[0].slave_indices.Add(i);
}
}
}
}

}

//Find active gravity sensor and then set gravity to a useable value
public void CheckGravSensor ()
{
this.FindActiveSensor();
}//endof void CheckGravSensor ()

//Updates grav readout and sets active state + finds an active sensor
//You can force sensor finding by setting grav_sensor_index to -1
public void FindActiveSensor ()
{
if (no_parts_in_last_check!=this.vessel.parts.Count)
{//if parts have change then the index is no longer valid
this.grav_sensor_index = -1;
this.grav_sensor_active = false;
}
if ( this.grav_sensor_index != -1 )
{//if grav sensor is valid check it is active and update readout
this.ReadGravSensor();
}
//We dont need to search again if we have an active sensor and parts have not changed
if ( no_parts_in_last_check==this.vessel.parts.Count && this.grav_sensor_active==true ) return;

this.grav_sensor_index = -1;
this.grav_sensor_active = false;
int i;
for (i=0; i<this.vessel.parts.Count; i++)
{
if (this.vessel.parts[i].name=="sensorGravimeter")
{
if (this.vessel.parts[i].FindModulesImplementing<ModuleEnviroSensor>().Count>0)
{
if (this.grav_sensor_index == -1)
{
this.grav_sensor_index=i;
}
this.ReadGravSensor();
if (this.grav_sensor_active)
{
this.grav_sensor_index = i;
break;
}
}
}
}
}//endof void FindActiveSensor ()

public void ReadGravSensor ()
{
if (this.grav_sensor_index==-1)
{
this.grav_readout = this.gravity_state = "No Sonsor Found";
this.gravity = 9.82f;
this.grav_sensor_active = false;
return;
}
float grav = float.NaN;
try
{
ModuleEnviroSensor sensor = this.vessel.parts[this.grav_sensor_index].FindModulesImplementing<ModuleEnviroSensor>()[0];
if (sensor.readoutInfo.Length>6)
{
grav = float.Parse(sensor.readoutInfo.Substring(0,sensor.readoutInfo.Length-6));
if (grav != float.NaN)
{
//duplicate of the grav sensors methode of getting gravity but with out the lose of precision from string convertion
grav = (float)FlightGlobals.getGeeForceAtPosition( base.transform.position ).magnitude;
}
}
}
catch
{
}

if (grav==float.NaN || float.IsInfinity(grav) || grav.ToString()=="NaN")
{
this.gravity_state = this.grav_readout = "Senneor Read Error";
this.gravity = 9.82f;
this.grav_sensor_active = false;
}
else
{
this.gravity_state = this.grav_readout = grav.ToString();
this.gravity = grav;
this.grav_sensor_active = true;
}
}

//part list builder. much better to use vessle.parts but its not avalible in the editor
public void AddPartToList ( Part p_, List<Part> list_ )
{
list_.Add(p_);
int i;
for (i=0; i<p_.children.Count; i++)
{
this.AddPartToList(p_.children[i],list_);
}
}

public override void OnLoad (ConfigNode node)
{
base.OnLoad(node);
this.HoverDisplayMode();
this.SetHoverGUI();
}

//place for adding right click info to parts in the editor ?
public override string GetInfo ()
{
string result = "";
result += "Energy Usage: "+this.power_consumption.ToString()+"/sec\n";
result += "Heat Production: "+this.heat_production.ToString()+"/sec\n";
result += "Controlable Engines: "+this.max_engines.ToString()+"/sec\n";
return base.GetInfo ()+result;
}
}//endof public class AHS : PartModule
}//endof namespace AHS

Edited by mpink
Update to V0.41
Link to comment
Share on other sites

Amazing demo video, I really enjoy to watch.

Is a nice plugin. But if we use twekables to control thrust in jet engines, this is not kinda cheat?

Becouse the cons with jet engines is that they need time to change their level of thrust.

But looking things from different perspective, we dont have the control tools needed to simule vtol jet engines like in the real world.

Aka harrier or others.

Seems nice. Good job.

Link to comment
Share on other sites

Is a nice plugin. But if we use twekables to control thrust in jet engines, this is not kinda cheat?

Yes and no. It dose not change the engines acceleration time so that part is not cheating and adds a lot of problems to the flight.

The craft in the video has rockets clipped into the jets in order to counter the problems with engine spool up time.

The idea of the plugin is that once the engines are up to speed to cancel out gravity you don't need to change there speed very much but you do need upwards rockets for rapid speed changes.

Who's Cupcake?

Cupcake is the player who made the video (using just a joystick and not this plugin).

Provide source code as per the forum rules

Source code is in the zip file named ASS.CS

V0.2 =

//M Pink provides ASS freely for anyone to use for anyreason they see fit.
//M Pink is not responsable for any damage caused by ASS.
//Ass Should Be Used At Your Own Risk !

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace ASS
{
public class ASS : PartModule
{
float throtel;

[KSPField (guiName = "Active", isPersistant = true, guiActive = true)]
public bool ass_on = true;

[KSPAction ("Activate")]
public void Activate (KSPActionParam param)
{
this.ass_on=true;
}
[KSPAction ("Deactivate")]
public void Deactivate (KSPActionParam param)
{
this.ass_on=false;
}
[KSPAction ("Toggle")]
public void Toggle (KSPActionParam param)
{
this.OnToggle ();
}
[KSPEvent (guiName = "Toggle ASS", guiActive = true)]
public void OnToggle ()
{
this.ass_on = !this.ass_on;
}

[UI_FloatRange (stepIncrement = 0.01f, maxValue = 1f, minValue = 0f), KSPField (guiName = "Translation G", isPersistant = true, guiActive = true, guiActiveEditor = true)]
public float max_g_change = 0.25f;

bool sas_on;
bool rcs_on;

public ASS ()
{
Debug.Log("(ASS) Constructor()");
throtel = 100f;

this.sas_on=false;
this.rcs_on=true;
}

public override void OnUpdate ()
{
base.OnUpdate();

if (!this.ass_on) return;

//check for change in control states
bool sas_state = this.vessel.ActionGroups[KSPActionGroup.SAS];
bool sas_changed = false;
// bool rcs_changed = false;
if (this.sas_on != sas_state)
{
this.sas_on = sas_state;
sas_changed = true;
}
if (this.rcs_on != this.vessel.ActionGroups[KSPActionGroup.RCS] )
{
this.rcs_on = this.vessel.ActionGroups[KSPActionGroup.RCS];
// rcs_changed = true;
}

//deal with changes in control state
if (!this.sas_on && sas_changed)
{
//set throttel to max on SAS deactivation
this.throtel=100f;
this.SetThrust();
}


//apply sas stablisation
if (this.sas_on)
{
CenterOfThrustQuery cot = new CenterOfThrustQuery();
Vector3 thrust_vec = Vector3.zero;
float t;
int i;
float target_thrust = 9.82f*this.vessel.GetTotalMass();
//find engines to check
for (i=0; i<this.vessel.parts.Count; i++)
{
if (this.vessel.parts[i].name=="JetEngine")
{//make a combined thrust vector for all engines
this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0].OnCenterOfThrustQuery(cot);
t = this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0].finalThrust;
cot.dir.x *= t;
cot.dir.y *= t;
cot.dir.z *= t;
thrust_vec += cot.dir;
}
}

if (this.rcs_on)
{//Change target thrust and thus vertical speed
float max_thrust_change = (this.max_g_change*9.82f)*this.vessel.GetTotalMass();
target_thrust -= (max_thrust_change*this.vessel.ctrlState.Z);
}

//Check if we should set thrust to max or none.
//It would be nice to calculate the exact thust needed but would be slow and hard.
if ((target_thrust-thrust_vec.magnitude)<0f) this.throtel=0f; else this.throtel=100f;
this.SetThrust();
}
}

public void SetThrust ()
{
//clamp throtel as setting it out of range is cheating !
if (this.throtel>100f) this.throtel=100f;
if (this.throtel<0f) this.throtel=0f;
int i;
//find engines
for (i=0; i<this.vessel.parts.Count; i++)
{
if (this.vessel.parts[i].name=="JetEngine")
{//apply thrtel to engine
this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0].thrustPercentage = this.throtel;
}
}
}
}
}

Link to comment
Share on other sites

v0.3 Update

Crank the Rock Radio up to 11 and enjoy a 3 minute tour of the KSC demonstrating the power of V0.3

Change log

Added reading of active gravity sensors for off world hovering.

Added engine types to tweakable options for control over what engine types to use for a hover.

Engine Types restricted to first 5 engine types found and should work for any engines (not just stock).

Added a restriction so the ASS may only control 20 engines at one time.

Code is still in the zip as ASS.cs

//M Pink provides ASS (v0.3) freely for anyone to use for anyreason they see fit.
//M Pink is not responsable for any damage caused by ASS.
//Ass Should Be Used At Your Own Risk !

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace ASS
{
public class ASS : PartModule
{
[KSPField (guiName = "throtel", isPersistant=true, guiActive=false)]
float throtel = 100f;

[KSPField (guiName = "Version", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public string version = "0.3";

[UI_FloatRange (stepIncrement=0.01f, maxValue=2f, minValue=0f), KSPField (guiName="Translation G", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public float max_g_change = 0.25f;

[KSPField (guiName="Gravity", isPersistant=false, guiActive=true, guiActiveEditor=false)]
public string gravity_state = "No Sensor Found";
public int grav_sensor_index = -1;
public bool grav_sensor_active = false;
public string grav_readout = ".....";
public float gravity = 9.82f;


[UI_Toggle (enabledText="Deactivate", disabledText="Activate"), KSPField(guiName="ASS", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool ass_on = true;
public bool ass_was_on = true;
[KSPAction ("Action Activate ASS")]
public void ActionActivateASS (KSPActionParam param)
{
this.ass_on=true;
}
[KSPAction ("Action Deactivate ASS")]
public void ActionDeactivateASS (KSPActionParam param)
{
this.ass_on=false;
}
[KSPAction ("Action Toggle ASS")]
public void ActionToggleASS (KSPActionParam param)
{
this.ass_on=!this.ass_on;
}


//
//engine list
int max_engine_types = 5;
[KSPField (guiName="no_engine_types", isPersistant=true, guiActive=false)]
int no_engine_types = 1;

[UI_Toggle (enabledText="Release", disabledText="Control"), KSPField(guiName="Basic Jet Engine", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool engine_1 = true;
[UI_Toggle (enabledText="Release", disabledText="Control"), KSPField(guiName="Engine 2", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool engine_2 = false;
[UI_Toggle (enabledText="Release", disabledText="Control"), KSPField(guiName="Engine 3", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool engine_3 = false;
[UI_Toggle (enabledText="Release", disabledText="Control"), KSPField(guiName="Engine 4", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool engine_4 = false;
[UI_Toggle (enabledText="Release", disabledText="Control"), KSPField(guiName="Engine 5", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool engine_5 = false;

[KSPField (guiName="engine_name_1", isPersistant=true, guiActive=false)]
public string engine_name_1;
[KSPField (guiName="engine_name_2", isPersistant=true, guiActive=false)]
public string engine_name_2;
[KSPField (guiName="engine_name_3", isPersistant=true, guiActive=false)]
public string engine_name_3;
[KSPField (guiName="engine_name_4", isPersistant=true, guiActive=false)]
public string engine_name_4;
[KSPField (guiName="engine_name_5", isPersistant=true, guiActive=false)]
public string engine_name_5;

public void SetEngineTypeName (int index_, string name_)
{
base.Fields[index_+6].guiName=name_;
switch(index_)
{
case 0: this.engine_name_1=name_; break;
case 1: this.engine_name_2=name_; break;
case 2: this.engine_name_3=name_; break;
case 3: this.engine_name_4=name_; break;
case 4: this.engine_name_5=name_; break;
}
}
public string GetEngineTypeName (int index_)
{
switch(index_)
{
case 0: return this.engine_name_1;
case 1: return this.engine_name_2;
case 2: return this.engine_name_3;
case 3: return this.engine_name_4;
case 4: return this.engine_name_5;
}
throw new SystemException("ASS.GetEngineTypeName() was passed invalid index_ of "+index_);
}
public void SetEngineControl (int index_, bool control_)
{
switch(index_)
{
case 0: this.engine_1=control_; break;
case 1: this.engine_2=control_; break;
case 2: this.engine_3=control_; break;
case 3: this.engine_4=control_; break;
case 4: this.engine_5=control_; break;
}
}
public bool GetEngineControl (int index_)
{
switch(index_)
{
case 0: return this.engine_1;
case 1: return this.engine_2;
case 2: return this.engine_3;
case 3: return this.engine_4;
case 4: return this.engine_5;
}
throw new SystemException("ASS.GetEngineControl() was passed invalid index_ of "+index_);
}
public void SetNoEngineTypes ( int count_ )
{
int a;
this.no_engine_types=count_;
for (a=0; a<this.max_engine_types; a++)
{
if (count_>a)
{
base.Fields[a+6].guiActive =
base.Fields[a+6].guiActiveEditor = true;
}
else
{
base.Fields[a+6].guiActive =
base.Fields[a+6].guiActiveEditor = false;
}
}
}
public void UpdateEngineState ()
{
int a;
this.engine_state=0;
int state=1;
for (a=0; a<this.no_engine_types; a++)
{
if (this.GetEngineControl(a)) this.engine_state += state;
state*=2;
}
}


[KSPEvent (guiName="Search For Engines", guiActive=true, guiActiveEditor=true)]
void EventSearchForEngines ()
{
Debug.Log("(ASS) EventSearchForEngines() Engine State = "+this.engine_state);
this.GenEngineTypeList();
}

bool sas_on=false;
bool rcs_on=true;

int no_parts_in_last_check = -1;//used to check if parts have changed from last update

//list of part indices that are engines the system is will control
int no_engines=0;
int max_engines=20;
int [] engine_indices = new int[20];

int engine_state = -1;
int last_engine_state = -1;

public ASS ()
{
// Debug.Log("(ASS) Constructor()");
}//endof public ASS ()

public override void OnStart (PartModule.StartState state)
{
base.OnStart (state);
// Debug.Log("(ASS) OnStart()");
/* Debug.Log(base.Events.Count+" Events");
int a;
for (a=0; a<base.Events.Count; a++)
{
Debug.Log(a+") "+base.Events.GetByIndex(a).guiName);
}

Debug.Log(base.Fields.Count+" Fields");
for (a=0; a<base.Fields.Count; a++)
{
Debug.Log(a+") "+base.Fields[a].guiName);
}*/
if (this.vessel!=null)
{//vessel = null in editor
//disable the find engines button if we are not in the editor
base.Events.GetByIndex(0).active=false;
}
}

public override void OnUpdate ()
{
base.OnUpdate();

this.CheckGravSensor();
this.UpdateEngineState();
this.GenEngineList();
this.no_parts_in_last_check = this.vessel.parts.Count;//all changes in craft must now have been detected and delt with

if (this.ass_was_on && !this.ass_on)
{
this.throtel=100f;
this.SetThrust();
}
this.ass_was_on = this.ass_on;
if (!this.ass_on) return;

//check for change in control states
bool sas_state = this.vessel.ActionGroups[KSPActionGroup.SAS];
bool sas_changed = false;
// bool rcs_changed = false;
if (this.sas_on != sas_state)
{
this.sas_on = sas_state;
sas_changed = true;
}
if (this.rcs_on != this.vessel.ActionGroups[KSPActionGroup.RCS] )
{
this.rcs_on = this.vessel.ActionGroups[KSPActionGroup.RCS];
// rcs_changed = true;
}

//deal with changes in control state
if (!this.sas_on && sas_changed)
{
//set throttel to max on SAS deactivation
this.throtel=100f;
this.SetThrust();
}


//apply sas stablisation
if (this.sas_on)
{
CenterOfThrustQuery cot = new CenterOfThrustQuery();
Vector3 thrust_vec = Vector3.zero;
float t;
int i;
float combined_thust_center = 0;
float target_thrust = this.gravity*this.vessel.GetTotalMass();
float thrust_range;
//check engine list
for (i=0; i<this.no_engines; i++)
{
this.vessel.parts[this.engine_indices[i]].FindModulesImplementing<ModuleEngines>()[0].OnCenterOfThrustQuery(cot);
t = this.vessel.parts[this.engine_indices[i]].FindModulesImplementing<ModuleEngines>()[0].finalThrust;
cot.dir.x *= t;
cot.dir.y *= t;
cot.dir.z *= t;
thrust_vec += cot.dir;

thrust_range = this.vessel.parts[this.engine_indices[i]].FindModulesImplementing<ModuleEngines>()[0].maxThrust - this.vessel.parts[this.engine_indices[i]].FindModulesImplementing<ModuleEngines>()[0].minThrust;
combined_thust_center += ((t-this.vessel.parts[this.engine_indices[i]].FindModulesImplementing<ModuleEngines>()[0].minThrust)/thrust_range);
}
combined_thust_center /= ((float)this.no_engines);

if (this.rcs_on)
{//Change target thrust and thus vertical speed
float max_thrust_change = (this.max_g_change*9.82f)*this.vessel.GetTotalMass ();
target_thrust -= (max_thrust_change*this.vessel.ctrlState.Z);
}

//Check if we should set thrust to full or none.
//It would be nice to calculate the exact thust needed but that would be very trouble some.
//if ((target_thrust-thrust_vec.magnitude)<0f) this.throtel=0f; else this.throtel=100f;

//Change of plans. Adjust throtle slowly and hope we find a sweatspot !
if (Mathf.Abs(target_thrust-thrust_vec.magnitude) > ((9.82f*this.vessel.GetTotalMass())*0.01) )
{
const float max_change = 4f;
if ((target_thrust-thrust_vec.magnitude)<0f) this.throtel-=max_change; else this.throtel+=max_change;
}
this.SetThrust();
}
}//endof public override void OnUpdate ()

public void SetThrust ()
{
//SetThrust can be caled from strange places like Actions so must have n engine list generator in it to prevent the use of invalid indices
if (this.no_parts_in_last_check!=this.vessel.parts.Co unt)
{
this.GenEngineList();
}
//clamp throtel as setting it out of range is cheating !
if (this.throtel>100f) this.throtel=100f;
if (this.throtel<0f) this.throtel=0f;
int i;
//find engines
for (i=0; i<this.no_engines; i++)
{
this.vessel.parts[this.engine_indices[i]].FindModulesImplementing<ModuleEngines>()[0].thrustPercentage = this.throtel;
}
}//endof void SetThrust ()

//Find active gravity sensor and then set gravity to a useable value
public void CheckGravSensor ()
{
this.FindActiveSensor();
if (this.grav_sensor_index==-1)
{
this.gravity_state = "No Sonsor Found";
}
else
{
if (this.grav_sensor_active)
{
this.gravity_state = "No Active Sensor";
}
else
{
try
{
// Debug.Log("(ASS) grav string = "+this.grav_readout);
if (this.grav_readout.Length>5)
{
string s = this.grav_readout.Substring(0,this.grav_readout.Le ngth-5);
// Debug.Log("(ASS) shrotend grav string = "+s);
this.gravity = float.Parse(s);
}
else this.gravity = 9.82f;
}
catch
{
this.gravity = 9.82f;
}
this.gravity_state = this.grav_readout;
}
}
}//endof void CheckGravSensor ()

public void FindActiveSensor ()
{
if (no_parts_in_last_check!=this.vessel.parts.Count)
{//if parts have change then the index is no longer valid
this.grav_sensor_index = -1;
this.grav_sensor_active = false;
}
if ( this.grav_sensor_index != -1 )
{//if grav sensor is valid check it is active and update readout
this.grav_readout = this.vessel.parts[this.grav_sensor_index].FindModulesImplementing<ModuleEnviroSensor>()[0].readoutInfo;
this.grav_sensor_active = this.grav_readout=="off";
}
//We dont need to search again if we have an active sensor and parts have not changed
if ( no_parts_in_last_check==this.vessel.parts.Count && this.grav_sensor_active==true ) return;

this.grav_sensor_index = -1;
this.grav_sensor_active = false;
int i;
for (i=0; i<this.vessel.parts.Count; i++)
{
if (this.vessel.parts[i].name=="sensorGravimeter")
{
if (this.vessel.parts[i].FindModulesImplementing<ModuleEnviroSensor>().Count>0)
{
// Debug.Log("(ASS) found sensor at "+i+") name="+this.vessel.parts[i].name);
if (this.grav_sensor_index == -1)
{
// Debug.Log("(ASS) grav sensor index set to "+i+" as it was invalid");
this.grav_sensor_index=i;
}
this.grav_readout = this.vessel.parts[i].FindModulesImplementing<ModuleEnviroSensor>()[0].readoutInfo;
this.grav_sensor_active = this.grav_readout=="off";
if (this.grav_sensor_active)
{
// Debug.Log("(ASS) found active sensor at "+i+" name="+this.vessel.parts[i].name);
this.grav_sensor_index = i;
break;
}
}
}
}
}//endof void FindActiveSensor ()

public void AddPartToList ( Part p_, List<Part> list_ )
{
list_.Add(p_);
int i;
for (i=0; i<p_.children.Count; i++)
{
this.AddPartToList(p_.children[i],list_);
}
}

public void GenEngineTypeList ()
{
// Debug.Log("(ASS) GenEngineTypeList() begining");
int c = 0;
string[] new_names = {"","","","",""};
bool[] new_states = {false,false,false,false,false};
//all engines should be off by default unless there is no predefined engine states and then the 1st engine type should be enabled
if (this.no_engine_types==0) new_states[0]=true;
int i;

//vessel will = null in editor so we must cycle through parts list another way

Part start_part = this.part;
while (start_part.parent!=null) start_part=start_part.parent;

List<Part> part_list = new List<Part>();
AddPartToList(start_part,part_list);

// Debug.Log("(ASS) GenEngineTypeList() searching "+part_list.Count+" parts");
for (i=0; i<part_list.Count; i++)
{
if (part_list[i].FindModulesImplementing<ModuleEngines>().Count>0)
{
string s = part_list[i].partInfo.title;
int a;
bool add_type=true;
for (a=0; a<c; a++)
{
if (new_names[a]==s) add_type=false;
}
if (add_type)
{
new_names[c]=s;
//search all engine types for match
for(a=0; a<this.max_engine_types; a++)
{
if (this.GetEngineTypeName(a)==s)
{
new_states[c]=this.GetEngineControl(a);
// Debug.Log("(ASS) GenEngineTypeList() "+s+" was found in old list and set as "+new_states[c]);
}
}
c++;
if (c>=this.max_engine_types) break;
}
}
}

for(i=0; i<c; i++)
{
// Debug.Log("(ASS) GenEngineTypeList() "+i+" = "+new_names[i]+" "+new_states[i]);
this.SetEngineTypeName(i,new_names[i]);
this.SetEngineControl(i,new_states[i]);
}
this.SetNoEngineTypes(c);
// Debug.Log("(ASS) GenEngineTypeList() end");
}

public void GenEngineList ()
{
if (this.no_parts_in_last_check!=this.vessel.parts.Co unt || this.last_engine_state!=this.engine_state)
{
// Debug.Log("(ASS) GenEngineList() Engine State = "+this.engine_state);
this.last_engine_state=this.engine_state;
this.GenEngineTypeList();
this.no_engines=0;
int p,t;
for (p=0; p<this.vessel.parts.Count; p++)
{
if (this.vessel.parts[p].FindModulesImplementing<ModuleEngines>().Count>0)
{
// Debug.Log("(ASS) found engine "+p+" "+this.vessel.parts[p].partInfo.title);
for (t=0; t<this.no_engine_types; t++)
{
if (this.GetEngineControl(t))
{
if ( this.vessel.parts[p].partInfo.title == this.GetEngineTypeName(t) )
{
// Debug.Log("(ASS) Adding engine "+p+" "+this.GetEngineTypeName(t));
this.engine_indices[this.no_engines] = p;
this.no_engines++;
if ( this.no_engines >= this.max_engines )
{
// Debug.Log("(ASS) Found max number of engines "+this.max_engines);
return;
}
}
}
}
}
}
// Debug.Log("(ASS) "+this.no_engines+" Engines in control list");
}
}

public override void OnLoad (ConfigNode node)
{
// Debug.Log("(ASS) OnLoad node = "+node.ToString());
base.OnLoad(node);
//This should eneble and disable the correct GUI engine components
this.SetNoEngineTypes(this.no_engine_types);
//This should set the GUI names from the stored data
int i;
for (i=0; i<this.no_engine_types; i++) this.SetEngineTypeName(i,this.GetEngineTypeName(i) );
}
}//endof public class ASS : PartModule
}//endof namespace ASS

Link to comment
Share on other sites

:D just added a new video for the 0.3 demonstration.

Iv also updated my to do list with some thoughts about where to go next.

Iv still not tested this off world yet but i am in my base component design phase hence the thoughts about what changes would be good for 0.4

Building the Mun Lander demonstrated to me how important it is to link translation engines into the RCS keys. Having 3 different key systems each with 3 directions of control will get Jeb killed !

If all gos well then i think 0.4 should be the completion of the plugin and a change to V1.

It should probably rename it then as to make it easier to understand. Ill certainly keep a reference to Cupcake in the description as the primary inspiration though.

Anyone got an idea for a good name ?

Advanced VTOL Control Systems Humm..... adding words to an acronym is just wrong.

Advanced Hover System (VTOL) humm... AHS might be a better name for the component and the plugin.

Rocket Operation Coordination Kontroler...... well yeah i might need to brush of an old fashioned dictionary and search for some good K words.

Id think knowing k words could nether be a bad thing when it comes to naming stuff in the PNK engineering department.

This would be a great time to suggest any ideas you may have.

The next update will probably involve another complete rewrite to get everything to function correctly so suggestions are best done sooner than later.

Have fun and fly safely.

Link to comment
Share on other sites

I can't get the part to show up in the part list. I didn't see any errors in the log.

I added the module to one of the cockpits and the effect is pretty slick. Control of jet engines is way better than I expected. You seem to have hit a good balance between automation and challenge; it makes VTOLs possible to fly with precision without making it cheaty.

Link to comment
Share on other sites

I can't get the part to show up in the part list. I didn't see any errors in the log.

I added the module to one of the cockpits and the effect is pretty slick.

Hummm.... Interesting

You said you added the module to a cockpit so you must know your way around KSP very well.

So i have to ask the stupid questions just to get them out of the way.

Did you copy the plugin to the GameData directory ? Of course you did or the module would not have been found.

Are you running version 0.23 ? Yes or the module would not work due to lack of tweakables.

Did you look in the correct place for the control system ? yes why where else would i look for a control system.

N3Y5S77.png

So for some speculation.

Looking at my log file i notice that there is a space in my part name.

[LOG 10:06:17.233] Config(PART) Im_A_Cupcake_Wannabe/part/PNK ASS

I think that can only be a bad thing. Im testing on windows 7 and it seams to work fine but im not sure why there's a directory style entry in the log with the part name in it anyway.

Im sure this could cause problems on some operating systems as file paths with spaces arnt a good thing but still I cant see how it could be an actual file reference.

It is something that looks very dodgy so you could try changing the part.cfg file from name = PNK ASS to name = PNK_ASS and seeing if that fixes it.

If it still dosnt work posting your log file using the code wrap would be very handy.

Control of jet engines is way better than I expected. You seem to have hit a good balance between automation and challenge; it makes VTOLs possible to fly with precision without making it cheaty.

Thanks :).

It was more luck than anything else. Just turns out that keeping things simple makes it work better. The original SAS system is where all the clever trickery is going on.

Iv tried doing the docking without using translation rockets (like cupcake dose) and it is almost impossible using keys even with the hover control so it dose just shift the problem from one of precision control to expert design.

Iv still not docked a full orange tank yet but im sue it can be done with a well designed craft (balanced translation rockets is the key).

Things do start to get very interesting with large masses and jets though as the spool up time starts to get out of hand but then using 2 types of engines for a hover fixes that at the cost of fuel usage.

The main thing is that its a lot of fun :)

Edited by mpink
Link to comment
Share on other sites

Got it, user error. I swear I looked through every part to try to find it but I must not have noticed the second 'copy' of the stabilizer.

Is there an easy way to query jets about their spool characteristics? If so you could have it only use the rockets while the jets are powering up, or if it needs more thrust than they can provide. Rocket fuel would last a lot longer that way.

Link to comment
Share on other sites

Im on the drunk side so im struggling with this a bit but hear gos.

Got it, user error. I swear I looked through every part to try to find it but I must not have noticed the second 'copy' of the stabilizer.

Is there an easy way to query jets about their spool characteristics? If so you could have it only use the rockets while the jets are powering up, or if it needs more thrust than they can provide. Rocket fuel would last a lot longer that way.

No problems.

Its all part of the development process and i most certainly should add making a model to the to do list.

all engines use the same module and so yes easy and no hard.

The basic jet engine looks like this


name = ModuleEngines
useEngineResponseTime = True
engineAccelerationSpeed = 0.12
engineDecelerationSpeed = 0.21

Very easy to find and hard to understand but

easy to not care about and fun to tweak around while making your life just a little harder.

The even more complicated part of calculating jet thrust comes from the height curves and pressure curves.

The plugin uses a simple is best strategy and so sets all controlled engines to the same thrust which proves far more interesting than you may imagine if jets are involved.

The very 1st video shows my pod racer hovering with jets and rockets enabled in the A.S.S.

This uses lots of fuel but provides instant vertical control response that was needed at times.

You could just Action group the rockets so they turn off and give yourself some up thrust prior to disabling to get you through the change.

Turning them back on would not be so bad as it would be an instant boost up and so not do any damage but the intent is to find the correct balance of jet to rocket and im sure it changes from planet to planet.

As spotted by others the plugin is not overpowered and just intended to change the form of the VTOL challenge.

The mod is very interesting hear as it runs on fact not fiction. It adjust the thrust tweak based on the current known output of the controlled engines and nothing else. This means adding thrust by activating none controlled engines will not effect the controlled engines so will result in drastic speed changes. With jets it also results in a constant yo yo thrust level as the due to spool rates.

It is finding the balance of jets to rockets that is intended to be the designers challenge (Replacing the piloting challenge).

The I build a Mun Lander video has just jets in the A.S.S control and all the other rockets are set to action groups bound to the key pad.

This is my third 3D set of controls that makes Jeb die so very much at the moment.

I use the (key pad) Arrows for translation with exception for very fine height controls provided by the RCS H,N key watching.

Then i use 1 action group for toggling the upwards rockets for the oh crap moments.

5 turns all the keypad controlled rockets off. This is needed as you will get confused and press the wrong button not know what you have done and start panicking. There are times in the video where forward and backward engines are both on and im so focused on the nav ball i just dont know so i hit the panic button and try to regain control.

Combine this with SAS drift and its no walk in the park.

So, Where's the license?

well in the 1st lines of the code

//M Pink provides ASS (v0.3) freely for anyone to use for anyreason they see fit.

//M Pink is not responsable for any damage caused by ASS.

//Ass Should Be Used At Your Own Risk !

Im not one for caring to much about licenses and so this dose bring up the possibility for anyone to claim the rights to this code and take ownership of it and or sell it as there own work but for me the greatest thing i could hope for is the plugin being claimed and used by squad and me not having to follow up with any problems of my bad coding :)

I think it dose count as a licence without using any stranded copyright.

If there is any way i could provide this more freely and clearly or something i should add please share.

I like the new demo video.

I buy it.

It works with turbine engines too?

Thanks

you could always send some cheap beer

I hope so but its probably a bit confusing as to how at the moment.

The tweakable is confusing and i should write something about it but hope to replace the system soon.

The design is meant to be a bit limiting so it can only recognize the first 5 found engine types.

It presents these as tweakable (Right click PNK A.S.S part) toggles with Control/Release next to them.

It gets very confusing though when in the editor as there is a scan for engines button that is needed there to find the engines.

What makes things truly confusing though is pressing sometimes dose nothing but if you right click the part again after triggering this, the menu will refresh and you will see the list.

Shortened names sure don't help but i don't think there's much i can do there and the next update should change this totally anyways.

0YOwM6v.png

not to be an insult, but

this basically turns your aircraft into an RCS block?

Well thats where im pushing for yes.

No insult taken its actually quite an achievement to be able to practice zero g whilst so close to a planet that you wont always die when you crash.

If memory serves some of the early moon lander practice craft were the most dangerous things we have ever tried to fly.

The idea is that the mod combined with some design skills should negate gravity until your fuel runs out and thus just a few RCS thrusters could get you from the launch pad to the VAB helipad with no danger.

The fun comes though when you try to move something or change the S.A.S lock. The hover thrust is controlled by your crafts angle and A.S.S dose nothing to change this so life can get very complicated very very quickly if you place you RCS thrusters in a stupid place.

This is truly the most basic form of fly by wire as it just removes the need for you to do some computing in flight. It just trys to keep a thrust stream = to the strength of gravity and nothing more, not even compare if the thrust and gravity are in the opposite directions (loops are not a good idea).

Link to comment
Share on other sites

This is truly the most basic form of fly by wire as it just removes the need for you to do some computing in flight. It just trys to keep a thrust stream = to the strength of gravity and nothing more, not even compare if the thrust and gravity are in the opposite directions (loops are not a good idea).

This brings up a simple addition that could be made: assume the engines are pointed 'down' on the aircraft (relative to the currently active control part), and add extra throttle based on the angle of your craft to the ground. Just blindly make it so that the vertical portion of the force is equal to gravity.

Link to comment
Share on other sites

This brings up a simple addition that could be made: assume the engines are pointed 'down' on the aircraft (relative to the currently active control part), and add extra throttle based on the angle of your craft to the ground. Just blindly make it so that the vertical portion of the force is equal to gravity.

Yeah it sounds interesting and I will give it a try. I dont think it will work though.

I think that if you rotated to 45 degrease the jets would take so long to spool up that you would find it not very useful but thats what rockets are for :huh:.

I do see that it would add a lot of power to a racer and let you focus more on not crashing rather than worrying about your height so much.

It would be very easy to extend on this and say that it should select the best engines for the hover and try to only thrust up but then we would be replacing more RCS systems and loosing translation ability.

I am very concerned about what should or will happen when the hover engines cant help with vertical climb though.

Currently with the fixed thrust system you can do loops without worrying that the jets might power down or power up to full when your pointing down :0.0:.

Hummmm..... I see ups and downs to both systems with nether being perfect :) so i like it and will add a mode system so you can choose what hover type to use.

Link to comment
Share on other sites

Perhaps just have thrust be at gravity if the angle is over 90 degrees? That way jets will never spool below what you need to maintain flight. I didn't try loops...I assume you'd have to hold the up key for awhile afterwards to cancel out the downward velocity you get?

Link to comment
Share on other sites

Perhaps just have thrust be at gravity if the angle is over 90 degrees? That way jets will never spool below what you need to maintain flight. I didn't try loops...I assume you'd have to hold the up key for awhile afterwards to cancel out the downward velocity you get?

My main reason for looping was a mismatch in jet output resulting in great climb rate after undocking LOL (not shown in a video).

It all depends on craft weight as light craft are very forgiving.

I think providing there are a decent amount of Action groups so you can choose the correct setup with 1 key then there should be no problem with getting 100% thrust when pointing over horizontal :)

I think no matter what i do, for anything approaching stunt flight things will get stupidly complicated and always need a vertical rocket toggle key (thats good).

Oh and there is always the normal throttle settings that override tweak thrust anyways. You can just hit X and throttle up again.

I cant wait to see how it handles :).

Im pondering what i should do to showoff the next version of Control system ? One thought is beating my own time for my KSC tour challenge but doing a loop around the walkway :) or my other thought is to make a monstrous 3 part supper heavy lifter for the today i build a rocket challenge.

Im still some way off of making my kethane mining colony that was my main reason for making the plugin but things are moving along there too.

Been looking into models today, mainly at the part merge tool.

x9j75GB.png

It done a very good job of showing me how to use stock parts and save game resources.

Im a big fan of the stock look and way prefer the laziness of not having to make new models and textures.

Only downside is a game bug forcing vertical stacking like with the 2 cross shaped parts.

Link to comment
Share on other sites

Ok i have what seams to be a working V0.4 Advanced Hover System update.

Im more than sure there shall be some bugs to squash though. For instance iv not tested it with solid rockets yet :huh:. It will probably work with them too so i should check that and see if it needs fixing.

Change Log

v0.4

Redesigned the ASS into an AHS so it now has hover direction, hover mode and some-other tweakables.

Added RCSEngineTranslation Module and added it to all engine parts on start-up.

Made 2 new models so that the AHS have there very own look (stock style).

Added resource usage so it now eats up the energy and leaks out heat.

Added Master/Slave system along with a more limiting number of controllable engines so multiple AHS should be needed for large craft.

Added the Active hover mode as suggested by Virindi (good call there ;))

V0.4 Code with do what you want licence

//M Pink provides AHS (v0.4) freely for anyone to use for anyreason they see fit.
//M Pink is not responsable for any damage caused by AHS.
//AHS Should Be Used At Your Own Risk !

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace AHS
{
[KSPAddon(KSPAddon.Startup.MainMenu,true)]
public class RCSAddonSetup :MonoBehaviour
{
void Awake ()
{
Debug.Log("(AHS.RCSAddonSetup) Awake()");

PartLoader pl = PartLoader.Instance;
if (pl==null)
{
Debug.Log("(AHS.RCSAddonSetup) Partloader was null");
}
else
{
Debug.Log("(AHS.RCSAddonSetup) Partloader Found");
Debug.Log("(AHS.RCSAddonSetup) Number of parts to scan = "+pl.parts.Count);
int a=0;
for (a=0; a<pl.parts.Count; a++)
{
bool has_engine = false;
bool has_rcs = false;
int b=0;
for (b=0; b<pl.parts[a].moduleInfos.Count; b++)
{
if ("Engine"==pl.parts[a].moduleInfos[b].moduleName) has_engine=true;
if ("RCSEngineTranslation"==pl.parts[a].moduleInfos[b].moduleName) has_rcs=true;
}
if (has_engine && !has_rcs)
{
Debug.Log("(AHS.RCSAddonSetup) Engine module found for "+pl.parts[a].title+" adding RCSEngineTranslation module");
pl.parts[a].partPrefab.AddModule("RCSEngineTranslation");
AvailablePart.ModuleInfo info = new AvailablePart.ModuleInfo();
info.moduleName = "RCSEngineTranslation";
pl.parts[a].moduleInfos.Add(info);
}//endof if (has_engine && !has_rcs)
}//endof for (a=0; a<pl.parts.Count; a++)
}//end of else if (pl==null)
}
}

public class RCSEngineTranslation : PartModule
{
/* [KSPAddon (KSPAddon.Startup.MainMenu,true)]
void KSPAddonSetupRCSModules ()
{
Debug.Log("(AHS.RCSEngineTranslation) KSPAddonSetupRCSModules()");

PartLoader pl = PartLoader.Instance;
if (pl==null)
{
Debug.Log("(AHS.RCSEngineTranslation) Partloader was null");
}
else
{
Debug.Log("(AHS.RCSEngineTranslation) Partloader Found");
Debug.Log("(AHS.RCSEngineTranslation) Number of parts to scan = "+pl.parts.Count);
int a=0;
for (a=0; a<pl.parts.Count; a++)
{
bool has_engine = false;
bool has_rcs = false;
int b=0;
for (b=0; b<pl.parts[a].moduleInfos.Count; b++)
{
if ("Engine"==pl.parts[a].moduleInfos[b].moduleName) has_engine=true;
if ("RCSEngineTranslation"==pl.parts[a].moduleInfos[b].moduleName) has_rcs=true;
}
if (has_engine && !has_rcs)
{
Debug.Log("(AHS.RCSEngineTranslation) Engine module found for "+pl.parts[a].title+" adding RCSEngineTranslation module");
pl.parts[a].partPrefab.AddModule("RCSEngineTranslation");
AvailablePart.ModuleInfo info = new AvailablePart.ModuleInfo();
info.moduleName = "RCSEngineTranslation";
pl.parts[a].moduleInfos.Add(info);
}//endof if (has_engine && !has_rcs)
}//endof for (a=0; a<pl.parts.Count; a++)
}//end of else if (pl==null)
}*/

[KSPField (guiName = "dimention", isPersistant=true, guiActive=false)]
int dimention = 0;
[KSPField (guiName = "direction", isPersistant=true, guiActive=false)]
bool invert_direction = false;

[KSPField (guiName = "RCS Direction", isPersistant=false, guiActive=true, guiActiveEditor=true)]
public string state_string = "Not Set Yet";

[KSPEvent ( guiName="Next RCS Dimention", guiActive=true, guiActiveEditor=true )]
public void EventNextDirection()
{
this.dimention++;
if (this.dimention>3) this.dimention=0;
this.DisplayMode();
}

[KSPEvent ( guiName="Togle RCS Direction", guiActive=true, guiActiveEditor=true )]
public void EventTogleDirection()
{
this.invert_direction = !this.invert_direction;
this.DisplayMode();
}

[KSPAction ("Set RCS None")]
void ActionSetRCSNone ()
{
this.dimention=0;
this.DisplayMode();
}
[KSPAction ("Set RCS Left")]
void ActionSetRCSLeft ()
{
this.dimention=1;
this.invert_direction=false;
this.DisplayMode();
}
[KSPAction ("Set RCS Right")]
void ActionSetRCSRight ()
{
this.dimention=1;
this.invert_direction=true;
this.DisplayMode();
}
[KSPAction ("Set RCS Up")]
void ActionSetRCSUp ()
{
this.dimention=2;
this.invert_direction=false;
this.DisplayMode();
}
[KSPAction ("Set RCS Down")]
void ActionSetRCSDown ()
{
this.dimention=2;
this.invert_direction=true;
this.DisplayMode();
}
[KSPAction ("Set RCS Forward")]
void ActionSetRCSForward ()
{
this.dimention=3;
this.invert_direction=false;
this.DisplayMode();
}
[KSPAction ("Set RCS Backward")]
void ActionSetRCSBackward ()
{
this.dimention=3;
this.invert_direction=true;
this.DisplayMode();
}


public void DisplayMode ()
{
switch (this.dimention)
{
case 1: if (!this.invert_direction) this.state_string="Left"; else this.state_string="Right"; break;
case 2: if (!this.invert_direction) this.state_string="Up"; else this.state_string="Down"; break;
case 3: if (!this.invert_direction) this.state_string="Forward"; else this.state_string="Backward"; break;
default: this.state_string = "None"; break;
}
}

public RCSEngineTranslation ()
{
}//end of public RCSEngineTranslation ()

public override void OnLoad (ConfigNode node)
{
this.DisplayMode();
}
public override void OnStart (PartModule.StartState state)
{
this.DisplayMode();
}

}//end of public class RCSEngineTranslation : PartModule

public class AHS : PartModule
{
//energy used per seconde
[KSPField (isPersistant=true)]
public float power_consumption = 0.5f;
//heat produced per seconde
[KSPField (isPersistant=true)]
public float heat_production = 50f;

//thrust settings for all dimentions
//isPersistant is probably not needed as saves cant be done throtled up in an atmoshpher
[KSPField (guiName = "throtel left", isPersistant=true, guiActive=false)]
public float thrust_left = 100f;
[KSPField (guiName = "throtel right", isPersistant=true, guiActive=false)]
public float thrust_right = 100f;
[KSPField (guiName = "throtel up", isPersistant=true, guiActive=false)]
public float thrust_up = 100f;
[KSPField (guiName = "throtel down", isPersistant=true, guiActive=false)]
public float thrust_down = 100f;
[KSPField (guiName = "throtel forward", isPersistant=true, guiActive=false)]
public float thrust_forward = 100f;
[KSPField (guiName = "throtel backward", isPersistant=true, guiActive=false)]
public float thrust_backward = 100f;




//start of display fields
[KSPField (guiName = "Version", guiActive=true, guiActiveEditor=true)]
public string version = "0.4";

[KSPField (guiName = "Comand State", guiActive=true, guiActiveEditor=true)]
public string comand_state = "Not Set Yet";//Master,Slave,No Energy

[KSPField (guiName = "Engine Usage", guiActive=true, guiActiveEditor=true)]
public string engine_usage = "Not Set Yet";//no / max

[KSPField (guiName="Gravity", isPersistant=false, guiActive=true, guiActiveEditor=false)]
public string gravity_state = "Not Set Yet";
public int grav_sensor_index = -1;
public bool grav_sensor_active = false;
public string grav_readout = ".....";
public float gravity = 9.82f;

[UI_Toggle (enabledText="Deactivate", disabledText="Activate"), KSPField(guiName="AHS", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public bool ahs_on = true;
public bool ahs_was_on = true;
[KSPAction ("Activate AHS")]
public void ActionActivateAHS (KSPActionParam param)
{
this.ahs_on=true;
}
[KSPAction ("Deactivate AHS")]
public void ActionDeactivateAHS (KSPActionParam param)
{
this.ahs_on=false;
}
[KSPAction ("Toggle AHS")]
public void ActionToggleAHS (KSPActionParam param)
{
this.ahs_on=!this.ahs_on;
}




//halfway range seporators
[UI_FloatRange (stepIncrement=0.01f, maxValue=4f, minValue=0.01f), KSPField (guiName="Translation G", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public float max_g_change = 0.25f;

[UI_FloatRange (stepIncrement=0.5f, maxValue=100f, minValue=0.5f), KSPField (guiName="Thrust Change Rate", isPersistant=true, guiActive=true, guiActiveEditor=true)]
public float max_thrust_change = 4f;





[KSPField (guiName = "dimention", isPersistant=true, guiActive=false)]
int hover_dimention = 3;
[KSPField (guiName = "direction", isPersistant=true, guiActive=false)]
bool hover_invert_direction = false;

[KSPField (guiName = "Hover Direction", guiActive=true, guiActiveEditor=true)]
public string hover_state_string = "Not Set Yet";

//index 0
[KSPEvent ( guiName="Next Hover Dimention", guiActive=true, guiActiveEditor=true )]
public void EventNextDirection()
{
this.hover_dimention++;
if (this.hover_dimention>3) this.hover_dimention=1;
this.HoverDisplayMode();
}
//inde 1
[KSPEvent ( guiName="Togle Hover Direction", guiActive=true, guiActiveEditor=true )]
public void EventTogleDirection()
{
this.hover_invert_direction = !this.hover_invert_direction;
this.HoverDisplayMode();
}



[KSPField (guiName="Hover Mode: Active", guiActive=false, isPersistant=true)]
public int hover_mode = 1;
//index 2
[KSPEvent (guiName="Hover Mode: Active", guiActive=true, guiActiveEditor=true)]
public void EventNextHoverMode()
{
this.hover_mode++;
if (this.hover_mode>2) this.hover_mode=0;
this.SetHoverGUI();
}
[KSPAction ("Set Active Hover Mode")]
public void ActionSetActiveHover (KSPActionParam param)
{
this.hover_mode=1;
this.SetHoverGUI();
}
[KSPAction ("Set Fixed Hover Mode")]
public void ActionSetFixedHover (KSPActionParam param)
{
this.hover_mode=2;
this.SetHoverGUI();
}
[KSPAction ("Set Hover Mode Off")]
public void ActionSetHoverModeOff (KSPActionParam param)
{
this.hover_mode=0;
this.SetHoverGUI();
}
public void SetHoverGUI ()
{
switch(this.hover_mode)
{
case 0: base.Events.GetByIndex(2).guiName="Hover Mode: Off"; break;
case 1: base.Events.GetByIndex(2).guiName="Hover Mode: Active"; break;
case 2: base.Events.GetByIndex(2).guiName="Hover Mode: Fixed"; break;
}
}

[KSPAction ("Hover Left")]
public void ActionHoverLeft (KSPActionParam param)
{
this.hover_dimention=1;
this.hover_invert_direction=false;
this.HoverDisplayMode();
}
[KSPAction ("Hover Right")]
public void ActionHoveRight (KSPActionParam param)
{
this.hover_dimention=1;
this.hover_invert_direction=true;
this.HoverDisplayMode();
}
[KSPAction ("Hover Up")]
public void ActionHoverUP (KSPActionParam param)
{
this.hover_dimention=2;
this.hover_invert_direction=false;
this.HoverDisplayMode();
}
[KSPAction ("Hover Down")]
public void ActionHoverDown (KSPActionParam param)
{
this.hover_dimention=2;
this.hover_invert_direction=true;
this.HoverDisplayMode();
}
[KSPAction ("Hover Forward")]
public void ActionHoverForward (KSPActionParam param)
{
this.hover_dimention=3;
this.hover_invert_direction=false;
this.HoverDisplayMode();
}
[KSPAction ("Hover Backward")]
public void ActionHoverBackward (KSPActionParam param)
{
this.hover_dimention=3;
this.hover_invert_direction=true;
this.HoverDisplayMode();
}

public void HoverDisplayMode ()
{
switch (this.hover_dimention)
{
case 1: if (!this.hover_invert_direction) this.hover_state_string="Left"; else this.hover_state_string="Right"; break;
case 2: if (!this.hover_invert_direction) this.hover_state_string="Up"; else this.hover_state_string="Down"; break;
case 3: if (!this.hover_invert_direction) this.hover_state_string="Forward"; else this.hover_state_string="Backward"; break;
default: this.hover_state_string = "None"; break;
}
}

//index 3
[KSPEvent (guiName="Force Editor Update", guiActive=true, guiActiveEditor=true)]
void EventForceEditorUpdate ()
{
this.CheckSlaveState();
this.CountEngines();
}

bool sas_on=false;
bool rcs_on=true;

int no_parts_in_last_check = -1;//used to check if parts have changed from last update

[KSPField (isPersistant=true)]
public int max_engines=10;
public int controlable_engines=0;
public List<int> slave_indices = new List<int>();

public AHS ()
{
Debug.Log("(AHS) Constructor()");
/*
PartLoader pl = PartLoader.Instance;
if (pl==null)
{
Debug.Log("(AHS) Partloader was null");
}
else
{
Debug.Log("(AHS) Partloader Found");
Debug.Log("(AHS) Number of parts to scan = "+pl.parts.Count);
int a=0;
for (a=0; a<pl.parts.Count; a++)
{
bool has_engine = false;
bool has_rcs = false;
int b=0;
for (b=0; b<pl.parts[a].moduleInfos.Count; b++)
{
if ("Engine"==pl.parts[a].moduleInfos[b].moduleName) has_engine=true;
if ("RCSEngineTranslation"==pl.parts[a].moduleInfos[b].moduleName) has_rcs=true;
}
if (has_engine && !has_rcs)
{
Debug.Log("(AHS) Engine module found for "+pl.parts[a].title+" adding RCSEngineTranslation module");
pl.parts[a].partPrefab.AddModule("RCSEngineTranslation");
AvailablePart.ModuleInfo info = new AvailablePart.ModuleInfo();
info.moduleName = "RCSEngineTranslation";
pl.parts[a].moduleInfos.Add(info);
}//endof if (has_engine && !has_rcs)
}//endof for (a=0; a<pl.parts.Count; a++)
}//end of else if (pl==null)
*/
}//endof public AHS ()

public override void OnStart (PartModule.StartState state)
{
base.OnStart (state);
if (this.vessel!=null)
{//vessel = null in editor
//disable editor only events
Debug.Log ("(AHS) Number of events = "+base.Events.Count);
base.Events.GetByIndex(3).active=false;
}
this.HoverDisplayMode();
this.SetHoverGUI();
if (base.part.stagingIcon == string.Empty)
{
base.part.stagingIcon = "ADV_SAS";
}
}

public override void OnUpdate ()
{
base.OnUpdate();

this.CheckGravSensor();
//all changes in craft must now have been detected and delt with
this.no_parts_in_last_check = this.vessel.parts.Count;

this.ahs_was_on = this.ahs_on;
if (!this.ahs_on) return;

//check for change in control states
bool sas_state = this.vessel.ActionGroups[KSPActionGroup.SAS];
bool sas_changed = false;
if (this.sas_on != sas_state)
{
this.sas_on = sas_state;
sas_changed = true;
}
if (this.rcs_on != this.vessel.ActionGroups[KSPActionGroup.RCS] )
{
this.rcs_on = this.vessel.ActionGroups[KSPActionGroup.RCS];
}

double amount_needed = (double)(this.power_consumption * TimeWarp.deltaTime);
double amount_got = this.part.RequestResource("ElectricCharge" ,amount_needed);
if ((amount_needed*0.95) > amount_got )
{
this.comand_state = "No Energy";
this.CheckSlaveState();
return;
}
else
{
if (this.comand_state=="No Energy")
{
this.comand_state="Rebooting";
this.CheckSlaveState();
return;
}
}
this.part.temperature += (this.heat_production * TimeWarp.deltaTime);

//deal with changes in control state
if (!this.sas_on && sas_changed)
{
//set thrust back to max on SAS deactivation
this.thrust_backward = 100f;
this.thrust_down = 100f;
this.thrust_forward = 100f;
this.thrust_left = 100f;
this.thrust_right = 100f;
this.thrust_up = 100f;
}
this.CheckSlaveState();//master slave states could change every frame so need checking
this.CountEngines();//engines may be changed anytime so need rechecking

//apply sas stablisation
if (this.sas_on)
{
CenterOfThrustQuery cot = new CenterOfThrustQuery();
Vector3 thrust_up_vec = Vector3.zero;
Vector3 thrust_down_vec = Vector3.zero;
Vector3 thrust_left_vec = Vector3.zero;
Vector3 thrust_right_vec = Vector3.zero;
Vector3 thrust_forward_vec = Vector3.zero;
Vector3 thrust_backward_vec = Vector3.zero;
float t;
int i;
int c=0;
List<ModuleEngines> engine_modules = new List<ModuleEngines>();
List<string> engine_rcs_state = new List<string>();

//make thrust vectors
for (i=0; i<this.vessel.parts.Count; i++)
{
if (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>().Count>0)
{
if (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string!="None" && c<this.controlable_engines)
{
c++;
if (c==this.controlable_engines) Debug.Log("(AHS) Max controlable engines reached");
engine_modules.Add(this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0]);
engine_rcs_state.Add (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string);
this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0].OnCenterOfThrustQuery(cot);
t = this.vessel.parts[i].FindModulesImplementing<ModuleEngines>()[0].finalThrust;
cot.dir.x *= t;
cot.dir.y *= t;
cot.dir.z *= t;
switch (this.vessel.parts[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string)
{
case "Up": thrust_up_vec += cot.dir; break;
case "Down": thrust_down_vec += cot.dir; break;
case "Left": thrust_left_vec += cot.dir; break;
case "Right": thrust_right_vec += cot.dir; break;
case "Forward": thrust_forward_vec += cot.dir; break;
case "Backward": thrust_backward_vec += cot.dir; break;
}
}
}
}

float target_up_thrust = 0f;
float target_down_thrust = 0f;
float target_left_thrust = 0f;
float target_right_thrust = 0f;
float target_forward_thrust = 0f;
float target_backward_thrust = 0f;

if (this.rcs_on)
{//Change target thrust and thus vertical speed
float g_change_in_thrust = (this.max_g_change*9.82f)*this.vessel.GetTotalMass();
target_down_thrust -= (g_change_in_thrust*this.vessel.ctrlState.Y);
target_up_thrust += (g_change_in_thrust*this.vessel.ctrlState.Y);
target_left_thrust -= (g_change_in_thrust*this.vessel.ctrlState.X);
target_right_thrust += (g_change_in_thrust*this.vessel.ctrlState.X);
target_forward_thrust -= (g_change_in_thrust*this.vessel.ctrlState.Z);
target_backward_thrust += (g_change_in_thrust*this.vessel.ctrlState.Z);
}

//adjust target thrust for hover correction
if (this.hover_mode>0)
{
switch (this.hover_state_string)
{
case "Up": target_up_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case "Down": target_down_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case "Left": target_left_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case "Right": target_right_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case "Forward": target_forward_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
case "Backward": target_backward_thrust += (this.gravity*this.vessel.GetTotalMass()); break;
}

if (this.hover_mode==1)
{
Vector3 sky_vector = this.vessel.mainBody.transform.position-this.vessel.transform.position;
Vector3 sky_n = sky_vector.normalized;
Vector3 thr_n = Vector3.zero;

//rotating one normal into the other should return 0 if they are on the same plan and 1 if at 90 degrease to each other
switch (this.hover_state_string)
{
case"Up": thr_n=thrust_up_vec.normalized; break;
case"Down": thr_n=thrust_down_vec.normalized; break;
case"Left": thr_n=thrust_left_vec.normalized; break;
case"Right": thr_n=thrust_right_vec.normalized; break;
case"Forward": thr_n=thrust_forward_vec.normalized; break;
case"Backward": thr_n=thrust_backward_vec.normalized; break;
}

float m = Mathf.Abs( (sky_n.x* thr_n.x) + (sky_n.y*thr_n.y) + (sky_n.z* thr_n.z));
if (m==0f) m=0.00000001f;//avoid divide by 0

switch (this.hover_state_string)
{
case"Up": target_up_thrust /= m; break;
case"Down": target_down_thrust /= m; break;
case"Left": target_left_thrust /= m; break;
case"Right": target_right_thrust /= m; break;
case"Forward": target_forward_thrust /= m; break;
case"Backward": target_backward_thrust /= m; break;
}
}
}

//Adjust throtles slowly and hope we find a sweatspot !
float irevavent_thrust = ((9.82f*this.vessel.GetTotalMass())*0.01f);
if (target_up_thrust==thrust_up_vec.magnitude && thrust_up_vec.magnitude==0f) this.thrust_up=0f; else
if (Mathf.Abs(target_up_thrust-thrust_up_vec.magnitude) > irevavent_thrust )
{
if ((target_up_thrust-thrust_up_vec.magnitude)<0f) this.thrust_up-=this.max_thrust_change; else this.thrust_up+=this.max_thrust_change;
this.thrust_up = Mathf.Clamp(this.thrust_up,0f,100f);
}
if (target_down_thrust==thrust_down_vec.magnitude && thrust_down_vec.magnitude==0f) this.thrust_down=0f; else
if (Mathf.Abs(target_down_thrust-thrust_down_vec.magnitude) > irevavent_thrust )
{
if ((target_down_thrust-thrust_down_vec.magnitude)<0f) this.thrust_down-=this.max_thrust_change; else this.thrust_down+=this.max_thrust_change;
this.thrust_down = Mathf.Clamp(this.thrust_down,0f,100f);
}
if (target_left_thrust==thrust_left_vec.magnitude && thrust_left_vec.magnitude==0f) this.thrust_left=0f; else
if (Mathf.Abs(target_left_thrust-thrust_left_vec.magnitude) > irevavent_thrust )
{
if ((target_left_thrust-thrust_left_vec.magnitude)<0f) this.thrust_left-=this.max_thrust_change; else this.thrust_left+=this.max_thrust_change;
this.thrust_left = Mathf.Clamp(this.thrust_up,0f,100f);
}
if (target_right_thrust==thrust_right_vec.magnitude && thrust_right_vec.magnitude==0f) this.thrust_right=0f; else
if (Mathf.Abs(target_right_thrust-thrust_right_vec.magnitude) > irevavent_thrust )
{
if ((target_right_thrust-thrust_right_vec.magnitude)<0f) this.thrust_right-=this.max_thrust_change; else this.thrust_right+=this.max_thrust_change;
this.thrust_right = Mathf.Clamp(this.thrust_right,0f,100f);
}
if (target_forward_thrust==thrust_forward_vec.magnitude && thrust_forward_vec.magnitude==0f) this.thrust_forward=0f; else
if (Mathf.Abs(target_forward_thrust-thrust_forward_vec.magnitude) > irevavent_thrust )
{
if ((target_forward_thrust-thrust_forward_vec.magnitude)<0f) this.thrust_forward-=this.max_thrust_change; else this.thrust_forward+=this.max_thrust_change;
this.thrust_forward = Mathf.Clamp(this.thrust_forward,0f,100f);
}
if (target_backward_thrust==thrust_backward_vec.magnitude && thrust_backward_vec.magnitude==0f) this.thrust_backward=0f; else
if (Mathf.Abs(target_backward_thrust-thrust_backward_vec.magnitude) > irevavent_thrust )
{
if ((target_backward_thrust-thrust_backward_vec.magnitude)<0f) this.thrust_backward-=this.max_thrust_change; else this.thrust_backward+=this.max_thrust_change;
this.thrust_backward = Mathf.Clamp(this.thrust_backward,0f,100f);
}

//set engine thrusts
for (i=0; i<engine_modules.Count; i++)
{
switch (engine_rcs_state[i])
{
case "Up": engine_modules[i].thrustPercentage = this.thrust_up; break;
case "Down": engine_modules[i].thrustPercentage = this.thrust_down; break;
case "Left": engine_modules[i].thrustPercentage = this.thrust_left; break;
case "Right": engine_modules[i].thrustPercentage = this.thrust_right; break;
case "Forward": engine_modules[i].thrustPercentage = this.thrust_forward; break;
case "Backward": engine_modules[i].thrustPercentage = this.thrust_backward; break;
}
}
}
}//endof public override void OnUpdate ()

//Used in editor to update gui info. Should be done in update as a full engine scan will be done every time as to check for states
public void CountEngines ()
{
int i;
int master_index=-1;

//vessel will = null in the editor so we must cycle through parts list another way if it is not set
List <Part> part_list;
if (this.vessel!=null)
{
part_list = this.vessel.Parts;
}
else
{
part_list = new List<Part>();
Part start_part = this.part;
while (start_part.parent!=null) start_part=start_part.parent;
AddPartToList(start_part,part_list);
}

int controlable_no = 0;
int found_engines = 0;
for (i=0; i<part_list.Count; i++)
{
if (part_list[i].FindModulesImplementing<AHS>().Count>0)
{
//functional states are only Master or Slave
if (part_list[i].FindModulesImplementing<AHS>()[0].comand_state=="Master")
{
master_index=i;
}
if ( part_list[i].FindModulesImplementing<AHS>()[0].comand_state=="Master" ||
part_list[i].FindModulesImplementing<AHS>()[0].comand_state=="Slave" )
{
controlable_no += part_list[i].FindModulesImplementing<AHS>()[0].max_engines;
}
}
if (part_list[i].FindModulesImplementing<RCSEngineTranslation>().Count>0)
{
if (part_list[i].FindModulesImplementing<RCSEngineTranslation>()[0].state_string!="None") found_engines++;
}
}
if (master_index!=-1)
{
part_list[master_index].FindModulesImplementing<AHS>()[0].controlable_engines = controlable_no;
part_list[master_index].FindModulesImplementing<AHS>()[0].engine_usage = found_engines+"/"+controlable_no;
List<int> slaves = part_list[master_index].FindModulesImplementing<AHS>()[0].slave_indices;
for (i=0; i<slaves.Count; i++)
{
part_list[slaves[i]].FindModulesImplementing<AHS>()[0].engine_usage = found_engines+"/"+controlable_no;
}
}
}

//Search for working slaves and update the master's slave list
public void CheckSlaveState ()
{
int master_index = -1;
int i;

//vessel will = null in the editor so we must cycle through parts list another way if it is not set
List <Part> part_list;
if (this.vessel!=null)
{
part_list = this.vessel.Parts;
}
else
{
part_list = new List<Part>();
Part start_part = this.part;
while (start_part.parent!=null) start_part=start_part.parent;
AddPartToList(start_part,part_list);
}

for (i=0; i<part_list.Count; i++)
{
if (part_list[i].FindModulesImplementing<AHS>().Count>0)
{
//All inactive states must be checked before asigning master slave states
if (!part_list[i].FindModulesImplementing<AHS>()[0].ahs_on)
{
part_list[i].FindModulesImplementing<AHS>()[0].comand_state = "Off";
}
else if ( part_list[i].FindModulesImplementing<AHS>()[0].comand_state!="No Power" )
{
if (master_index==-1)
{
master_index=i;
part_list[master_index].FindModulesImplementing<AHS>()[0].comand_state = "Master";
part_list[master_index].FindModulesImplementing<AHS>()[0].slave_indices.Clear();
}
else
{
part_list[i].FindModulesImplementing<AHS>()[0].comand_state = "Slave";
part_list[master_index].FindModulesImplementing<AHS>()[0].slave_indices.Add(i);
}
}
}
}

}

//Find active gravity sensor and then set gravity to a useable value
public void CheckGravSensor ()
{
this.FindActiveSensor();
if (this.grav_sensor_index==-1)
{
this.gravity_state = "No Sonsor Found";
}
else
{
if (this.grav_sensor_active)
{
this.gravity_state = "No Active Sensor";
}
else
{
try
{
// Debug.Log("(AHS) grav string = "+this.grav_readout);
if (this.grav_readout.Length>5)
{
string s = this.grav_readout.Substring(0,this.grav_readout.Length-5);
// Debug.Log("(AHS) shrotend grav string = "+s);
this.gravity = float.Parse(s);
}
else this.gravity = 9.82f;
}
catch
{
this.gravity = 9.82f;
}
this.gravity_state = this.grav_readout;
}
}
}//endof void CheckGravSensor ()

//Updates grav readout and sets active state + finds an active sensor
//You can force sensor finding by setting grav_sensor_index to -1
public void FindActiveSensor ()
{
if (no_parts_in_last_check!=this.vessel.parts.Count)
{//if parts have change then the index is no longer valid
this.grav_sensor_index = -1;
this.grav_sensor_active = false;
}
if ( this.grav_sensor_index != -1 )
{//if grav sensor is valid check it is active and update readout
this.grav_readout = this.vessel.parts[this.grav_sensor_index].FindModulesImplementing<ModuleEnviroSensor>()[0].readoutInfo;
this.grav_sensor_active = this.grav_readout=="off";
}
//We dont need to search again if we have an active sensor and parts have not changed
if ( no_parts_in_last_check==this.vessel.parts.Count && this.grav_sensor_active==true ) return;

this.grav_sensor_index = -1;
this.grav_sensor_active = false;
int i;
for (i=0; i<this.vessel.parts.Count; i++)
{
if (this.vessel.parts[i].name=="sensorGravimeter")
{
if (this.vessel.parts[i].FindModulesImplementing<ModuleEnviroSensor>().Count>0)
{
// Debug.Log("(AHS) found sensor at "+i+") name="+this.vessel.parts[i].name);
if (this.grav_sensor_index == -1)
{
// Debug.Log("(AHS) grav sensor index set to "+i+" as it was invalid");
this.grav_sensor_index=i;
}
this.grav_readout = this.vessel.parts[i].FindModulesImplementing<ModuleEnviroSensor>()[0].readoutInfo;
this.grav_sensor_active = this.grav_readout=="off";
if (this.grav_sensor_active)
{
// Debug.Log("(AHS) found active sensor at "+i+" name="+this.vessel.parts[i].name);
this.grav_sensor_index = i;
break;
}
}
}
}
}//endof void FindActiveSensor ()

//part list builder. much better to use vessle.parts but its not avalible in the editor
public void AddPartToList ( Part p_, List<Part> list_ )
{
list_.Add(p_);
int i;
for (i=0; i<p_.children.Count; i++)
{
this.AddPartToList(p_.children[i],list_);
}
}

public override void OnLoad (ConfigNode node)
{
base.OnLoad(node);
this.HoverDisplayMode();
this.SetHoverGUI();
}

//place for adding right click info to parts in the editor ?
public override string GetInfo ()
{
string result = "";
result += "Energy Usage: "+this.power_consumption.ToString()+"/sec\n";
result += "Heat Production: "+this.heat_production.ToString()+"/sec\n";
result += "Controlable Engines: "+this.max_engines.ToString()+"/sec\n";
return base.GetInfo ()+result;
}
}//endof public class AHS : PartModule
}//endof namespace AHS

Link to comment
Share on other sites

I'm well excited about this mod- I have investigated hover-based craft in the past but they're all but impossible to get running. This should make that a lot easier :P

:) I hope it makes things achievable for you and don't forgot to let me know how you get on ?

Id love to see craft photos and stories about any achievements or even near misses from the plugin (Its the little things that make me feel the worth of publishing).

I can confirm that v0.4 certainly dose work with solid rockets and that i should fix this by checking for ModuleEngines.throttleLocked = True.

I also found a nasty bug where setting time warp causes the small AHS to explode from over heating.

You can avoid this by turning it off before warping.

Its a very fine balance with heat on the small AHS but i hope that changing both the heat and power over usage into a crashed state should fix this.

This would crash the system and stop it generating heat if you reach 95% heat or run out of power. You would then need to turn the system off and back on again (like you don't know how to deal with a crash !).

I could use some feed back on power usage and heat production.

I am sure its going to be hard to please every one with my moral obligation to transferring the difficulty from one place to another.

I think 0.4 is starting to get on the overpowered side with both the active hover and RCS Translation systems but feedback always helps.

I have not tested the Large AHS yet but at a 6T mass i was hoping it would make large hover systems unwieldy.

Iv also found that low gravity effects result in more unstable hovers and suspect that there is a major reduction in the sensitivity of a grav sensor readout when compered to a 32bit float.

It should not be to hard to check this but will take a few mins of work.

Hummm.... ill have to update the to do list when i sober up

Link to comment
Share on other sites

i tried everything, but i cant get it too work. should i have the engines emiting thrusts downward be set as forward or down? i need a tutorial!

Yeah the video ended up being a bit too fast and so not very easy to understand + i should have built the craft so you could see the full setup process.

You should right click on an engine and use the Next RCS Dimension and Toggle RCS Direction to set the RCS Direction and thus define how the engine is controlled.

I use Forward as my preferred hover direction as the H/N keys are called Forward/Backward in the settings and my 1st craft had a cockpit pointing at the sky. These directions have no relevance to what way you are facing (unlike RCS) but are just a way to connect keys with events. If you like I/K as your hover control keys then theres no reason not to use Up as your Hover direction but be Warned that the default keys are K/I = Up/Down.

By default the AHS is set to use Forward engines for hover but this is changeable using the change Hover tweakables or action groups.

It may well be too flexible now and so stupidly complex to setup.

You must then enable SAS for the system to attempt any form of control and enable RCS if you want translation control (This seams to now be total missing from any text and was in the part description).

That seams very silly now as the hover mode setting allows you to enable/disable hover and thus replaces the need to check the SAS state.

The only reason for using these states now would be that they have a good GUI on the HUD.

You must also check your energy usage but if your using jets that tends not to be a problem.

I will do another video very soon once iv fixed a few little bugs.

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...