package com.planet_ink.coffee_mud.Behaviors;
import com.planet_ink.coffee_mud.core.interfaces.*;
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.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 2003-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 BribeGateGuard extends StdBehavior
{
protected Exit e;
protected int dir = -1;
protected int tickTock = 0;
protected Vector<MOB> paidPlayers = new Vector<MOB>();
protected Hashtable<String,Boolean> toldAlready = new Hashtable<String,Boolean>();
protected static boolean debug = false; // debuggin
protected static boolean surviveReboot=false; // survive reboot
protected static Map<String,Map<String,Double>> notTheJournal=new Hashtable<String,Map<String,Double>>();
@Override
public String ID()
{
return "BribeGateGuard";
}
@Override
public CMObject copyOf()
{
final BribeGateGuard obj=(BribeGateGuard)super.copyOf();
obj.paidPlayers=new Vector<MOB>();
obj.paidPlayers.addAll(paidPlayers);
obj.toldAlready=new Hashtable<String,Boolean>();
obj.toldAlready.putAll(toldAlready);
return obj;
}
@Override
public String accountForYourself()
{
return "corruptable gate guarding";
}
protected double price()
{
return getVal(getParms(), "price", 5);
}
protected String gates()
{
return ID() + getVal(getParms(), "gates", "General");
}
protected int findGate(final MOB mob)
{
if (!CMLib.flags().isInTheGame(mob,false))
return -1;
final Room R=mob.location();
if(R!=null)
for (int d = 0; d < Directions.NUM_DIRECTIONS(); d++)
{
if (R.getRoomInDir(d) != null)
{
final Exit e = R.getExitInDir(d);
if((e!=null)&&(e.hasADoor()))
{
return d;
}
}
}
return -1;
}
protected DoorKey getMyKeyTo(final MOB mob, final Exit e)
{
DoorKey key = null;
final String keyCode = e.keyName();
for (int i = 0; i < mob.numItems(); i++)
{
final Item item = mob.getItem(i);
if ( (item instanceof DoorKey) && ( ( (DoorKey) item).getKey().equals(keyCode)))
{
key = (DoorKey) item;
break;
}
}
if (key == null)
{
key = (DoorKey) CMClass.getItem("StdKey");
key.setKey(keyCode);
mob.addItem(key);
}
return key;
}
protected void payment(final Coins given, final MOB gateGuard, final MOB mob)
{
// make a note in the journal
double newNum = given.getTotalValue();
newNum += getBalance(mob);
final Coins item = CMLib.beanCounter().makeBestCurrency(CMLib.beanCounter().getCurrency(gateGuard),newNum);
delBalance(mob);
if(item!=null)
writeBalance(item, mob);
}
protected boolean checkBalance(final double charge, final MOB mob)
{
// Does this MOB have the cash for the charge?
if (getBalance(mob) > charge)
{
return true;
}
return false;
}
protected double getBalance(final MOB mob)
{
double balance = 0;
// return the balance in int form
if(surviveReboot)
{
final List<JournalEntry> V =CMLib.database().DBReadJournalMsgsByUpdateDate("BRIBEGATE_"+gates(), true);
final Vector<JournalEntry> mine = new Vector<JournalEntry>();
for (int v = 0; v < V.size(); v++)
{
final JournalEntry V2 =V.get(v);
if ( ( V2.from().equalsIgnoreCase(mob.Name())))
{
mine.addElement(V2);
}
}
for (int v = 0; v < mine.size(); v++)
{
final JournalEntry V2 = mine.elementAt(v);
final String fullName = V2.subj();
if (fullName.equals("COINS"))
{
final Coins item = (Coins) CMClass.getItem("StdCoins");
if (item != null)
{
CMLib.coffeeMaker().setPropertiesStr(item,V2.msg(), true);
item.recoverPhyStats();
item.text();
balance += item.getTotalValue();
}
}
}
}
else
{
Map<String,Double> H=notTheJournal.get(gates());
if(H==null)
{
H=new Hashtable<String,Double>();
notTheJournal.put(gates(),H);
}
Double D=H.get(mob.Name());
if(D==null)
{
D=Double.valueOf(0.0);
H.put(mob.Name(),D);
}
balance=D.doubleValue();
}
return balance;
}
protected void charge(final double charge, final MOB gateGuard, final MOB mob)
{
// update the balance in the journal
final Coins item = (Coins) CMClass.getItem("StdCoins");
double newNum = getBalance(mob);
newNum -= charge;
if (newNum > 0)
{
item.setCurrency(CMLib.beanCounter().getCurrency(gateGuard));
item.setDenomination(CMLib.beanCounter().getLowestDenomination(CMLib.beanCounter().getCurrency(gateGuard)));
item.setNumberOfCoins(Math.round(newNum/item.getDenomination()));
delBalance(mob);
writeBalance(item, mob);
}
else
{
delBalance(mob);
}
}
protected void delBalance(final MOB mob)
{
// kill the journal entries for that mob
if(surviveReboot)
{
final List<JournalEntry> V = CMLib.database().DBReadJournalMsgsByUpdateDate("BRIBEGATE_"+gates(), true);
for (int v = 0; v < V.size(); v++)
{
final JournalEntry V2 = V.get(v);
if ( ( V2.from()).equalsIgnoreCase(mob.Name()))
{
final String fullName = V2.subj();
if (fullName.equals("COINS"))
{
CMLib.database().DBDeleteJournal("BRIBEGATE_"+gates(), V2.key());
}
}
}
}
else
{
final Map<String,Double> H=notTheJournal.get(gates());
if(H==null)
return;
final Double D=H.get(mob.Name());
if(D==null)
return;
H.remove(mob.Name());
}
}
protected void writeBalance(final Coins balance, final MOB mob)
{
// write an entry for that mob
if(surviveReboot)
{
CMLib.database().DBWriteJournal("BRIBEGATE_"+gates(), mob.Name(), CMClass.classID(balance),
"COINS", CMLib.coffeeMaker().getPropertiesStr(balance, true));
}
else
{
Map<String,Double> H=notTheJournal.get(gates());
if(H==null)
{
H=new Hashtable<String,Double>();
notTheJournal.put(gates(),H);
}
final Double D=H.get(mob.Name());
if(D!=null)
H.remove(mob.Name());
H.put(mob.Name(),Double.valueOf(balance.getTotalValue()));
}
}
public static int getVal(String text, String key, final int defaultValue)
{
text = text.toUpperCase();
key = key.toUpperCase();
int x = text.indexOf(key);
while (x >= 0)
{
if ( (x == 0) || (!Character.isLetter(text.charAt(x - 1))))
{
while ( (x < text.length()) && (text.charAt(x) != '=') && (!Character.isDigit(text.charAt(x))))
{
x++;
}
if ( (x < text.length()) && (text.charAt(x) == '='))
{
while ( (x < text.length()) && (!Character.isDigit(text.charAt(x))))
{
x++;
}
if (x < text.length())
{
text = text.substring(x);
x = 0;
while ( (x < text.length()) && (Character.isDigit(text.charAt(x))))
{
x++;
}
return CMath.s_int(text.substring(0, x));
}
}
x = -1;
}
else
{
x = text.toUpperCase().indexOf(key.toUpperCase(), x + 1);
}
}
return defaultValue;
}
public static String getVal(String text, String key, final String defaultValue)
{
text = text.toUpperCase();
key = key.toUpperCase();
final int x = text.indexOf(key);
while (x >= 0)
{
final int y = text.indexOf('=', x);
final int z = text.indexOf(' ', y);
if (z < 0)
{
return text.substring(y + 1);
}
if ( (y > 0) && (z > y + 1))
{
return text.substring(y + 1, z - 1);
}
return defaultValue;
}
return defaultValue;
}
@Override
public boolean okMessage(final Environmental host, final CMMsg msg)
{
if (!super.okMessage(host, msg))
{
if (debug)
{
//CMLib.commands().postSay( (MOB) oking, msg.source(), L("super FALSE"), true, true);
}
return false;
}
final MOB mob = msg.source();
if (!canFreelyBehaveNormal(host))
{
return true;
}
final MOB monster = (MOB) host;
final String currency=CMLib.beanCounter().getCurrency(monster);
if (msg.amITarget(monster)
&& (!msg.amISource(monster))
&& (msg.targetMinor() == CMMsg.TYP_GIVE)
&& (msg.tool()instanceof Coins)
&& (!((Coins)msg.tool()).getCurrency().equals(currency)))
{
final double denomination=CMLib.beanCounter().getLowestDenomination(currency);
CMLib.commands().postSay(monster,mob,L("I only accept @x1.",CMLib.beanCounter().getDenominationName(currency,denomination)),false,false);
return false;
}
if (msg.target() == null)
{
if (debug)
{
//CMLib.commands().postSay( (MOB) oking, msg.source(), L("Effect Target null"), true, true);
}
return true;
}
if (!CMLib.flags().canBeSeenBy(msg.source(), monster))
{
if (debug)
{
CMLib.commands().postSay( (MOB) host, msg.source(), L("can't be seen"), true, true);
}
return true;
}
if (mob.location() == monster.location())
{
if (msg.target()instanceof Exit)
{
if (debug)
{
CMLib.commands().postSay( (MOB) host, msg.source(),
L("okAffect triggered. Not Charging @x1 from balance @x2.",""+price(),""+getBalance(msg.source())), true, true);
}
if (!msg.source().isMonster())
{
if ( (msg.targetMinor() != CMMsg.TYP_CLOSE))
{
//|| (msg.target() instanceof Room) )
if (debug)
{
CMLib.commands().postSay( (MOB) host, msg.source(),
L("Close or Leave"), true, true);
}
if (checkBalance(price(), mob))
{
return true;
}
final CMMsg msgs = CMClass.getMsg(monster, mob, CMMsg.MSG_NOISYMOVEMENT,
L("<S-NAME> won't let <T-NAME> through there."));
if (monster.location().okMessage(monster, msgs))
{
monster.location().send(monster, msgs);
final double denomination=CMLib.beanCounter().getLowestDenomination(currency);
final String thePrice=CMLib.beanCounter().getDenominationName(currency,denomination,Math.round(price()/denomination));
CMLib.commands().postSay(monster, mob, L("I'll let you through here if you pay the fee of @x1.",thePrice), true, false);
if (debug) // debugging
CMLib.commands().postSay(monster, mob, L("I'm telling you this from okAffect"), true, false);
return false;
}
if (debug) // debugging
CMLib.commands().postSay(monster, mob, L("I'm telling you this from okAffect (2)"), true, false);
return false;
}
if (msg.target() instanceof Room)
{
if (debug) // debugging
CMLib.commands().postSay(monster, mob, L("I'm telling you this from okAffect (3)"), true, false);
}
if (debug)
{
CMLib.commands().postSay( (MOB) host, msg.source(),L("tarMin @x1 ? @x2",""+msg.targetMinor(),""+CMMsg.TYP_CLOSE), true, true);
CMLib.commands().postSay( (MOB) host, msg.source(),L("srcMin @x1 ? @x2",""+msg.sourceMinor(),""+CMMsg.TYP_LEAVE), true, true);
CMLib.commands().postSay( (MOB) host, msg.source(),L("source Monster? @x1",""+msg.source().isMonster()), true, true);
}
return true;
}
// And for leaving...
return true;
}
}
if (debug)
{
//CMLib.commands().postSay((MOB)oking,msg.source(),L("okAffect triggered. WRONG LOCATION TO FIRE."), true,true);
}
if ( (mob.location() == monster.location())
&& (mob != monster)
&& (msg.target() != null)
&& (!BrotherHelper.isBrother(mob, monster,false))
&& (CMLib.flags().canSenseEnteringLeaving(mob, monster))
&& (!CMLib.masking().maskCheck(getParms(), mob,false)))
{
if ((msg.target() instanceof Room)
&& (msg.tool() instanceof Exit))
{
return false;
}
}
return true;
}
@Override
public void executeMsg(final Environmental affecting, final CMMsg msg)
{
super.executeMsg(affecting, msg);
final MOB source = msg.source();
if (!canActAtAll(affecting))
{
return;
}
final MOB observer = (MOB) affecting;
if ( (msg.sourceMinor() == CMMsg.TYP_ENTER)
&& (!msg.amISource(observer))
&& (CMLib.flags().canSenseEnteringLeaving(msg.source(), observer))
&& (!msg.source().isMonster()))
{
// check if the msg.source() has paid enough. if so, time to react
if (checkBalance(price(), source))
{
paidPlayers.addElement(source);
toldAlready.put(source.Name(),Boolean.FALSE);
}
}
else
if ( (msg.sourceMinor() == CMMsg.TYP_LEAVE)
&& (!msg.amISource(observer))
&& (!msg.source().isMonster()))
{
toldAlready.remove(source.Name());
if (paidPlayers.contains(source))
{ // the player that the guard acknowledged as paid has now left
paidPlayers.remove(source);
if (msg.tool()instanceof Exit)
{
final Exit exit = (Exit) msg.tool();
if (exit.Name().equals(e.Name()))
{ // the player is walking through the gate. NOW we charge their balance
charge(price(), observer, source);
if(debug)
{
CMLib.commands().postSay(observer,source,L("Charging @x1, balance @x2.",""+price(),""+getBalance(source)), true,true);
}
}
}
}
else
{
// unpaid!
}
}
else
if (msg.amITarget(observer)
&& (!msg.amISource(observer))
&& (msg.targetMinor() == CMMsg.TYP_GIVE)
&& (msg.tool() instanceof Coins))
{
payment( (Coins) msg.tool(), observer, msg.source());
CMLib.commands().postSay(observer, source, L("Thank you very much."), true, false);
if(getBalance(source) > price())
{
final String currency=CMLib.beanCounter().getCurrency(observer);
final double denomination=CMLib.beanCounter().getLowestDenomination(currency);
final long diff=Math.round((getBalance(source) - price())/denomination);
final String difference=CMLib.beanCounter().getDenominationName(currency,denomination,diff);
CMLib.commands().postSay(observer, source, L("I'll hang on to the additional @x1 for you",difference), true, false);
paidPlayers.addElement(source);
toldAlready.put(source.Name(),Boolean.FALSE);
if (debug) // debugging
CMLib.commands().postSay(observer, source,
L("I'm telling you this from execute"), true, false);
try
{
if (dir >= 0)
observer.doCommand(CMParms.parse("OPEN " + CMLib.directions().getDirectionName(dir)),MUDCmdProcessor.METAFLAG_FORCED);
observer.doCommand(CMParms.parse("BOW " + source.Name()),MUDCmdProcessor.METAFLAG_FORCED);
}
catch (final Exception e1)
{
}
}
else
if(getBalance(source) == price())
{
paidPlayers.addElement(source);
toldAlready.put(source.Name(),Boolean.FALSE);
try
{
if (dir >= 0)
observer.doCommand(CMParms.parse("OPEN " +CMLib.directions().getDirectionName(dir)),MUDCmdProcessor.METAFLAG_FORCED);
observer.doCommand(CMParms.parse("BOW " + source.Name()),MUDCmdProcessor.METAFLAG_FORCED);
}
catch (final Exception e1)
{
}
}
else
if(getBalance(source) < price())
{
CMMsg msg2=CMClass.getMsg(observer,null,msg.tool(),CMMsg.MSG_EMOTE,L("^E<S-NAME> look(s) carefully at <O-NAME>.^?"));
msg.addTrailerMsg(msg2);
msg2=CMClass.getMsg(observer,null,null,CMMsg.MSG_SPEAK,L("^T<S-NAME> say(s) 'I'm afraid that this is insufficient.'^?."));
msg.addTrailerMsg(msg2);
msg2=CMClass.getMsg(observer,source,msg.tool(),CMMsg.MSG_GIVE,L("<S-NAME> give(s) <O-NAME> to <T-NAMESELF>."));
msg.addTrailerMsg(msg2);
charge(( (Coins) msg.tool()).value(), observer, msg.source());
}
}
}
@Override
public boolean tick(final Tickable ticking, final int tickID)
{
super.tick(ticking, tickID);
if (tickID != Tickable.TICKID_MOB)
{
return true;
}
if (!canFreelyBehaveNormal(ticking))
{
return true;
}
final MOB mob = (MOB) ticking;
dir = findGate(mob);
if (dir < 0)
{
CMLib.commands().postSay(mob, null, L("I'd shut the gate, but there isn't one..."), false, false);
return true;
}
e = mob.location().getExitInDir(dir);
if (!e.isOpen())
{
for (int j = 0; j < mob.location().numInhabitants(); j++)
{
final MOB M = mob.location().fetchInhabitant(j);
if(M.playerStats()!=null)
{
if ( (paidPlayers.contains(M)) && (toldAlready.containsKey(M.Name())))
{
final Boolean B = toldAlready.get(M.Name());
if (!B.booleanValue())
{
final String currency=CMLib.beanCounter().getCurrency(mob);
final double denomination=CMLib.beanCounter().getLowestDenomination(currency);
final String balanceStr=CMLib.beanCounter().getDenominationName(currency,denomination,Math.round(getBalance(M)/denomination));
CMLib.commands().postSay(mob, M,L("We still have record that you gave us @x1 before if you're heading through",balanceStr), true, false);
}
toldAlready.put(M.Name(), Boolean.TRUE);
if (dir >= 0)
mob.doCommand(CMParms.parse("OPEN " +CMLib.directions().getDirectionName(dir)),MUDCmdProcessor.METAFLAG_FORCED);
}
else
{
if(toldAlready.containsKey(M.Name()))
continue;
final String currency=CMLib.beanCounter().getCurrency(mob);
final double denomination=CMLib.beanCounter().getLowestDenomination(currency);
final String priceStr=CMLib.beanCounter().getDenominationName(currency,denomination,Math.round(price()/denomination));
CMLib.commands().postSay(mob, M,L("I'll let you through here if you pay the fee of @x1.",priceStr), true, false);
toldAlready.put(M.Name(), Boolean.TRUE);
if (debug) // debugging
CMLib.commands().postSay(mob, M,L("I'm telling you this from tick"), true, false);
}
}
}
}
final boolean nightTime = (mob.location().getArea().getTimeObj().getTODCode() == TimeClock.TimeOfDay.NIGHT);
if (nightTime)
{
if ( (!e.isLocked()) && (e.hasALock()))
{
if (getMyKeyTo(mob, e) != null)
{
final CMMsg msg = CMClass.getMsg(mob, e, CMMsg.MSG_LOCK, L("<S-NAME> lock(s) <T-NAME><O-WITHNAME>."));
if (mob.location().okMessage(mob, msg))
{
CMLib.utensils().roomAffectFully(msg, mob.location(), dir);
}
}
}
}
else
if (e.isLocked())
{
if (getMyKeyTo(mob, e) != null)
{
final CMMsg msg = CMClass.getMsg(mob, e, CMMsg.MSG_UNLOCK, L("<S-NAME> unlock(s) <T-NAME><O-WITHNAME>."));
if (mob.location().okMessage(mob, msg))
{
CMLib.utensils().roomAffectFully(msg, mob.location(), dir);
}
}
}
tickTock++;
if (tickTock > 2)
{
tickTock = 0;
if ( (e.isOpen()) && (paidPlayers.isEmpty()))
{
mob.doCommand(CMParms.parse("CLOSE " +CMLib.directions().getDirectionName(dir)),MUDCmdProcessor.METAFLAG_FORCED);
}
}
return true;
}
}