package com.lyndir.omicron.api;
import static com.lyndir.omicron.api.Security.*;
import com.google.common.collect.*;
import com.lyndir.lhunath.opal.system.error.AlreadyCheckedException;
import com.lyndir.lhunath.opal.system.error.InternalInconsistencyException;
import com.lyndir.lhunath.opal.system.util.ObjectUtils;
import com.lyndir.omicron.api.error.NotAuthenticatedException;
import com.lyndir.omicron.api.util.Maybe;
import com.lyndir.omicron.api.util.Maybool;
import java.util.Set;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;
// TODO: Should this module's logic be moved to GameObjectController?
// TODO: It uniquely describes a game object and can exist only once and lots of external code assumes there is one and only one...
public class BaseModule extends Module implements IBaseModule, IBaseModuleController {
private final int maxHealth;
private final int armor;
private final int viewRange;
private final ImmutableSet<LevelType> supportedLayers;
private int damage;
protected BaseModule(final ImmutableResourceCost resourceCost, final int maxHealth, final int armor, final int viewRange,
final Set<LevelType> supportedLayers) {
super( resourceCost );
this.maxHealth = maxHealth;
this.armor = armor;
this.viewRange = viewRange;
this.supportedLayers = ImmutableSet.copyOf( supportedLayers );
}
static Builder0 createWithStandardResourceCost() {
return createWithExtraResourceCost( ResourceCost.immutable() );
}
static Builder0 createWithExtraResourceCost(final ImmutableResourceCost resourceCost) {
return new Builder0( ModuleType.BASE.getStandardCost().add( resourceCost ) );
}
/**
* @see GameObservable#getLocation()
*/
@Override
public Maybool canObserve(@Nonnull final GameObservable observable)
throws NotAuthenticatedException {
if (observable.equals( getGameObject() ))
return Maybool.yes();
Maybe<Tile> ourLocation = getGameObject().getLocation();
switch (ourLocation.presence()) {
case EMPTY:
return Maybool.no();
case UNKNOWN:
return Maybool.unknown();
case PRESENT:
ITile observableLocation = null;
if (isGod() || getGameObject().isOwnedByCurrentPlayer()) {
// FIXME: HACKS! Get rid of the instanceof.
if (observable instanceof ITile)
observableLocation = (ITile) observable;
else if (observable instanceof IGameObject)
observableLocation = observable.getLocation().get();
else
throw new InternalInconsistencyException( "This hack failed. We seem to have an unexpected kind of observable." );
}
else {
Maybe <? extends ITile> location = observable.getLocation();
switch (location.presence()) {
case EMPTY:
return Maybool.no();
case UNKNOWN:
return Maybool.unknown();
case PRESENT:
observableLocation = location.get();
}
}
return Maybool.from(ourLocation.get().getPosition().distanceTo( observableLocation.getPosition() ) <= viewRange);
}
throw new AlreadyCheckedException( "Switch statement should handle all cases." );
}
@Nonnull
@NotNull
@Override
public Stream<? extends ITile> observableTiles() {
Maybe<Tile> location = getGameObject().getLocation();
if (location.presence() != Maybe.Presence.PRESENT)
return ImmutableList.<Tile>of().stream();
return location.get().getLevel().getTilesByPosition().values().stream().filter( tile -> canObserve( tile ).isTrue() );
}
@Override
public int getMaxHealth() {
return maxHealth;
}
@Override
public int getDamage() {
return damage;
}
@Override
public int getArmor() {
return armor;
}
@Override
public int getViewRange() {
return viewRange;
}
@Override
public ImmutableSet<LevelType> getSupportedLayers() {
return supportedLayers;
}
@Override
protected void onReset() {
}
@Override
protected void onNewTurn() {
}
void addDamage(final int incomingDamage) {
ChangeInt.From damageChange = ChangeInt.from( damage );
damage += Math.max( 0, incomingDamage - armor );
if (getRemainingHealth() <= 0)
getGameObject().getController().die();
Security.currentGame().getController().fireIfObservable( getGameObject() ) //
.onBaseDamaged( this, damageChange.to( damage ) );
}
@Override
public IBaseModuleController getController() {
return this;
}
@Override
public IBaseModule getModule() {
return this;
}
@SuppressWarnings({ "ParameterHidesMemberVariable", "InnerClassFieldHidesOuterClassField" })
static class Builder0 {
private final ImmutableResourceCost resourceCost;
private Builder0(final ImmutableResourceCost resourceCost) {
this.resourceCost = resourceCost;
}
Builder1 maxHealth(final int maxHealth) {
return new Builder1( maxHealth );
}
class Builder1 {
private final int maxHealth;
private Builder1(final int maxHealth) {
this.maxHealth = maxHealth;
}
Builder2 armor(final int armor) {
return new Builder2( armor );
}
class Builder2 {
private final int armor;
private Builder2(final int armor) {
this.armor = armor;
}
Builder3 viewRange(final int viewRange) {
return new Builder3( viewRange );
}
class Builder3 {
private final int viewRange;
private Builder3(final int viewRange) {
this.viewRange = viewRange;
}
BaseModule supportedLayers(final LevelType... supportedLayers) {
return supportedLayers( ImmutableSet.copyOf( supportedLayers ) );
}
BaseModule supportedLayers(final ImmutableSet<LevelType> supportedLayers) {
return new BaseModule( resourceCost, maxHealth, armor, viewRange, supportedLayers );
}
}
}
}
}
}