/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.Items.CompTech;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.CMSecurity.DbgFlag;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.TechComponent.ShipDir;
import com.planet_ink.coffee_mud.Items.interfaces.Technical.TechType;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

/*
   Copyright 2013-2016 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
public class StdShipThruster extends StdCompFuelConsumer implements ShipEngine
{
	@Override
	public String ID()
	{
		return "StdShipThruster";
	}

	protected int		maxThrust		= 8900000;
	protected int		minThrust		= 0;
	protected double	thrust			= 0;
	protected long		specificImpulse	= SpaceObject.VELOCITY_SUBLIGHT;
	protected double	fuelEfficiency	= 0.33;
	protected boolean	constantThrust	= true;
	
	protected TechComponent.ShipDir[] ports 		= TechComponent.ShipDir.values(); 

	public StdShipThruster()
	{
		super();
		setName("a thruster engine");
		basePhyStats.setWeight(5000);
		setDisplayText("a thruster engine sits here.");
		setDescription("");
		baseGoldValue=500000;
		basePhyStats().setLevel(1);
		recoverPhyStats();
		setMaterial(RawMaterial.RESOURCE_STEEL);
		setCapacity(basePhyStats.weight()+100000);
	}

	@Override
	public boolean sameAs(Environmental E)
	{
		if(!(E instanceof StdShipThruster))
			return false;
		return super.sameAs(E);
	}

	protected static double getFuelDivisor() 
	{ 
		return 100.0; 
	}

	@Override
	public double getFuelEfficiency()
	{
		return fuelEfficiency;
	}

	@Override
	public void setFuelEfficiency(double amt)
	{
		fuelEfficiency = amt;
	}

	@Override
	public int getMaxThrust()
	{
		return maxThrust;
	}

	@Override
	public void setMaxThrust(int max)
	{
		maxThrust = max;
	}

	@Override
	public double getThrust()
	{
		return thrust;
	}

	@Override
	public void setThrust(double current)
	{
		thrust = current;
	}

	@Override
	public long getSpecificImpulse()
	{
		return specificImpulse;
	}

	@Override
	protected double getComputedEfficiency()
	{
		return super.getComputedEfficiency() * this.getInstalledFactor();
	}
	
	@Override
	public void setSpecificImpulse(long amt)
	{
		if(amt > 0)
			specificImpulse = amt;
	}

	@Override
	public TechType getTechType()
	{
		return TechType.SHIP_ENGINE;
	}

	@Override
	protected boolean willConsumeFuelIdle()
	{
		return getThrust() > 0;
	}

	@Override
	public int getMinThrust()
	{
		return minThrust;
	}

	@Override
	public void setMinThrust(int min)
	{
		this.minThrust = min;
	}

	@Override
	public boolean isConstantThruster()
	{
		return constantThrust;
	}
	
	@Override
	public void setConstantThruster(boolean isConstant)
	{
		constantThrust = isConstant;
	}
	
	/**
	 * Gets set of available thrust ports on this engine.
	 * @see ShipEngine#setAvailPorts(Set<ThrustPort>)
	 * @return the set of available thrust ports.
	 */
	@Override
	public TechComponent.ShipDir[] getAvailPorts()
	{
		return ports;
	}

	/**
	 * Sets set of available thrust ports on this engine.
	 * @see ShipEngine#getAvailPorts()
	 * @param ports the set of available thrust ports.
	 */
	@Override
	public void setAvailPorts(TechComponent.ShipDir[] ports)
	{
		this.ports = ports;
	}

	@Override
	public void executeMsg(Environmental myHost, CMMsg msg)
	{
		super.executeMsg(myHost, msg);
		executeThrusterMsg(this, myHost, circuitKey, msg);
	}

	public static boolean reportError(final ShipEngine me, final Software controlI, final MOB mob, final String literalMessage, final String controlMessage)
	{
		if((mob!=null) && (mob.location()==CMLib.map().roomLocation(me)) && (literalMessage!=null))
			mob.tell(literalMessage);
		if(controlMessage!=null)
		{
			if(controlI!=null)
				controlI.addScreenMessage(controlMessage);
			else
			if((mob!=null)&&(me!=null))
				mob.tell(CMLib.lang().L("A panel on @x1 reports '@x2'.",me.name(mob),controlMessage));
		}
		return false;
	}

	public static boolean tellWholeShip(final ShipEngine me, final MOB mob, final int msgCode, final String message)
	{
		Room R=CMLib.map().roomLocation(me);
		if(R==null)
			R=mob.location();
		if(R!=null)
		{
			if(R.getArea() instanceof SpaceShip)
			{
				for(Enumeration<Room> r=R.getArea().getProperMap();r.hasMoreElements();)
					r.nextElement().show(mob, null, msgCode, message);
			}
			else
				R.show(mob, null, msgCode, message);
		}
		return false;
	}

	protected static void sendComputerMessage(final ShipEngine me, final String circuitKey, final MOB mob, final Item controlI, final String code)
	{
		for(final Iterator<Computer> c=CMLib.tech().getComputers(circuitKey);c.hasNext();)
		{
			final Computer C=c.next();
			if((controlI==null)||(C!=controlI.owner()))
			{
				final CMMsg msg2=CMClass.getMsg(mob, C, me, CMMsg.NO_EFFECT, null, CMMsg.MSG_ACTIVATE|CMMsg.MASK_CNTRLMSG, code, CMMsg.NO_EFFECT,null);
				if(C.okMessage(mob, msg2))
					C.executeMsg(mob, msg2);
			}
		}
	}
	
	private static int getFuelToConsume(final ShipEngine me, final Manufacturer manufacturer, double thrust)
	{
		return (int)Math.round(
			CMath.ceiling(
				thrust
				*me.getFuelEfficiency()
				*Math.max(.33, Math.abs(2.0-manufacturer.getEfficiencyPct())) 
				/ getFuelDivisor()
			)
		);
	}
	
	public static boolean executeThrust(final ShipEngine me, final String circuitKey, final MOB mob, final Software controlI, final TechComponent.ShipDir portDir, final double amount)
	{
		final LanguageLibrary lang=CMLib.lang();
		final SpaceObject obj=CMLib.map().getSpaceObject(me, true);
		final Manufacturer manufacturer=me.getFinalManufacturer();
		final String rumbleWord = (me instanceof FuelConsumer) ? "rumble" : "hum";
		if(!(obj instanceof SpaceShip))
			return reportError(me, controlI, mob, lang.L("@x1 "+rumbleWord+"s and fires, but nothing happens.",me.name(mob)), lang.L("Failure: @x1: exhaust ports.",me.name(mob)));
		final SpaceShip ship=(SpaceShip)obj;
		if((portDir==null)||(amount<0))
			return reportError(me, controlI, mob, lang.L("@x1 "+rumbleWord+"s loudly, but accomplishes nothing.",me.name(mob)), lang.L("Failure: @x1: exhaust control.",me.name(mob)));
		if(!CMParms.contains(me.getAvailPorts(), portDir))
			return reportError(me, controlI, mob, lang.L("@x1 "+rumbleWord+"s a little, but accomplishes nothing.",me.name(mob)), lang.L("Failure: @x1: port control.",me.name(mob)));
		double thrust=me.getInstalledFactor() * amount;
		if(thrust > me.getMaxThrust())
			thrust=me.getMaxThrust();
		thrust=manufacturer.getReliabilityPct() * thrust;
		final double oldThrust = me.getThrust();
		if(portDir==TechComponent.ShipDir.AFT) // when thrusting aft, the thrust is continual, so save it
			me.setThrust(amount); // also, its always the intended amount, not the adjusted amount
		
		final int fuelToConsume=getFuelToConsume(me, manufacturer, thrust);
		
		final double activeThrust;
		if(portDir==TechComponent.ShipDir.AFT) // when thrusting aft, there's a smidgeon more power
		{
			activeThrust = (thrust * me.getSpecificImpulse() / ship.getMass());
			if(activeThrust < me.getMinThrust())
			{
				return reportError(me, controlI, mob, lang.L("@x1 "+rumbleWord+"s loudly, but nothing happens.",me.name(mob)), lang.L("Failure: @x1: insufficient thrust.",me.name(mob)));
			}
		}
		else
			activeThrust = thrust;
		
		final double accelleration=activeThrust;
		
		if((amount > 1)&&((portDir!=TechComponent.ShipDir.AFT) || (me.getThrust() > oldThrust)))
			tellWholeShip(me,mob,CMMsg.MSG_NOISE,CMLib.lang().L("You feel a "+rumbleWord+" and hear the blast of @x1.",me.name(mob)));
		if(accelleration == 0)
		{
			final String code=Technical.TechCommand.COMPONENTFAILURE.makeCommand(TechType.SHIP_ENGINE, "Failure: "+me.name()+": insufficient_thrust_capacity.");
			sendComputerMessage(me,circuitKey,mob,controlI,code);
			return reportError(me, controlI, mob, lang.L("@x1 "+rumbleWord+"s very loudly, but nothing is happening.",me.name(mob)), lang.L("Failure: @x1: insufficient engine thrust capacity.",me.name(mob)));
		}
		else
		if(me.consumeFuel(fuelToConsume))
		{
			final SpaceObject spaceObject=ship.getShipSpaceObject();
			final String code=Technical.TechCommand.ACCELLLERATION.makeCommand(portDir,Double.valueOf(accelleration),Boolean.valueOf(me.isConstantThruster()));
			final CMMsg msg=CMClass.getMsg(mob, spaceObject, me, CMMsg.NO_EFFECT, null, CMMsg.MSG_ACTIVATE|CMMsg.MASK_CNTRLMSG, code, CMMsg.NO_EFFECT,null);
			if(spaceObject.okMessage(mob, msg))
			{
				spaceObject.executeMsg(mob, msg);
				return true;
			}
		}
		else
		{
			final String code=Technical.TechCommand.COMPONENTFAILURE.makeCommand(TechType.SHIP_ENGINE, "Failure:_"+me.name().replace(' ','_')+":_insufficient_fuel.");
			sendComputerMessage(me,circuitKey,mob,controlI,code);
			return reportError(me, controlI, mob, lang.L("@x1 "+rumbleWord+"s loudly, then sputters down.",me.name(mob)), lang.L("Failure: @x1: insufficient fuel.",me.name(mob)));
		}
		return false;
	}

	public static boolean executeCommand(ShipEngine me, String circuitKey, CMMsg msg)
	{
		final LanguageLibrary lang=CMLib.lang();
		final Software controlI=(msg.tool() instanceof Software)?((Software)msg.tool()):null;
		final MOB mob=msg.source();
		if(msg.targetMessage()==null)
		{
			me.setThrust(0);
			return true;
		}
		else
		{
			final String[] parts=msg.targetMessage().split(" ");
			final TechCommand command=TechCommand.findCommand(parts);
			if(command==null)
				return reportError(me, controlI, mob, lang.L("@x1 does not respond.",me.name(mob)), lang.L("Failure: @x1: control failure.",me.name(mob)));
			final Object[] parms=command.confirmAndTranslate(parts);
			if(parms==null)
				return reportError(me, controlI, mob, lang.L("@x1 did not respond.",me.name(mob)), lang.L("Failure: @x1: control syntax failure.",me.name(mob)));
			if(command == TechCommand.THRUST)
				return executeThrust(me, circuitKey, mob, controlI, (TechComponent.ShipDir)parms[0],((Double)parms[1]).doubleValue());
			return reportError(me, controlI, mob, lang.L("@x1 refused to respond.",me.name(mob)), lang.L("Failure: @x1: control command failure.",me.name(mob)));
		}
	}

	public static void executeThrusterMsg(ShipEngine me, Environmental myHost, String circuitKey, CMMsg msg)
	{
		if(msg.amITarget(me))
		{
			switch(msg.targetMinor())
			{
			case CMMsg.TYP_ACTIVATE:
				if(executeCommand(me, circuitKey, msg))
					me.activate(true);
				break;
			case CMMsg.TYP_DEACTIVATE:
				me.setThrust(0);
				if(me.activated())
				{
					// when a constant thruster deactivates, all speed stops
					final SpaceObject obj=CMLib.map().getSpaceObject(me, true);
					if(obj instanceof SpaceShip)
					{
						final MOB mob=msg.source();
						final SpaceShip ship=(SpaceShip)obj;
						final SpaceObject spaceObject=ship.getShipSpaceObject();
						final String code=Technical.TechCommand.ACCELLLERATION.makeCommand(TechComponent.ShipDir.AFT,Double.valueOf(0),Boolean.valueOf(true));
						final CMMsg msg2=CMClass.getMsg(mob, spaceObject, me, CMMsg.NO_EFFECT, null, CMMsg.MSG_ACTIVATE|CMMsg.MASK_CNTRLMSG, code, CMMsg.NO_EFFECT,null);
						if(spaceObject.okMessage(mob, msg2))
							spaceObject.executeMsg(mob, msg2);
					}
				}
				me.activate(false);
				break;
			case CMMsg.TYP_POWERCURRENT:
			{
				final Manufacturer manufacturer=me.getFinalManufacturer();
				final int fuelToConsume=(int)Math.round(CMath.ceiling(me.getThrust()*me.getFuelEfficiency()*Math.max(.33, Math.abs(2.0-manufacturer.getEfficiencyPct()))/getFuelDivisor()));
				if(me.consumeFuel(fuelToConsume))
				{
					if((me.getThrust() > 0) && (me.isConstantThruster()) && (CMParms.contains(me.getAvailPorts(),TechComponent.ShipDir.AFT)))
					{
						final SpaceObject obj=CMLib.map().getSpaceObject(me, true);
						if(obj instanceof SpaceShip)
						{
							final SpaceObject ship=((SpaceShip)obj).getShipSpaceObject();
							final String code=Technical.TechCommand.THRUST.makeCommand(TechComponent.ShipDir.AFT,Double.valueOf(me.getThrust()));
							final CMMsg msg2=CMClass.getMsg(msg.source(), me, null, CMMsg.NO_EFFECT, null, CMMsg.MSG_ACTIVATE|CMMsg.MASK_CNTRLMSG, code, CMMsg.NO_EFFECT,null);
							if(me.owner() instanceof Room)
							{
								if(me.owner().okMessage(msg.source(), msg2))
									((Room)me.owner()).send(msg.source(), msg2);
							}
							else
							if(ship.okMessage(msg.source(), msg2))
								ship.executeMsg(msg.source(), msg2);
						}
					}
				}
				else
				{
					CMMsg msg2=CMClass.getMsg(msg.source(), me, me, CMMsg.NO_EFFECT, null, CMMsg.MSG_DEACTIVATE|CMMsg.MASK_CNTRLMSG, "", CMMsg.NO_EFFECT,null);
					if(me.owner() instanceof Room)
					{
						if(((Room)me.owner()).okMessage(msg.source(), msg2))
							((Room)me.owner()).send(msg.source(), msg2);
					}
					else
					if(me.okMessage(msg.source(), msg2))
						me.executeMsg(msg.source(), msg2);
					final String code=Technical.TechCommand.COMPONENTFAILURE.makeCommand(TechType.SHIP_ENGINE, "Failure: "+me.name()+": insufficient_fuel.");
					sendComputerMessage(me,circuitKey,msg.source(),null,code);
				}
				break;
			}
			}
		}
	}
}