package com.planet_ink.coffee_mud.Areas;
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.core.interfaces.BoundedObject;
import com.planet_ink.coffee_mud.core.interfaces.BoundedObject.BoundedCube;
import com.planet_ink.coffee_mud.core.interfaces.Places;
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.Basic.GenPortal;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.Technical.TechCommand;
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.lang.ref.WeakReference;
import java.util.*;
/*
Copyright 2004-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 StdSpaceShip extends StdBoardableShip implements SpaceShip
{
private static final long STALE_AIR_INTERVAL = 5 * 60 * 1000;
private static final long STALE_WARN_INTERVAL = 5 * 30 * 1000;
protected static Climate climateObj = null;
protected volatile ShipEngine lastEngine = null;
protected volatile double lastEThrust = 0.0;
protected volatile int mass = -1;
protected SpaceObject spaceSource = null;
protected TimeClock localClock = (TimeClock) CMClass.getCommon("DefaultTimeClock");
protected int atmosphere = RawMaterial.RESOURCE_AIR;
protected long radius = 50;
protected double omlCoeff = SpaceObject.ATMOSPHERIC_DRAG_STREAMLINE + ((SpaceObject.ATMOSPHERIC_DRAG_BRICK - SpaceObject.ATMOSPHERIC_DRAG_STREAMLINE) / 2.0);
protected volatile long nextStaleCheck = System.currentTimeMillis() + STALE_AIR_INTERVAL;
protected volatile long nextStaleWarn = System.currentTimeMillis() + STALE_WARN_INTERVAL;
protected Set<String> staleAirList = new HashSet<String>();
protected Ability gravityFloaterA = null;
@Override
public String ID()
{
return "StdSpaceShip";
}
@Override
public void initializeClass()
{
}
@Override
public Room getIsDocked()
{
return CMLib.map().getRoom(savedDock);
}
@Override
public void setClimateObj(final Climate obj)
{
climateObj = obj;
}
@Override
public Climate getClimateObj()
{
if (climateObj == null)
{
climateObj = (Climate) CMClass.getCommon("DefaultClimate");
climateObj.setCurrentWeatherType(Climate.WEATHER_CLEAR);
climateObj.setNextWeatherType(Climate.WEATHER_CLEAR);
}
return climateObj;
}
@Override
public double getOMLCoeff()
{
return omlCoeff;
}
@Override
public void setOMLCoeff(final double coeff)
{
omlCoeff = coeff;
}
@Override
public TimeClock getTimeObj()
{
return localClock;
}
@Override
public void setTimeObj(final TimeClock obj)
{
localClock = obj;
}
@Override
public int getAtmosphereCode()
{
return atmosphere;
}
@Override
public void setAtmosphere(final int resourceCode)
{
atmosphere = resourceCode;
}
@Override
public int getAtmosphere()
{
return atmosphere == ATMOSPHERE_INHERIT ? RawMaterial.RESOURCE_AIR : atmosphere;
}
@Override
public long radius()
{
return radius;
}
@Override
public void setRadius(final long radius)
{
this.radius = radius;
}
@Override
public long flags()
{
return 0;
}
@Override
public SpaceObject knownSource()
{
return spaceSource;
}
@Override
public void setKnownSource(final SpaceObject O)
{
spaceSource = O;
}
@Override
public long[] coordinates()
{
return (shipItem instanceof SpaceShip) ? ((SpaceShip) shipItem).coordinates() : new long[3];
}
@Override
public void setCoords(final long[] coords)
{
if (shipItem instanceof SpaceShip)
((SpaceShip) shipItem).setCoords(coords);
}
@Override
public double[] direction()
{
return (shipItem instanceof SpaceShip) ? ((SpaceShip) shipItem).direction() : new double[2];
}
@Override
public double roll()
{
return (shipItem instanceof SpaceShip) ? ((SpaceShip) shipItem).roll() : 0;
}
@Override
public void setRoll(final double dir)
{
if (shipItem instanceof SpaceShip)
((SpaceShip) shipItem).setRoll(dir);
}
@Override
public void setDirection(final double[] dir)
{
if (shipItem instanceof SpaceShip)
((SpaceShip) shipItem).setDirection(dir);
}
@Override
public double[] facing()
{
return (shipItem instanceof SpaceShip) ? ((SpaceShip) shipItem).facing() : new double[2];
}
@Override
public void setFacing(final double[] dir)
{
if (shipItem instanceof SpaceShip)
((SpaceShip) shipItem).setFacing(dir);
}
@Override
public double speed()
{
return (shipItem instanceof SpaceShip) ? ((SpaceShip) shipItem).speed() : 0;
}
@Override
public void setSpeed(final double v)
{
if (shipItem instanceof SpaceShip)
((SpaceShip) shipItem).setSpeed(v);
}
@Override
public SpaceObject knownTarget()
{
return (shipItem instanceof SpaceShip) ? ((SpaceShip) shipItem).knownTarget() : null;
}
@Override
public void setKnownTarget(final SpaceObject O)
{
if (shipItem instanceof SpaceShip)
((SpaceShip) shipItem).setKnownTarget(O);
}
@Override
public void setShipFlag(final ShipFlag flag, final boolean setShipFlag)
{
if(shipItem instanceof SpaceShip)
((SpaceShip) shipItem).setShipFlag(flag,setShipFlag);
}
@Override
public boolean getShipFlag(final ShipFlag flag)
{
return (shipItem instanceof SpaceShip) ? ((SpaceShip) shipItem).getShipFlag(flag) : false;
}
@Override
public void setDockableItem(final Item dockableItem)
{
if(dockableItem instanceof SpaceShip)
shipItem=(SpaceShip)dockableItem;
}
@Override
public BoundedCube getBounds()
{
return new BoundedObject.BoundedCube(coordinates(),radius());
}
@Override
public long getMass()
{
final long mass=this.mass;
if(mass<0)
{
int newMass=phyStats().weight();
for(final Enumeration<Room> r=getProperMap(); r.hasMoreElements();)
{
final Room R=r.nextElement();
if(R!=null)
{
for(int i=0;i<R.numItems();i++)
{
final Item I=R.getItem(i);
if(I!=null)
newMass += I.phyStats().weight();
}
for(int i=0;i<R.numInhabitants();i++)
{
final MOB M=R.fetchInhabitant(i);
if(M!=null)
newMass += M.phyStats().weight();
}
}
}
this.mass=newMass;
}
return this.mass;
}
@Override
public void destroy()
{
CMLib.map().delObjectInSpace(this);
super.destroy();
spaceSource=null;
climateObj=null;
}
@Override
public int getClimateTypeCode()
{
return Places.CLIMASK_NORMAL;
}
@Override
public int getClimateType()
{
return Places.CLIMASK_NORMAL;
}
@Override
public void setClimateType(final int newClimateType)
{
}
public StdSpaceShip()
{
super();
setName("a space ship");
}
@Override
public void setName(final String newName)
{
super.setName(newName);
localClock.setLoadName(newName);
}
@Override
public int getTheme()
{
return Area.THEME_TECHNOLOGY;
}
@Override
public int getThemeCode()
{
return Area.THEME_TECHNOLOGY;
}
@Override
public void setTheme(final int level)
{
}
@Override
public CMObject newInstance()
{
try
{
return this.getClass().newInstance();
}
catch(final Exception e)
{
Log.errOut(ID(),e);
}
return new StdSpaceShip();
}
@Override
public boolean isGeneric()
{
return false;
}
@Override
protected void cloneFix(final StdBoardableShip ship)
{
super.cloneFix(ship);
setTimeObj((TimeClock)CMClass.getCommon("DefaultTimeClock"));
}
@Override
public CMObject copyOf()
{
try
{
final StdSpaceShip E=(StdSpaceShip)this.clone();
//CMClass.bumpCounter(E,CMClass.CMObjectType.AREA);//removed for mem & perf
E.xtraValues=(xtraValues==null)?null:(String[])xtraValues.clone();
E.cloneFix(this);
return E;
}
catch(final CloneNotSupportedException e)
{
return this.newInstance();
}
}
@Override
public void executeMsg(final Environmental myHost, final CMMsg msg)
{
super.executeMsg(myHost, msg);
if((msg.sourceMinor()==CMMsg.TYP_DROP)||(msg.sourceMinor()==CMMsg.TYP_GET))
mass=-1;
if(msg.amITarget(this))
{
switch(msg.targetMinor())
{
case CMMsg.TYP_ACTIVATE:
if(CMath.bset(msg.targetMajor(), CMMsg.MASK_CNTRLMSG))
{
final String[] parts=msg.targetMessage().split(" ");
final TechCommand command=TechCommand.findCommand(parts);
if(command!=null)
{
final Object[] parms=command.confirmAndTranslate(parts);
if(parms!=null)
{
if(command==Technical.TechCommand.AIRREFRESH)
{
if((staleAirList.size()==0)
&&(msg.tool() instanceof Item)
&&(((Item)msg.tool()).owner() instanceof Room)
&&(((Room)((Item)msg.tool()).owner()).getAtmosphere()<=0))
doStaleCheck();
if(staleAirList.size()>0)
{
final double pct=((Double)parms[0]).doubleValue();
final int atmoResource=((Integer)parms[1]).intValue();
int numToClear=(int)Math.round(CMath.mul(staleAirList.size(),pct));
while((numToClear>0)&&(staleAirList.size()>0))
{
final String roomID=staleAirList.iterator().next();
staleAirList.remove(roomID);
changeRoomAir(getRoom(roomID),null,atmoResource);
numToClear--;
}
changeRoomAir(getRandomMetroRoom(),null,atmoResource);
for(final Pair<Room,Integer> p : shipExitCache)
changeRoomAir(p.first,null,atmoResource);
}
//if(CMSecurity.isDebugging(DbgFlag.SPACESHIP))
// Log.debugOut("Refreshed the air in "+Name()+", stale rooms: "+staleAirList.size());
}
}
}
}
break;
}
}
else
{
switch(msg.sourceMinor())
{
case CMMsg.TYP_STAND:
case CMMsg.TYP_DISMOUNT:
{
if(this.getShipFlag(ShipFlag.NO_GRAVITY))
{
msg.addTrailerRunnable(new Runnable()
{
@Override
public void run()
{
final Ability floater = getGravityFloat();
if(floater != null)
floater.invoke(floater.invoker(), msg.source(), false, 0);
}
});
}
break;
}
}
switch(msg.targetMinor())
{
case CMMsg.TYP_DROP:
case CMMsg.TYP_THROW:
if(((msg.target() instanceof Item)||(msg.tool() instanceof Item))
&&(!msg.targetMajor(CMMsg.MASK_INTERMSG))) // give is different
{
if(this.getShipFlag(ShipFlag.NO_GRAVITY))
{
final Item I=(msg.target() instanceof Item) ? (Item)msg.target(): (Item)msg.tool();
msg.addTrailerRunnable(new Runnable()
{
@Override
public void run()
{
final Ability floater = getGravityFloat();
if(floater != null)
floater.invoke(floater.invoker(), I, false, 0);
}
});
}
}
break;
}
}
}
public int[] addMaskAndReturn(final int[] one, final int[] two)
{
if(one.length!=two.length)
return one;
final int[] returnable=new int[one.length];
for(int o=0;o<one.length;o++)
returnable[o]=one[o]+two[o];
return returnable;
}
protected boolean changeRoomAir(final Room R, final Room notifyRoom, final int atmoResource)
{
if(R==null)
return false;
if(R.getAtmosphere()!=atmoResource)
{
if(atmoResource==0)
{
R.showHappens(CMMsg.MSG_OK_ACTION, L("@x1 rushes out of the room.",RawMaterial.CODES.NAME(R.getAtmosphere()).toLowerCase()));
if((notifyRoom!=null)&&(notifyRoom!=R))
notifyRoom.showHappens(CMMsg.MSG_OK_ACTION, L("@x1 rushes out of the room.",RawMaterial.CODES.NAME(R.getAtmosphere()).toLowerCase()));
}
else
{
R.showHappens(CMMsg.MSG_OK_ACTION, L("@x1 rushes into the room.",RawMaterial.CODES.NAME(atmoResource).toLowerCase()));
if((notifyRoom!=null)&&(notifyRoom!=R))
notifyRoom.showHappens(CMMsg.MSG_OK_ACTION, L("@x1 rushes into the room.",RawMaterial.CODES.NAME(atmoResource).toLowerCase()));
}
if(atmoResource==getAtmosphere())
R.setAtmosphere(-1);
else
R.setAtmosphere(atmoResource);
return true;
}
return false;
}
protected void moveAtmosphereOut(final Set<Room> doneRooms, final Room startRoom, final int atmo)
{
final LinkedList<Room> toDoRooms=new LinkedList<Room>();
toDoRooms.add(startRoom);
while(toDoRooms.size()>0)
{
final Room R=toDoRooms.removeFirst();
doneRooms.add(R);
if(atmo == RawMaterial.RESOURCE_NOTHING)
{
if((R.roomID().length()>0)
&&(!staleAirList.contains(R.roomID())))
staleAirList.add(R.roomID());
}
else
if(atmo == RawMaterial.RESOURCE_AIR)
staleAirList.remove(R.roomID());
if(changeRoomAir(R,startRoom,atmo))
break;
for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
{
final Room R2=R.getRoomInDir(d);
final Exit E2=R.getExitInDir(d);
if((R2!=null)
&&(R2.getArea()==R.getArea())
&&(E2!=null)
&&(E2.isOpen())
&&(!doneRooms.contains(R2)))
toDoRooms.add(R2);
}
}
}
protected void doStaleCheck()
{
nextStaleCheck=System.currentTimeMillis()+STALE_AIR_INTERVAL;
for(final Enumeration<Room> r=getProperMap();r.hasMoreElements();)
{
final Room R=r.nextElement();
if(!staleAirList.contains(R.roomID()))
{
if(R.numInhabitants()>0)
staleAirList.add(R.roomID());
}
else
R.setAtmosphere(RawMaterial.RESOURCE_NOTHING); // WE NOW HAVE A VACUUM HERE!!!
}
}
protected Ability getGravityFloat()
{
if(gravityFloaterA == null)
{
gravityFloaterA=CMClass.getAbility("GravityFloat");
if(gravityFloaterA != null)
{
final MOB M=CMClass.getMOB("StdMOB");
M.setName(Name());
M.setLocation(this.getRandomProperRoom());
gravityFloaterA.setInvoker(M);
}
}
return gravityFloaterA;
}
protected void doGravityChanges()
{
if((lastEngine != null)
&&(!lastEngine.amDestroyed())
&&(lastEngine.activated())
&&(lastEngine.getThrust()>1.0)
&&(CMLib.map().areaLocation(lastEngine)==this))
lastEThrust = lastEngine.getThrust();
else
{
lastEngine = null;
lastEThrust = 0.0;
final List<Electronics> electronics=CMLib.tech().getMakeRegisteredElectronics(CMLib.tech().getElectronicsKey(this));
for(final Electronics E : electronics)
{
if((E instanceof ShipEngine)
&&(E.activated())
&&((lastEngine == null)
||(((ShipEngine)E).getThrust() > lastEngine.getThrust())))
lastEngine = (ShipEngine)E;
}
lastEThrust = (lastEngine == null) ? 0.0 : lastEngine.getThrust();
}
final boolean gravExistsNow =
getShipFlag(ShipFlag.IN_THE_AIR)
|| getShipFlag(ShipFlag.ARTI_GRAV)
|| (getIsDocked() != null)
|| (lastEThrust > 0.2);
setShipFlag(ShipFlag.ARTI_GRAV, false);
if(gravExistsNow == getShipFlag(ShipFlag.NO_GRAVITY)) // opposite, so it needs changing
{
final Ability floater = getGravityFloat();
if(floater != null)
{
final SpaceObject spaceObject=getShipSpaceObject();
final String code=Technical.TechCommand.GRAVITYCHANGE.makeCommand(Boolean.valueOf(gravExistsNow));
final String msgStr;
if(gravExistsNow)
msgStr=L("You feel the pull of gravity returning.");
else
msgStr=L("You no longer feel the pull of gravity.");
final CMMsg msg=CMClass.getMsg(floater.invoker(), spaceObject, me, CMMsg.NO_EFFECT, null, CMMsg.MSG_ACTIVATE|CMMsg.MASK_CNTRLMSG, code, CMMsg.MSG_QUIETMOVEMENT,msgStr);
CMMsg gmsg;
if(lastEThrust >= (SpaceObject.ACCELERATION_PASSOUT-0.49))
{
gmsg=CMClass.getMsg(floater.invoker(), null, me, CMMsg.NO_EFFECT, null, CMMsg.NO_EFFECT, null, CMMsg.MSG_GRAVITY, null);
gmsg.setValue((int)Math.round(lastEThrust));
}
else
gmsg=null;
boolean cancelled = false;
for(final Enumeration<Room> r=getProperMap();r.hasMoreElements();)
{
final Room R=r.nextElement();
if(R!=null)
{
if(!R.okMessage(msg.source(), msg))
cancelled=true;
if((gmsg != null)
&& ((!R.okMessage(gmsg.source(), gmsg))
||((gmsg.value() < (SpaceObject.ACCELERATION_PASSOUT-0.49)))))
gmsg = null;
}
}
setShipFlag(ShipFlag.NO_GRAVITY, !gravExistsNow);
if(gmsg != null)
{
for(final Enumeration<Room> r=getProperMap();r.hasMoreElements();)
{
final Room R=r.nextElement();
if(R!=null)
R.send(gmsg.source(), gmsg);
}
}
if(cancelled)
{
return;
}
for(final Enumeration<Room> r=getProperMap();r.hasMoreElements();)
{
final Room R=r.nextElement();
if(R!=null)
R.send(floater.invoker(), msg);
}
for(final Enumeration<Room> r=getProperMap();r.hasMoreElements();)
{
final Room R=r.nextElement();
if(R!=null)
{
for(int i=0;i<R.numInhabitants();i++)
{
final MOB M=R.fetchInhabitant(i);
if(M!=null)
floater.invoke(floater.invoker(), M, gravExistsNow, 0);
}
for(int i=0;i<R.numItems();i++)
{
final Item I=R.getItem(i);
if(I!=null)
floater.invoke(floater.invoker(), I, gravExistsNow, 0);
}
}
}
}
}
}
protected void doAtmosphereChanges()
{
final Set<Room> doneRooms=new HashSet<Room>();
for(final Pair<Room,Integer> p : shipExitCache)
{
final Room R=p.first;
final Exit E=R.getExitInDir(p.second.intValue());
if((E!=null)&&(E.isOpen()))
{
final Room exitRoom=R;
final Room otherRoom=R.getRoomInDir(p.second.intValue());
final int atmo=otherRoom.getAtmosphere();
moveAtmosphereOut(doneRooms,exitRoom,atmo);
}
}
if((System.currentTimeMillis() > nextStaleWarn)
&&(staleAirList.size()>0))
{
nextStaleWarn = System.currentTimeMillis() + STALE_WARN_INTERVAL;
for(final Enumeration<Room> r=getProperMap();r.hasMoreElements();)
{
final Room R=r.nextElement();
if((staleAirList.contains(R.roomID()))
&&(R.numInhabitants()>0)
&&(R.numPCInhabitants()>0))
{
final int atmo=R.getAtmosphere();
if(atmo>0)
for(int i=0;i<R.numInhabitants();i++)
{
final MOB M=R.fetchInhabitant(i);
if((M!=null)
&&(!M.isMonster())
&&(!CMLib.flags().canBreatheThis(M,RawMaterial.RESOURCE_NOTHING)))
M.tell(L("The @x1 is seeming a bit stale.",RawMaterial.CODES.NAME(atmo).toLowerCase()));
}
}
}
}
if(System.currentTimeMillis() >= nextStaleCheck)
{
final int numStaleRooms = staleAirList.size();
doStaleCheck();
if(staleAirList.size()>numStaleRooms)
nextStaleWarn = System.currentTimeMillis() + STALE_WARN_INTERVAL;
if(CMSecurity.isDebugging(DbgFlag.SPACESHIP) && (staleAirList.size()>0))
Log.debugOut("Used up the air in "+Name()+", stale rooms: "+staleAirList.size());
}
}
@Override
public boolean tick(final Tickable ticking, final int tickID)
{
if(!super.tick(ticking, tickID))
return false;
tickStatus=Tickable.STATUS_START;
if(tickID==Tickable.TICKID_AREA)
{
doAtmosphereChanges();
doGravityChanges();
final BoardableShip item=this.shipItem;
if(item != null)
item.tick(ticking, tickID);
}
tickStatus=Tickable.STATUS_NOT;
return true;
}
@Override
public void dockHere(final Room roomR)
{
super.dockHere(roomR);
if(roomR==null)
return;
CMLib.map().delObjectInSpace(getShipSpaceObject());
}
@Override
public SpaceObject getShipSpaceObject()
{
return (shipItem instanceof SpaceObject) ? (SpaceObject)shipItem : null;
}
private static final String[] LOCAL_CODES={"RADIUS","AUTHOR"};
private static String[] codes=null;
@Override
public String[] getStatCodes()
{
if(codes!=null)
return codes;
final String[] superCodes=super.getStatCodes();
codes=new String[superCodes.length+LOCAL_CODES.length];
int i=0;
for(;i<superCodes.length;i++)
codes[i]=superCodes[i];
for(int x=0;x<LOCAL_CODES.length;i++,x++)
codes[i]=LOCAL_CODES[x];
return codes;
}
@Override
public boolean isStat(final String code)
{
return CMParms.indexOf(getStatCodes(), code.toUpperCase().trim()) >= 0;
}
@Override
public String getStat(final String code)
{
if(getCodeNum(code) < super.getStatCodes().length)
return super.getStat(code);
switch(CMParms.indexOf(LOCAL_CODES,code.toUpperCase().trim()))
{
case 0:
return "" + getOMLCoeff();
case 1:
return "" + radius();
}
return "";
}
@Override
public void setStat(final String code, final String val)
{
if(getCodeNum(code) < super.getStatCodes().length)
super.setStat(code, val);
else
switch(CMParms.indexOf(LOCAL_CODES,code.toUpperCase().trim()))
{
case 0:
setOMLCoeff(CMath.s_double(val));
break;
case 1:
setRadius(CMath.s_long(val));
break;
}
}
}