/*
* Copyright 2014 The Skfiy Open Association.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.skfiy.typhon.domain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.skfiy.typhon.dobj.ItemDobj;
import org.skfiy.typhon.domain.item.AbstractItem;
import org.skfiy.typhon.util.DomainUtils;
import org.skfiy.typhon.util.SortedList;
import com.alibaba.fastjson.annotation.JSONType;
/**
* 背包实体对象设置.
*
* @author Kevin Zou <kevinz@skfiy.org>
*/
@JSONType(ignores = {"full"})
public class Bag extends AbstractChangeable {
private static final int MIN_POS = 1;
private final List<Node> nodes = new SortedList<>();
private int maxSize = Integer.MAX_VALUE;
/**
*
* @return
*/
public List<Node> getNodes() {
return Collections.unmodifiableList(nodes);
}
/**
*
* @param nodes
*/
public void setNodes(List<Node> nodes) {
this.nodes.clear();
this.nodes.addAll(nodes);
for (Node n : this.nodes) {
n.set(this, "nodes");
}
}
/**
* 获取背包最大容易, 默认值为100.
*
* @return 一个无符号的数字
*/
public int getMaxSize() {
return maxSize;
}
/**
* 设置背包最大容量.
*
* @param maxSize 一个无符号的数字
*/
public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
/**
* 往背包里面放置一个道具并且返回是否放置成功.
*
* @param itemDobj 道具对象
* @return 是否放置成功
*/
public int intoItem(ItemDobj itemDobj) {
return intoItem(itemDobj, 1);
}
/**
* 往背包里面放置指定道具数量并且返回是否放置成功. 该接口发现ItemDobj如果是可堆叠的会自动叠加到现有的
* 道具中去.
*
* @param itemDobj 道具对象
* @param count 道具数量
* @return 是否放置成功
*/
public synchronized int intoItem(ItemDobj itemDobj, int count) {
int c = count;
if (isOverlapped(itemDobj)) {
Node n = findNode(itemDobj.getId());
if (n != null) {
int av = n.item.getOverlapping() - n.total;
if (av > 0) {
c -= av;
if (c <= 0) {
n.setTotal(n.total + count);
} else {
n.setTotal(n.total + av);
}
n.setLastGainTime((int) (System.currentTimeMillis() / 1000));
}
return n.pos;
}
if (c > itemDobj.getOverlapping()) {
c = itemDobj.getOverlapping();
}
}
if (c <= 0) {
return -1;
}
// 背包已满无法放置新的道具
if (isFull()) {
return -1;
}
if (!isOverlapped(itemDobj) && c > 1) {
for (int i = 0; i < c; i++) {
addNode(itemDobj, 1);
}
} else {
return addNode(itemDobj, c);
}
// int pos = nextPos();
// if (pos < MIN_POS) {
// return -1;
// }
//
// Node node = new Node();
// node.setPos(pos);
// node.setItem(itemDobj.toDomainItem());
// node.setTotal(c);
// node.setLastGainTime((int) (System.currentTimeMillis() / 1000));
//
// nodes.add(node);
//
// // 通知客户端添加一个道具
// DomainUtils.fireIndexPropertyAdd(this, "nodes", node);
//
// node.set(this, "nodes");
return -1;
}
/**
* 查询指定位置的道具信息.
*
* @param pos 道具位置
* @return 道具信息
*/
public Node findNode(int pos) {
for (Node node : nodes) {
if (node.pos == pos) {
return node;
}
}
return null;
}
/**
* 根据道具ID查询背包符合条件的第一个道具信息.
*
* @param iid 道具ID
* @return 第一个符合条件的道具信息
*/
public Node findNode(String iid) {
for (Node node : nodes) {
if (node.item.getId().equals(iid)) {
return node;
}
}
return null;
}
/**
* 根据道具ID查询背包里面的具体节点道具信息.
*
* @param iid 道具ID
* @return 符合条件的背包节点道具
*/
public Node[] findNodes(String iid) {
List<Node> ns = new ArrayList<>();
for (Node node : nodes) {
if (node.getItem().getId().equals(iid)) {
ns.add(node);
}
}
Node[] nodeArray = new Node[ns.size()];
ns.toArray(nodeArray);
return nodeArray;
}
/**
* 移除背包某个位置的道具并且返回该位置对应的具体道具对象.
*
* @param pos 需要移除的位置
* @return {@code pos }位置对应的具体道具对象
*/
public synchronized Node removeNode(int pos) {
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
if (node.getPos() == pos) {
nodes.remove(i);
DomainUtils.fireIndexPropertyRemove(node);
return node;
}
}
return null;
}
/**
* 移除背包中的某个节点道具.
*
* @param node 具体的道具
* @return 是否移除成功
*/
public synchronized boolean removeNode(Node node) {
boolean r = nodes.remove(node);
if (r) {
DomainUtils.fireIndexPropertyRemove(node);
}
return r;
}
/**
* 交换两个位置的道具. {@code srcPos }必须是一个有效的位置并且该位置存在一个道具.
*
* @param srcPos 原始位置
* @param destPost 目标位置
* @return 是否交换成功
*/
public synchronized boolean swap(int srcPos, int destPost) {
if (destPost > maxSize) {
return false;
}
Node srcNode = findNode(srcPos);
if (srcNode == null) {
return false;
}
Node destNode = findNode(destPost);
if (destNode != null) {
destNode.setPos(srcPos);
nodes.remove(destNode);
nodes.add(destNode);
}
srcNode.setPos(destPost);
nodes.remove(srcNode);
nodes.add(srcNode);
return true;
}
/**
* 减少某一个道具节点的数量.
*
* @param node 被减少的道具
* @param count 减少的数量
* @return 是否减少成功
*/
public synchronized boolean decrementTotal(Node node, int count) {
if (node == null || node.getTotal() < count) {
return false;
}
int newTotal = node.getTotal() - count;
if (newTotal <= 0) {
removeNode(node);
} else {
node.setTotal(newTotal);
}
return true;
}
/**
* 减少背包中某种道具类型的数量.
*
* @param iid 道具ID
* @param count 减少的数量
* @return 是否减少成功
*/
public synchronized boolean decrementTotal(String iid, int count) {
Node[] nodeData = findNodes(iid);
int allCount = 0;
for (Node node : nodeData) {
allCount += node.getTotal();
}
if (allCount < count) {
return false;
}
int newTotal;
for (Node node : nodeData) {
newTotal = node.getTotal() - count;
if (newTotal <= 0) {
removeNode(node);
if (newTotal == 0) {
break;
}
count = -(newTotal);
} else {
node.setTotal(newTotal);
break;
}
}
return true;
}
/**
* 背包道具的数量.
*
* @return 数量
*/
public int size() {
return nodes.size();
}
/**
* 背包是否已满.
*
* @return 是/否
*/
public boolean isFull() {
return (maxSize == size());
}
private int addNode(ItemDobj itemDobj, int c) {
int pos = nextPos();
if (pos < MIN_POS) {
return -1;
}
Node node = new Node();
node.setPos(pos);
node.setItem(itemDobj.toDomainItem());
node.setTotal(c);
node.setLastGainTime((int) (System.currentTimeMillis() / 1000));
nodes.add(node);
// 通知客户端添加一个道具
DomainUtils.fireIndexPropertyAdd(this, "nodes", node);
node.set(this, "nodes");
return pos;
}
/**
* 放置新道具的位置. 如果收到的返回值为-1则不是一个有效的位置, 此时背包无法容纳更多的道具.
*
* @return 道具的位置, 返回值大于等于1并且小于等余{@link #getMaxSize() }时为一个有效索引
*/
private int nextPos() {
if (nodes.isEmpty()) {
return MIN_POS;
}
Node prevNode = null;
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
// 如果上一个节点为null, 并且第一个元素pos不是从MIN_POS
if (prevNode == null) {
if (node == null || node.getPos() > MIN_POS) {
return MIN_POS;
}
} else {
// 如果当前节点与上节点的pos差距大于1则返回之间的pos
if ((node.getPos() - prevNode.getPos()) > 1) {
return prevNode.getPos() + 1;
}
}
prevNode = node;
}
if (prevNode.getPos() < maxSize) {
return (prevNode.getPos() + 1);
}
return -1;
}
private boolean isOverlapped(ItemDobj itemDobj) {
return (itemDobj.getOverlapping() > 1);
}
/**
*
*/
@JSONType(ignores = {"index"})
public static class Node extends AbstractIndexable implements Comparable<Node> {
private int pos;
private AbstractItem item;
private int total;
private int lastGainTime;
public int getPos() {
return pos;
}
public void setPos(int pos) {
DomainUtils.firePropertyChange(this, "pos", pos);
this.pos = pos;
}
public <T extends AbstractItem> T getItem() {
return (T) item;
}
public void setItem(AbstractItem item) {
this.item = item;
// FIXME 设置道具的Parent属性
this.item.set(this, "item");
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
DomainUtils.firePropertyChange(this, "total", total);
}
public int getLastGainTime() {
return lastGainTime;
}
public void setLastGainTime(int lastGainTime) {
this.lastGainTime = lastGainTime;
DomainUtils.firePropertyChange(this, "lastGainTime", lastGainTime);
}
// ==================================================================================
@Override
public String parentPropertyName() {
return "nodes";
}
@Override
public int index() {
return pos;
}
// ==================================================================================
@Override
public int compareTo(Node o) {
return Integer.compare(pos, o.pos);
}
}
}