package com.planet_ink.coffee_mud.Abilities.Properties;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.DatabaseEngine.PlayerData;
import com.planet_ink.coffee_mud.core.*;
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.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;
import java.util.*;
/*
Copyright 2003-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 Prop_RoomForSale extends Property implements LandTitle
{
@Override
public String ID()
{
return "Prop_RoomForSale";
}
@Override
public String name()
{
return "Putting a room up for sale";
}
@Override
protected int canAffectCode()
{
return Ability.CAN_ROOMS;
}
protected int lastItemNums = -1;
protected int lastDayDone = -1;
protected boolean scheduleReset = false;
@Override
public String accountForYourself()
{
return "For Sale";
}
@Override
public boolean allowsExpansionConstruction()
{
return false;
}
@Override
public void setMiscText(String newMiscText)
{
super.setMiscText(newMiscText);
}
@Override
public int getPrice()
{
if(text().length()==0)
return 100000;
final String s=text();
int index=s.length();
while((--index)>=0)
{
if((!Character.isDigit(s.charAt(index)))
&&(!Character.isWhitespace(s.charAt(index))))
break;
}
int price=CMath.s_int(s.substring(index+1).trim());
if(price<=0)
price=100000;
return price;
}
@Override
public List<Room> getConnectedPropertyRooms()
{
return getAllTitledRooms();
}
protected void saveData(String owner, int price, boolean rental, int backTaxes)
{
setMiscText(owner+"/"
+(rental?"RENTAL ":"")
+((backTaxes>0)?"TAX"+backTaxes+"X ":"")
+price);
}
@Override
public void setPrice(int price)
{
saveData(getOwnerName(), price, rentalProperty(), backTaxes());
}
@Override
public String getOwnerName()
{
final int dex=text().indexOf('/');
if(dex<0)
return "";
return text().substring(0,dex);
}
@Override
public CMObject getOwnerObject()
{
final String owner=getOwnerName();
if(owner.length()==0)
return null;
final Clan C=CMLib.clans().getClan(owner);
if(C!=null)
return C;
return CMLib.players().getLoadPlayer(owner);
}
@Override
public void setOwnerName(String owner)
{
if((owner.length()==0)&&(getOwnerName().length()>0))
scheduleReset=true;
saveData(owner, getPrice(), rentalProperty(), backTaxes());
}
@Override
public int backTaxes()
{
final int dex=text().indexOf('/');
if(dex<0)
return 0;
final int x=text().indexOf("TAX",dex);
if(x<0)
return 0;
final String s=CMParms.parse(text().substring(x+3)).firstElement();
return CMath.s_int(s.substring(0,s.length()-1)); // last char always X, so eat it
}
@Override
public void setBackTaxes(int tax)
{
saveData(getOwnerName(), getPrice(), rentalProperty(), tax);
}
@Override
public boolean rentalProperty()
{
final String upperText=text().toUpperCase();
final int dex=upperText.indexOf('/');
if(dex<0)
return upperText.indexOf("RENTAL")>=0;
return upperText.indexOf("RENTAL",dex)>0;
}
@Override
public void setRentalProperty(boolean truefalse)
{
saveData(getOwnerName(), getPrice(), truefalse, backTaxes());
}
// update title, since it may affect clusters, worries about ALL involved
@Override
public void updateTitle()
{
if(affected instanceof Room)
CMLib.database().DBUpdateRoom((Room)affected);
else
{
final Room R=CMLib.map().getRoom(landPropertyID());
if(R!=null)
CMLib.database().DBUpdateRoom(R);
}
}
@Override
public String getTitleID()
{
if(affected instanceof Room)
return "LAND_TITLE_FOR#"+CMLib.map().getExtendedRoomID((Room)affected);
else
{
final Room R=CMLib.map().getRoom(landPropertyID());
if(R!=null)
return "LAND_TITLE_FOR#"+CMLib.map().getExtendedRoomID(R);
}
return "";
}
@Override
public String getUniqueLotID()
{
return "ROOM_PROPERTY_" + landPropertyID();
}
@Override
public String landPropertyID()
{
if((affected!=null)&&(affected instanceof Room))
return CMLib.map().getExtendedRoomID(((Room)affected));
return "";
}
@Override
public void setLandPropertyID(String landID)
{
}
@Override
public LandTitle generateNextRoomTitle()
{
final LandTitle newTitle=(LandTitle)this.copyOf();
newTitle.setOwnerName("");
newTitle.setBackTaxes(0);
return newTitle;
}
public static boolean shopkeeperMobPresent(Room R)
{
if(R==null)
return false;
MOB M=null;
for(int i=0;i<R.numInhabitants();i++)
{
M=R.fetchInhabitant(i);
if((M.getStartRoom()==R)
&&(M.isMonster())
&&(CMLib.coffeeShops().getShopKeeper(M)!=null))
return true;
}
return false;
}
public static boolean robberyCheck(LandTitle A, CMMsg msg)
{
if(((msg.targetMinor()==CMMsg.TYP_GET)&&(!msg.isTarget(CMMsg.MASK_INTERMSG)))
||(msg.targetMinor()==CMMsg.TYP_PUSH)
||(msg.targetMinor()==CMMsg.TYP_PULL))
{
if((msg.target() instanceof Item)
&&(((Item)msg.target()).owner() ==msg.source().location())
&&((!(msg.tool() instanceof Item))||(msg.source().isMine(msg.tool())))
&&(!msg.sourceMajor(CMMsg.MASK_ALWAYS))
&&(A.getOwnerName().length()>0)
&&(msg.source().location()!=null)
&&(msg.othersMessage()!=null)
&&(msg.othersMessage().length()>0)
&&(!shopkeeperMobPresent(msg.source().location()))
&&(!CMLib.law().doesHavePriviledgesHere(msg.source(),msg.source().location())))
{
final Room R=msg.source().location();
final LegalBehavior B=CMLib.law().getLegalBehavior(R);
if(B!=null)
{
for(int m=0;m<R.numInhabitants();m++)
{
final MOB M=R.fetchInhabitant(m);
if(CMLib.law().doesHavePriviledgesHere(M,R))
return true;
}
MOB D=null;
final Clan C=CMLib.clans().getClan(A.getOwnerName());
if(C!=null)
D=C.getResponsibleMember();
else
D=CMLib.players().getLoadPlayer(A.getOwnerName());
if(D==null)
return true;
B.accuse(CMLib.law().getLegalObject(R),msg.source(),D,new String[]{"PROPERTYROB","THIEF_ROBBERY"});
}
}
return true;
}
return false;
}
@Override
public void executeMsg(final Environmental myHost, final CMMsg msg)
{
super.executeMsg(myHost,msg);
if(((msg.sourceMinor()==CMMsg.TYP_SHUTDOWN)
||((msg.targetMinor()==CMMsg.TYP_EXPIRE)&&(msg.target()==affected))
||(msg.sourceMinor()==CMMsg.TYP_ROOMRESET))
&&(affected instanceof Room))
{
updateLot(null);
final Vector<MOB> mobs=new Vector<MOB>();
Room R=(Room)affected;
if(R!=null)
{
synchronized(("SYNC"+R.roomID()).intern())
{
R=CMLib.map().getRoom(R);
for(int m=0;m<R.numInhabitants();m++)
{
final MOB M=R.fetchInhabitant(m);
if((M!=null)
&&(M.isSavable())
&&(M.getStartRoom()==R)
&&((M.basePhyStats().rejuv()==0)||(M.basePhyStats().rejuv()==PhyStats.NO_REJUV)))
{
CMLib.catalog().updateCatalogIntegrity(M);
mobs.addElement(M);
}
}
if(!CMSecurity.isSaveFlag(CMSecurity.SaveFlag.NOPROPERTYMOBS))
CMLib.database().DBUpdateTheseMOBs(R,mobs);
}
}
}
}
@Override
public boolean okMessage(final Environmental myHost, final CMMsg msg)
{
if(!super.okMessage(myHost,msg))
return false;
robberyCheck(this,msg);
return true;
}
@Override
public List<Room> getAllTitledRooms()
{
final List<Room> V=new Vector<Room>();
if(affected instanceof Room)
V.add((Room)affected);
else
{
final Room R=CMLib.map().getRoom(landPropertyID());
if(R!=null)
V.add(R);
}
return V;
}
public static int updateLotWithThisData(Room R,
LandTitle T,
boolean resetRoomName,
boolean clearAllItems,
List<String> optPlayerList,
int lastNumItems)
{
boolean updateItems=false;
boolean updateExits=false;
boolean updateRoom=false;
synchronized(("SYNC"+R.roomID()).intern())
{
R=CMLib.map().getRoom(R);
if(T.getOwnerName().length()==0)
{
Item I=null;
for(int i=R.numItems()-1;i>=0;i--)
{
I=R.getItem(i);
if((I==null)||(I.Name().equalsIgnoreCase("id")))
continue;
CMLib.catalog().updateCatalogIntegrity(I);
if(clearAllItems)
{
I.destroy();
updateItems=true;
}
else
{
if(I.expirationDate()==0)
{
long now=System.currentTimeMillis();
now+=(TimeManager.MILI_MINUTE*CMProps.getIntVar(CMProps.Int.EXPIRE_PLAYER_DROP));
I.setExpirationDate(now);
}
if((I.phyStats().rejuv()!=PhyStats.NO_REJUV)
&&(I.phyStats().rejuv()!=0))
{
I.basePhyStats().setRejuv(PhyStats.NO_REJUV);
I.recoverPhyStats();
}
}
}
Ability A=null;
if(clearAllItems)
{
for(final Enumeration<Ability> a=R.effects();a.hasMoreElements();)
{
A=a.nextElement();
if(((A!=null)&&((A.classificationCode()&Ability.ALL_ACODES)!=Ability.ACODE_PROPERTY)))
{
A.unInvoke();
R.delEffect(A);
updateRoom=true;
}
}
}
for(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--)
{
final Room R2=R.rawDoors()[d];
Exit E=R.getRawExit(d);
if((E!=null)&&(E.hasALock())&&(E.isGeneric()))
{
E.setKeyName("");
E.setDoorsNLocks(E.hasADoor(),E.isOpen(),E.defaultsClosed(),false,false,false);
updateExits=true;
if(R2!=null)
{
E=R2.getRawExit(Directions.getOpDirectionCode(d));
if((E!=null)&&(E.hasALock())&&(E.isGeneric()))
{
E.setKeyName("");
E.setDoorsNLocks(E.hasADoor(),E.isOpen(),E.defaultsClosed(),false,false,false);
CMLib.database().DBUpdateExits(R2);
R2.getArea().fillInAreaRoom(R2);
}
}
}
}
if(updateExits)
{
CMLib.database().DBUpdateExits(R);
R.getArea().fillInAreaRoom(R);
}
if(updateItems)
CMLib.database().DBUpdateItems(R);
if(updateRoom)
CMLib.database().DBUpdateRoom(R);
CMLib.law().colorRoomForSale(R,T.rentalProperty(),resetRoomName);
return -1;
}
if((lastNumItems<0)
&&(!CMSecurity.isDisabled(CMSecurity.DisFlag.PROPERTYOWNERCHECKS))
&&(optPlayerList!=null))
{
boolean playerExists=(CMLib.players().getPlayer(T.getOwnerName())!=null);
if(!playerExists)
playerExists=(CMLib.clans().getClan(T.getOwnerName())!=null);
if(!playerExists)
playerExists=optPlayerList.contains(T.getOwnerName());
if(!playerExists)
for(int i=0;i<optPlayerList.size();i++)
if(optPlayerList.get(i).equalsIgnoreCase(T.getOwnerName()))
{ playerExists=true; break;}
if(!playerExists)
{
T.setOwnerName("");
T.updateLot(null);
return -1;
}
}
int x=R.description().indexOf(LegalLibrary.SALESTR);
if(x>=0)
{
R.setDescription(R.description().substring(0,x));
CMLib.database().DBUpdateRoom(R);
}
x=R.description().indexOf(LegalLibrary.RENTSTR);
if(x>=0)
{
R.setDescription(R.description().substring(0,x));
CMLib.database().DBUpdateRoom(R);
}
// this works on the priciple that
// 1. if an item has ONLY been removed, the lastNumItems will be != current # items
// 2. if an item has ONLY been added, the dispossessiontime will be != null
// 3. if an item has been added AND removed, the dispossession time will be != null on the added
if((lastNumItems>=0)&&(R.numItems()!=lastNumItems))
updateItems=true;
for(int i=0;i<R.numItems();i++)
{
final Item I=R.getItem(i);
if((I.expirationDate()!=0)
&&((I.isSavable())||(I.Name().equalsIgnoreCase("id")))
&&((!(I instanceof DeadBody))||(((DeadBody)I).isPlayerCorpse())))
{
I.setExpirationDate(0);
updateItems=true;
}
if((I.phyStats().rejuv()!=Integer.MAX_VALUE)
&&(I.phyStats().rejuv()!=0))
{
I.basePhyStats().setRejuv(PhyStats.NO_REJUV);
I.recoverPhyStats();
updateItems=true;
}
}
lastNumItems=R.numItems();
if((!CMSecurity.isSaveFlag(CMSecurity.SaveFlag.NOPROPERTYITEMS))
&&(updateItems))
CMLib.database().DBUpdateItems(R);
}
return lastNumItems;
}
@SuppressWarnings("unchecked")
public static boolean doRentalProperty(Area A, String ID, String owner, int rent)
{
if(!CMProps.getBoolVar(CMProps.Bool.MUDSTARTED))
return false;
final int month=A.getTimeObj().getMonth();
final int day=A.getTimeObj().getDayOfMonth();
final int year=A.getTimeObj().getYear();
final Object O=Resources.getResource("RENTAL INFO/"+owner);
List<PlayerData> pDataV=null;
if(O instanceof List)
pDataV=(List<PlayerData>)O;
else
pDataV=CMLib.database().DBReadData(owner,"RENTAL INFO");
if(pDataV==null)
pDataV=new Vector<PlayerData>();
DatabaseEngine.PlayerData pData = null;
if(pDataV.size()==0)
{
pData = CMLib.database().createPlayerData();
pData.who(owner);
pData.section("RENTAL INFO");
pData.key("RENTAL INFO/"+owner);
pData.xml(ID+"|~>|"+day+" "+month+" "+year+"|~;|");
CMLib.database().DBCreateData(owner,"RENTAL INFO","RENTAL INFO/"+owner,pData.xml());
pDataV.add(pData);
Resources.submitResource("RENTAL INFO/"+owner,pDataV);
return false;
}
else
if(pDataV.get(0) != null)
{
pData=pDataV.get(0);
String parse=pData.xml();
int x=parse.indexOf("|~;|");
final StringBuffer reparse=new StringBuffer("");
boolean changesMade=false;
boolean needsToPay=false;
while(x>=0)
{
String thisOne=parse.substring(0,x);
if(thisOne.startsWith(ID+"|~>|"))
{
thisOne=thisOne.substring((ID+"|~>|").length());
final Vector<String> dateV=CMParms.parse(thisOne);
if(dateV.size()==3)
{
int lastYear=CMath.s_int(dateV.lastElement());
int lastMonth=CMath.s_int(dateV.elementAt(1));
final int lastDay=CMath.s_int(dateV.firstElement());
while(!needsToPay)
{
if(lastYear<year)
needsToPay=true;
else
if((lastYear==year)&&(lastMonth<month)&&(day>=lastDay))
needsToPay=true;
if(needsToPay)
{
if(CMLib.beanCounter().modifyLocalBankGold(A,
owner,
CMLib.utensils().getFormattedDate(A)+":Withdrawal of "+rent+": Rent for "+ID,
CMLib.beanCounter().getCurrency(A),
(-rent)))
{
lastMonth++;
if(lastMonth>A.getTimeObj().getMonthsInYear())
{
lastMonth=1;
lastYear++;
}
changesMade=true;
needsToPay=false;
}
}
else
break;
}
if(changesMade)
reparse.append(ID+"|~>|"+lastDay+" "+lastMonth+" "+lastYear+"|~;|");
if(needsToPay&&(!changesMade))
return true;
}
}
else
reparse.append(thisOne+"|~;|");
parse=parse.substring(x+4);
x=parse.indexOf("|~;|");
}
if(changesMade)
{
CMLib.database().DBReCreateData(owner,"RENTAL INFO","RENTAL INFO/"+owner,reparse.toString());
pData = CMLib.database().createPlayerData();
pData.who(owner);
pData.section("RENTAL INFO");
pData.key("RENTAL INFO/"+owner);
pData.xml(reparse.toString());
pDataV.set(0,pData);
Resources.removeResource("RENTAL INFO/"+owner);
Resources.submitResource("RENTAL INFO/"+owner,pDataV);
}
return needsToPay;
}
return false;
}
// update lot, since its called by the savethread, ONLY worries about itself
@Override
public void updateLot(List<String> optPlayerList)
{
if(affected instanceof Room)
{
Room R=(Room)affected;
synchronized(("SYNC"+R.roomID()).intern())
{
R=CMLib.map().getRoom(R);
lastItemNums=updateLotWithThisData(R,this,false,scheduleReset,optPlayerList,lastItemNums);
if((lastDayDone!=R.getArea().getTimeObj().getDayOfMonth())
&&(CMProps.getBoolVar(CMProps.Bool.MUDSTARTED)))
{
lastDayDone=R.getArea().getTimeObj().getDayOfMonth();
if((getOwnerName().length()>0)&&rentalProperty()&&(R.roomID().length()>0))
{
if(doRentalProperty(R.getArea(),R.roomID(),getOwnerName(),getPrice()))
{
setOwnerName("");
CMLib.database().DBUpdateRoom(R);
lastItemNums=updateLotWithThisData(R,this,false,scheduleReset,optPlayerList,lastItemNums);
}
}
}
scheduleReset=false;
}
}
}
}