/** * Check if the user is close enough the parent entity of the slot. If the user is too far away * the window should not be opened, and it should be closed if it was already open. * * @return <code>true</code> if the user is close enough to have the window open, <code>false * </code> otherwise. */ public boolean isCloseEnough() { final User user = User.get(); if ((user != null) && (parent != null)) { // null checks are fixes for Bug 1825678: // NullPointerException happened // after double clicking one // monster and a fast double // click on another monster // Check if the parent is user RPObject root = parent.getRPObject().getBaseContainer(); // We don't want to close our own stuff // The root entity may have been removed, but still if it was // the user we do not want to close it. // User may have been changed by the main thread, so we can not rely // on user.getRPObject() being equal to root. (bug #3159058) final String type = root.getRPClass().getName(); if (type.equals("player") && root.has("name")) { if (StendhalClient.get().getCharacter().equalsIgnoreCase(root.get("name"))) { return true; } } return isCloseEnough(user.getX(), user.getY()); } return true; }
/** * Get a keyed string value on a named slot. * * @param slotOwner the object owning the slot * @param name The slot name. * @param key The value key. * @return The keyed value of the slot, or <code>null</code> if not set. */ public static String getKeyedSlot( final SlotOwner slotOwner, final String name, final String key) { final RPObject object = KeyedSlotUtil.getKeyedSlotObject(slotOwner, name); if (object == null) { return null; } return object.get(key); }
/** * Initialize this entity for an object. * * @param object The object. * @see #release() */ @Override public void initialize(final RPObject object) { super.initialize(object); if (object.hasSlot("content")) { content = object.getSlot("content"); } else { content = null; } }
/** * TODO: there is a bug in marauroa, remove this if marauroa stops storing volatile attributes. * review then also which attributes should be volatile and which shouldnt. * * @param player */ private void removeVolatile(final RPObject player) { if (player.has(AWAY)) { player.remove(AWAY); } // remove grumpy on login to give postman a chance to deliver messages // (and in the hope that player is receptive now) if (player.has(GRUMPY)) { player.remove(GRUMPY); } }
/** * Check whether the given RPObject is the RPObject representing the user's character. When an * RPObject is the subclass of the "character" RPClass and has the same name as the user has * chosen as its character by the start, an object is considered to be the character object. This * relies on having unique character names. * * @param object the RPObject to check whether it's the user's character object * @return true if the given object is the user's character object */ protected boolean isCharacter(final RPObject object) { if (name == null) { return false; } if (object.getRPClass().subclassOf("character")) { return name.equalsIgnoreCase(object.get("name")); } else { return false; } }
/** * Places the player (and his/her sheep if there is one) into the world on login. * * @param object RPObject representing the player * @param player Player-object */ public static void placePlayerIntoWorldOnLogin(final RPObject object, final Player player) { StendhalRPZone zone = null; String zoneName = System.getProperty("stendhal.forcezone"); if (zoneName != null) { zone = SingletonRepository.getRPWorld().getZone(zoneName); zone.placeObjectAtEntryPoint(player); return; } try { if (object.has("zoneid") && object.has("x") && object.has("y")) { if (Version.checkCompatibility(object.get("release"), Debug.VERSION)) { zone = SingletonRepository.getRPWorld().getZone(object.get("zoneid")); } else { if (player.getLevel() >= 2) { TutorialNotifier.newrelease(player); } } player.put("release", Debug.VERSION); } } catch (final RuntimeException e) { // If placing the player at its last position // fails, we reset to default zone logger.warn("Cannot place player at its last position. Using default", e); } if (zone != null) { /* * Put the player in their zone (use placeat() for collision rules) */ if (!StendhalRPAction.placeat(zone, player, player.getX(), player.getY())) { logger.warn("Cannot place player at their last position: " + player.getName()); zone = null; } } if (zone == null) { /* * Fallback to default zone */ final String defaultZoneName = getDefaultZoneForPlayer(player); zone = SingletonRepository.getRPWorld().getZone(defaultZoneName); if (zone == null) { logger.error("Unable to locate default zone [" + defaultZoneName + "]"); return; } zone.placeObjectAtEntryPoint(player); } }
/** * The object removed attribute(s). * * @param object The base object. * @param changes The changes. */ @Override public void onChangedRemoved(final RPObject object, final RPObject changes) { super.onChangedRemoved(object, changes); if (changes.has("away")) { onAway(null); } if (changes.has("grumpy")) { onGrumpy(null); } if (changes.has(LAST_PLAYER_KILL_TIME)) { badboy = false; fireChange(PROP_PLAYER_KILLER); } }
/** * Set a keyed string value on a named slot. * * @param slotOwner the object owning the slot * @param name The slot name. * @param key The value key. * @param value The value to assign (or remove if <code>null</code>). * @return <code>true</code> if value changed, <code>false</code> if there was a problem. */ public static boolean setKeyedSlot( final SlotOwner slotOwner, final String name, final String key, final String value) { final RPObject object = KeyedSlotUtil.getKeyedSlotObject(slotOwner, name); if (object == null) { return false; } if (value != null) { object.put(key, value); } else if (object.has(key)) { object.remove(key); } return true; }
/** * Takes the given RPObject as the new RPObject representing the user's character. Does nothing if * the provided RPObject isn't the user's character object as determined by * isCharacter(newCharacter) or the provided RPObject is the same as the current one. * * @param newCharacter the new RPObject representing the users character */ public void setCharacter(final RPObject newCharacter) { if (isCharacter(newCharacter) && this.myCharacter != newCharacter) { myCharacter = newCharacter; name = newCharacter.get("name"); } else { log.warn("Character not set because it's not our character."); } }
/** * Initialize this entity for an object. * * @param base The object. * @see #release() */ @Override public void initialize(final RPObject base) { double speed; super.initialize(base); if (base.has("dir")) { setDirection(Direction.build(base.getInt("dir"))); } if (base.has("speed")) { speed = base.getDouble("speed"); } else { speed = 0.0; } dx = direction.getdx() * speed; dy = direction.getdy() * speed; }
/** Tests for onAdded. */ @Test public void testOnAdded() { player.onAdded(new StendhalRPZone("playertest")); RPObject object = KeyedSlotUtil.getKeyedSlotObject(player, "!visited"); if (object == null) { fail("slot not found"); } assertTrue(object.has("playertest")); assertThat(player.get("visibility"), is("100")); player.onAdded(new StendhalRPZone(PlayerDieer.DEFAULT_DEAD_AREA)); object = KeyedSlotUtil.getKeyedSlotObject(player, "!visited"); if (object == null) { fail("slot not found"); } assertTrue(object.has(PlayerDieer.DEFAULT_DEAD_AREA)); assertThat(player.get("visibility"), is("50")); player.onRemoved(new StendhalRPZone(PlayerDieer.DEFAULT_DEAD_AREA)); assertThat(player.get("visibility"), is("100")); }
/** * Loads the items into the slots of the player on login. * * @param player Player * @param slot original slot * @param newSlot new Stendhal specific slot */ private void loadSlotContent(final Player player, final RPSlot slot, final PlayerSlot newSlot) { final List<RPObject> objects = new LinkedList<RPObject>(); for (final RPObject objectInSlot : slot) { objects.add(objectInSlot); } slot.clear(); player.removeSlot(slot.getName()); player.addSlot(newSlot); ItemTransformer transformer = new ItemTransformer(); for (final RPObject rpobject : objects) { try { // remove admin items the player does not deserve if (ITEMS_FOR_ADMINS.contains(rpobject.get("name")) && (!player.has("adminlevel") || player.getInt("adminlevel") < 1000)) { logger.warn( "removed admin item " + rpobject.get("name") + " from player " + player.getName()); new ItemLogger().destroyOnLogin(player, slot, rpobject); continue; } Item item = transformer.transform(rpobject); // log removed items if (item == null) { int quantity = 1; if (rpobject.has("quantity")) { quantity = rpobject.getInt("quantity"); } logger.warn( "Cannot restore " + quantity + " " + rpobject.get("name") + " on login of " + player.getName() + " because this item" + " was removed from items.xml"); new ItemLogger().destroyOnLogin(player, slot, rpobject); continue; } boundOldItemsToPlayer(player, item); newSlot.add(item); } catch (final Exception e) { logger.error("Error adding " + rpobject + " to player slot" + slot, e); } } }
/** * Process attribute changes that may affect positioning. This is needed because different * entities may want to process coordinate changes more gracefully. * * @param base The previous values. * @param diff The changes. */ @Override protected void processPositioning(final RPObject base, final RPObject diff) { // Real movement case final int oldx = base.getInt("x"); final int oldy = base.getInt("y"); int newX = oldx; int newY = oldy; if (diff.has("x")) { newX = diff.getInt("x"); } if (diff.has("y")) { newY = diff.getInt("y"); } Direction tempDirection; if (diff.has("dir")) { tempDirection = Direction.build(diff.getInt("dir")); setDirection(tempDirection); } else if (base.has("dir")) { tempDirection = Direction.build(base.getInt("dir")); setDirection(tempDirection); } else { tempDirection = Direction.STOP; } double speed; /* * Speed change must be fired only after the new speed has been stored * (done in onMove()) */ boolean speedChanged = false; if (diff.has("speed")) { speed = diff.getDouble("speed"); speedChanged = true; } else if (base.has("speed")) { speed = base.getDouble("speed"); } else { speed = 0; } onMove(newX, newY, tempDirection, speed); if (speedChanged) { fireChange(PROP_SPEED); } boolean positionChanged = false; if ((Direction.STOP.equals(tempDirection)) || (speed == 0)) { setSpeed(0.0, 0.0); /* * Try to ensure relocation in the case the client and server were * in disagreement about the position at the moment of stopping. */ if (!(compareDouble(y, newY, EPSILON) && compareDouble(x, newX, EPSILON))) { positionChanged = true; } // Store the new position before signaling it with onPosition(). x = newX; y = newY; } /* * Change in position? */ if (positionChanged || ((oldx != newX) && (oldy != newY))) { onPosition(newX, newY); } }
private void sendPlayerPerception( PlayerEntry entry, Perception perception, RPObject playerObject) { if (perception == null) { /** Until player enters game perception is null */ return; } MessageS2CPerception messages2cPerception = new MessageS2CPerception(entry.channel, perception); stats.add("Perceptions " + (perception.type == 0 ? "DELTA" : "SYNC"), 1); /* * The perception is build of two parts: the general information and the * private information about our object. This private information * consists only of attributes that are not visible to every player but * the owner, because visible attributes are already stored in the * perception. */ if (perception.type == Perception.SYNC) { RPObject copy = new RPObject(); copy.fill(playerObject); if (!playerObject.isHidden()) { copy.clearVisible(true); } messages2cPerception.setMyRPObject(copy, null); } else { RPObject added = new RPObject(); RPObject deleted = new RPObject(); try { playerObject.getDifferences(added, deleted); if (!playerObject.isHidden()) { added.clearVisible(false); deleted.clearVisible(false); } if (added.size() == 0) { added = null; } if (deleted.size() == 0) { deleted = null; } } catch (Exception e) { logger.error("Error getting object differences", e); logger.error(playerObject); added = null; deleted = null; } messages2cPerception.setMyRPObject(added, deleted); } messages2cPerception.setClientID(entry.clientid); messages2cPerception.setPerceptionTimestamp(entry.getPerceptionTimestamp()); messages2cPerception.setProtocolVersion(entry.getProtocolVersion()); netMan.sendMessage(messages2cPerception); }