Add support for "rear" detail layer in clients
This commit is contained in:
parent
f0da4328b7
commit
20a671639b
8 changed files with 114 additions and 5 deletions
5
data/sprites/outfit/outfits.json
Normal file
5
data/sprites/outfit/outfits.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"detail": {
|
||||
"rear": []
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,9 @@ Changelog
|
|||
- melee weapons can have longer range
|
||||
- descreased damage penalty compensating for player hit chance handicap
|
||||
|
||||
*clients*
|
||||
- support "rear" layers
|
||||
|
||||
*web client*
|
||||
- added support for drawing weather effects
|
||||
- added login sound
|
||||
|
|
|
|||
|
|
@ -21,12 +21,16 @@ import java.awt.Color;
|
|||
import java.awt.Composite;
|
||||
import java.awt.Graphics;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import games.stendhal.client.gui.OutfitColor;
|
||||
import games.stendhal.client.gui.wt.core.WtWindowManager;
|
||||
|
|
@ -35,6 +39,7 @@ import games.stendhal.client.sprite.ImageSprite;
|
|||
import games.stendhal.client.sprite.Sprite;
|
||||
import games.stendhal.client.sprite.SpriteCache;
|
||||
import games.stendhal.client.sprite.SpriteStore;
|
||||
import games.stendhal.client.util.JSONLoader;
|
||||
|
||||
/**
|
||||
* An outfit store.
|
||||
|
|
@ -48,12 +53,17 @@ public class OutfitStore {
|
|||
// these layers should return an empty sprite for index "0"
|
||||
final List<String> emptyForZeroIndex = Arrays.asList("dress", "mouth", "mask", "hair", "hat", "detail");
|
||||
|
||||
private List<Integer> detailRearLayers;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
/**
|
||||
* The singleton.
|
||||
*/
|
||||
private static final OutfitStore sharedInstance = new OutfitStore(
|
||||
SpriteStore.get());
|
||||
|
||||
|
||||
/**
|
||||
* The sprite store.
|
||||
*/
|
||||
|
|
@ -67,12 +77,49 @@ public class OutfitStore {
|
|||
*/
|
||||
private OutfitStore(final SpriteStore store) {
|
||||
this.store = store;
|
||||
detailRearLayers = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
if (initialized) {
|
||||
logger.warn("tried to re-initialize OutfitStore");
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
final JSONLoader loader = new JSONLoader();
|
||||
loader.onDataReady = new Runnable() {
|
||||
public void run() {
|
||||
final JSONObject document = (JSONObject) loader.data;
|
||||
final JSONArray detailRear = (JSONArray) ((JSONObject) document.get("detail")).get("rear");
|
||||
for (final Object o: detailRear) {
|
||||
try {
|
||||
detailRearLayers.add(Integer.parseInt(o.toString()));
|
||||
} catch (final NumberFormatException e) {
|
||||
logger.error("Couldn't parse rear detail layer \"" + o.toString() + "\"", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
loader.load(OUTFITS + "/outfits.json");
|
||||
}
|
||||
|
||||
//
|
||||
// OutfitStore
|
||||
//
|
||||
|
||||
/**
|
||||
* Checks if we can draw a "rear" detail layer.
|
||||
*
|
||||
* @param detail
|
||||
* Detail index.
|
||||
* @return
|
||||
* <code>true</code> if a rear sprite is available.
|
||||
*/
|
||||
private boolean detailHasRearLayer(final int detail) {
|
||||
return detailRearLayers.contains(detail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an outfit sprite.
|
||||
*
|
||||
|
|
@ -84,8 +131,12 @@ public class OutfitStore {
|
|||
private Sprite buildOutfit(final String strcode, final OutfitColor color) {
|
||||
final Map<String, Integer> layer_map = new HashMap<>();
|
||||
|
||||
// make a copy of layer names so it can be amended in cases of special layers
|
||||
final List<String> lnames = new LinkedList<>();
|
||||
lnames.addAll(LAYER_NAMES);
|
||||
|
||||
// initialize outfit parts to 0 in case some haven't been specified
|
||||
for (String n: LAYER_NAMES) {
|
||||
for (String n: lnames) {
|
||||
layer_map.put(n, 0);
|
||||
}
|
||||
|
||||
|
|
@ -100,6 +151,13 @@ public class OutfitStore {
|
|||
|
||||
Sprite layer;
|
||||
|
||||
// detail rear layer
|
||||
final int detailIndex = layer_map.get("detail");
|
||||
if (detailHasRearLayer(detailIndex)) {
|
||||
lnames.add(0, "detail-rear");
|
||||
layer_map.put("detail-rear", detailIndex);
|
||||
}
|
||||
|
||||
// Body layer
|
||||
final int bodyIndex = layer_map.get("body");
|
||||
boolean busty = false;
|
||||
|
|
@ -120,7 +178,7 @@ public class OutfitStore {
|
|||
sprite = new ImageSprite(layer);
|
||||
final Graphics g = sprite.getGraphics();
|
||||
|
||||
for (String lname: LAYER_NAMES) {
|
||||
for (String lname: lnames) {
|
||||
// hair is not drawn under certain hats/helmets
|
||||
if (lname.equals("hair") && HATS_NO_HAIR.contains(layer_map.get("hat"))) {
|
||||
continue;
|
||||
|
|
@ -195,7 +253,7 @@ public class OutfitStore {
|
|||
* @return
|
||||
* The Sprite or <code>null</code>.
|
||||
*/
|
||||
public Sprite getLayerSprite(final String layer, final int index, final OutfitColor color,
|
||||
public Sprite getLayerSprite(String layer, final int index, final OutfitColor color,
|
||||
final boolean busty) {
|
||||
if (emptyForZeroIndex.contains(layer)) {
|
||||
if (index <= 0) {
|
||||
|
|
@ -207,7 +265,13 @@ public class OutfitStore {
|
|||
}
|
||||
}
|
||||
|
||||
String ref = OUTFITS + "/" + layer + "/" + getSpriteSuffix(index);
|
||||
String ref = getSpriteSuffix(index);
|
||||
if (layer.endsWith("-rear")) {
|
||||
ref += "-rear";
|
||||
layer = layer.replaceFirst("-rear$", "");
|
||||
}
|
||||
|
||||
ref = OUTFITS + "/" + layer + "/" + ref;
|
||||
if (layer.equals("body") && WtWindowManager.getInstance().getPropertyBoolean("gamescreen.nonude", true)) {
|
||||
final URL nonudeURL = DataLoader.getResource(ref + "-nonude.png");
|
||||
if (nonudeURL != null) {
|
||||
|
|
|
|||
|
|
@ -274,6 +274,8 @@ public final class stendhal {
|
|||
TileStore.init();
|
||||
// initialize emoji data
|
||||
ClientSingletonRepository.getEmojiStore().init();
|
||||
// initialize outfit data
|
||||
OutfitStore.get().init();
|
||||
|
||||
final UserContext userContext = UserContext.get();
|
||||
final PerceptionDispatcher perceptionDispatch = new PerceptionDispatcher();
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public class Outfits {
|
|||
|
||||
// layers that can be re-colored
|
||||
public static final List<String> RECOLORABLE_OUTFIT_PARTS = Arrays.asList(
|
||||
"detail", "dress", "hair", "body", "head", "eyes", "mask", "hat");
|
||||
"detail", "detail-rear", "dress", "hair", "body", "head", "eyes", "mask", "hat");
|
||||
|
||||
public static final List<String> SKIN_LAYERS = Arrays.asList("body", "head");
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,8 @@ export class Client {
|
|||
singletons.getTileStore().init();
|
||||
// initialize emoji data
|
||||
singletons.getEmojiStore().init();
|
||||
// initialize outfit data
|
||||
stendhal.data.outfit.init();
|
||||
|
||||
new DesktopUserInterfaceFactory().create();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,15 @@
|
|||
* *
|
||||
***************************************************************************/
|
||||
|
||||
import { Paths } from "./Paths";
|
||||
import { JSONLoader } from "../util/JSONLoader";
|
||||
|
||||
|
||||
export class OutfitStore {
|
||||
|
||||
private detailRearLayers: number[] = [];
|
||||
|
||||
|
||||
// player pickable layers
|
||||
private count: {[key: string]: number} = {
|
||||
"hat": 19,
|
||||
|
|
@ -56,6 +62,14 @@ export class OutfitStore {
|
|||
// do nothing
|
||||
}
|
||||
|
||||
init() {
|
||||
const loader = new JSONLoader();
|
||||
loader.onDataReady = () => {
|
||||
this.detailRearLayers = loader.data["detail"]["rear"];
|
||||
}
|
||||
loader.load(Paths.sprites + "/outfit/outfits.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if hair should be drawn under a determinted hat index.
|
||||
*
|
||||
|
|
@ -82,4 +96,16 @@ export class OutfitStore {
|
|||
drawBustyDress(dress: number, body: number): boolean {
|
||||
return body == 1 && this.busty_dress.indexOf(dress) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we can draw a "rear" detail layer.
|
||||
*
|
||||
* @param detail
|
||||
* Detail index.
|
||||
* @return
|
||||
* <code>true</code> if a rear sprite is available.
|
||||
*/
|
||||
detailHasRearLayer(detail: number): boolean {
|
||||
return this.detailRearLayers.indexOf(detail) > -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -241,6 +241,10 @@ export class RPEntity extends ActiveEntity {
|
|||
if (this.octx) {
|
||||
this.octx.clearRect(0, 0, this.octx.canvas.width, this.octx.canvas.height);
|
||||
}
|
||||
if (stendhal.data.outfit.detailHasRearLayer(outfit["detail"])) {
|
||||
layers.splice(0, 0, "detail-rear");
|
||||
outfit["detail-rear"] = outfit["detail"];
|
||||
}
|
||||
for (const layer of layers) {
|
||||
// hair is not drawn under certain hats/helmets
|
||||
if (layer == "hair" && !stendhal.data.outfit.drawHair(outfit["hat"])) {
|
||||
|
|
@ -287,6 +291,9 @@ export class RPEntity extends ActiveEntity {
|
|||
n += "-nonude";
|
||||
} else if (part === "dress" && stendhal.data.outfit.drawBustyDress(index, body)) {
|
||||
n += "b";
|
||||
} else if (part.endsWith("-rear")) {
|
||||
n += "-rear";
|
||||
part = part.replace(/-rear$/, "");
|
||||
}
|
||||
|
||||
const filename = stendhal.paths.sprites + "/outfit/" + part + "/" + n + ".png";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue