diff --git a/AndorsTrail/res/anim/hidecombatbar.xml b/AndorsTrail/res/anim/hidecombatbar.xml new file mode 100644 index 000000000..77f471c13 --- /dev/null +++ b/AndorsTrail/res/anim/hidecombatbar.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/AndorsTrail/res/anim/scalebeat.xml b/AndorsTrail/res/anim/scalebeat.xml new file mode 100644 index 000000000..3471f26ab --- /dev/null +++ b/AndorsTrail/res/anim/scalebeat.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/AndorsTrail/res/anim/scaledown.xml b/AndorsTrail/res/anim/scaledown.xml new file mode 100644 index 000000000..0c15c5a1c --- /dev/null +++ b/AndorsTrail/res/anim/scaledown.xml @@ -0,0 +1,12 @@ + + diff --git a/AndorsTrail/res/anim/scaleup.xml b/AndorsTrail/res/anim/scaleup.xml new file mode 100644 index 000000000..138d2a36b --- /dev/null +++ b/AndorsTrail/res/anim/scaleup.xml @@ -0,0 +1,12 @@ + + diff --git a/AndorsTrail/res/anim/showcombatbar.xml b/AndorsTrail/res/anim/showcombatbar.xml new file mode 100644 index 000000000..aa6461964 --- /dev/null +++ b/AndorsTrail/res/anim/showcombatbar.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/AndorsTrail/res/layout/main.xml b/AndorsTrail/res/layout/main.xml index fb98625f9..9be5d0193 100644 --- a/AndorsTrail/res/layout/main.xml +++ b/AndorsTrail/res/layout/main.xml @@ -54,13 +54,12 @@ android:shadowColor="#000" /> - diff --git a/AndorsTrail/res/values/strings.xml b/AndorsTrail/res/values/strings.xml index 1fcb53d3c..c8a2de429 100644 --- a/AndorsTrail/res/values/strings.xml +++ b/AndorsTrail/res/values/strings.xml @@ -504,5 +504,8 @@ When making an attack on a target whose block chance (BC) is at least %1$d lower than your attack chance (AC), there is a %2$d %% chance that the hit will cause a concussion on the target. A concussion will severely lower the target\'s offensive combat abilities, making the target less able to land successful attacks. About + Interface + Enable animations + Show animations for various interface elements, such as the combat bar. diff --git a/AndorsTrail/res/xml/preferences.xml b/AndorsTrail/res/xml/preferences.xml index 1102f6a2b..656df7d0a 100644 --- a/AndorsTrail/res/xml/preferences.xml +++ b/AndorsTrail/res/xml/preferences.xml @@ -72,4 +72,12 @@ android:summary="@string/preferences_movement_dpad_minimizeable" android:key="dpadMinimizeable" /> + + + diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java index 0e84f3ec5..3993bb796 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/AndorsTrailPreferences.java @@ -34,6 +34,7 @@ public class AndorsTrailPreferences { public int dpadPosition; public boolean dpadMinimizeable = true; public boolean optimizedDrawing = false; + public boolean enableUiAnimations = true; public static void read(final Context androidContext, AndorsTrailPreferences dest) { try { @@ -48,6 +49,7 @@ public class AndorsTrailPreferences { dest.dpadPosition = Integer.parseInt(prefs.getString("dpadposition", Integer.toString(DPAD_POSITION_DISABLED))); dest.dpadMinimizeable = prefs.getBoolean("dpadMinimizeable", true); dest.optimizedDrawing = prefs.getBoolean("optimized_drawing", false); + dest.enableUiAnimations = prefs.getBoolean("enableUiAnimations", true); // This might be implemented as a skill in the future. //dest.movementAggressiveness = Integer.parseInt(prefs.getString("movementaggressiveness", Integer.toString(MOVEMENTAGGRESSIVENESS_NORMAL))); @@ -63,6 +65,7 @@ public class AndorsTrailPreferences { dest.dpadPosition = DPAD_POSITION_DISABLED; dest.dpadMinimizeable = true; dest.optimizedDrawing = false; + dest.enableUiAnimations = true; } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java index badc06f6a..0f70c143d 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/MainActivity.java @@ -16,6 +16,7 @@ import com.gpl.rpg.AndorsTrail.model.actor.Player; import com.gpl.rpg.AndorsTrail.model.item.ItemContainer.ItemEntry; import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.view.CombatView; +import com.gpl.rpg.AndorsTrail.view.DisplayActiveActorConditionIcons; import com.gpl.rpg.AndorsTrail.view.MainView; import com.gpl.rpg.AndorsTrail.view.VirtualDpadView; import com.gpl.rpg.AndorsTrail.view.QuickButton.QuickButtonContextMenuInfo; @@ -33,7 +34,7 @@ import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.view.MenuItem.OnMenuItemClickListener; import android.view.View.OnClickListener; -import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; @@ -59,7 +60,7 @@ public final class MainActivity extends Activity { public StatusView statusview; public CombatView combatview; public QuickitemView quickitemview; - private LinearLayout activeConditions; + private DisplayActiveActorConditionIcons activeConditions; private VirtualDpadView dpad; private static final int NUM_MESSAGES = 3; @@ -84,7 +85,7 @@ public final class MainActivity extends Activity { statusview = (StatusView) findViewById(R.id.main_statusview); combatview = (CombatView) findViewById(R.id.main_combatview); quickitemview = (QuickitemView) findViewById(R.id.main_quickitemview); - activeConditions = (LinearLayout) findViewById(R.id.statusview_activeconditions); + activeConditions = new DisplayActiveActorConditionIcons(app.preferences, world.tileManager, this, (RelativeLayout) findViewById(R.id.statusview_activeconditions)); dpad = (VirtualDpadView) findViewById(R.id.main_virtual_dpad); statusText = (TextView) findViewById(R.id.statusview_statustext); @@ -165,6 +166,8 @@ public final class MainActivity extends Activity { view.gameRoundController.pause(); view.movementController.stopMovement(); + activeConditions.unsubscribe(world); + save(Savegames.SLOT_QUICKSAVE); } @@ -173,6 +176,8 @@ public final class MainActivity extends Activity { super.onResume(); if (!AndorsTrailApplication.getApplicationFromActivity(this).setup.isSceneReady) return; + activeConditions.subscribe(world); + view.gameRoundController.resume(); if (world.model.uiSelections.isInCombat) { @@ -257,7 +262,6 @@ public final class MainActivity extends Activity { public void updateStatus() { statusview.updateStatus(); - statusview.updateActiveConditions(this, activeConditions); quickitemview.refreshQuickitems(); combatview.updateStatus(); } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ActorStatsController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ActorStatsController.java index 0d53eab0e..8937faaaf 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ActorStatsController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ActorStatsController.java @@ -59,10 +59,12 @@ public class ActorStatsController { if (!type.conditionTypeID.equals(c.conditionType.conditionTypeID)) continue; if (c.duration != duration) continue; - actor.conditions.remove(i); - magnitude = c.magnitude - magnitude; - if (magnitude > 0) { - actor.conditions.add(new ActorCondition(type, magnitude, duration)); + if (c.magnitude > magnitude) { + c.magnitude -= magnitude; + actor.conditionListener.onActorConditionMagnitudeChanged(actor, c); + } else { + actor.conditions.remove(i); + actor.conditionListener.onActorConditionRemoved(actor, c); } break; } @@ -109,12 +111,14 @@ public class ActorStatsController { if (!type.conditionTypeID.equals(c.conditionType.conditionTypeID)) continue; if (c.duration == duration) { // If the actor already has a condition of this type and the same duration, just increase the magnitude instead. - actor.conditions.remove(i); - magnitude += c.magnitude; - break; + c.magnitude += magnitude; + actor.conditionListener.onActorConditionMagnitudeChanged(actor, c); + return; } } - actor.conditions.add(new ActorCondition(type, magnitude, duration)); + ActorCondition c = new ActorCondition(type, magnitude, duration); + actor.conditions.add(c); + actor.conditionListener.onActorConditionAdded(actor, c); } private static void addNonStackableActorCondition(Actor actor, ActorConditionEffect e, int duration) { final ActorConditionType type = e.conditionType; @@ -123,24 +127,34 @@ public class ActorStatsController { ActorCondition c = actor.conditions.get(i); if (!type.conditionTypeID.equals(c.conditionType.conditionTypeID)) continue; if (c.magnitude > e.magnitude) return; + else if (c.magnitude == e.magnitude) { + if (c.duration >= duration) return; + } // If the actor already has this condition, but of a lower magnitude, we remove the old one and add this higher magnitude. actor.conditions.remove(i); + actor.conditionListener.onActorConditionRemoved(actor, c); } - actor.conditions.add(e.createCondition(duration)); + + ActorCondition c = e.createCondition(duration); + actor.conditions.add(c); + actor.conditionListener.onActorConditionAdded(actor, c); } public static void removeAllTemporaryConditions(final Actor actor) { for(int i = actor.conditions.size() - 1; i >= 0; --i) { - if (!actor.conditions.get(i).isTemporaryEffect()) continue; + ActorCondition c = actor.conditions.get(i); + if (!c.isTemporaryEffect()) continue; actor.conditions.remove(i); + actor.conditionListener.onActorConditionRemoved(actor, c); } } private static void removeAllConditionsOfType(final Actor actor, final String conditionTypeID) { for(int i = actor.conditions.size() - 1; i >= 0; --i) { - if (actor.conditions.get(i).conditionType.conditionTypeID.equals(conditionTypeID)) { - actor.conditions.remove(i); - } + ActorCondition c = actor.conditions.get(i); + if (!c.conditionType.conditionTypeID.equals(conditionTypeID)) continue; + actor.conditions.remove(i); + actor.conditionListener.onActorConditionRemoved(actor, c); } } @@ -210,10 +224,13 @@ public class ActorStatsController { if (SkillController.rollForSkillChance(player, SkillCollection.SKILL_REJUVENATION, SkillCollection.PER_SKILLPOINT_INCREASE_REJUVENATION_CHANCE)) { int i = getRandomConditionForRejuvenate(player); if (i >= 0) { - ActorCondition c = player.conditions.remove(i); + ActorCondition c = player.conditions.get(i); if (c.magnitude > 1) { - int magnitude = c.magnitude - 1; - player.conditions.add(i, new ActorCondition(c.conditionType, magnitude, c.duration)); + c.magnitude -= 1; + player.conditionListener.onActorConditionMagnitudeChanged(player, c); + } else { + player.conditions.remove(i); + player.conditionListener.onActorConditionRemoved(player, c); } recalculateActorCombatTraits(player); } @@ -258,6 +275,7 @@ public class ActorStatsController { for (ActorCondition c : actor.conditions) { StatsModifierTraits effect = isFullRound ? c.conditionType.statsEffect_everyFullRound : c.conditionType.statsEffect_everyRound; effectToStart = applyStatsModifierEffect(actor, effect, c.magnitude, effectToStart); + if (effect != null) actor.conditionListener.onActorConditionRoundEffectApplied(actor, c); } startVisualEffect(actor, effectToStart); } @@ -267,10 +285,13 @@ public class ActorStatsController { for(int i = actor.conditions.size() - 1; i >= 0; --i) { ActorCondition c = actor.conditions.get(i); if (!c.isTemporaryEffect()) continue; - c.duration -= 1; - if (c.duration <= 0) { + if (c.duration <= 1) { actor.conditions.remove(i); + actor.conditionListener.onActorConditionRemoved(actor, c); removedAnyConditions = true; + } else { + c.duration -= 1; + actor.conditionListener.onActorConditionDurationChanged(actor, c); } } if (removedAnyConditions) { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/CombatController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/CombatController.java index a0911b735..ebc2da269 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/CombatController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/CombatController.java @@ -6,7 +6,6 @@ import android.content.res.Resources; import android.os.Handler; import android.os.Message; import android.util.FloatMath; -import android.view.View; import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; import com.gpl.rpg.AndorsTrail.Dialogs; @@ -49,8 +48,7 @@ public final class CombatController implements VisualEffectCompletedCallback { public static final int BEGIN_TURN_CONTINUE = 2; public void enterCombat(int beginTurnAs) { - context.mainActivity.combatview.setVisibility(View.VISIBLE); - context.mainActivity.combatview.bringToFront(); + context.mainActivity.combatview.show(); model.uiSelections.isInCombat = true; killedMonsterBags.clear(); context.mainActivity.clearMessages(); @@ -61,7 +59,7 @@ public final class CombatController implements VisualEffectCompletedCallback { } public void exitCombat(boolean pickupLootBags) { setCombatSelection(null, null); - context.mainActivity.combatview.setVisibility(View.GONE); + context.mainActivity.combatview.hide(); model.uiSelections.isInCombat = false; context.mainActivity.clearMessages(); currentActiveMonster = null; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java index 32a061c5e..ce7fbe663 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/MovementController.java @@ -241,16 +241,12 @@ public final class MovementController implements TimedMessageTask.Callback { if (!world.model.currentMap.isWalkable(world.model.player.position)) { // If the player somehow spawned on an unwalkable tile, we move the player to the first mapchange area. // This could happen if we change some tile to non-walkable in a future version. - MapObject dest = null; for (MapObject o : model.currentMap.eventObjects) { if (o.type == MapObject.MAPEVENT_NEWMAP) { - dest = o; + model.player.position.set(o.position.topLeft); break; } } - if (dest != null) { - model.player.position.set(dest.position.topLeft); - } } // If any monsters somehow spawned on an unwalkable tile, we move the monster to a new position on the spawnarea diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/ability/ActorCondition.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/ability/ActorCondition.java index 772a48f4b..bb0848e6f 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/ability/ActorCondition.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/ability/ActorCondition.java @@ -11,7 +11,7 @@ public class ActorCondition { public static final int DURATION_FOREVER = 999; public final ActorConditionType conditionType; - public final int magnitude; + public int magnitude; public int duration; public ActorCondition(ActorConditionType conditionType, int magnitude, int duration) { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/actor/Actor.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/actor/Actor.java index bec0b1035..20efe7490 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/actor/Actor.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/actor/Actor.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import com.gpl.rpg.AndorsTrail.context.WorldContext; import com.gpl.rpg.AndorsTrail.model.CombatTraits; import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition; +import com.gpl.rpg.AndorsTrail.model.listeners.ActorConditionListeners; import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.util.CoordRect; import com.gpl.rpg.AndorsTrail.util.Range; @@ -20,6 +21,7 @@ public class Actor { public final Coord position; public final CoordRect rectPosition; public final ArrayList conditions = new ArrayList(); + public final ActorConditionListeners conditionListener = new ActorConditionListeners(); public final boolean isPlayer; public final boolean isImmuneToCriticalHits; diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ActorConditionListener.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ActorConditionListener.java new file mode 100644 index 000000000..6db177052 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ActorConditionListener.java @@ -0,0 +1,12 @@ +package com.gpl.rpg.AndorsTrail.model.listeners; + +import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition; +import com.gpl.rpg.AndorsTrail.model.actor.Actor; + +public interface ActorConditionListener { + public void onActorConditionAdded(Actor actor, ActorCondition condition); + public void onActorConditionRemoved(Actor actor, ActorCondition condition); + public void onActorConditionDurationChanged(Actor actor, ActorCondition condition); + public void onActorConditionMagnitudeChanged(Actor actor, ActorCondition condition); + public void onActorConditionRoundEffectApplied(Actor actor, ActorCondition condition); +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ActorConditionListeners.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ActorConditionListeners.java new file mode 100644 index 000000000..3ea927190 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ActorConditionListeners.java @@ -0,0 +1,48 @@ +package com.gpl.rpg.AndorsTrail.model.listeners; + +import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition; +import com.gpl.rpg.AndorsTrail.model.actor.Actor; + +public class ActorConditionListeners extends ListOfListeners implements ActorConditionListener { + + private final Function2 onActorConditionAdded = new Function2() { + @Override public void call(ActorConditionListener listener, Actor actor, ActorCondition condition) { listener.onActorConditionAdded(actor, condition); } + }; + private final Function2 onActorConditionRemoved = new Function2() { + @Override public void call(ActorConditionListener listener, Actor actor, ActorCondition condition) { listener.onActorConditionRemoved(actor, condition); } + }; + private final Function2 onActorConditionDurationChanged = new Function2() { + @Override public void call(ActorConditionListener listener, Actor actor, ActorCondition condition) { listener.onActorConditionDurationChanged(actor, condition); } + }; + private final Function2 onActorConditionMagnitudeChanged = new Function2() { + @Override public void call(ActorConditionListener listener, Actor actor, ActorCondition condition) { listener.onActorConditionMagnitudeChanged(actor, condition); } + }; + private final Function2 onActorConditionRoundEffectApplied = new Function2() { + @Override public void call(ActorConditionListener listener, Actor actor, ActorCondition condition) { listener.onActorConditionRoundEffectApplied(actor, condition); } + }; + + @Override + public void onActorConditionAdded(Actor actor, ActorCondition condition) { + callAllListeners(this.onActorConditionAdded, actor, condition); + } + + @Override + public void onActorConditionRemoved(Actor actor, ActorCondition condition) { + callAllListeners(this.onActorConditionRemoved, actor, condition); + } + + @Override + public void onActorConditionDurationChanged(Actor actor, ActorCondition condition) { + callAllListeners(this.onActorConditionDurationChanged, actor, condition); + } + + @Override + public void onActorConditionMagnitudeChanged(Actor actor, ActorCondition condition) { + callAllListeners(this.onActorConditionMagnitudeChanged, actor, condition); + } + + @Override + public void onActorConditionRoundEffectApplied(Actor actor, ActorCondition condition) { + callAllListeners(this.onActorConditionRoundEffectApplied, actor, condition); + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ListOfListeners.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ListOfListeners.java new file mode 100644 index 000000000..c968e3e46 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/model/listeners/ListOfListeners.java @@ -0,0 +1,61 @@ +package com.gpl.rpg.AndorsTrail.model.listeners; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; + +public class ListOfListeners { + private final ArrayList> listeners = new ArrayList>(); + + public void add(T listener) { + if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) { + for (WeakReference ref : listeners) { + if (ref.get() == listener) { + throw new IndexOutOfBoundsException("FAIL: listener added twice to ListOfListeners."); + } + } + } + listeners.add(new WeakReference(listener)); + } + public void remove(T listenerToRemove) { + for (int i = listeners.size()-1; i >= 0; --i) { + T listener = listeners.get(i).get(); + if (listener == null || listener == listenerToRemove) { + listeners.remove(i); + } + } + } + + protected void callAllListeners(Function e) { + for (int i = listeners.size()-1; i >= 0; --i) { + T listener = listeners.get(i).get(); + if (listener == null) listeners.remove(i); + else e.call(listener); + } + } + protected void callAllListeners(Function1 e, Arg1 arg) { + for (int i = listeners.size()-1; i >= 0; --i) { + T listener = listeners.get(i).get(); + if (listener == null) listeners.remove(i); + else e.call(listener, arg); + } + } + protected void callAllListeners(Function2 e, Arg1 arg1, Arg2 arg2) { + for (int i = listeners.size()-1; i >= 0; --i) { + T listener = listeners.get(i).get(); + if (listener == null) listeners.remove(i); + else e.call(listener, arg1, arg2); + } + } + + protected static interface Function { + public void call(Listener listener); + } + protected static interface Function1 { + public void call(Listener listener, Arg1 arg1); + } + protected static interface Function2 { + public void call(Listener listener, Arg1 arg1, Arg2 arg2); + } +} \ No newline at end of file diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CombatView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CombatView.java index a34722494..db6102635 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CombatView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CombatView.java @@ -5,11 +5,15 @@ import android.content.Context; import android.content.res.Resources; import android.util.AttributeSet; import android.view.View; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnimationUtils; import android.widget.Button; import android.widget.ImageButton; import android.widget.RelativeLayout; import android.widget.TextView; +import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; import com.gpl.rpg.AndorsTrail.Dialogs; import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; import com.gpl.rpg.AndorsTrail.R; @@ -33,15 +37,20 @@ public final class CombatView extends RelativeLayout { private final WorldContext world; private final ViewContext view; private final Resources res; + private final AndorsTrailPreferences preferences; private final Player player; + private final Animation displayAnimation; + private final Animation hideAnimation; private Monster currentMonster; + public CombatView(final Context context, AttributeSet attr) { super(context, attr); AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivityContext(context); this.world = app.world; this.player = world.model.player; this.view = app.currentView.get(); + this.preferences = app.preferences; this.res = getResources(); setFocusable(false); @@ -90,6 +99,16 @@ public final class CombatView extends RelativeLayout { monsterBar.setBackgroundColor(res.getColor(color.transparent)); actionBar.setBackgroundColor(res.getColor(color.transparent)); + + displayAnimation = AnimationUtils.loadAnimation(context, R.anim.showcombatbar); + hideAnimation = AnimationUtils.loadAnimation(context, R.anim.hidecombatbar); + hideAnimation.setAnimationListener(new AnimationListener() { + @Override public void onAnimationStart(Animation animation) {} + @Override public void onAnimationRepeat(Animation animation) {} + @Override public void onAnimationEnd(Animation arg0) { + CombatView.this.setVisibility(View.GONE); + } + }); } public void updateTurnInfo(Monster currentActiveMonster) { @@ -135,4 +154,20 @@ public final class CombatView extends RelativeLayout { } updateCombatSelection(world.model.uiSelections.selectedMonster, world.model.uiSelections.selectedPosition); } + + public void show() { + setVisibility(View.VISIBLE); + bringToFront(); + if (preferences.enableUiAnimations) { + startAnimation(displayAnimation); + } + } + + public void hide() { + if (preferences.enableUiAnimations) { + startAnimation(hideAnimation); + } else { + setVisibility(View.GONE); + } + } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/DisplayActiveActorConditionIcons.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/DisplayActiveActorConditionIcons.java new file mode 100644 index 000000000..c14864707 --- /dev/null +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/DisplayActiveActorConditionIcons.java @@ -0,0 +1,220 @@ +package com.gpl.rpg.AndorsTrail.view; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +import android.content.Context; +import android.content.res.Resources; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnimationUtils; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.RelativeLayout.LayoutParams; + +import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; +import com.gpl.rpg.AndorsTrail.R; +import com.gpl.rpg.AndorsTrail.context.WorldContext; +import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition; +import com.gpl.rpg.AndorsTrail.model.actor.Actor; +import com.gpl.rpg.AndorsTrail.model.listeners.ActorConditionListener; +import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager; + +public class DisplayActiveActorConditionIcons implements ActorConditionListener { + + private final AndorsTrailPreferences preferences; + private final TileManager tileManager; + private final RelativeLayout activeConditions; + private final ArrayList currentConditionIcons = new ArrayList(); + private final WeakReference androidContext; + + public DisplayActiveActorConditionIcons( + final AndorsTrailPreferences preferences, + final TileManager tileManager, + Context androidContext, + RelativeLayout activeConditions) { + this.preferences = preferences; + this.tileManager = tileManager; + this.androidContext = new WeakReference(androidContext); + this.activeConditions = activeConditions; + } + + @Override + public void onActorConditionAdded(Actor actor, ActorCondition condition) { + ActiveConditionIcon icon = getFirstFreeIcon(); + icon.setActiveCondition(condition); + icon.show(); + } + + @Override + public void onActorConditionRemoved(Actor actor, ActorCondition condition) { + ActiveConditionIcon icon = getIconFor(condition); + if (icon == null) return; + icon.hide(true); + } + + @Override + public void onActorConditionDurationChanged(Actor actor, ActorCondition condition) { + } + + @Override + public void onActorConditionMagnitudeChanged(Actor actor, ActorCondition condition) { + ActiveConditionIcon icon = getIconFor(condition); + if (icon == null) return; + icon.setIconText(); + } + + @Override + public void onActorConditionRoundEffectApplied(Actor actor, ActorCondition condition) { + ActiveConditionIcon icon = getIconFor(condition); + if (icon == null) return; + icon.pulseAnimate(); + } + + public void unsubscribe(final WorldContext world) { + world.model.player.conditionListener.remove(this); + for (ActiveConditionIcon icon : currentConditionIcons) icon.condition = null; + } + + public void subscribe(final WorldContext world) { + for (ActiveConditionIcon icon : currentConditionIcons) icon.hide(false); + for (ActorCondition condition : world.model.player.conditions) { + getFirstFreeIcon().setActiveCondition(condition); + } + world.model.player.conditionListener.add(this); + } + + private final class ActiveConditionIcon implements AnimationListener { + public final int id; + public ActorCondition condition; + public final ImageView image; + public final TextView text; + private final Animation onNewIconAnimation; + private final Animation onRemovedIconAnimation; + private final Animation onAppliedEffectAnimation; + + public ActiveConditionIcon(Context context, int id) { + this.id = id; + this.image = new ImageView(context); + this.image.setId(id); + this.text = new TextView(context); + this.onNewIconAnimation = AnimationUtils.loadAnimation(context, R.anim.scaleup); + this.onRemovedIconAnimation = AnimationUtils.loadAnimation(context, R.anim.scaledown); + this.onAppliedEffectAnimation = AnimationUtils.loadAnimation(context, R.anim.scalebeat); + this.onRemovedIconAnimation.setAnimationListener(this); + + final Resources res = context.getResources(); + + text.setTextColor(res.getColor(android.R.color.white)); + text.setShadowLayer(1, 1, 1, res.getColor(android.R.color.black)); + } + + private void setActiveCondition(ActorCondition condition) { + this.condition = condition; + tileManager.setImageViewTile(image, condition.conditionType); + image.setVisibility(View.VISIBLE); + setIconText(); + } + + public void setIconText() { + boolean showMagnitude = (condition.magnitude != 1); + if (showMagnitude) { + text.setText(Integer.toString(condition.magnitude)); + text.setVisibility(View.VISIBLE); + } else { + text.setVisibility(View.GONE); + } + } + + public void hide(boolean useAnimation) { + if (useAnimation) { + if (preferences.enableUiAnimations) { + image.startAnimation(onRemovedIconAnimation); + } else { + onAnimationEnd(onRemovedIconAnimation); + } + } else { + image.setVisibility(View.GONE); + condition = null; + } + text.setVisibility(View.GONE); + } + public void show() { + if (!preferences.enableUiAnimations) return; + image.startAnimation(onNewIconAnimation); + if (text.getVisibility() == View.VISIBLE) text.startAnimation(onNewIconAnimation); + } + + public void pulseAnimate() { + if (!preferences.enableUiAnimations) return; + image.startAnimation(onAppliedEffectAnimation); + } + + public boolean isVisible() { + return condition != null; + } + + @Override + public void onAnimationEnd(Animation animation) { + if (animation == this.onRemovedIconAnimation) { + hide(false); + rearrangeIconsLeftOf(this); + } + } + + @Override public void onAnimationRepeat(Animation animation) { } + @Override public void onAnimationStart(Animation animation) { } + } + + protected void rearrangeIconsLeftOf(ActiveConditionIcon icon) { + int i = currentConditionIcons.indexOf(icon); + currentConditionIcons.remove(i); + currentConditionIcons.add(icon); + for(; i < currentConditionIcons.size(); ++i) { + currentConditionIcons.get(i).image.setLayoutParams(getLayoutParamsForIconIndex(i)); + } + } + + private ActiveConditionIcon getIconFor(ActorCondition condition) { + for (ActiveConditionIcon icon : currentConditionIcons) { + if (icon.condition == condition) return icon; + } + return null; + } + private ActiveConditionIcon getFirstFreeIcon() { + for (ActiveConditionIcon icon : currentConditionIcons) { + if (!icon.isVisible()) return icon; + } + return addNewActiveConditionIcon(); + } + + private RelativeLayout.LayoutParams getLayoutParamsForIconIndex(int index) { + RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + layout.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + if (index == 0) { + layout.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + } else { + layout.addRule(RelativeLayout.LEFT_OF, currentConditionIcons.get(index-1).id); + } + return layout; + } + + private ActiveConditionIcon addNewActiveConditionIcon() { + int index = currentConditionIcons.size(); + + ActiveConditionIcon icon = new ActiveConditionIcon(androidContext.get(), index+1); + + activeConditions.addView(icon.image, getLayoutParamsForIconIndex(index)); + + RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + layout.addRule(RelativeLayout.ALIGN_RIGHT, icon.id); + layout.addRule(RelativeLayout.ALIGN_BOTTOM, icon.id); + activeConditions.addView(icon.text, layout); + + currentConditionIcons.add(icon); + + return icon; + } +} diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/StatusView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/StatusView.java index c6d0a36a8..f2897c781 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/StatusView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/StatusView.java @@ -6,7 +6,6 @@ import com.gpl.rpg.AndorsTrail.activity.MainActivity; import com.gpl.rpg.AndorsTrail.activity.HeroinfoActivity; import com.gpl.rpg.AndorsTrail.context.ViewContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; -import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition; import com.gpl.rpg.AndorsTrail.model.actor.Player; import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager; @@ -17,8 +16,6 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.ImageButton; @@ -97,15 +94,6 @@ public final class StatusView extends RelativeLayout { world.tileManager.setImageViewTile(heroImage, player); } } - - public void updateActiveConditions(Context androidContext, LinearLayout activeConditions) { - GreedyImageViewAppender t = new GreedyImageViewAppender(androidContext, activeConditions); - for (ActorCondition condition : player.conditions) { - ImageView iv = t.getNextImage(); - world.tileManager.setImageViewTile(iv, condition.conditionType); - } - t.removeOtherImages(); - } public void updateQuickItemImage(boolean visible){ if(visible){ @@ -114,41 +102,4 @@ public final class StatusView extends RelativeLayout { world.tileManager.setImageViewTileForUIIcon(quickToggle, TileManager.iconID_boxclosed); } } - - private static class GreedyImageViewAppender { - private final LinearLayout container; - private final Context context; - private int currentChildIndex = 0; - private final int previousChildCount; - private final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - - public GreedyImageViewAppender(Context context, LinearLayout container) { - this.container = container; - this.context = context; - this.previousChildCount = container.getChildCount(); - } - public ImageView getNextImage() { - // Since this is called a lot, we do not want to recreate the view objects every time. - // Therefore, we reuse existing ImageView:s if they are present, but just change the image on them. - ImageView iv; - if (currentChildIndex < previousChildCount) { - // There already is a create dimage on this position, reuse it. - iv = (ImageView) container.getChildAt(currentChildIndex); - iv.setVisibility(View.VISIBLE); - } else { - // The player has never had this many conditions, create a new ImageView to hold the condition image. - iv = new ImageView(context); - container.addView(iv, layoutParams); - } - ++currentChildIndex; - return iv; - } - public void removeOtherImages() { - for(int i = previousChildCount - 1; i >= currentChildIndex; --i) { - //container.removeViewAt(i); - // Don't actually remove them, just hide them (so we won't have to recreate them next time the player get a condition) - container.getChildAt(i).setVisibility(View.GONE); - } - } - } }