/* ** 2011 April 5 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. */ package info.ata4.bsplib.entity; import info.ata4.bsplib.vector.Vector3f; import info.ata4.log.LogUtils; import java.io.PrintStream; import java.util.*; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; /** * Abstract entity representation that works roughly like in Hammer. Has two * KeyValue lists, one for normal, unique key-values and one for I/O that may * contain duplicates. * * @author Nico Bergemann <barracuda415 at yahoo.de> */ public class Entity { private static final Logger L = LogUtils.getLogger(); private Map<String, String> keyValue = new LinkedHashMap<>(); private List<KeyValue> keyValueIO = new ArrayList<>(); private String className; /** * Creates a new empty entity with the given class name. * * @param className entity class name, must not be null or empty */ public Entity(String className) { if (className == null) { throw new NullPointerException(); } if (className.length() == 0) { throw new IllegalArgumentException("Empty class name"); } this.className = className; } /** * Creates a new entity from a list of raw key-values. * * @param kvList raw key-value list */ public Entity(List<KeyValue> kvList) { for (KeyValue kv : kvList) { String key = kv.getKey(); String value = kv.getValue(); // special KV, don't add it if (key.equals("classname")) { if (className == null) { className = value; } else { L.log(Level.WARNING, "Found duplicate classname key, ignoring {0}", kv); } continue; } if (EntityIO.isEntityIO(kv)) { keyValueIO.add(kv); } else { keyValue.put(key, value); } } // check and add missing class name if (className == null || className.isEmpty()) { L.log(Level.WARNING, "Missing or empty class name, using \"unknown_entity\""); className = "unknown_entity"; } } public List<KeyValue> getIO() { return keyValueIO; } public Set<String> getKeys() { return keyValue.keySet(); } public Collection<String> getValues() { return keyValue.values(); } public Set<Entry<String, String>> getEntrySet() { return keyValue.entrySet(); } public boolean hasKey(String key) { return keyValue.containsKey(key); } public String getValue(String key) { return keyValue.get(key); } public void setValue(String key, Object value) { keyValue.put(key, String.valueOf(value)); } public void setValue(KeyValue kv) { setValue(kv.getKey(), kv.getValue()); } public void removeValue(String key) { keyValue.remove(key); } public void clear() { keyValue.clear(); keyValueIO.clear(); } public String getClassName() { return className; } public void setClassName(String value) { className = value; } public String getTargetName() { return getValue("targetname"); } public void setTargetName(String value) { setValue("targetname", value); } public Vector3f getVector3f(String key) { String str = getValue(key); if (str == null) { return null; } // parse origin values try { // split string by whitespaces String[] costr = StringUtils.split(str, ' '); float x = costr.length > 0 ? Float.parseFloat(costr[0]) : 0; float y = costr.length > 1 ? Float.parseFloat(costr[1]) : 0; float z = costr.length > 2 ? Float.parseFloat(costr[2]) : 0; return new Vector3f(x, y, z); } catch (NumberFormatException ex) { return null; } } public void setVector3f(String key, Vector3f value) { setValue(key, value.x + " " + value.y + " " + value.z); } public Vector3f getOrigin() { return getVector3f("origin"); } public void setOrigin(Vector3f origin) { setVector3f("origin", origin); } public Vector3f getAngles() { Vector3f a = getVector3f("angles"); if (a == null) { return null; } // swap pitch yaw roll (Y Z X) axes return new Vector3f(a.z, -a.x, a.y); } public void setAngles(Vector3f a) { setVector3f("angles", new Vector3f(a.z, -a.x, a.y)); } /** * Returns the model number for this entity. * * @return The model number of this entity. -1 if no valid model number was * assigned or -2 if this entity is a prop. */ public int getModelNum() { String model = getValue("model"); if (model == null) { // no model return -1; } else if (model.startsWith("*")) { try { // get model id return Integer.parseInt(model.substring(1)); } catch (NumberFormatException ex) { return -2; } } else if (model.length() == 0) { // worldspawn return 0; } else { // studio model or invalid model format return -2; } } public void setModelNum(int modelnum) { setValue("model", "*" + modelnum); } /** * Prints all key-values to a PrintStream. * * @param ps PrintStream to write to */ public void dump(PrintStream ps) { ps.println(getClassName() + ":"); for (String key : keyValue.keySet()) { String value = keyValue.get(key); if (key.equals("classname")) { continue; } ps.println(" " + key + " = " + value); } for (KeyValue kv : keyValueIO) { ps.println(" " + kv.getKey() + ": " + kv.getValue()); } ps.println(); } /** * Prints all key-values to standard output */ public void dump() { dump(System.out); } @Override public String toString() { return getClassName() + (getTargetName() == null ? "" : " (" + getTargetName() + ")"); } }