/** * Update whether a named signal source is active. * * @param name signal's name (not null) * @param sourceIndex index of the signal source (key or button) which is being updated * @param newState true if the source is active; false is the source is inactive */ private void update(String name, int sourceIndex, boolean newState) { Indices status = statusMap.get(name); if (status == null) { logger.log(Level.WARNING, "Unknown signal: {0}", MyString.quote(name)); return; } logger.log( Level.INFO, "name = {0}, newState = {1}", new Object[] {MyString.quote(name), newState}); status.addRemove(sourceIndex, newState); }
/** * Reload a prop and unpick it. * * @param originalProp which prop to reload (not null) */ public void reload(Spatial originalProp) { String name = originalProp.getName(); logger.log(Level.INFO, "reloading prop {0}", MyString.quote(name)); Node reloaded = duplicate(originalProp); scene.deleteSpatial(originalProp); reloaded.setName(name); }
/** * Determine the mass of a standard-sized prop. * * @param propType the type of the prop (not null) * @return mass (in kilograms, >0) */ private static float standardMass(String propType) { assert propType != null; switch (propType) { case "barrel": return 500f; case "mantlet": return 100f; } logger.log(Level.SEVERE, "Unexpected prop type {0}", MyString.quote(propType)); return 1f; }
/** * Create a prop without adding it to the scene. * * @param propType which kind of prop to add (not null) * @param relativeSize the prop's size relative to standard (>0) * @return a new node to represent the prop, or null if the model is invalid */ private Node create(String propType, float relativeSize) { assert propType != null; assert relativeSize > 0f : relativeSize; Node propNode = loadModel(propType); Node modelNode = (Node) propNode.getChild(0); List<Spatial> parts = modelNode.getChildren(); /* * Texture each part of the prop and specify its hardness. */ for (Spatial part : parts) { String partName = part.getName(); if (!(part instanceof Geometry)) { logger.log(Level.SEVERE, "Prop part {0} is not a geometry.", MyString.quote(partName)); return null; } String materialName = material(partName); SpatialProperties.setHardness(part, materialName); Material material = Assets.loadBlockMaterial(materialName); part.setMaterial(material); } /* * Set the scale factor. */ float scaleFactor = scene.getScaleFactor(); float modelScale = relativeSize / scaleFactor; propNode.setLocalScale(modelScale); assert getRelativeSize(propNode) == relativeSize : relativeSize; /* * Set other properties. */ propNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); /* * Give the prop a physics control. */ CollisionShape shape = createShape(propType, relativeSize, parts); float mass = calculateMass(propType, relativeSize); RigidBodyControl rigidBodyControl = new RigidBodyControl(shape, mass); propNode.addControl(rigidBodyControl); /* * Configure the physics control. */ rigidBodyControl.setFriction(friction); scene.getWorld().getPhysics().addObject(rigidBodyControl); /* * Give the prop a PickedControl. */ SpatialProperties.setPickType(propNode, pickType); PickedControl pickedControl = new PickedControl(); propNode.addControl(pickedControl); return propNode; }
/** * Duplicate a prop. Duplicating differs from cloning because the duplicate gets a unique name and * any descendants of the duplicate get renamed to match. Besides, cloning doesn't work well on * physics controls. * * @param originalProp which prop to duplicate (not null) * @return the new prop */ public Node duplicate(Spatial originalProp) { String name = originalProp.getName(); logger.log(Level.INFO, "duplicating prop {0}", MyString.quote(name)); String propType = SpatialProperties.getPrefixType(originalProp); float relativeSize = originalProp.getWorldScale().x; Vector3f centerLocation = MySpatial.getWorldLocation(originalProp); Quaternion orientation = MySpatial.getWorldOrientation(originalProp); Node result = add(propType, relativeSize, centerLocation, orientation); return result; }
/** * Determine the correct material for a name prop part. * * @param partName the name of the prop part (not null) * @return name of the material (or null if unknown part) */ private static String material(String partName) { assert partName != null; switch (partName) { case "barrel1": case "mantlet2": return "bronze"; case "barrel2": case "mantlet1": return "wood"; } logger.log(Level.SEVERE, "Unexpected prop part {0}", MyString.quote(partName)); return null; }
/** * Process a signal action. * * @param actionString * @param isOngoing * @param unused */ @Override public void onAction(String actionString, boolean isOngoing, float unused) { logger.log(Level.INFO, "action = {0}", MyString.quote(actionString)); /* * Parse the action string into words. */ String[] words = actionString.split("\\s+"); assert words.length == 3; assert "signal".equals(words[0]); String name = words[1]; int sourceIndex = Integer.parseInt(words[2]); update(name, sourceIndex, isOngoing); }
/** * Test whether the named signal is active. * * @param name signal's name (not null) * @return true if any of the signal's sources is active; false if all of the signal's sources are * inactive */ public boolean test(String name) { assert name != null; Indices status = statusMap.get(name); if (status == null) { logger.log( Level.WARNING, "Testing a signal which has not yet been added: {0}.", MyString.quote(name)); status = new Indices(); statusMap.put(name, status); } boolean result = !status.isEmpty(); return result; }
/** * Create a prop, add it to the scene with its base at the insertion point, and pick it. Assumes * that the application is in world-building mode. * * @param propType which kind of prop to add (not null) */ private void addAsPick(String propType) { assert propType != null; switch (propType) { case "barrel": addAsPick("barrel", 0.62f); return; case "hogshead": addAsPick("barrel", 0.78f); return; case "mantlet": addAsPick("mantlet", 1f); return; case "tun": addAsPick("barrel", 1.24f); return; } logger.log(Level.SEVERE, "Unknown prop type {0}!", MyString.quote(propType)); }