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-2019 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 long specificImpulse = SpaceObject.VELOCITY_SUBLIGHT;
protected double fuelEfficiency = 0.33;
protected boolean constantThrust = true;
protected volatile double thrust = 0;
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(final 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(final double amt)
{
fuelEfficiency = amt;
}
@Override
public int getMaxThrust()
{
return maxThrust;
}
@Override
public void setMaxThrust(final int max)
{
maxThrust = max;
}
@Override
public double getThrust()
{
return thrust;
}
@Override
public void setThrust(final double current)
{
thrust = current;
}
@Override
public long getSpecificImpulse()
{
return specificImpulse;
}
@Override
protected double getComputedEfficiency()
{
return super.getComputedEfficiency() * this.getInstalledFactor();
}
@Override
public void setSpecificImpulse(final 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(final int min)
{
this.minThrust = min;
}
@Override
public boolean isConstantThruster()
{
return constantThrust;
}
@Override
public void setConstantThruster(final boolean isConstant)
{
constantThrust = isConstant;
}
/**
* Gets set of available thrust ports on this engine.
* @see ShipEngine#setAvailPorts(TechComponent.ShipDir[])
* @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(final TechComponent.ShipDir[] ports)
{
this.ports = ports;
}
@Override
public void executeMsg(final Environmental myHost, final 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(final 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, final ShipDir portDir, double thrust)
{
if((portDir != ShipDir.AFT)&&(portDir != ShipDir.FORWARD))
thrust = 1.0;
final int fuel=(int)Math.round(
CMath.ceiling(
thrust
*me.getFuelEfficiency()
*Math.max(.33, Math.abs(2.0-manufacturer.getEfficiencyPct()))
/ getFuelDivisor()
)
);
if((thrust>0)&&(fuel<=0))
return 1;
return fuel;
}
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();
if(me.subjectToWearAndTear())
{
if(me.usesRemaining()<75)
{
final double pct = CMath.mul(me.usesRemaining(), 100.0) + (0.35 * manufacturer.getReliabilityPct());
if(pct < 1.0)
thrust = thrust * pct;
}
}
else
thrust=manufacturer.getReliabilityPct() * thrust;
if(portDir==TechComponent.ShipDir.AFT) // when thrusting aft, the thrust is continual, so save it
{
if(amount == 0.0)
{
if(me.getThrust()>0.0)
{
me.setThrust(0.0);
//return reportError(me, controlI, mob, lang.L("@x1 goes quiet.",me.name(mob)), lang.L("Info: @x1: Engine shut down.",me.name(mob)));
}
me.setThrust(0.0);
return false;
}
if(me.getThrust()==0.0)
{
me.setThrust(amount); // also, its always the intended amount, not the adjusted amount
//return reportError(me, controlI, mob, lang.L("@x1 roars to life.",me.name(mob)), lang.L("Info: @x1: Engine activated.",me.name(mob)));
}
me.setThrust(amount); // also, its always the intended amount, not the adjusted amount
}
final int fuelToConsume=getFuelToConsume(me, manufacturer, portDir, thrust);
final double acceleration;
if(portDir==TechComponent.ShipDir.AFT) // when thrusting aft, there's a smidgeon more power
{
acceleration = (thrust * me.getSpecificImpulse() / ship.getMass());
if(acceleration < 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
acceleration = thrust;
//if((amount > 1)&&((portDir!=TechComponent.ShipDir.AFT) || (me.getThrust() > (oldThrust * 10))))
// tellWholeShip(me,mob,CMMsg.MSG_NOISE,CMLib.lang().L("You feel a "+rumbleWord+" and hear the blast of @x1.",me.name(mob)));
if(acceleration == 0.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.ACCELERATION.makeCommand(portDir.opposite(),Double.valueOf(acceleration),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(final ShipEngine me, final String circuitKey, final 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(final ShipEngine me, final Environmental myHost, final String circuitKey, final 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:
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.ACCELERATION.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.setThrust(0);
me.activate(false);
break;
case CMMsg.TYP_POWERCURRENT:
{
if(me.activated())
{
if((me.getThrust()>0.0)
&& (CMParms.contains(me.getAvailPorts(),TechComponent.ShipDir.AFT)))
{
final Manufacturer manufacturer=me.getFinalManufacturer();
//TODO: isn't there a method for this fuel thing?
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))
{
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
{
final 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;
}
}
}
}
}