All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.rpgframework.genericrpg.chargen.CharacterControllerImpl Maven / Gradle / Ivy

The newest version!
package de.rpgframework.genericrpg.chargen;

import java.io.IOException;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Optional;

import de.rpgframework.character.Attachment;
import de.rpgframework.character.Attachment.Format;
import de.rpgframework.character.Attachment.Type;
import de.rpgframework.character.CharacterHandle;
import de.rpgframework.character.CharacterProvider;
import de.rpgframework.character.CharacterProviderLoader;
import de.rpgframework.character.ProcessingStep;
import de.rpgframework.character.RuleSpecificCharacterObject;
import de.rpgframework.core.BabylonEventBus;
import de.rpgframework.core.BabylonEventType;
import de.rpgframework.genericrpg.ToDoElement;
import de.rpgframework.genericrpg.data.CommonCharacter;
import de.rpgframework.genericrpg.data.CommonCharacter.DataSetControl;
import de.rpgframework.genericrpg.data.DataItem;
import de.rpgframework.genericrpg.data.DataSet;
import de.rpgframework.genericrpg.data.IAttribute;
import de.rpgframework.genericrpg.data.RuleController;
import de.rpgframework.genericrpg.modification.Modification;

/**
 * @author Stefan
 *
 */
public abstract class CharacterControllerImpl> implements CharacterController {

	private final static Logger logger = System.getLogger(CharacterControllerImpl.class.getPackageName()+".main");

	protected M model;
	protected CharacterHandle handle;
	protected RuleController ruleCtrl;
	protected LevellingProfileController profileCtrl;
	protected Optional> recommender;

	protected Locale locale = Locale.getDefault();

	private Collection listener;

	protected List processChain;
	protected boolean dontProcess;
	private List unitTestModifications;
	protected boolean allowRunProcessor = true;

	//-------------------------------------------------------------------
	protected CharacterControllerImpl() {
		listener = new ArrayList();
		processChain = new ArrayList();
		unitTestModifications = new ArrayList();
		recommender = Optional.empty();
	}

	//-------------------------------------------------------------------
	protected CharacterControllerImpl(M model, CharacterHandle handle) {
		this();
		this.model = model;
		this.handle= handle;
		recommender = Optional.empty();
	}

	//-------------------------------------------------------------------
	public Locale getLocale() {
		return locale;
	}
	public void setLocale(Locale locale) {
		this.locale = locale;
	}

	//-------------------------------------------------------------------
	public boolean showDataItem(DataItem item) {
		// If the item is restricted to a language and that doesn't match, hide it
		if (item.getLanguage()!=null && !locale.getLanguage().equals(item.getLanguage()))
			return false;
		// If this is the default item, but there is one specific to the chosen language, hide it
		if (item.getLanguage()==null && item.hasLanguageAlternative(locale.getLanguage()))
			return false;

		if (model instanceof CommonCharacter) {
			DataSetControl dsCtrl = ((CommonCharacter)model).getDataSets();
			if (dsCtrl.mode==DataSetMode.SELECTED) {
				boolean found = false;
				for (DataSet set : item.getAssignedDataSets()) {
					if (dsCtrl.selected.contains(set.getID()) || set.getID().equalsIgnoreCase("CORE"))
						found= true;
				}
				if (!found)
					return false;
			}
		}
		return true;
	}

	//-------------------------------------------------------------------
	public void addUnitTestModification(Modification mod) {
		unitTestModifications.add(mod);
		runProcessors();
	}

	//-------------------------------------------------------------------
	public void removeUnitTestModification(Modification mod) {
		unitTestModifications.remove(mod);
		runProcessors();
	}

	//-------------------------------------------------------------------
	@Override
	public M getModel() {
		return model;
	}

	//-------------------------------------------------------------------
	@Override
	public void setModel(M data) {
		this.model = data;
	}

	//-------------------------------------------------------------------
	/**
	 * @see de.rpgframework.genericrpg.chargen.CharacterController#addListener(de.rpgframework.genericrpg.chargen.ControllerListener)
	 */
	@Override
	public void addListener(ControllerListener callback) {
		if (!listener.contains(callback)) {
			listener.add(callback);
		}
	}

	//-------------------------------------------------------------------
	@Override
	public void removeListener(ControllerListener callback) {
		listener.remove(callback);
	}

	//-------------------------------------------------------------------
	/**
	 * @see de.rpgframework.genericrpg.chargen.CharacterController#hasListener(de.rpgframework.genericrpg.chargen.ControllerListener)
	 */
	@Override
	public boolean hasListener(ControllerListener callback) {
		return listener.contains(callback);
	}

	//-------------------------------------------------------------------
	@Override
	public Collection getListener() {
		return listener;
	}

	//-------------------------------------------------------------------
	@Override
	public void fireEvent(ControllerEvent type, Object...param) {
		logger.log(Level.WARNING, "########"+type+" to "+listener.size()+" listeners of "+this.getClass());
		System.err.println("CharacterControllerImpl########"+type+" to "+listener.size()+" listeners of "+this.getClass());
		if (listener.size()==0) {
			logger.log(Level.ERROR, "No listeners for character controller - that can only be an error");
			System.err.println( "CharacterControllerImpl: No listeners for character controller - that can only be an error");
		}
		for (ControllerListener callback : new ArrayList<>(listener)) {
			try {
				callback.handleControllerEvent(type, param);
			} catch (Exception e) {
				logger.log(Level.ERROR, "Error delivering generation event",e);
			}
		}
		if (type==BasicControllerEvents.CHARACTER_PROFILES_CHANGED) {
			recommender.ifPresent(r -> r.update());
		}
	}

	//-------------------------------------------------------------------
	/**
	 * @see de.rpgframework.genericrpg.chargen.CharacterController#getRuleController()
	 */
	@Override
	public RuleController getRuleController() {
		return ruleCtrl;
	}

	//-------------------------------------------------------------------
	/**
	 * @see de.rpgframework.genericrpg.chargen.CharacterController#getProfileController()
	 */
	@Override
	public LevellingProfileController getProfileController() {
		return profileCtrl;
	}

	//-------------------------------------------------------------------
	@Override
	public List getToDos() {
		List ret = new ArrayList();
		if (model==null)
			return ret;

		for (ProcessingStep step : processChain) {
			if (step instanceof PartialController) {
				ret.addAll( ((PartialController)step).getToDos());
			}
		}

		Collections.sort(ret, new Comparator() {
			public int compare(ToDoElement o1, ToDoElement o2) {
				return Integer.compare(o1.getSeverity().ordinal(), o2.getSeverity().ordinal());
			}
		});

		return ret;
	}

	//-------------------------------------------------------------------
	/**
	 * @see de.rpgframework.genericrpg.chargen.CharacterController#setAllowRunProcessor(boolean)
	 */
	@Override
	public void setAllowRunProcessor(boolean value) {
		this.allowRunProcessor = value;
	}

	//-------------------------------------------------------------------
	@Override
	public void runProcessors() {
		if (dontProcess || !allowRunProcessor)
			return;

		try {
			dontProcess = true;
			logger.log(Level.DEBUG, "\n\nSTART: runProcessors: "+processChain.size()+"-------------------------------------------------------");
			List unprocessed = new ArrayList<>(unitTestModifications);
			for (ProcessingStep processor : processChain) {
				if (processor==null) {
					logger.log(Level.ERROR, "Found NULL in processChain");
					continue;
				}
				try {
					unprocessed = processor.process(unprocessed);
				} catch (Exception e) {
					logger.log(Level.ERROR, "Exception in processor "+processor.getClass(),e);
					BabylonEventBus.fireEvent(BabylonEventType.UI_MESSAGE, 2, "Error calculating character",e);
				}
				logger.log(Level.WARNING, "------ after {0}:\t {1}",processor.getClass().getSimpleName(),unprocessed);
			}
			logger.log(Level.DEBUG, "Remaining mods  = "+unprocessed);
			logger.log(Level.INFO, "ToDos = "+getToDos());
			logger.log(Level.WARNING, "STOP : runProcessors: "+processChain.size()+"-------------------------------------------------------");
			fireEvent(BasicControllerEvents.CHARACTER_CHANGED, model);
		} finally {
			dontProcess = false;
		}
	}

	//-------------------------------------------------------------------
	/**
	 * Save the current version of the character to a long time storage.
	 * If implemented by a CharacterGenerator, an unfinished version is
	 * saved so creation can be continued later.
	 *
	 * @param data Native save format (XML bytes)
	 * @return TRUE, if saving has been successful
	 * @throws IOException
	 * @see de.rpgframework.genericrpg.chargen.CharacterController#save()
	 */
	@Override
	public boolean save(byte[] data) throws IOException {
		logger.log(Level.DEBUG, "save called for handle {0} and char {0}", handle, model.getName());
		CharacterProvider prov = CharacterProviderLoader.getCharacterProvider();
		if (handle!=null) {
			logger.log(Level.DEBUG, "handle already exists");
			boolean modelNeedsSaving = true;
			boolean imageNeedsSaving = true;
			// Character has been continued and is only modified
			for (Attachment attach : prov.listAttachments(handle)) {
				if (attach.getType()==Type.CHARACTER && attach.getFormat()==Format.RULESPECIFIC) {
					attach.setData(data);
					logger.log(Level.INFO, "Update character file");
					prov.modifyAttachment(handle, attach);
					modelNeedsSaving = false;
				}
				if (attach.getType()==Type.CHARACTER && attach.getFormat()==Format.IMAGE) {
					if (model.getImage()!=null) {
						attach.setData(model.getImage());
						logger.log(Level.INFO, "Update character image");
						prov.modifyAttachment(handle, attach);
					} else {
						logger.log(Level.INFO, "Delete character image, since not present in model anymore");
						prov.deleteAttachment(handle, attach);
					}
					imageNeedsSaving = false;
				}
			}
			// See if anything must be created
			if (modelNeedsSaving) {
				prov.addAttachment(handle, Type.CHARACTER, Format.RULESPECIFIC, handle.getName(), data);
			}
			if (imageNeedsSaving && model.getImage()!=null) {
				prov.addAttachment(handle, Type.CHARACTER, Format.IMAGE, handle.getName(), model.getImage());
			}
			return true;
		}

		// Character has not previously been saved
		// Before creating a new handle, make sure there isn't already
		// one that would be overwritten
		logger.log(Level.DEBUG, "handle does not exist yet");
		handle = prov.getCharacter(model.getName(), model.getRules());
		if (handle!=null) {
			// Would overwrite existing character.
			logger.log(Level.WARNING, "Trying to overwrite existing character with this one");
			return false;
		}
		// Create new character
		handle = prov.createCharacter(model.getName(), model.getRules());
		if (handle==null)
			throw new IOException("Failed: No character handle");
		if (handle.getUUID()==null)
			throw new IOException("Failed: No UUID in character handle");

		// A valid character handle exists - now create attachments
		prov.addAttachment(handle, Type.CHARACTER, Format.RULESPECIFIC, model.getName()+".xml", data);
		if (model.getImage()!=null) {
			prov.addAttachment(handle, Type.CHARACTER, Format.IMAGE, model.getName()+".img", model.getImage());
		}
		return true;
	}

	//-------------------------------------------------------------------
	/**
	 * @see de.rpgframework.genericrpg.chargen.CharacterController#getRecommender()
	 */
	public Optional> getRecommender() {
		return recommender;
	}

	//-------------------------------------------------------------------
	/**
	 * @param recommender the recommender to set
	 */
	public void setRecommender(IRecommender recommender) {
		this.recommender = Optional.ofNullable(recommender);
	}

//	//-------------------------------------------------------------------
//	protected abstract void updateEffectiveRules() ;
//
//	//-------------------------------------------------------------------
//	public void setRule(Rule rule, Object value) {
//		model.setRuleValue(rule, String.valueOf(value));
//		updateEffectiveRules();
//	}
//
//	//-------------------------------------------------------------------
//	/**
//	 * @see de.rpgframework.genericrpg.chargen.CharacterController#getRule(de.rpgframework.genericrpg.chargen.Rule)
//	 */
//	@Override
//	public RuleValue getRule(Rule rule) {
//		for (Entry entry : effectiveRules.entrySet()) {
//			if (entry.getKey()==rule) {
//				return entry.getValue();
//			}
//		}
//		return null;
//	}
//
//	//-------------------------------------------------------------------
//	/**
//	 * @see de.rpgframework.genericrpg.chargen.CharacterController#getRules()
//	 */
//	@Override
//	public List getRules() {
//		return new ArrayList<>(effectiveRules.values());
//	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy