001    package components;
002    
003    import java.awt.event.ActionEvent;
004    import java.util.HashSet;
005    import java.util.Map;
006    import java.util.Set;
007    
008    import physics3d.Angle;
009    import physics3d.Shape;
010    import physics3d.Vect3;
011    
012    /** <p>
013     * A Flipper is a mutable object that represents a flipper in the game
014     * A flipper
015     * </p>
016     * @specfield homeState : boolean       //says whether the Flipper is down or up
017     * @specfield currentAngle : Angle              //the current angle of the Flipper
018     */
019    
020    public abstract class Flipper extends GameObject {
021    /*
022     * Abstraction Function
023     * AF(r) = a Flipper, f, such that
024     * f.homeState = r.homeState
025     * f.current = r.currentAngle
026     */
027    
028    /*
029     * Representation Invariant
030     * homeState, Current != null
031     * the Flipper is within the bounds of its GameSpace, as specified by the Walls of 
032     * the GameSpace
033     * current is between 0 and 90 degrees
034     */
035    
036            
037            //fields
038            private Boolean homeState;  //true if Flipper is in home state, false if in away state
039            private Angle current; //current angle of Flipper
040            
041            private Vect3 diff; //difference between tlf and center
042            final double RADS_PER_SECOND = 9.85;
043            
044            //constructors
045            
046            /**
047             * @effects constructs a new flipper
048             */
049            public Flipper(Vect3 tlf, Vect3 center, Vect3 ov, Angle oa, String name, GameSpace g) {
050                    super(tlf, ov,oa,name,g);
051                    this.shape = Shape.getFlipperShape();
052                    this.current = Angle.ZERO;
053                    this.homeState = true;
054                    this.center = center; 
055            diff = center.minus(tlf);
056            }
057            
058            public Flipper(Map<String, String> props, GameSpace gs) {
059                    super(props, gs);
060                    this.shape = Shape.getFlipperShape();
061                    this.current = Angle.ZERO;
062                    this.homeState = true;
063            }
064            
065            //methods
066            public abstract Vect3 getDiff(); //different for left and right flippers
067            
068            @Override
069            protected Shape shape() {
070                    return Shape.getFlipperShape();
071            }
072            
073            /** default coref for flippers is 0.95. also, flippers require an orientation. **/
074            @Override
075            protected Map<String, String> defaults() {
076                    Map<String, String> def = super.defaults();
077                    def.remove("orientation");
078                    def.put("coref", "0.95");
079                    return def;
080            }
081            
082            
083            @Override
084            public Angle getOrientAngle() {
085                    return orientAngle.plus(current);
086            }
087            
088            /**
089             * @effects changes homeState to the opposite boolean value 
090             */
091            @Override
092            public void actionPerformed(ActionEvent e) {
093                    homeState = !homeState;
094            }
095            
096            /**
097             * @effects if flipper is in home state, but its angle is not 0 degrees, decrease
098             * the angle by the radians/frame. if flipper is not in home state, but its angle 
099             * is not 90 degrees, increase the angle by the radians/frame. 
100             */
101            public void stepFrame() {
102                    double radsPerFrame = RADS_PER_SECOND / g.getSettings().getFPS();
103                    Angle angleToMove = new Angle(radsPerFrame);
104                    if (homeState && !current.equals(Angle.ZERO)) {
105                            //since Angles are never negative, this effectively tests overshooting 0 
106                            if (current.minus(angleToMove).compareTo(Angle.DEG_90) > 0) {
107                                    current = Angle.ZERO;
108                            } else  { 
109                                    current = current.minus(angleToMove);
110                            }
111                    } 
112                    else if (!homeState && current.compareTo(Angle.DEG_90) < 0) {
113                            if (current.plus(angleToMove).compareTo(Angle.DEG_90) > 0) {
114                                    current = Angle.DEG_90;
115                            } else { 
116                                    current = current.plus(angleToMove);
117                            }
118                    } 
119            }
120            
121            /**
122             * @return the GameObjectClassification of this object
123             */
124            public GameObjectClassification getGOClassification() {
125                    return GameObjectClassification.FLIPPER;
126            }
127            
128            
129            /**
130             * @return the top left front corners of the grid locations occupied by the flipper,
131             * where the x grid positions go from 0-20, y from 0-20, and z from 0-10
132             */
133            public Set<Vect3> getOccupiedPositions() {
134                    Set<Vect3> ops = new HashSet<Vect3>();
135                    Vect3 tlf = getTLF();
136                    ops.add(tlf);
137                    ops.add(tlf.plus(new Vect3(1, 0, 0)));
138                    ops.add(tlf.plus(new Vect3(0, 1, 0)));
139                    ops.add(tlf.plus(new Vect3(1, 1, 0)));
140                    return ops;
141            }
142            
143            /** @return the direction of the vector represents the axis about which it rotates,
144             *  the magnitude of the vector is how fast, in rad/sec
145             * 
146             */
147            public Vect3 getAngularVelocity() {
148                    if (homeState && current.compareTo(Angle.ZERO) > 0 ||
149                                    !homeState && current.compareTo(Angle.DEG_90) < 0) { //isMoving
150                            return orientVect.unitSize().times(RADS_PER_SECOND);
151                    } else {
152                            return Vect3.ZERO;
153                    }
154            }
155    }