/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.content;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.R;
import org.catrobat.catroid.common.MessageContainer;
import org.catrobat.catroid.content.bricks.Brick;
import org.catrobat.catroid.content.bricks.FormulaBrick;
import org.catrobat.catroid.content.bricks.PointToBrick;
import org.catrobat.catroid.content.bricks.SceneStartBrick;
import org.catrobat.catroid.content.bricks.SceneTransitionBrick;
import org.catrobat.catroid.content.bricks.UserBrick;
import org.catrobat.catroid.content.bricks.UserListBrick;
import org.catrobat.catroid.content.bricks.UserVariableBrick;
import org.catrobat.catroid.formulaeditor.DataContainer;
import org.catrobat.catroid.formulaeditor.Formula;
import org.catrobat.catroid.formulaeditor.UserList;
import org.catrobat.catroid.formulaeditor.UserVariable;
import org.catrobat.catroid.io.XStreamFieldKeyOrder;
import org.catrobat.catroid.physics.PhysicsWorld;
import org.catrobat.catroid.utils.ToastUtil;
import org.catrobat.catroid.utils.Utils;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@XStreamAlias("scene")
// Remove checkstyle disable when https://github.com/checkstyle/checkstyle/issues/1349 is fixed
// CHECKSTYLE DISABLE IndentationCheck FOR 7 LINES
@XStreamFieldKeyOrder({
"name",
"objectList",
"data",
"originalWidth",
"originalHeight"
})
public class Scene implements Serializable {
private static final long serialVersionUID = 1L;
@XStreamAlias("name")
private String sceneName;
@XStreamAlias("objectList")
private List<Sprite> spriteList = new ArrayList<>();
@XStreamAlias("data")
private DataContainer dataContainer = null;
@XStreamAlias("originalWidth")
private int originalWidth = 0;
@XStreamAlias("originalHeight")
private int originalHeight = 0;
private transient PhysicsWorld physicsWorld;
private transient Project project;
public transient boolean firstStart = true;
public transient boolean isBackPackScene = false;
public Scene(Context context, String name, Project project) {
sceneName = name;
dataContainer = new DataContainer(project);
this.project = project;
if (project != null) {
originalWidth = project.getXmlHeader().virtualScreenWidth;
originalHeight = project.getXmlHeader().virtualScreenHeight;
}
if (context == null) {
return;
}
Sprite background;
try {
background = new SingleSprite(context.getString(R.string.background));
} catch (Resources.NotFoundException e) {
//Because in test project we can't find the string
background = new SingleSprite("Background");
}
background.look.setZIndex(0);
addSprite(background);
}
public String getName() {
return sceneName;
}
public synchronized void addSprite(Sprite sprite) {
if (spriteList.contains(sprite)) {
return;
}
spriteList.add(sprite);
}
public synchronized boolean removeSprite(Sprite sprite) {
return spriteList.remove(sprite);
}
@Override
public Scene clone() {
Scene clonedScene = new Scene();
clonedScene.sceneName = sceneName;
clonedScene.originalWidth = originalWidth;
clonedScene.originalHeight = originalHeight;
clonedScene.physicsWorld = new PhysicsWorld(originalWidth, originalHeight);
clonedScene.project = project;
clonedScene.firstStart = firstStart;
clonedScene.isBackPackScene = isBackPackScene;
clonedScene.dataContainer = new DataContainer(project);
ProjectManager.getInstance().setCurrentScene(this);
for (Sprite sprite : spriteList) {
Sprite cloneSprite = sprite.cloneForScene();
for (UserBrick userBrick : cloneSprite.getUserBrickList()) {
ProjectManager.getInstance().setCurrentScene(clonedScene);
userBrick.updateUserBrickParametersAndVariables();
ProjectManager.getInstance().setCurrentScene(this);
}
clonedScene.spriteList.add(cloneSprite);
}
clonedScene.dataContainer.cloneSpriteListsForScene(clonedScene, dataContainer);
clonedScene.dataContainer.cloneSpriteVariablesForScene(clonedScene, dataContainer);
if (!isBackPackScene) {
clonedScene.correctUserVariableAndListReferences();
}
return clonedScene;
}
public Scene cloneForBackPack() {
Scene clonedScene;
try {
ProjectManager.getInstance().checkNestingBrickReferences(false, true, true);
clonedScene = clone();
clonedScene.isBackPackScene = false;
if (!clonedScene.mergeProjectVariables()) {
return null;
}
clonedScene.correctUserVariableAndListReferences();
} catch (Exception e) {
Log.e("Scene.cloneForBackpack", e.getMessage());
return null;
}
return clonedScene;
}
public boolean mergeProjectVariables() {
List<String> variables = new ArrayList<>();
List<String> lists = new ArrayList<>();
for (Sprite sprite : spriteList) {
for (Brick brick : sprite.getListWithAllBricks()) {
if (brick instanceof UserVariableBrick && !variables.contains(((UserVariableBrick) brick)
.getUserVariable().getName())) {
variables.add(((UserVariableBrick) brick).getUserVariable().getName());
}
if (brick instanceof UserListBrick
&& !lists.contains(((UserListBrick) brick).getUserList().getName())) {
lists.add(((UserListBrick) brick).getUserList().getName());
}
if (brick instanceof FormulaBrick) {
for (Formula formula : ((FormulaBrick) brick).getFormulas()) {
formula.getVariableAndListNames(variables, lists);
}
}
}
}
for (String variable : variables) {
if (dataContainer.existVariableInAnySprite(variable, spriteList)) {
return false;
}
if (!dataContainer.existProjectVariableWithName(variable) && !dataContainer.existUserVariableWithName(variable)) {
dataContainer.addProjectUserVariable(variable);
}
}
for (String list : lists) {
if (dataContainer.existListInAnySprite(list, spriteList)) {
return false;
}
if (!dataContainer.existProjectListWithName(list)) {
dataContainer.addProjectUserList(list);
}
}
return true;
}
public boolean isBackgroundObject(Sprite sprite) {
if (spriteList.indexOf(sprite) == 0) {
return true;
}
return false;
}
@Override
public String toString() {
return sceneName;
}
public int getOriginalWidth() {
return originalWidth;
}
public int getOriginalHeight() {
return originalHeight;
}
public List<Sprite> getSpriteList() {
return spriteList;
}
public void removeAllClones() {
ProjectManager.getInstance().getCurrentProject().removeInvalidVariablesAndLists(dataContainer);
dataContainer.removeVariablesOfClones();
for (Sprite s : new ArrayList<>(spriteList)) {
if (s.isClone) {
spriteList.remove(s);
}
}
}
public synchronized void setSpriteList(List<Sprite> spriteList) {
this.spriteList = spriteList;
}
public synchronized void setDataContainer(DataContainer container) {
dataContainer = container;
}
public synchronized void setPhysicsWorld(PhysicsWorld world) {
physicsWorld = world;
}
public synchronized void resetDataContainerForDefaultScene() {
dataContainer = new DataContainer(project);
}
public synchronized boolean rename(String name, Context context, boolean showError) {
if (name.equals(getName())) {
return true;
}
File oldSceneDirectory = new File(Utils.buildScenePath(project.getName(), sceneName));
File newSceneDirectory = new File(Utils.buildScenePath(project.getName(), name));
String oldName = sceneName;
boolean directoryRenamed = true;
if (!oldSceneDirectory.getAbsolutePath().equalsIgnoreCase(newSceneDirectory.getAbsolutePath())) {
directoryRenamed = oldSceneDirectory.renameTo(newSceneDirectory);
}
if (directoryRenamed) {
sceneName = name;
ProjectManager.getInstance().saveProject(context);
}
if (!directoryRenamed) {
if (showError) {
ToastUtil.showError(context, R.string.error_rename_scene);
}
Log.e("Scene", "rename: could not rename " + oldSceneDirectory.getAbsolutePath() + " to " + newSceneDirectory.getAbsolutePath());
return false;
}
for (Scene scene : ProjectManager.getInstance().getCurrentProject().getSceneList()) {
for (Sprite sprite : scene.spriteList) {
for (Brick brick : sprite.getListWithAllBricks()) {
if (brick instanceof SceneStartBrick && ((SceneStartBrick) brick).getSceneToStart().equals(oldName)) {
((SceneStartBrick) brick).setSceneToStart(name);
}
if (brick instanceof SceneTransitionBrick && ((SceneTransitionBrick) brick).getSceneForTransition().equals(oldName)) {
((SceneTransitionBrick) brick).setSceneForTransition(name);
}
}
}
}
return true;
}
public synchronized void setSceneName(String name) {
sceneName = name;
}
public PhysicsWorld getPhysicsWorld() {
if (physicsWorld == null) {
resetPhysicsWorld();
}
return physicsWorld;
}
public synchronized PhysicsWorld resetPhysicsWorld() {
return (physicsWorld = new PhysicsWorld(project.getXmlHeader().virtualScreenWidth, project.getXmlHeader()
.virtualScreenHeight));
}
// default constructor for XMLParser
public Scene() {
}
public synchronized void setProject(Project project) {
this.project = project;
}
public Project getProject() {
return project;
}
public DataContainer getDataContainer() {
return dataContainer;
}
public synchronized void removeUnusedBroadcastMessages() {
List<String> usedMessages = new ArrayList<>();
for (Sprite currentSprite : spriteList) {
for (int scriptIndex = 0; scriptIndex < currentSprite.getNumberOfScripts(); scriptIndex++) {
Script currentScript = currentSprite.getScript(scriptIndex);
if (currentScript instanceof BroadcastMessage) {
addBroadcastMessage(((BroadcastMessage) currentScript).getBroadcastMessage(), usedMessages);
}
for (int brickIndex = 0; brickIndex < currentScript.getBrickList().size(); brickIndex++) {
Brick currentBrick = currentScript.getBrick(brickIndex);
if (currentBrick instanceof BroadcastMessage) {
addBroadcastMessage(((BroadcastMessage) currentBrick).getBroadcastMessage(), usedMessages);
}
}
}
for (UserBrick userBrick : currentSprite.getUserBrickList()) {
Script userScript = userBrick.getDefinitionBrick().getUserScript();
for (Brick currentBrick : userScript.getBrickList()) {
if (currentBrick instanceof BroadcastMessage) {
addBroadcastMessage(((BroadcastMessage) currentBrick).getBroadcastMessage(), usedMessages);
}
}
}
}
MessageContainer.removeUnusedMessages(usedMessages);
}
private void addBroadcastMessage(String broadcastMessageToAdd, List<String> broadcastMessages) {
if (broadcastMessageToAdd != null && !broadcastMessageToAdd.isEmpty()
&& !broadcastMessages.contains(broadcastMessageToAdd)) {
broadcastMessages.add(broadcastMessageToAdd);
}
}
public boolean screenshotExists(String screenshotName) {
File screenShot = new File(Utils.buildScenePath(project.getName(), getName()), screenshotName);
if (screenShot.exists()) {
return false;
}
return true;
}
public boolean containsSpriteBySpriteName(String searchedSprite) {
return getSpriteBySpriteName(searchedSprite) != null;
}
public UserVariable getProjectVariableWithName(String name) {
for (UserVariable variable : dataContainer.getProjectVariables()) {
if (name.equals(variable.getName())) {
return variable;
}
}
return null;
}
public UserList getProjectListWithName(String name) {
for (UserList list : dataContainer.getProjectLists()) {
if (name.equals(list.getName())) {
return list;
}
}
return null;
}
public boolean existProjectVariable(UserVariable variable) {
return dataContainer.existProjectVariable(variable);
}
public boolean existSpriteVariable(UserVariable variable, Sprite sprite) {
if (!spriteList.contains(sprite)) {
return false;
}
return dataContainer.existSpriteVariable(variable, sprite);
}
public boolean existProjectList(UserList list) {
return dataContainer.existProjectList(list);
}
public boolean existSpriteList(UserList list, Sprite sprite) {
if (!spriteList.contains(sprite)) {
return false;
}
return dataContainer.existSpriteList(list, sprite);
}
public Sprite getSpriteByUserVariable(UserVariable variable) {
Sprite spriteByUserVariable = null;
for (Sprite sprite : spriteList) {
if (dataContainer.existSpriteVariable(variable, sprite)) {
spriteByUserVariable = sprite;
break;
}
}
return spriteByUserVariable;
}
public Sprite getSpriteByUserList(UserList list) {
Sprite spriteByUserList = null;
for (Sprite sprite : spriteList) {
if (dataContainer.existSpriteList(list, sprite)) {
spriteByUserList = sprite;
break;
}
}
return spriteByUserList;
}
public Sprite getSpriteBySpriteName(String searchedSprite) {
Sprite spriteBySpriteName = null;
for (Sprite sprite : spriteList) {
if (searchedSprite.equals(sprite.getName())) {
spriteBySpriteName = sprite;
break;
}
}
return spriteBySpriteName;
}
public synchronized void replaceBackgroundSprite(Sprite unpackedSprite) {
unpackedSprite.setName(spriteList.get(0).getName());
spriteList.set(0, unpackedSprite);
}
public boolean containsSprite(Sprite selectedSprite) {
for (Sprite sprite : spriteList) {
if (sprite.equals(selectedSprite)) {
return true;
}
}
return false;
}
public synchronized void correctUserVariableAndListReferences() {
for (Sprite sprite : spriteList) {
for (Brick brick : sprite.getListWithAllBricks()) {
if (brick instanceof UserVariableBrick) {
((UserVariableBrick) brick).setUserVariable(dataContainer.getUserVariable(((UserVariableBrick)
brick).getUserVariable().getName(), sprite));
}
if (brick instanceof UserListBrick) {
((UserListBrick) brick).setUserList(dataContainer.getUserList(((UserListBrick) brick).getUserList().getName(), sprite));
}
}
}
}
public void refreshSpriteReferences() {
for (Brick brick : getAllBricks()) {
if (!(brick instanceof PointToBrick)) {
continue;
}
PointToBrick pointToBrick = (PointToBrick) brick;
Sprite pointedSprite = pointToBrick.getPointedObject();
if (pointedSprite == null) {
continue;
}
Sprite newSprite = getSpriteBySpriteName(pointToBrick.getPointedObject().getName());
pointToBrick.setPointedObject(newSprite);
}
}
public List<Brick> getAllBricks() {
List<Brick> result = new ArrayList<>();
for (Sprite sprite : spriteList) {
result.addAll(sprite.getAllBricks());
}
return result;
}
}