/*
* Copyright 2015 Daniel Dittmar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dan.dit.whatsthat.util.flatworld.world;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import dan.dit.whatsthat.util.flatworld.collision.CollisionController;
import dan.dit.whatsthat.util.flatworld.effects.WorldEffect;
import dan.dit.whatsthat.util.flatworld.effects.WorldEffectMovedText;
import dan.dit.whatsthat.util.flatworld.look.NinePatchLook;
import dan.dit.whatsthat.util.flatworld.mover.HitboxAttachedMover;
import dan.dit.whatsthat.util.flatworld.mover.HitboxNoMover;
/**
* Created by daniel on 03.06.15.
*/
public abstract class FlatWorld {
private final CollisionController mCollider;
final FlatWorldCallback mCallback;
private List<Actor> mActorsIterateData = new ArrayList<>();
private final List<Actor> mActorsData = Collections.synchronizedList(new ArrayList<Actor>());
private final List<Actor> mActiveActors = new ArrayList<>();
private List<WorldEffect> mEffectsIterateData = new ArrayList<>();
private final List<WorldEffect> mEffectsData = Collections.synchronizedList(new ArrayList<WorldEffect>());
FlatWorld(CollisionController collider, FlatWorldCallback callback) {
mCollider = collider;
if (collider == null) {
throw new IllegalArgumentException("No collider given (use NoCollider for no collision).");
}
mCallback = callback;
if (callback == null) {
throw new IllegalArgumentException("No callback.");
}
}
public final void update(long updatePeriod) {
updateActors(updatePeriod);
updateEffects(updatePeriod);
checkCollision();
}
private void updateEffects(long updatePeriod) {
List<WorldEffect> effectIterate = mEffectsIterateData;
for (int i = 0; i < effectIterate.size(); i++) {
effectIterate.get(i).update(updatePeriod);
}
}
public void draw(Canvas canvas, Paint paint) {
drawActors(canvas, paint);
drawEffects(canvas, paint);
}
private void drawActors(Canvas canvas, Paint paint) {
List<Actor> actorIterate = mActorsIterateData;
for (int i = 0; i < actorIterate.size(); i++) {
actorIterate.get(i).draw(canvas, paint);
}
}
private void drawEffects(Canvas canvas, Paint paint) {
List<WorldEffect> effectIterate = mEffectsIterateData;
for (int i = 0; i < effectIterate.size(); i++) {
effectIterate.get(i).draw(canvas, paint);
}
}
protected abstract void checkLeaveWorld(Actor actor);
protected abstract void checkReachOutside(Actor actor);
private void updateActors(long updatePeriod) {
mActiveActors.clear();
List<Actor> actorIterate = mActorsIterateData;
for (int i = 0; i < actorIterate.size(); i++) {
Actor actor = actorIterate.get(i);
if (actor.isActive()) {
if (actor.update(updatePeriod)) {
mCallback.onMoverStateChange(actor);
}
mActiveActors.add(actor);
checkReachOutside(actor);
checkLeaveWorld(actor);
}
}
}
private void checkCollision() {
mCollider.checkCollision(mActiveActors, mCallback);
}
private void pushEffect(WorldEffect effect) {
synchronized (mEffectsData) {
Iterator<WorldEffect> it = mEffectsData.iterator();
while (it.hasNext()) {
WorldEffect next = it.next();
if (next == effect) {
return; // trying to add already added effect!
}
if (next.getState() == WorldEffect.STATE_TIMEOUT) {
it.remove();
}
}
mEffectsData.add(effect);
mEffectsIterateData = new ArrayList<>(mEffectsData);
}
}
public void addEffect(WorldEffect effect, long duration, long fadeOffset, int fadeFrom, int
fadeTo, boolean fadeAlphaOnly) {
effect.setDuration(duration);
effect.startFade(fadeFrom, fadeTo, duration - fadeOffset, fadeOffset, fadeAlphaOnly);
pushEffect(effect);
}
public WorldEffect addTimedMessage(NinePatchLook background, String message, float x, float y, long duration) {
WorldEffectMovedText effect = new WorldEffectMovedText(background, x, y, HitboxNoMover.INSTANCE, message, -1);
effect.setDuration(duration);
pushEffect(effect);
return effect;
}
WorldEffect attachTimedMessage(NinePatchLook background, String message, int textMaxWidth, Actor toAttach, long duration) {
WorldEffectMovedText effect = new WorldEffectMovedText(background, 0, 0, new HitboxAttachedMover(toAttach.getHitbox()), message, textMaxWidth);
effect.setDuration(duration);
pushEffect(effect);
return effect;
}
public void addActor(Actor actor) {
mActorsData.add(actor);
synchronized (mActorsData) {
mActorsIterateData = new ArrayList<>(mActorsData);
}
}
public boolean removeActor(Actor actor) {
boolean removed = mActorsData.remove(actor);
synchronized (mActorsData) {
mActorsIterateData = new ArrayList<>(mActorsData);
}
return removed;
}
public abstract void setRandomPositionInside(Actor actor, Random rand);
public CollisionController getCollider() {
return mCollider;
}
}