/** * 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.importer; import com.google.inject.Inject; import com.google.inject.Singleton; import es.eucm.ead.importer.subconverters.ConversationsConverter; import es.eucm.ead.importer.subconverters.conditions.ConditionsConverter; import es.eucm.ead.importer.subconverters.effects.EffectsConverter; import es.eucm.ead.importer.subconverters.effects.TriggerMacroConverter; import es.eucm.ead.model.elements.BasicElement; import es.eucm.ead.model.elements.Chapter; import es.eucm.ead.model.elements.conditions.Condition; import es.eucm.ead.model.elements.conditions.EmptyCond; import es.eucm.ead.model.elements.effects.Effect; import es.eucm.ead.model.elements.effects.EmptyEffect; import es.eucm.ead.model.elements.effects.text.SpeakEf; import es.eucm.ead.model.elements.effects.variables.ChangeFieldEf; import es.eucm.ead.model.elements.extra.EAdList; import es.eucm.ead.model.elements.operations.ElementField; import es.eucm.ead.model.elements.predef.effects.SpeakSceneElementEf; import es.eucm.ead.model.elements.scenes.SceneElement; import es.eucm.ead.model.elements.scenes.SceneElementDef; import es.eucm.ead.model.params.fills.Paint; import es.eucm.ead.model.params.guievents.MouseGEv; import es.eucm.ead.model.params.paint.EAdPaint; import es.eucm.ead.model.params.text.EAdString; import es.eucm.eadventure.common.data.adventure.AdventureData; import es.eucm.eadventure.common.data.adventure.DescriptorData; import es.eucm.eadventure.common.data.chapter.conditions.GlobalState; import es.eucm.eadventure.common.data.chapter.conditions.GlobalStateCondition; import es.eucm.eadventure.common.data.chapter.conversation.Conversation; import es.eucm.eadventure.common.data.chapter.effects.AbstractEffect; import es.eucm.eadventure.common.data.chapter.effects.Macro; import es.eucm.eadventure.common.data.chapter.effects.MacroReferenceEffect; import es.eucm.eadventure.common.data.chapter.elements.Element; import es.eucm.eadventure.common.data.chapter.elements.NPC; import es.eucm.eadventure.common.data.chapter.elements.Player; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Singleton public class ModelQuerier { static private Logger logger = LoggerFactory.getLogger(ModelQuerier.class); private ConditionsConverter conditionConverter; private EffectsConverter effectsConverter; private UtilsConverter utilsConverter; private ConversationsConverter conversationsConverter; private EAdElementsCache elementsCache; private AdventureData adventureData; private Chapter currentChapter; /** * Index of the chapter being imported */ private int currentChapterIndex; private es.eucm.eadventure.common.data.chapter.Chapter oldChapter; private Map<String, ElementField> flagFields; private Map<String, ElementField> variableFields; private Map<String, Condition> globalStates; private Map<String, EAdList<Effect>> macros; private Map<String, Effect> conversations; private Map<String, EAdPaint> npcTexts; private Map<String, EAdPaint> npcBubbles; // Auxiliary variables: // These stacks are to solve cycles and assure that everything is loaded an // the right moment in macros and global state (a macro calling another // macro, a global state depending on another global state) private ArrayList<Macro> macrosToLoad; private ArrayList<GlobalState> globalStatesToLoad; private boolean firstPersonGame; private String chapterId; @Inject public ModelQuerier(EAdElementsCache elementsCache) { this.elementsCache = elementsCache; flagFields = new HashMap<String, ElementField>(); variableFields = new HashMap<String, ElementField>(); globalStates = new HashMap<String, Condition>(); macros = new HashMap<String, EAdList<Effect>>(); macrosToLoad = new ArrayList<Macro>(); globalStatesToLoad = new ArrayList<GlobalState>(); // Conversations conversations = new HashMap<String, Effect>(); npcTexts = new HashMap<String, EAdPaint>(); npcBubbles = new HashMap<String, EAdPaint>(); } public void setConditionConverter(ConditionsConverter conditionConverter) { this.conditionConverter = conditionConverter; } public void setEffectsConverter(EffectsConverter effectsConverter) { this.effectsConverter = effectsConverter; } public void setAdventureData(AdventureData adventureData) { this.adventureData = adventureData; } public void setConversationsConverter( ConversationsConverter conversationsConverter) { this.conversationsConverter = conversationsConverter; } public void setUtilsConverter(UtilsConverter utilsConverter) { this.utilsConverter = utilsConverter; } public AdventureData getAventureData() { return adventureData; } public void setCurrentChapter(Chapter chapter, es.eucm.eadventure.common.data.chapter.Chapter c) { this.currentChapter = chapter; this.oldChapter = c; flagFields.clear(); variableFields.clear(); // Load text and bubble colors for (NPC npc : oldChapter.getCharacters()) { EAdPaint textPaint = utilsConverter.getPaint(npc .getTextFrontColor(), npc.getTextBorderColor()); npcTexts.put(npc.getId(), textPaint); if (npc.getShowsSpeechBubbles()) { EAdPaint bubblePaint = utilsConverter.getPaint(npc .getBubbleBkgColor(), npc.getBubbleBorderColor()); npcBubbles.put(npc.getId(), bubblePaint); } else { npcBubbles.put(npc.getId(), Paint.BLACK_ON_WHITE); } } // Load text and bubbles from player NPC npc = oldChapter.getPlayer(); EAdPaint textPaint = utilsConverter.getPaint(npc.getTextFrontColor(), npc.getTextBorderColor()); npcTexts.put(npc.getId(), textPaint); if (npc.getShowsSpeechBubbles()) { EAdPaint bubblePaint = utilsConverter.getPaint(npc .getBubbleBkgColor(), npc.getBubbleBorderColor()); npcBubbles.put(npc.getId(), bubblePaint); } else { npcBubbles.put(npc.getId(), Paint.BLACK_ON_WHITE); } } /** * Loads the global states in the current chapter, and removes any global * state in the cache */ public void loadGlobalStates() { globalStates.clear(); // Add global states globalStatesToLoad.addAll(oldChapter.getGlobalStates()); int iterations = 0; boolean toWait = false; while (iterations <= globalStatesToLoad.size() && !globalStatesToLoad.isEmpty()) { // We check for references to other global states, if any and the // global state is still not loaded, we send this global state to // the end of the queue GlobalState g = globalStatesToLoad.remove(0); for (List<es.eucm.eadventure.common.data.chapter.conditions.Condition> l : g .getConditionsList()) { for (es.eucm.eadventure.common.data.chapter.conditions.Condition c : l) { if (c.getType() == es.eucm.eadventure.common.data.chapter.conditions.Condition.GLOBAL_STATE_CONDITION) { GlobalStateCondition gs = (GlobalStateCondition) c; if (!globalStates.containsKey(gs.getId())) { toWait = true; break; } } } if (toWait) { break; } } if (toWait) { globalStatesToLoad.add(g); iterations++; } else { iterations = 0; Condition cond = conditionConverter.convert(g); if (cond == null) { logger.warn("Global state returned a {} after conversion", g.getId()); } globalStates.put(g.getId(), cond); } toWait = false; } // We report errors if (globalStatesToLoad.size() > 0) { String globalStatesIds = ""; for (GlobalState gs : globalStatesToLoad) { globalStatesIds += gs.getId() + ","; } logger .error("Cycle detected in global states: {}", globalStatesIds); } } /** * Loads the macros of the current chapter, and removes any existing macro * in the cache */ public void loadMacros() { // Add macros macrosToLoad.addAll(oldChapter.getMacros()); int iterations = 0; boolean toWait = false; while (iterations <= macrosToLoad.size() && !macrosToLoad.isEmpty()) { Macro m = macrosToLoad.remove(0); // We check for other macros. If there's any and is still not // defined, we send this macro to the end of the queue to wait for (AbstractEffect e : m.getEffects()) { if (e instanceof MacroReferenceEffect) { MacroReferenceEffect mr = (MacroReferenceEffect) e; if (!macros.containsKey(mr.getTargetId())) { toWait = true; break; } } } // If macro has a reference to a non-defined macro, we wait to load // it if (toWait) { macrosToLoad.add(m); iterations++; // If not, we load it } else { iterations = 0; EAdList<Effect> macro = getMacro(m.getId()); List<Effect> effect = effectsConverter.convert(m); // Adds a final effect that sets that the macro is over if (effect.size() > 0) { ChangeFieldEf finalEffect = new ChangeFieldEf( currentChapter, TriggerMacroConverter.IN_MACRO + m.getId(), EmptyCond.FALSE); effect.get(effect.size() - 1).addNextEffect(finalEffect); effect.add(finalEffect); macro.add(effect.get(0)); } } toWait = false; } if (macrosToLoad.size() > 0) { String macrosIds = ""; for (Macro m : macrosToLoad) { macrosIds += m.getId() + ","; } logger.error("Cycle detected in macros: {}", macrosIds); } } /** * Loads the conversations of the current chapter, and clear any existing * conversation in the cache */ public void loadConversations() { // Load conversations for (Conversation c : oldChapter.getConversations()) { Effect conversation = conversationsConverter.convert(c); Effect proxy = conversations.get(c.getId()); if (proxy != null) { proxy.addNextEffect(conversation); } else { conversations.put(c.getId(), conversation); } } } public ElementField getFlag(String id) { ElementField field = flagFields.get(id); if (field == null) { field = new ElementField(currentChapter, id, false); currentChapter.putProperty(id, false); flagFields.put(id, field); } return field; } public ElementField getVariable(String id) { ElementField field = variableFields.get(id); if (field == null) { field = new ElementField(currentChapter, id, 0); currentChapter.putProperty(id, 0); variableFields.put(id, field); } return field; } public Condition getGlobalState(String id) { Condition globalState = globalStates.get(id); if (globalState == null) { logger.warn("Global state '{}' not found", id); } return globalState; } public EAdList<Effect> getMacro(String id) { EAdList<Effect> macro = macros.get(id); if (macro == null) { macro = new EAdList<Effect>(); macros.put(id, macro); } return macro; } public void addActionsInteraction(SceneElementDef def, Effect effect) { def.addBehavior(MouseGEv.MOUSE_RIGHT_PRESSED, effect); if (getAventureData().getDefaultClickAction() == DescriptorData.DefaultClickAction.SHOW_ACTIONS) { def.addBehavior(MouseGEv.MOUSE_LEFT_PRESSED, effect); } } /** * Creates an speak effect for the given npc id and the given text * * @param npc * @param text * @return */ public SpeakEf getSpeakFor(String npc, EAdString text) { BasicElement element = elementsCache.get(npc); SpeakEf effect; if (adventureData.getPlayerMode() == AdventureData.MODE_PLAYER_1STPERSON && npc.equals(Player.IDENTIFIER)) { effect = new SpeakEf(text); } else { effect = new SpeakSceneElementEf(element, text); effect.setX(elementsCache.getField(element, SceneElement.VAR_CENTER_X)); effect.setY(elementsCache.getField(element, SceneElement.VAR_TOP)); } effect.setColor(npcTexts.get(npc), npcBubbles.get(npc)); return effect; } /** * Returns the conversation for the given id * * @param id * @return */ public Effect getConversation(String id) { Effect conversation = conversations.get(id); if (conversation == null) { conversation = new EmptyEffect(); conversations.put(id, conversation); } return conversation; } public es.eucm.eadventure.common.data.chapter.Chapter getOldChapter() { return oldChapter; } public void clear() { conversations.clear(); elementsCache.clear(); flagFields.clear(); globalStates.clear(); macros.clear(); npcBubbles.clear(); npcTexts.clear(); variableFields.clear(); } public boolean isFirstPersonGame() { return this.getAventureData().getPlayerMode() != AdventureData.MODE_PLAYER_3RDPERSON; } public Element getElementById(String targetId) { Element e; if (Player.IDENTIFIER.equals(targetId)) { e = oldChapter.getPlayer(); } else { e = oldChapter.getAtrezzo(targetId); if (e == null) { e = oldChapter.getCharacter(targetId); } if (e == null) { e = oldChapter.getItem(targetId); } } return e; } public String generateChapterId(int index) { return "$chapter" + index; } public Chapter getCurrentChapter() { return currentChapter; } public void setChapterId(String chapterId) { this.chapterId = chapterId; } public String getChapterId() { return chapterId; } public int getCurrentChapterIndex() { return currentChapterIndex; } public void setCurrentChapterIndex(int currentChapterIndex) { this.currentChapterIndex = currentChapterIndex; } }