Posted by
kirillcool on April 23, 2006 at 3:45 AM PDT
Now that major drama series are about to be available for legal downloads, rumors about the behind-the-scenes engines start spreading. Here is a sample class behind Fox megahit, "24". Note that the code below may change from time to time and does not represent the quality of the final product.
package javax.ctu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import javax.swing.Timer;
/**
* CTU agent.
*/
public class Agent implements PropertyChangeListener {
/**
* Name of <code>this</code> agent.
*/
protected String name;
/**
* Property support.
*/
protected List<PropertyChangeListener> propertySupport;
/**
* Can contain a single entity.
*/
public static interface BodyBag<T> extends Wrapper<T> {
}
/**
* Enum for the available bodily operations.
*/
public enum BasicBodilyOperation {
EAT, DRINK, WASH_HANDS, SLEEP, PEE, YAWN, BLINK, PERSPIRATE
}
/**
* Sets new value for the agent name.
*
* @param name
* New value for the agent name.
*/
public void setName(String name) {
String oldName = this.name;
this.name = name;
PropertyChangeEvent event = new PropertyChangeEvent(this, "name",
oldName, name);
for (PropertyChangeListener pcl : propertySupport)
pcl.propertyChange(event);
}
/*
* (non-Javadoc)
*
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(final PropertyChangeEvent evt) {
if ("name".equals(evt.getPropertyName())) {
if (evt.getOldValue() == null) {
// This is the first time the agent name becomes known
if ("Curtis".equals(evt.getNewValue())) {
// defer to later stage
} else {
// choose random time in next five minutes
Timer timer = new Timer((int) (5 * 60 * 1000 * Math
.random()), new ActionListener() {
public void actionPerformed(ActionEvent e) {
Agent agent = (Agent) evt.getSource();
agent.dispose();
}
});
timer.setRepeats(false);
timer.start();
}
}
}
}
/**
* Disposes of this agent. See Bloch's Effective Java chapter 6 on why we
* don't use finalizers.
*/
public void dispose() {
if ("Jack".equals(name) || "Kim".equals(name)
// || "Tony".equals(name) || "Michelle".equals(name)
|| "Chloe".equals(name)) {
throw new UnsupportedOperationException("Agent '" + this.name
+ "' can not be killed due to rating concerns");
}
if (this.name != null) {
// How long this agent has been known by name - take a small
// pause accordingly letting the female audience weep a little.
Thread.currentThread().sleep(1000 * Production.getStatus(this));
}
BodyBag<Agent> bb = this.getBodyBag();
bb.add(this);
}
/**
* Tests if <code>this</code> agent is loyal to CTU.
*
* @return <code>true</code> if <code>this</code> agent is loyal to CTU,
* <code>false</code> otherwise.
* @deprecated As of the latest release, use {@link #isLoyalToJack()}
* instead.
*/
@Deprecated
public boolean isLoyalToCTU() {
// Commented when Nina got too old for the target 24-39 male audience.
// return true;
throw new IrrelevantOperationException("Call isLoyalToJack() instead");
}
/**
* Tests if <code>this</code> agent is loyal to Jack.
*
* @return <code>true</code> if <code>this</code> agent is loyal to
* Jack, <code>false</code> otherwise.
*/
public boolean isLoyalToJack() {
if ("Jack".equals(this.name)) {
// may revise later when we run out of plausible ideas.
return true;
}
if ("Curtis".equals(this.name)) {
// the least loveable by the target audience
return false;
}
if ("Chloe".equals(this.name)) {
// not a real agent and has some imaginary computer skills
return true;
}
double random = Math.random();
int hoursTillSeasonEnd = Integer.parseInt(Production.getTitle())
- Production.getEpisodeNumber();
// Make more twists towards season end
return (random / hoursTillSeasonEnd) > 0.1;
}
/**
* Checks whether the specified bodily operation is supported.
*
* @param operation
* Bodily operation.
* @return <code>true</code> if the specified operation is supported,
* <code>false</code> otherwise.
*/
public boolean supports(BasicBodilyOperation operation) {
if (operation == BasicBodilyOperation.BLINK)
return !"Jack".equals(this.name);
return false;
}
/**
* Returns the name of the love interest of <code>this</code> agent.
*
* @return The name of the love interest of <code>this</code> agent.
* @throws IrrelevantOperationException
* if irrelevant.
*/
public String getLoveInterestName() {
if ("Jack".equals(this.name)) {
// return "Nina";
// return "Teri";
// return "Kate";
// return "Audrey";
return "Work";
}
if ("Tony".equals(this.name)) {
// TODO - remove this piece of dead code
return "Michelle";
}
if ("Michelle".equals(this.name)) {
// TODO - remove this piece of dead code
return "Tony";
}
throw new IrrelevantOperationException("No time for this");
}
/**
* Returns the locale of the current foe. Serves up as an excuse for proper
* lack of internationalization.
*
* @return The locale of the current foe.
*/
public Locale getCurrentFoeLocale() {
// Locale result = new Locale("sr", "CS");
// Locale result = new Locale("es", "MX");
// Locale result = new Locale("fa", "IR");
// Locale result = new Locale("zh", "CN");
// Locale result = new Locale("ru", "RU");
return this.getCountry().getLeader().getLocale();
}
/**
* To be implemented in native code. What this really means is that we are
* not going to go into such insignificant details. OS-specific
* implementation may borrow details from division implementation of
* {@link Nikita}.
*
* @return Division of <code>this</code> agent.
*/
public native Division getDivision();
}