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; } } } } }