Project

General

Profile

Download (9.73 KB) Statistics
| Branch: | Revision:

distorted-objectlib / src / main / java / org / distorted / objectlib / effects / scramble / ScrambleEffect.java @ 00057bb1

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.objectlib.effects.scramble;
11

    
12
import java.lang.reflect.Method;
13
import java.util.Random;
14

    
15
import org.distorted.library.effect.Effect;
16
import org.distorted.library.main.DistortedEffects;
17
import org.distorted.library.message.EffectListener;
18

    
19
import org.distorted.objectlib.main.ObjectPreRender;
20
import org.distorted.objectlib.main.TwistyObject;
21
import org.distorted.objectlib.effects.BaseEffect;
22
import org.distorted.objectlib.helpers.MovesFinished;
23
import org.distorted.objectlib.main.TwistyObjectNode;
24

    
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26

    
27
public abstract class ScrambleEffect extends BaseEffect implements EffectListener, MovesFinished
28
{
29
  public enum Type
30
    {
31
    NONE         (ScrambleEffectNone.class        ),
32
    ROTATIONS    (ScrambleEffectRotations.class   ),
33
    ;
34

    
35
    final Class<? extends ScrambleEffect> effect;
36

    
37
    Type(Class<? extends ScrambleEffect> effect)
38
      {
39
      this.effect= effect;
40
      }
41
    }
42

    
43
  private static final int NUM_EFFECTS = Type.values().length;
44
  private static final int FAKE_EFFECT_ID  = -3;
45
  private static final Type[] types;
46

    
47
  static
48
    {
49
    int i=0;
50
    types = new Type[NUM_EFFECTS];
51

    
52
    for(Type type: Type.values())
53
      {
54
      types[i++] = type;
55
      }
56
    }
57

    
58
  private int mEffectReturned;
59
  private int mNumScramblesLeft;
60
  private int mDurationPerDegree;
61
  private final Random mRnd;
62
  private int[][] mBasicAngle;
63
  private boolean mRotReady, mPluginReady;
64

    
65
  ObjectPreRender mPre;
66
  TwistyObject mObject;
67
  TwistyObjectNode mObjectNode;
68
  Effect[] mNodeEffects;
69
  int[] mNodeEffectPosition;
70
  Effect[] mCubeEffects;
71
  int[] mCubeEffectPosition;
72
  int mCubeEffectNumber, mNodeEffectNumber;
73
  int mNumScrambles;
74
  int[][] mScrambles;
75

    
76
///////////////////////////////////////////////////////////////////////////////////////////////////
77

    
78
  ScrambleEffect()
79
    {
80
    mRnd = new Random( System.currentTimeMillis() );
81
    }
82

    
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84

    
85
  abstract void createEffects(int duration, int numScrambles);
86
  abstract void effectFinishedPlugin(final long effectID);
87

    
88
///////////////////////////////////////////////////////////////////////////////////////////////////
89

    
90
  private int computeRowFromBitmap(int rowBitmap)
91
    {
92
    int index = 0;
93

    
94
    while(index<32)
95
      {
96
      if( (rowBitmap&0x1) != 0 ) return index;
97
      rowBitmap>>=1;
98
      index++;
99
      }
100
    return 0;
101
    }
102

    
103
///////////////////////////////////////////////////////////////////////////////////////////////////
104

    
105
  private void createBaseEffects(int duration, int numScrambles)
106
    {
107
    mNumScramblesLeft = numScrambles;
108
    int absAngle, angle, axis, row, basicDegrees, totalDegrees = 0;
109

    
110
    for(int scramble=0; scramble<mNumScramblesLeft; scramble++)
111
      {
112
      mObject.randomizeNewScramble(mScrambles, mRnd, scramble, numScrambles);
113
      int[] s = mScrambles[scramble];
114
      axis  = s[0];
115
      row   = computeRowFromBitmap(s[1]);
116
      angle = s[2];
117
      absAngle = (angle<0 ? -angle : angle);
118
      basicDegrees = 360/mBasicAngle[axis][row];
119
      totalDegrees += absAngle*basicDegrees;
120
      }
121

    
122
    // tablebase scrambling can return a scramble which is shorter than requested,
123
    // padding the end with three 0s. Take note of that.
124
    for(int scramble=0; scramble<mNumScramblesLeft; scramble++)
125
      {
126
      int[] s = mScrambles[scramble];
127
      if( s[0]==0 && s[1]==0 && s[2]==0 ) mNumScramblesLeft = scramble;
128
      }
129

    
130
    mDurationPerDegree = duration/totalDegrees;
131
    mNumScrambles = 0;
132

    
133
    mRotReady    = false;
134
    mPluginReady = false;
135

    
136
    addNewScramble();
137
    }
138

    
139
///////////////////////////////////////////////////////////////////////////////////////////////////
140

    
141
  private void addNewScramble()
142
    {
143
    if( mNumScramblesLeft>0 )
144
      {
145
      int axis = mScrambles[mNumScrambles][0];
146
      int row  = mScrambles[mNumScrambles][1];
147
      int angle= mScrambles[mNumScrambles][2];
148

    
149
      mNumScramblesLeft--;
150
      mPre.addRotation(this, axis, row, angle, mDurationPerDegree);
151
      mNumScrambles++;
152
      }
153
    else
154
      {
155
      mRotReady = true;
156
      if( mPluginReady ) mPre.effectFinished(FAKE_EFFECT_ID);
157
      }
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

    
162
  private void assignEffects()
163
    {
164
    for(int i=0; i<mCubeEffectNumber; i++)
165
      {
166
      mObject.applyEffect(mCubeEffects[i],mCubeEffectPosition[i]);
167
      mCubeEffects[i].notifyWhenFinished(this);
168
      }
169

    
170
    DistortedEffects nodeEffects = mObjectNode.getEffects();
171

    
172
    for(int i=0; i<mNodeEffectNumber; i++)
173
      {
174
      nodeEffects.apply(mNodeEffects[i],mNodeEffectPosition[i]);
175
      mNodeEffects[i].notifyWhenFinished(this);
176
      }
177
    }
178

    
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180

    
181
  private void disassignEffects()
182
    {
183
    for(int i=0; i<mCubeEffectNumber; i++)
184
      {
185
      mObject.removeEffect(mCubeEffects[i].getID());
186
      }
187

    
188
    DistortedEffects nodeEffects = mObjectNode.getEffects();
189

    
190
    for(int i=0; i<mNodeEffectNumber; i++)
191
      {
192
      nodeEffects.abortById(mNodeEffects[i].getID());
193
      }
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197
// PUBLIC API
198
///////////////////////////////////////////////////////////////////////////////////////////////////
199

    
200
  @SuppressWarnings("unused")
201
  public static String[] getNames()
202
    {
203
    String[] names = new String[NUM_EFFECTS];
204

    
205
    for( int i=0; i<NUM_EFFECTS; i++)
206
      {
207
      names[i] = types[i].name();
208
      }
209

    
210
    return names;
211
    }
212

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214

    
215
  @SuppressWarnings("unused")
216
  public static ScrambleEffect create(int ordinal) throws InstantiationException, IllegalAccessException
217
    {
218
    return types[ordinal].effect.newInstance();
219
    }
220

    
221
///////////////////////////////////////////////////////////////////////////////////////////////////
222

    
223
  public void onActionFinished(final long effectID)
224
    {
225
    addNewScramble();
226
    }
227

    
228
///////////////////////////////////////////////////////////////////////////////////////////////////
229

    
230
  private void effectFinishedAction(final long effectID)
231
    {
232
    mEffectReturned++;
233
    effectFinishedPlugin(effectID);
234

    
235
    if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
236
      {
237
      disassignEffects();
238

    
239
      mPluginReady = true;
240
      if( mRotReady ) mPre.effectFinished(FAKE_EFFECT_ID);
241
      }
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

    
246
  public void effectFinished(final long effectID)
247
    {
248
    for(int i=0; i<mCubeEffectNumber; i++)
249
      {
250
      long id = mCubeEffects[i].getID();
251

    
252
      if( effectID == id )
253
        {
254
        effectFinishedAction(effectID);
255
        return;
256
        }
257
      }
258

    
259
    for(int i=0; i<mNodeEffectNumber; i++)
260
      {
261
      long id = mNodeEffects[i].getID();
262

    
263
      if( effectID == id )
264
        {
265
        effectFinishedAction(effectID);
266
        return;
267
        }
268
      }
269
    }
270

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

    
273
  @SuppressWarnings("unused")
274
  public long start(int duration, ObjectPreRender pre)
275
    {
276
    mObject     = pre.getObject();
277
    mObjectNode = pre.getObjectNode();
278
    mPre        = pre;
279

    
280
    // NOT mController.solve() !! This would be a very subtle bug. We need to do this immediately,
281
    // because here we are already inside the mController.preRender() function (doing 'scrambleObjectNow')
282
    // and doing a delayed 'solve()' here would mean we'd be sometimes first doing the first rotation,
283
    // and only after it - the solve.
284
    mObject.solve();
285

    
286
    mBasicAngle = mObject.getBasicAngles();
287

    
288
    int numScrambles = pre.getNumScrambles();
289
    mScrambles = new int[numScrambles][3];
290
    int dura = (int)(duration*Math.pow(numScrambles,0.66f));
291
    createBaseEffects(dura,numScrambles);
292
    createEffects    (dura,numScrambles);
293

    
294
    if( mCubeEffectNumber==0 && mNodeEffectNumber==0 )
295
      {
296
      throw new RuntimeException("Cube and Node Plugin Effects not created!");
297
      }
298

    
299
    assignEffects();
300

    
301
    return FAKE_EFFECT_ID;
302
    }
303

    
304
///////////////////////////////////////////////////////////////////////////////////////////////////
305

    
306
  @SuppressWarnings("unused")
307
  public static void enableEffects()
308
    {
309
    Method method;
310

    
311
    for(Type type: Type.values())
312
      {
313
      try
314
        {
315
        method = type.effect.getDeclaredMethod("enable"); // enable not public, thus getDeclaredMethod
316
        }
317
      catch(NoSuchMethodException ex)
318
        {
319
        android.util.Log.e("ScrambleEffect", type.effect.getSimpleName()+": exception getting method: "+ex.getMessage());
320
        method = null;
321
        }
322

    
323
      try
324
        {
325
        if( method!=null ) method.invoke(null);
326
        }
327
      catch(Exception ex)
328
        {
329
        android.util.Log.e("ScrambleEffect", type.effect.getSimpleName()+": exception invoking method: "+ex.getMessage());
330
        }
331
      }
332
    }
333
}
(1-1/3)