/** * 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 ead.importer.subimporters.chapter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import es.eucm.ead.model.elements.operations.ElementField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import es.eucm.ead.model.assets.drawable.basics.Image; import es.eucm.ead.model.assets.drawable.basics.enums.Alignment; import es.eucm.ead.model.elements.BasicInventory; import es.eucm.ead.model.elements.conditions.ANDCond; import es.eucm.ead.model.elements.conditions.EmptyCond; import es.eucm.ead.model.elements.conditions.NOTCond; import es.eucm.ead.model.elements.conditions.ORCond; import es.eucm.ead.model.elements.conditions.OperationCond; import es.eucm.ead.model.elements.effects.ActorActionsEf; import es.eucm.ead.model.elements.effects.ModifyInventoryEf; import es.eucm.ead.model.elements.effects.TriggerMacroEf; import es.eucm.ead.model.elements.effects.enums.InventoryEffectAction; 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.predef.effects.MoveActiveElementToMouseEf; import es.eucm.ead.model.elements.scenes.SceneElement; import es.eucm.ead.model.elements.scenes.SceneElementDef; import es.eucm.ead.model.params.guievents.DragGEv; import es.eucm.ead.model.params.guievents.MouseGEv; import es.eucm.ead.model.params.guievents.enums.DragGEvType; import es.eucm.ead.model.params.text.EAdString; import ead.importer.EAdElementImporter; import ead.importer.annotation.ImportAnnotator; import ead.importer.interfaces.EAdElementFactory; import ead.importer.interfaces.EffectsImporterFactory; import ead.importer.interfaces.ResourceImporter; import es.eucm.ead.tools.StringHandler; import es.eucm.eadventure.common.data.chapter.Action; import es.eucm.eadventure.common.data.chapter.CustomAction; import es.eucm.eadventure.common.data.chapter.conditions.Conditions; import es.eucm.eadventure.common.data.chapter.effects.AbstractEffect; import es.eucm.eadventure.common.data.chapter.effects.CancelActionEffect; import es.eucm.eadventure.common.data.chapter.effects.Effects; public class ActionImporter implements EAdElementImporter<Action, SceneElementDef> { private static Logger logger = LoggerFactory .getLogger(ActionImporter.class); private StringHandler stringHandler; private EffectsImporterFactory effectsImporterFactory; private EAdElementImporter<Conditions, Condition> conditionsImporter; /** * Resources importer */ private ResourceImporter resourceImporter; private EAdElementFactory factory; public static final String DRAWABLE_PATH = "@" + ResourceImporter.DRAWABLE; protected ImportAnnotator annotator; @Inject public ActionImporter(StringHandler stringHandler, EffectsImporterFactory effectsImporterFactory, ResourceImporter resourceImporter, EAdElementImporter<Conditions, Condition> conditionsImporter, EAdElementFactory factory, ImportAnnotator annotator) { this.stringHandler = stringHandler; this.effectsImporterFactory = effectsImporterFactory; this.conditionsImporter = conditionsImporter; this.resourceImporter = resourceImporter; this.factory = factory; this.annotator = annotator; } @Override public SceneElementDef init(Action oldObject) { SceneElementDef basicAction = new SceneElementDef(); return basicAction; } @Override public SceneElementDef convert(Action oldObject, Object object) { return null; } public Condition[] setCondition(Action oldObject, SceneElementDef action, Condition previousCondition) { Condition conditions[] = new Condition[2]; Condition condition = conditionsImporter .init(oldObject.getConditions()); condition = conditionsImporter.convert(oldObject.getConditions(), condition); Condition andCondition; if (previousCondition == null && condition == null) { andCondition = EmptyCond.TRUE; } else if (previousCondition != null && condition != null) { andCondition = new ANDCond(condition, new NOTCond(previousCondition)); } else if (condition == null) { andCondition = previousCondition; } else { andCondition = condition; } conditions[0] = andCondition; conditions[1] = condition; return conditions; } public void convert(Action oldObject, SceneElementDef action, SceneElementDef owner, Condition condition, boolean isActiveArea, TriggerMacroEf effecttrigger, TriggerMacroEf notEffectTrigger) { // Action name setName(oldObject, action); // Add appearance addAppearance(oldObject, action); // Add effects addEffects(effecttrigger, notEffectTrigger, oldObject, action, owner, condition, isActiveArea); } private void setName(Action oldObject, SceneElementDef action) { String actionName; if (oldObject instanceof CustomAction) { CustomAction customAction = (CustomAction) oldObject; actionName = customAction.getName(); } else { actionName = getActionName(oldObject.getType()); } EAdString nameString = stringHandler.generateNewString(); stringHandler.setString(nameString, actionName); action.setVarInitialValue(SceneElementDef.VAR_DOC_NAME, nameString); } private void addEffects(TriggerMacroEf effectTrigger, TriggerMacroEf notEffectTrigger, Action oldObject, SceneElementDef action, SceneElementDef actor, Condition condition, boolean isActiveArea) { // Add effects EAdList<Effect> macro = effectsImporterFactory .getMacroEffects(oldObject.getEffects()); // Add default effects for the action Effect defaultEffect = getDefaultEffects(oldObject, actor, isActiveArea, action); if (defaultEffect != null) { if (macro == null) { macro = new EAdList<Effect>(); } macro.add(0, defaultEffect); } // Add conditions and get to if (macro != null) { effectTrigger.putEffects(condition, macro); } // Add no effects EAdList<Effect> notEffects = effectsImporterFactory .getMacroEffects(oldObject.getNotEffects()); if (notEffects != null) { notEffectTrigger.putEffects(new NOTCond(condition), notEffects); } } private void addAppearance(Action oldObject, SceneElementDef action) { // If it's a standard action if (oldObject.getType() != Action.CUSTOM && oldObject.getType() != Action.CUSTOM_INTERACT) { action.addAsset(SceneElementDef.appearance, new Image( getDrawablePath(oldObject.getType()))); action.addAsset(SceneElementDef.overAppearance, new Image( getHighlightDrawablePath(oldObject.getType()))); } else { Map<String, String> resourcesStrings = new LinkedHashMap<String, String>(); Map<String, Object> resourcesClasses = new LinkedHashMap<String, Object>(); resourcesStrings.put("buttonOver", SceneElementDef.appearance); resourcesStrings .put("buttonNormal", SceneElementDef.overAppearance); resourcesClasses.put("buttonOver", Image.class); resourcesClasses.put("buttonNormal", Image.class); resourceImporter.importResources(action, ((CustomAction) oldObject) .getResources(), resourcesStrings, resourcesClasses); } } public static String getDrawablePath(int actionType) { String image; switch (actionType) { case Action.DRAG_TO: image = "drag-normal.png"; break; case Action.GIVE_TO: image = "giveto-normal.png"; break; case Action.GRAB: image = "grab-normal.png"; break; case Action.USE: image = "use-normal.png"; break; case Action.USE_WITH: image = "usewith-normal.png"; break; case Action.EXAMINE: image = "examine-normal.png"; break; case Action.TALK_TO: image = "talk-normal.png"; break; default: image = "use-normal.png"; } image = DRAWABLE_PATH + "/" + image; return image; } public Effect getDefaultEffects(Action a, SceneElementDef actor, boolean isActiveArea, SceneElementDef newAction) { for (AbstractEffect e : a.getEffects().getEffects()) { if (e instanceof CancelActionEffect) { return null; } } switch (a.getType()) { case Action.GRAB: if (!isActiveArea) { ElementField sceneElement = new ElementField( actor, SceneElementDef.VAR_SCENE_ELEMENT); ElementField inInventory = new ElementField( sceneElement, BasicInventory.VAR_IN_INVENTORY); ModifyInventoryEf addToInventory = new ModifyInventoryEf(actor, InventoryEffectAction.ADD_TO_INVENTORY); OperationCond c = new OperationCond(inInventory); addToInventory.setCondition(new NOTCond(c)); ChangeFieldEf change = new ChangeFieldEf(); change.addField(inInventory); change.setOperation(EmptyCond.TRUE); addToInventory.getNextEffects().add(change); return addToInventory; } break; } return null; } private static EAdString examineString; private static Image examineImage, examineOverImage; private static void initExamineAction(StringHandler handler) { examineString = new EAdString("engine.Examine"); examineImage = new Image(getDrawablePath(Action.EXAMINE)); examineOverImage = new Image(getHighlightDrawablePath(Action.EXAMINE)); } /** * Add examine if there's no examine action added * * @param actor * the new actor * @param sound * @param element * the old element */ @SuppressWarnings( { "unchecked", "rawtypes" }) public void addExamine(SceneElementDef actor, List<Action> actionsList, Effect sound) { for (Action a : actionsList) { if (a.getType() == Action.EXAMINE) { return; } } if (examineString == null) { initExamineAction(stringHandler); } SceneElementDef examineAction = new SceneElementDef(); examineAction.setVarInitialValue(SceneElementDef.VAR_DOC_NAME, examineString); if (sound != null) { examineAction.addBehavior(MouseGEv.MOUSE_LEFT_PRESSED, sound); } // Effect ElementField descField = new ElementField(actor, SceneElementDef.VAR_DOC_DETAILED_DESC); SpeakEf effect = new SpeakEf(stringHandler.generateNewString()); stringHandler.setString(effect.getCaption().getText(), "[0]"); effect.getCaption().getOperations().add(descField); effect.setAlignment(Alignment.CENTER); examineAction.addBehavior(MouseGEv.MOUSE_LEFT_PRESSED, effect); // Appearance examineAction.addAsset(SceneElementDef.appearance, examineImage); examineAction .addAsset(SceneElementDef.overAppearance, examineOverImage); EAdList list = (EAdList) actor.getVars() .get(ActorActionsEf.VAR_ACTIONS); if (list == null) { list = new EAdList<SceneElementDef>(); actor.setVarInitialValue(ActorActionsEf.VAR_ACTIONS, list); } list.add(examineAction); } public static String getHighlightDrawablePath(int actionType) { String image; switch (actionType) { case Action.DRAG_TO: image = "drag-pressed.png"; break; case Action.GIVE_TO: image = "giveto-pressed.png"; break; case Action.GRAB: image = "grab-pressed.png"; break; case Action.USE: image = "use-pressed.png"; break; case Action.USE_WITH: image = "usewith-pressed.png"; break; case Action.EXAMINE: image = "examine-pressed.png"; break; case Action.TALK_TO: image = "talk-pressed.png"; break; default: image = "use-pressed.png"; } image = DRAWABLE_PATH + "/" + image; return image; } private String getActionName(int actionType) { switch (actionType) { case Action.DRAG_TO: return "Drag to"; case Action.EXAMINE: return "Examine"; case Action.GIVE_TO: return "Give to"; case Action.GRAB: return "Grab"; case Action.TALK_TO: return "Talk to"; case Action.USE: return "Use"; case Action.USE_WITH: return "Use with"; default: return "Action"; } } @SuppressWarnings( { "rawtypes", "unchecked" }) public void addAllActions(List<Action> actionsList, SceneElementDef actor, boolean isActiveArea, Effect sound) { logger.debug("adding all actions for list of {} actions, " + "with an actor called {}, areaActive {}, and sound {}", new Object[] { actionsList.size(), actor.getId(), isActiveArea, sound }); // add examine addExamine(actor, actionsList, sound); // Yeah, I know. But all of them are necessary. Map<Integer, SceneElementDef> actions = new LinkedHashMap<Integer, SceneElementDef>(); Map<String, SceneElementDef> customActions = new LinkedHashMap<String, SceneElementDef>(); Map<String, SceneElementDef> interactActions = new LinkedHashMap<String, SceneElementDef>(); Map<SceneElementDef, Condition> previousConditions = new LinkedHashMap<SceneElementDef, Condition>(); Map<SceneElementDef, Condition> orConditions = new LinkedHashMap<SceneElementDef, Condition>(); Map<SceneElementDef, TriggerMacroEf> effectsTriggers = new LinkedHashMap<SceneElementDef, TriggerMacroEf>(); Map<SceneElementDef, TriggerMacroEf> notEffectsTriggers = new LinkedHashMap<SceneElementDef, TriggerMacroEf>(); Map<SceneElementDef, SceneElementDef> targets = new LinkedHashMap<SceneElementDef, SceneElementDef>(); Map<SceneElementDef, Boolean> getsTo = new LinkedHashMap<SceneElementDef, Boolean>(); // Actions list EAdList list = (EAdList) actor.getVars() .get(ActorActionsEf.VAR_ACTIONS); if (list == null) { list = new EAdList<SceneElementDef>(); actor.setVarInitialValue(ActorActionsEf.VAR_ACTIONS, list); } for (Action a : actionsList) { SceneElementDef action = null; // Init action switch (a.getType()) { case Action.CUSTOM: CustomAction customAction = (CustomAction) a; if (customActions.containsKey(customAction.getName())) { action = customActions.get(customAction.getName()); } else { action = init(a); list.add(action); customActions.put(customAction.getName(), action); } break; case Action.EXAMINE: case Action.USE: case Action.GRAB: case Action.TALK_TO: if (actions.containsKey(a.getType())) { action = actions.get(a.getType()); } else { action = init(a); list.add(action); actions.put(a.getType(), action); } break; case Action.CUSTOM_INTERACT: customAction = (CustomAction) a; String name = customAction.getName() + "" + customAction.getTargetId(); if (interactActions.containsKey(name)) { action = interactActions.get(name); } else { action = init(a); interactActions.put(name, action); } break; case Action.DRAG_TO: case Action.GIVE_TO: case Action.USE_WITH: name = a.getType() + "" + a.getTargetId(); if (interactActions.containsKey(name)) { action = interactActions.get(name); } else { action = init(a); interactActions.put(name, action); } break; } // Effects TriggerMacroEf effectTrigger = effectsTriggers.get(action); if (effectTrigger == null) { effectTrigger = new TriggerMacroEf(); effectsTriggers.put(action, effectTrigger); } // Not effects TriggerMacroEf notEffectTrigger = null; if (a.isActivatedNotEffects()) { notEffectTrigger = notEffectsTriggers.get(action); if (notEffectTrigger == null) { notEffectTrigger = new TriggerMacroEf(); notEffectsTriggers.put(action, notEffectTrigger); } } // Set condition Condition conds[] = setCondition(a, action, previousConditions .get(action)); Condition c = conds[0]; previousConditions.put(action, c); // Or condition Condition orCondition = orConditions.get(action); if (orCondition == null) { orCondition = conds[1]; } else { orCondition = new ORCond(conds[1], orCondition); } orConditions.put(action, orCondition); // Add effects if (a.getType() == Action.DRAG_TO) { SceneElementDef target = addDrag(effectTrigger, notEffectTrigger, a, actor, c); targets.put(action, target); } else if (isInteraction(a)) { SceneElementDef target = addInteraction(effectTrigger, notEffectTrigger, a, actor, c); targets.put(action, target); } else { convert(a, (SceneElementDef) action, actor, c, isActiveArea, effectTrigger, notEffectTrigger); } getsTo.put(action, a.isNeedsGoTo()); } logger.debug("iterating {} effect triggers...", effectsTriggers.size()); // First, effects for every action are added. All actions with the // same name are merged in only one, with one big trigger macro effect, // whose effects are conditioned by the old action conditions, chained // with ORs for (Entry<SceneElementDef, TriggerMacroEf> e : effectsTriggers .entrySet()) { SceneElementDef a = e.getKey(); TriggerMacroEf trigger = e.getValue(); Condition orCondition = orConditions.get(e.getKey()); trigger.setCondition(orCondition); if (logger.isDebugEnabled()) { Object v = getsTo.get(a); logger.debug("will operate on {}, {}, {}, {}", new Object[] { v, trigger, actor, a }); if (v == null) { for (SceneElementDef x : getsTo.keySet()) { logger.debug("\t{} {} :: {}", new Object[] { x, x.getId(), x.equals(a) }); } } } addMoveTo(getsTo.get(a), trigger, actor, a); } // Now, the non-effects are added in a second trigger effect whose // condition is enabled when any of the main effects for the action are // active for (Entry<SceneElementDef, TriggerMacroEf> e : notEffectsTriggers .entrySet()) { SceneElementDef a = e.getKey(); TriggerMacroEf trigger = e.getValue(); Condition orCondition = orConditions.get(e.getKey()); trigger.setCondition(new NOTCond(orCondition)); addMoveTo(getsTo.get(a), trigger, actor, a); } for (Entry<SceneElementDef, SceneElementDef> e : targets .entrySet()) { DragGEv dragEvent = new DragGEv(actor.getId(), DragGEvType.DROP); TriggerMacroEf trigger = effectsTriggers.get(e.getKey()); e.getValue().addBehavior(dragEvent, trigger); } } @SuppressWarnings( { "unchecked", "rawtypes" }) private void addMoveTo(boolean needsGoTo, TriggerMacroEf triggerEffect, SceneElementDef actor, SceneElementDef action) { EAdList list = (EAdList) actor.getVars() .get(ActorActionsEf.VAR_ACTIONS); if (!factory.isFirstPerson() && needsGoTo) { MoveActiveElementToMouseEf moveActiveElement = new MoveActiveElementToMouseEf(); moveActiveElement.setTarget(actor); moveActiveElement.getNextEffects().add(triggerEffect); list.add(moveActiveElement); } else { list.add(triggerEffect); } } private SceneElementDef addDrag(TriggerMacroEf effectTrigger, TriggerMacroEf notEffectTrigger, Action a, SceneElementDef actor, Condition c) { BasicElement element = factory.getElementById(a.getTargetId()); SceneElementDef target = null; if (element instanceof SceneElementDef) { target = (SceneElementDef) element; } else if (element instanceof SceneElement) { target = ((SceneElement) element).getDefinition(); } EAdList<Effect> macro = this.effectsImporterFactory.getMacroEffects(a .getEffects()); if (effectTrigger != null) { effectTrigger.putEffects(c, macro); } EAdList<Effect> noEAdList = this.effectsImporterFactory .getMacroEffects(a.getNotEffects()); if (noEAdList != null) { notEffectTrigger.putEffects(new NOTCond(c), noEAdList); } factory.addDraggableActor(actor); return target; } private boolean isInteraction(Action a) { return a.getType() == Action.GIVE_TO || a.getType() == Action.USE_WITH || a.getType() == Action.CUSTOM_INTERACT; } private SceneElementDef addInteraction(TriggerMacroEf effectTrigger, TriggerMacroEf notEffectTrigger, Action a, SceneElementDef actor, Condition condition) { EAdList<Effect> macro = effectsImporterFactory.getMacroEffects(a .getEffects()); if (macro != null) { ModifyInventoryEf removeFromInventory = new ModifyInventoryEf( actor, InventoryEffectAction.REMOVE_FROM_INVENTORY); if (a.getType() == Action.GIVE_TO && !hasCancelEffect(a.getEffects())) { macro.add(removeFromInventory); } } effectTrigger.putEffects(condition, macro); EAdList<Effect> noEAdList = this.effectsImporterFactory .getMacroEffects(a.getNotEffects()); if (noEAdList != null) { notEffectTrigger.putEffects(new NOTCond(condition), noEAdList); } BasicElement e = factory.getElementById(a.getTargetId()); SceneElementDef target = null; if (e instanceof SceneElement) { target = ((SceneElement) e).getDefinition(); } else if (e instanceof SceneElementDef) { target = (SceneElementDef) e; } return target; } private boolean hasCancelEffect(Effects effects) { if (effects != null) { for (AbstractEffect e : effects.getEffects()) { if (e instanceof CancelActionEffect) { return true; } } } return false; } }