HaxeFlixel/flixel

View on GitHub
flixel/FlxBasic.hx

Summary

Maintainability
Test Coverage
package flixel;

import flixel.group.FlxContainer;
import flixel.util.FlxDestroyUtil;
import flixel.util.FlxStringUtil;

/**
 * This is a useful "generic" Flixel object. Both `FlxObject` and
 * `FlxGroup` extend this class. Has no size, position or graphical data.
 */
class FlxBasic implements IFlxDestroyable
{
    #if FLX_DEBUG
    /**
     * Static counters for performance tracking.
     */
    @:allow(flixel.FlxGame)
    static var activeCount:Int = 0;

    @:allow(flixel.FlxGame)
    static var visibleCount:Int = 0;
    #end

    /**
     * A unique ID starting from 0 and increasing by 1 for each subsequent `FlxBasic` that is created.
     */
    public var ID:Int = idEnumerator++;

    @:noCompletion
    static var idEnumerator:Int = 0;

    /**
     * Controls whether `update()` is automatically called by `FlxState`/`FlxGroup`.
     */
    public var active(default, set):Bool = true;

    /**
     * Controls whether `draw()` is automatically called by `FlxState`/`FlxGroup`.
     */
    public var visible(default, set):Bool = true;

    /**
     * Useful state for many game objects - "dead" (`!alive`) vs `alive`. `kill()` and
     * `revive()` both flip this switch (along with `exists`, but you can override that).
     */
    public var alive(default, set):Bool = true;

    /**
     * Controls whether `update()` and `draw()` are automatically called by `FlxState`/`FlxGroup`.
     */
    public var exists(default, set):Bool = true;

    /**
     * Gets or sets the first camera of this object.
     */
    public var camera(get, set):FlxCamera;

    /**
     * This determines on which `FlxCamera`s this object will be drawn. If it is `null` / has not been
     * set, it uses the list of default draw targets, which is controlled via `FlxG.camera.setDefaultDrawTarget`
     * as well as the `DefaultDrawTarget` argument of `FlxG.camera.add`.
     */
    public var cameras(get, set):Array<FlxCamera>;

    /**
     * Enum that informs the collision system which type of object this is (to avoid expensive type casting).
     */
    @:noCompletion
    var flixelType(default, null):FlxType = NONE;

    @:noCompletion
    var _cameras:Array<FlxCamera>;
    
    /**
     * The parent containing this basic, typically if you check this recursively you should reach the state
     * @since 5.7.0
     */
    public var container(get, null):Null<FlxContainer>;

    public function new() {}

    /**
     * **WARNING:** A destroyed `FlxBasic` can't be used anymore.
     * It may even cause crashes if it is still part of a group or state.
     * You may want to use `kill()` instead if you want to disable the object temporarily only and `revive()` it later.
     *
     * This function is usually not called manually (Flixel calls it automatically during state switches for all `add()`ed objects).
     *
     * Override this function to `null` out variables manually or call `destroy()` on class members if necessary.
     * Don't forget to call `super.destroy()`!
     */
    public function destroy():Void
    {
        if (container != null)
            container.remove(this);
        
        container = null;
        exists = false;
        _cameras = null;
    }

    /**
     * Handy function for "killing" game objects. Use `reset()` to revive them.
     * Default behavior is to flag them as nonexistent AND dead.
     * However, if you want the "corpse" to remain in the game, like to animate an effect or whatever,
     * you should `override` this, setting only `alive` to `false`, and leaving `exists` `true`.
     */
    public function kill():Void
    {
        alive = false;
        exists = false;
    }

    /**
     * Handy function for bringing game objects "back to life". Just sets `alive` and `exists` back to `true`.
     * In practice, this function is most often called by `FlxObject#reset()`.
     */
    public function revive():Void
    {
        alive = true;
        exists = true;
    }

    /**
     * Override this function to update your class's position and appearance.
     * This is where most of your game rules and behavioral code will go.
     */
    public function update(elapsed:Float):Void
    {
        #if FLX_DEBUG
        activeCount++;
        #end
    }

    /**
     * Override this function to control how the object is drawn.
     * Doing so is rarely necessary, but can be very useful.
     */
    public function draw():Void
    {
        #if FLX_DEBUG
        visibleCount++;
        #end
    }

    public function toString():String
    {
        return FlxStringUtil.getDebugString([
            LabelValuePair.weak("active", active),
            LabelValuePair.weak("visible", visible),
            LabelValuePair.weak("alive", alive),
            LabelValuePair.weak("exists", exists)
        ]);
    }

    @:noCompletion
    function set_visible(Value:Bool):Bool
    {
        return visible = Value;
    }

    @:noCompletion
    function set_active(Value:Bool):Bool
    {
        return active = Value;
    }

    @:noCompletion
    function set_exists(Value:Bool):Bool
    {
        return exists = Value;
    }

    @:noCompletion
    function set_alive(Value:Bool):Bool
    {
        return alive = Value;
    }

    @:noCompletion
    function get_camera():FlxCamera
    {
        return (_cameras == null || _cameras.length == 0) ? FlxCamera._defaultCameras[0] : _cameras[0];
    }

    @:noCompletion
    function set_camera(Value:FlxCamera):FlxCamera
    {
        if (_cameras == null)
            _cameras = [Value];
        else
            _cameras[0] = Value;
        return Value;
    }
    
    /**
     * The cameras that will draw this. Use `this.cameras` to set specific cameras for this object,
     * otherwise the container's cameras are used, or the container's container and so on. If there
     * is no container, say, if this is inside `FlxGroups` rather than a `FlxContainer` then the
     * default draw cameras are returned.
     * @since 5.7.0
     */
    public function getCameras()
    {
        return if (_cameras != null)
                _cameras;
            else if (_cameras == null && container != null)
                container.getCameras();
            else
                @:privateAccess FlxCamera._defaultCameras;
    }
    
    /**
     * Helper while moving away from `get_cameras`. Should only be used in the draw phase
     */
    @:noCompletion
    function getCamerasLegacy()
    {
        @:privateAccess
        return (_cameras == null) ? FlxCamera._defaultCameras : _cameras;
    }
    
    @:noCompletion
    function get_cameras():Array<FlxCamera>
    {
        return getCamerasLegacy();
    }

    @:noCompletion
    function set_cameras(Value:Array<FlxCamera>):Array<FlxCamera>
    {
        return _cameras = Value;
    }
    
    // Only needed for FlxSpriteContainer.SpriteContainer
    // TODO: remove this when FlxSpriteContainer is removed
    @:noCompletion
    function get_container()
    {
        return this.container;
    }
}

/**
 * Types of flixel objects - mainly for collisions.
 */
enum abstract FlxType(Int)
{
    var NONE = 0;
    var OBJECT = 1;
    var GROUP = 2;
    var TILEMAP = 3;
    var SPRITEGROUP = 4;
}

interface IFlxBasic
{
    var ID:Int;
    var active(default, set):Bool;
    var visible(default, set):Bool;
    var alive(default, set):Bool;
    var exists(default, set):Bool;

    function draw():Void;
    function update(elapsed:Float):Void;
    function destroy():Void;

    function kill():Void;
    function revive():Void;

    function toString():String;
}