package com.rubenvandeven.emotionhero; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; import com.affectiva.android.affdex.sdk.Frame; import com.affectiva.android.affdex.sdk.detector.Face; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; /** * Created by ruben on 16/08/16. */ public class Scenario { public int id; public Bitmap currentFrameBitmap; public static final int LVL_NONE = 0; public static final int LVL_ANGER = 1; public static final int LVL_JOY = 2; public static final int LVL_SURPRISE = 3; // the levels in the right order. public static final ArrayList SCENARIOS = new ArrayList() {{ add(LVL_ANGER); add(LVL_JOY); add(LVL_SURPRISE); }}; static int DESIRED_FPS = 25; float duration = 0; /** * @deprecated */ long startTime = 0; /** * The timer that provides the tick */ Timer timer; /** * Increment on each tick */ float runningTime = -1; boolean isRunning = false; private GamingActivity _activity; /** * The scorres in this moment, as to draw them on the screen. * Indexes are Emotion ordinals */ private Face currentFace; ArrayList targets = new ArrayList<>(); class Target { public Emotion emotion; public float value; public float timestamp; public Score score; } ArrayList scores = new ArrayList<>(); class Score { public float value; /** * Extra bonus given */ public boolean isSpotOn = false; // public Bitmap image; //image at time of score public Face face; // face at time of score } /** * Constructor */ public Scenario(int lvl_id, GamingActivity activity) { // go to first scenario if unexisting is given if(!SCENARIOS.contains(lvl_id)) { lvl_id = SCENARIOS.get(0); } this.id = lvl_id; _activity = activity; createTargets(); init(); } public void init() { timer = new Timer("ScenarioTimer"); TimerTask tickTask; tickTask = new TimerTask() { @Override public void run() { // if (System.currentTimeMillis() - scheduledExecutionTime() >= // MAX_TARDINESS) // return; // Too late; skip this execution. tick(); } }; timer.schedule(tickTask, 0, 1000/DESIRED_FPS); } /** * To be called on each timer tick */ public void tick() { if(!isRunning) return; runningTime += 1.0f/DESIRED_FPS; if(isFinished()) { stop(); return; } for (int i = targets.size() - 1; i >= 0; i--) { Target target = targets.get(i); // skip targets that are already scored if(target.score != null) { continue; } if(target.timestamp <= runningTime) { float scored_value = target.emotion.getValueFromFace(currentFace); float required_value = target.value; Score score = new Score(); score.value = Math.round(100 - Math.abs(scored_value-required_value)); score.face = currentFace; scores.add(score); // _activity.sound.play(_activity.soundIds.get(_activity.SOUND_SCORE), 1, 1, 1,0, score.value / 200f + 0.5f ); // play back the sound slower // depending on score value target.score = score; } } } /** * Add a target on given timestamp * @param emotion * @param value * @param timestamp */ public void setTarget(Emotion emotion, float value, float timestamp) { // Log.e(GamingActivity.LOG_TAG, "Set target:" + Float.toString(timestamp) + " " + Float.toString(duration)); if((timestamp + 1) > duration) { duration = timestamp + 1; // always a bit onger than last target, as it otherwise the game does not finish pretty } Target target = new Target(); target.timestamp = timestamp; target.value = value; target.emotion = emotion; targets.add(target); } /** * Add target after existing targets, give delta with last item instead of absolute time. * @param emotion * @param value * @param interval */ public void addTarget(Emotion emotion, float value, float interval) { float timestamp; if(targets.isEmpty()) { timestamp = interval; } else { timestamp = targets.get(targets.size() - 1).timestamp + interval; } setTarget(emotion, value, timestamp); } /** * @deprecated use @see tick() * @param face * @param timestamp */ public void validateFaceOnTime(Face face, float timestamp) { // TODO: interpolation of time for (int i = targets.size() - 1; i >= 0; i--) { Target target = targets.get(i); if(target.timestamp > timestamp - 0.2 && target.timestamp < timestamp + 0.2) { float scored_value = target.emotion.getValueFromFace(face); float required_value = target.value; Score score = new Score(); score.value = 100 - Math.abs(scored_value-required_value); target.score = score; scores.add(score); } } } public float getTotalScore() { float value = 0; for (Score score : scores) { value += score.value; } return value; } /** * Check whether given timestamp is within duration of the scenario * @param timestamp * @return */ public boolean isWithinTime(float timestamp) { return timestamp <= duration; } public ArrayList getTargets() { return targets; } /** * Get the time within the scenario (so since start() has been called) */ public float getTime() { return runningTime; // if not started, don't move the labels, if started, move them by diff_y // if(startTime == 0) { // return 0; // } else { // float diff_t = ((System.currentTimeMillis() - startTime)) / (float) 1000; // if(diff_t > duration) { // never larger than scenario duration // return duration; // } // return diff_t; // } } public void start() { startTime = System.currentTimeMillis(); isRunning = true; } public void pause() { isRunning = false; } public void stop() { isRunning = false; } // TODO: create AttributeScoreCollection class, with this method. public void setCurrentFace(Face face) { currentFace = face; } public boolean isFinished() { return runningTime > duration; } public Highscore getHighscore() { if(!isFinished()) { return null; } return new Highscore("Levelname!", getTotalScore()); } public int getMaxScore() { return targets.size() * 100; } public void createTargets() { switch(id) { case LVL_ANGER: setTarget(Emotion.ANGER, 10, 1); setTarget(Emotion.ANGER, 20, 2); setTarget(Emotion.ANGER, 40, 3); setTarget(Emotion.ANGER, 70, 4); setTarget(Emotion.ANGER, 100, 5); setTarget(Emotion.JOY, 100, 8); setTarget(Emotion.ANGER, 100, 10); setTarget(Emotion.JOY, 100, 15); setTarget(Emotion.ANGER, 100, 20); break; case LVL_JOY: break; case LVL_SURPRISE: setTarget(Emotion.SURPRISE, 20, 1); setTarget(Emotion.SURPRISE, 50, 2); setTarget(Emotion.SURPRISE, 80, 3); setTarget(Emotion.SURPRISE, 100, 4); break; } } public int getNextLevelId() { int nextIdx = SCENARIOS.indexOf(id) + 1; if(SCENARIOS.size() <= nextIdx) { return SCENARIOS.get(0); } return SCENARIOS.get(nextIdx); } public boolean isFinalLevel() { if(SCENARIOS.get(SCENARIOS.size()-1) == id) return true; return false; } }