/**
* eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the
* <e-UCM> research group.
*
* Copyright 2005-2010 <e-UCM> research group.
*
* You can access a list of all the contributors to eAdventure at:
* http://e-adventure.e-ucm.es/contributors
*
* <e-UCM> is a research group of the Department of Software Engineering
* and Artificial Intelligence at the Complutense University of Madrid
* (School of Computer Science).
*
* C Profesor Jose Garcia Santesmases sn,
* 28040 Madrid (Madrid), Spain.
*
* For more info please visit: <http://e-adventure.e-ucm.es> or
* <http://www.e-ucm.es>
*
* ****************************************************************************
*
* This file is part of eAdventure, version 2.0
*
* eAdventure is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* eAdventure is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with eAdventure. If not, see <http://www.gnu.org/licenses/>.
*/
package es.eucm.ead.engine.assets.drawables;
import com.badlogic.gdx.graphics.Texture;
import com.google.inject.Inject;
import es.eucm.ead.engine.assets.AbstractRuntimeAsset;
import es.eucm.ead.engine.assets.AssetHandler;
import es.eucm.ead.engine.assets.fonts.RuntimeFont;
import es.eucm.ead.engine.canvas.GdxCanvas;
import es.eucm.ead.engine.game.GameState;
import es.eucm.ead.model.assets.drawable.basics.EAdCaption;
import es.eucm.ead.model.assets.drawable.basics.shapes.RectangleShape;
import es.eucm.ead.model.elements.operations.SystemFields;
import es.eucm.ead.model.params.util.Rectangle;
import es.eucm.ead.tools.StringHandler;
import java.util.ArrayList;
import java.util.List;
public class RuntimeCaption extends AbstractRuntimeAsset<EAdCaption> implements
RuntimeDrawable<EAdCaption> {
protected String text;
protected List<String> lines;
protected List<Integer> widths;
protected RuntimeFont font;
protected Rectangle bounds;
protected float alpha;
protected int lineHeight;
private int linesInPart;
/**
* When some text is too long, it could be divided separate parts that will
* be shown one by one
*
*/
protected int totalParts;
protected int currentPart;
/**
* Times the text has been read (shown entirely at the screen)
*/
protected int timesRead;
/**
* Times the text loops after it gets to its last part. If -1, loops
* infinite
*/
private int loops;
private int heightOffset;
/**
* Current text wrapped
*/
private String currentText;
private GameState gameState;
private StringHandler stringsHandler;
@SuppressWarnings("rawtypes")
private RuntimeDrawable shape;
@Inject
public RuntimeCaption(GameState valueMap, StringHandler stringsHandler,
AssetHandler assetHandler) {
super(assetHandler);
this.gameState = valueMap;
this.stringsHandler = stringsHandler;
bounds = new Rectangle(0, 0, 0, 0);
lines = new ArrayList<String>();
widths = new ArrayList<Integer>();
}
/*
* (non-Javadoc)
*
* @see es.eucm.eadventure.engine.engine.platform.RuntimeAsset#loadAsset()
*/
@Override
public boolean loadAsset() {
super.loadAsset();
font = assetHandler.getFont(descriptor.getFont());
lines.clear();
widths.clear();
text = getProcessedText();
wrapText();
// Draw bubble
if (getAssetDescriptor().hasBubble()) {
RectangleShape rect = new RectangleShape(getWidth(), getHeight());
rect.setPaint(getAssetDescriptor().getBubblePaint());
shape = assetHandler.getDrawableAsset(rect);
}
return true;
}
private String getProcessedText() {
String text;
if (descriptor.getOperations().size() > 0) {
text = gameState.processTextVars(stringsHandler
.getString(descriptor.getText()), descriptor
.getOperations());
} else {
text = stringsHandler.getString(descriptor.getText());
}
return text;
}
public void freeMemory() {
super.freeMemory();
if (shape != null)
shape.freeMemory();
shape = null;
lines = null;
widths = null;
}
/*
* (non-Javadoc)
*
* @see es.eucm.eadventure.engine.engine.platform.RuntimeAsset#update(es.eucm.
* eadventure.engine.engine.GameState)
*/
// FIXME this is the only asset using the update method, this must be
// deleted, because with getDrawable can be done
public void update() {
/*timeShown -= gui.getSkippedMilliseconds();
if (timeShown <= 0) {
goForward(1);
}*/
text = getProcessedText();
// If text has changed
if (!currentText.equals(text))
wrapText();
}
@Override
public int getWidth() {
if (bounds == null)
return 1;
return bounds.width;
}
@Override
public int getHeight() {
if (bounds == null)
return 1;
return bounds.height;
}
private void wrapText() {
this.currentText = text;
lines.clear();
widths.clear();
totalParts = 0;
lineHeight = font.lineHeight();
bounds.setBounds(0, 0, 0, 0);
int preferredWidth;
switch (descriptor.getPreferredWidth()) {
case EAdCaption.AUTO_SIZE:
preferredWidth = Integer.MAX_VALUE;
break;
case EAdCaption.SCREEN_SIZE:
preferredWidth = gameState.getValue(SystemFields.GAME_WIDTH, 800);
break;
default:
preferredWidth = descriptor.getPreferredWidth();
}
preferredWidth -= descriptor.getPadding() * 2;
bounds.width = 0;
String[] words = text.split(" ");
// Current line
String line = "";
int contWord = 0;
int currentLineWidth = 0;
while (contWord < words.length) {
int nextWordWidth = font.stringWidth(words[contWord] + " ");
if (currentLineWidth + nextWordWidth <= preferredWidth) {
currentLineWidth += nextWordWidth;
line += words[contWord++] + " ";
} else if (!"".equals(line)) {
lines.add(line);
currentLineWidth = font.stringWidth(line);
widths.add(currentLineWidth);
bounds.width = currentLineWidth > bounds.width ? currentLineWidth
: bounds.width;
currentLineWidth = 0;
line = "";
} else {
line = splitLongWord(font, lines, words[contWord++],
preferredWidth);
currentLineWidth = font.stringWidth(line);
}
}
if (!"".equals(line)) {
lines.add(line);
currentLineWidth = font.stringWidth(line);
widths.add(currentLineWidth);
bounds.width = currentLineWidth > bounds.width ? currentLineWidth
: bounds.width;
}
int preferredHeight;
switch (descriptor.getPreferredHeight()) {
case EAdCaption.SCREEN_SIZE:
preferredHeight = gameState.getValue(SystemFields.GAME_HEIGHT, 600);
break;
case EAdCaption.AUTO_SIZE:
preferredHeight = Integer.MAX_VALUE;
break;
default:
preferredHeight = descriptor.getPreferredHeight();
}
preferredHeight -= descriptor.getPadding() * 2;
linesInPart = preferredHeight / lineHeight;
linesInPart = linesInPart < lines.size() ? linesInPart : lines.size();
totalParts = (int) Math
.ceil((float) lines.size() / (float) linesInPart);
bounds.height = descriptor.getPreferredHeight() == EAdCaption.AUTO_SIZE ? linesInPart
* lineHeight
: preferredHeight;
bounds.width = descriptor.getPreferredWidth() == EAdCaption.AUTO_SIZE ? bounds.width
: preferredWidth;
heightOffset = descriptor.getPreferredHeight() != EAdCaption.AUTO_SIZE ? (preferredHeight - (linesInPart * lineHeight)) / 2
: 0;
bounds.width += descriptor.getPadding() * 2;
bounds.height += descriptor.getPadding() * 2;
reset();
}
private String splitLongWord(RuntimeFont f, List<String> lines,
String word, int lineWidth) {
boolean finished = false;
String currentLine = "";
int i = 0;
while (!finished) {
currentLine = "";
while (i < word.length()
&& f.stringWidth(currentLine + word.charAt(i)) < lineWidth) {
currentLine += word.charAt(i++);
}
if (i == word.length()) {
finished = true;
} else {
lines.add(currentLine);
int currentLineWidth = f.stringWidth(currentLine);
widths.add(currentLineWidth);
bounds.width = currentLineWidth > bounds.width ? currentLineWidth
: bounds.width;
}
}
return currentLine;
}
/**
* If text is divided in parts and current part is n, this method advances
* the text to n + i part
*
* @param i
* steps to go forward
*/
public void goForward(int i) {
if (totalParts > 0) {
currentPart += i;
if (currentPart >= totalParts) {
while (currentPart >= totalParts) {
currentPart -= totalParts;
loops--;
timesRead++;
}
if (loops <= 0)
currentPart = totalParts - 1;
}
}
}
public int getCurrentPart() {
return currentPart;
}
public int getTotalParts() {
return totalParts;
}
public List<String> getText() {
int beginIndex = currentPart * linesInPart;
int lastIndex = beginIndex + linesInPart;
lastIndex = lastIndex > lines.size() ? lines.size() : lastIndex;
return lines.subList(beginIndex, lastIndex);
}
/**
* Returns the number of times the text has been read by the player. This
* calculation is made from an reading time estimation
*
* @return the number of times the text has been read by the player. This
* calculation is made from an reading time estimation
*/
public int getTimesRead() {
return timesRead;
}
/**
* Sets how many times this text game object loops before adding one to
* times read. Negative number will be interpreted as infinitum
*
* @param loops
* times text loops
*/
public void setLoops(int loops) {
this.loops = loops;
}
public void reset() {
currentPart = 0;
timesRead = 0;
loops = 0;
}
public EAdCaption getCaption() {
return descriptor;
}
public Rectangle getBounds() {
return bounds;
}
public int getLineHeight() {
return this.font.lineHeight();
}
public RuntimeFont getFont() {
return font;
}
public void render(GdxCanvas c) {
if (shape != null) {
shape.render(c);
}
int xOffset;
int yOffset = getAssetDescriptor().getPadding();
if (currentPart == totalParts - 1 && lines.size() % linesInPart != 0) {
yOffset += (bounds.height - getAssetDescriptor().getPadding() * 2 - ((lines
.size() % linesInPart) * lineHeight)) / 2;
} else {
yOffset += heightOffset;
}
int i = currentPart * linesInPart;
// Draw lines
try {
for (String s : getText()) {
switch (descriptor.getAlignment()) {
case CENTER:
xOffset = (bounds.width - widths.get(i)) / 2;
break;
case RIGHT:
xOffset = (bounds.width - widths.get(i))
- descriptor.getPadding();
break;
default:
xOffset = descriptor.getPadding();
}
c
.drawText(s, xOffset, yOffset, font, descriptor
.getTextPaint());
yOffset += getLineHeight();
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean contains(int x, int y) {
return x > 0 && y > 0 && x < getWidth() && y < getHeight();
}
@Override
public RuntimeDrawable<?> getDrawable(int time, List<String> states,
int level) {
// FIXME man, fix this
update();
return this;
}
@Override
public void refresh() {
String newText = getProcessedText();
if (!newText.equals(text)) {
freeMemory();
loadAsset();
}
}
@Override
public Texture getTextureHandle() {
return null;
}
}