1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.replica.replicaisland; 18 19/** 20 * A very simple manager for orthographic in-game UI elements. 21 * TODO: This should probably manage a number of hud objects in keeping with the component-centric 22 * architecture of this engine. The current code is monolithic and should be refactored. 23 */ 24public class HudSystem extends BaseObject { 25 private static final int FUEL_BAR_EDGE_PADDING = 15; 26 private static final float FUEL_DECREASE_BAR_SPEED = 0.75f; 27 private static final float FUEL_INCREASE_BAR_SPEED = 2.0f; 28 private static final float FLY_BUTTON_X = -12.0f; 29 private static final float FLY_BUTTON_Y = -5.0f; 30 private static final float STOMP_BUTTON_X = 85.0f; 31 private static final float STOMP_BUTTON_Y = -10.0f; 32 private static final float STOMP_BUTTON_SCALE = 0.65f; 33 private static final int COLLECTABLE_EDGE_PADDING = 8; 34 private static final int MAX_DIGITS = 4; 35 private static final float MOVEMENT_SLIDER_BASE_X = 20.0f; 36 private static final float MOVEMENT_SLIDER_BASE_Y = 32.0f; 37 private static final float MOVEMENT_SLIDER_BUTTON_X = MOVEMENT_SLIDER_BASE_X + 32.0f; 38 private static final float MOVEMENT_SLIDER_BUTTON_Y = MOVEMENT_SLIDER_BASE_Y - 16.0f; 39 private static final float FLY_BUTTON_WIDTH = 128; 40 private static final float STOMP_BUTTON_WIDTH = FLY_BUTTON_WIDTH * STOMP_BUTTON_SCALE; 41 private static final float MOVEMENT_SLIDER_WIDTH = 128; 42 43 private DrawableBitmap mFuelDrawable; 44 private DrawableBitmap mFuelBackgroundDrawable; 45 private float mFuelPercent; 46 private float mFuelTargetPercent; 47 48 private Texture mFadeTexture; 49 private float mFadeStartTime; 50 private float mFadeDuration; 51 private boolean mFadeIn; 52 private boolean mFading; 53 private int mFadePendingEventType; 54 private int mFadePendingEventIndex; 55 56 private DrawableBitmap mFlyButtonEnabledDrawable; 57 private DrawableBitmap mFlyButtonDisabledDrawable; 58 private DrawableBitmap mFlyButtonDepressedDrawable; 59 60 private DrawableBitmap mStompButtonEnabledDrawable; 61 private DrawableBitmap mStompButtonDepressedDrawable; 62 63 private DrawableBitmap mMovementSliderBaseDrawable; 64 private DrawableBitmap mMovementSliderButtonDrawable; 65 private DrawableBitmap mMovementSliderButtonDepressedDrawable; 66 67 68 private Vector2 mFlyButtonLocation; 69 private boolean mFlyButtonActive; 70 private boolean mFlyButtonPressed; 71 72 private Vector2 mStompButtonLocation; 73 private boolean mStompButtonPressed; 74 75 private Vector2 mMovementSliderBaseLocation; 76 private Vector2 mMovementSliderButtonLocation; 77 private boolean mMovementSliderMode; 78 private boolean mMovementSliderButtonPressed; 79 80 private DrawableBitmap mRubyDrawable; 81 private DrawableBitmap mCoinDrawable; 82 83 private int mCoinCount; 84 private int mRubyCount; 85 private Vector2 mCoinLocation; 86 private Vector2 mRubyLocation; 87 private int[] mCoinDigits; 88 private int[] mRubyDigits; 89 private boolean mCoinDigitsChanged; 90 private boolean mRubyDigitsChanged; 91 92 private int mFPS; 93 private Vector2 mFPSLocation; 94 private int[] mFPSDigits; 95 private boolean mFPSDigitsChanged; 96 private boolean mShowFPS; 97 98 private DrawableBitmap[] mDigitDrawables; 99 private DrawableBitmap mXDrawable; 100 101 102 public HudSystem() { 103 super(); 104 mFlyButtonLocation = new Vector2(); 105 mStompButtonLocation = new Vector2(); 106 mCoinLocation = new Vector2(); 107 mRubyLocation = new Vector2(); 108 mFPSLocation = new Vector2(); 109 mDigitDrawables = new DrawableBitmap[10]; 110 mCoinDigits = new int[MAX_DIGITS]; 111 mRubyDigits = new int[MAX_DIGITS]; 112 mFPSDigits = new int[MAX_DIGITS]; 113 mMovementSliderBaseLocation = new Vector2(); 114 mMovementSliderButtonLocation = new Vector2(); 115 116 reset(); 117 } 118 119 @Override 120 public void reset() { 121 mFuelDrawable = null; 122 mFadeTexture = null; 123 mFuelPercent = 1.0f; 124 mFuelTargetPercent = 1.0f; 125 mFading = false; 126 mFlyButtonDisabledDrawable = null; 127 mFlyButtonEnabledDrawable = null; 128 mFlyButtonDepressedDrawable = null; 129 mFlyButtonLocation.set(FLY_BUTTON_X, FLY_BUTTON_Y); 130 mFlyButtonActive = true; 131 mFlyButtonPressed = false; 132 mStompButtonEnabledDrawable = null; 133 mStompButtonDepressedDrawable = null; 134 mStompButtonLocation.set(STOMP_BUTTON_X, STOMP_BUTTON_Y); 135 mStompButtonPressed = false; 136 mCoinCount = 0; 137 mRubyCount = 0; 138 mCoinDigits[0] = 0; 139 mCoinDigits[1] = -1; 140 mRubyDigits[0] = 0; 141 mRubyDigits[1] = -1; 142 mCoinDigitsChanged = true; 143 mRubyDigitsChanged = true; 144 mFPS = 0; 145 mFPSDigits[0] = 0; 146 mFPSDigits[1] = -1; 147 mFPSDigitsChanged = true; 148 mShowFPS = false; 149 for (int x = 0; x < mDigitDrawables.length; x++) { 150 mDigitDrawables[x] = null; 151 } 152 mXDrawable = null; 153 mFadePendingEventType = GameFlowEvent.EVENT_INVALID; 154 mFadePendingEventIndex = 0; 155 156 mMovementSliderBaseDrawable = null; 157 mMovementSliderButtonDrawable = null; 158 mMovementSliderButtonDepressedDrawable = null; 159 mMovementSliderBaseLocation.set(MOVEMENT_SLIDER_BASE_X, MOVEMENT_SLIDER_BASE_Y); 160 mMovementSliderButtonLocation.set(MOVEMENT_SLIDER_BUTTON_X, MOVEMENT_SLIDER_BUTTON_Y); 161 mMovementSliderMode = false; 162 mMovementSliderButtonPressed = false; 163 } 164 165 public void setFuelPercent(float percent) { 166 mFuelTargetPercent = percent; 167 } 168 169 public void setFuelDrawable(DrawableBitmap fuel, DrawableBitmap background) { 170 mFuelDrawable = fuel; 171 mFuelBackgroundDrawable = background; 172 } 173 174 public void setFadeTexture(Texture texture) { 175 mFadeTexture = texture; 176 } 177 178 public void setButtonDrawables(DrawableBitmap disabled, DrawableBitmap enabled, DrawableBitmap depressed, 179 DrawableBitmap stompEnabled, DrawableBitmap stompDepressed, 180 DrawableBitmap sliderBase, DrawableBitmap sliderButton, DrawableBitmap sliderDepressed) { 181 mFlyButtonDisabledDrawable = disabled; 182 mFlyButtonEnabledDrawable = enabled; 183 mFlyButtonDepressedDrawable = depressed; 184 mStompButtonEnabledDrawable = stompEnabled; 185 mStompButtonDepressedDrawable = stompDepressed; 186 mMovementSliderBaseDrawable = sliderBase; 187 mMovementSliderButtonDrawable = sliderButton; 188 mMovementSliderButtonDepressedDrawable = sliderDepressed; 189 } 190 191 public void setDigitDrawables(DrawableBitmap[] digits, DrawableBitmap xMark) { 192 mXDrawable = xMark; 193 for (int x = 0; x < mDigitDrawables.length && x < digits.length; x++) { 194 mDigitDrawables[x] = digits[x]; 195 } 196 } 197 198 public void setCollectableDrawables(DrawableBitmap coin, DrawableBitmap ruby) { 199 mCoinDrawable = coin; 200 mRubyDrawable = ruby; 201 } 202 203 public void setButtonState(boolean pressed, boolean attackPressed, boolean sliderPressed) { 204 mFlyButtonPressed = pressed; 205 mStompButtonPressed = attackPressed; 206 mMovementSliderButtonPressed = sliderPressed; 207 } 208 209 public void startFade(boolean in, float duration) { 210 mFadeStartTime = sSystemRegistry.timeSystem.getRealTime(); 211 mFadeDuration = duration; 212 mFadeIn = in; 213 mFading = true; 214 } 215 216 public void clearFade() { 217 mFading = false; 218 } 219 220 public boolean isFading() { 221 return mFading; 222 } 223 224 public void updateInventory(InventoryComponent.UpdateRecord newInventory) { 225 mCoinDigitsChanged = (mCoinCount != newInventory.coinCount); 226 mRubyDigitsChanged = (mRubyCount != newInventory.rubyCount); 227 228 mCoinCount = newInventory.coinCount; 229 mRubyCount = newInventory.rubyCount; 230 } 231 232 public void setFPS(int fps) { 233 mFPSDigitsChanged = (fps != mFPS); 234 mFPS = fps; 235 } 236 237 public void setShowFPS(boolean show) { 238 mShowFPS = show; 239 } 240 241 public void setMovementSliderMode(boolean sliderOn) { 242 mMovementSliderMode = sliderOn; 243 if (sliderOn) { 244 ContextParameters params = sSystemRegistry.contextParameters; 245 mFlyButtonLocation.set(params.gameWidth - FLY_BUTTON_WIDTH - FLY_BUTTON_X, FLY_BUTTON_Y); 246 mStompButtonLocation.set(params.gameWidth - STOMP_BUTTON_WIDTH - STOMP_BUTTON_X, STOMP_BUTTON_Y); 247 } else { 248 mFlyButtonLocation.set(FLY_BUTTON_X, FLY_BUTTON_Y); 249 mStompButtonLocation.set(STOMP_BUTTON_X, STOMP_BUTTON_Y); 250 } 251 } 252 public void setMovementSliderOffset(float offset) { 253 mMovementSliderButtonLocation.set(MOVEMENT_SLIDER_BUTTON_X + (offset * (MOVEMENT_SLIDER_WIDTH / 2.0f)), MOVEMENT_SLIDER_BUTTON_Y); 254 } 255 256 @Override 257 public void update(float timeDelta, BaseObject parent) { 258 final RenderSystem render = sSystemRegistry.renderSystem; 259 final VectorPool pool = sSystemRegistry.vectorPool; 260 final ContextParameters params = sSystemRegistry.contextParameters; 261 final DrawableFactory factory = sSystemRegistry.drawableFactory; 262 263 final GameObjectManager manager = sSystemRegistry.gameObjectManager; 264 265 if (manager != null && manager.getPlayer() != null) { 266 // Only draw player-specific HUD elements when there's a player. 267 if (mFuelDrawable != null && mFuelBackgroundDrawable != null 268 && render != null && pool != null && factory != null && params != null) { 269 if (mFuelPercent < mFuelTargetPercent) { 270 mFuelPercent += (FUEL_INCREASE_BAR_SPEED * timeDelta); 271 if (mFuelPercent > mFuelTargetPercent) { 272 mFuelPercent = mFuelTargetPercent; 273 } 274 } else if (mFuelPercent > mFuelTargetPercent) { 275 mFuelPercent -= (FUEL_DECREASE_BAR_SPEED * timeDelta); 276 if (mFuelPercent < mFuelTargetPercent) { 277 mFuelPercent = mFuelTargetPercent; 278 } 279 } 280 281 if (mFuelBackgroundDrawable.getWidth() == 0) { 282 // first time init 283 Texture tex = mFuelDrawable.getTexture(); 284 mFuelDrawable.resize(tex.width, tex.height); 285 Texture backgroundTex = mFuelBackgroundDrawable.getTexture(); 286 mFuelBackgroundDrawable.resize(backgroundTex.width, backgroundTex.height); 287 } 288 289 final int height = mFuelDrawable.getHeight(); 290 291 292 Vector2 location = pool.allocate(); 293 location.set(FUEL_BAR_EDGE_PADDING, 294 params.gameHeight - height - FUEL_BAR_EDGE_PADDING); 295 render.scheduleForDraw(mFuelBackgroundDrawable, location, SortConstants.HUD, false); 296 location.x += 2; 297 location.y += 2; 298 final int barWidth = (int)((100 - 4) * mFuelPercent); 299 if (barWidth >= 1) { 300 DrawableBitmap bitmap = factory.allocateDrawableBitmap(); 301 if (bitmap != null) { 302 bitmap.resize(barWidth, mFuelDrawable.getHeight()); 303 bitmap.setTexture(mFuelDrawable.getTexture()); 304 render.scheduleForDraw(bitmap, location, SortConstants.HUD + 1, false); 305 } 306 } 307 308 pool.release(location); 309 } 310 311 if (mFlyButtonDisabledDrawable != null && mFlyButtonEnabledDrawable != null 312 && mFlyButtonDepressedDrawable != null) { 313 314 DrawableBitmap bitmap = mFlyButtonEnabledDrawable; 315 if (mFlyButtonActive && mFlyButtonPressed) { 316 bitmap = mFlyButtonDepressedDrawable; 317 } else if (!mFlyButtonActive) { 318 bitmap = mFlyButtonDisabledDrawable; 319 } 320 321 if (bitmap.getWidth() == 0) { 322 // first time init 323 Texture tex = bitmap.getTexture(); 324 bitmap.resize(tex.width, tex.height); 325 } 326 327 render.scheduleForDraw(bitmap, mFlyButtonLocation, SortConstants.HUD, false); 328 } 329 330 331 332 if (mStompButtonEnabledDrawable != null && mStompButtonDepressedDrawable != null) { 333 334 DrawableBitmap bitmap = mStompButtonEnabledDrawable; 335 if (mStompButtonPressed) { 336 bitmap = mStompButtonDepressedDrawable; 337 } 338 339 if (bitmap.getWidth() == 0) { 340 // first time init 341 Texture tex = bitmap.getTexture(); 342 bitmap.resize(tex.width, tex.height); 343 bitmap.setWidth((int)(tex.width * STOMP_BUTTON_SCALE)); 344 bitmap.setHeight((int)(tex.height * STOMP_BUTTON_SCALE)); 345 } 346 347 render.scheduleForDraw(bitmap, mStompButtonLocation, SortConstants.HUD, false); 348 } 349 350 if (mMovementSliderMode && 351 mMovementSliderBaseDrawable != null && mMovementSliderButtonDrawable != null) { 352 353 if (mMovementSliderBaseDrawable.getWidth() == 0) { 354 // first time init 355 Texture tex = mMovementSliderBaseDrawable.getTexture(); 356 mMovementSliderBaseDrawable.resize(tex.width, tex.height); 357 } 358 359 if (mMovementSliderButtonDrawable.getWidth() == 0) { 360 // first time init 361 Texture tex = mMovementSliderButtonDrawable.getTexture(); 362 mMovementSliderButtonDrawable.resize(tex.width, tex.height); 363 } 364 365 if (mMovementSliderButtonDepressedDrawable.getWidth() == 0) { 366 // first time init 367 Texture tex = mMovementSliderButtonDepressedDrawable.getTexture(); 368 mMovementSliderButtonDepressedDrawable.resize(tex.width, tex.height); 369 } 370 371 DrawableBitmap bitmap = mMovementSliderButtonDrawable; 372 373 if (mMovementSliderButtonPressed) { 374 bitmap = mMovementSliderButtonDepressedDrawable; 375 } 376 377 render.scheduleForDraw(mMovementSliderBaseDrawable, mMovementSliderBaseLocation, SortConstants.HUD, false); 378 render.scheduleForDraw(bitmap, mMovementSliderButtonLocation, SortConstants.HUD + 1, false); 379 380 } 381 382 383 if (mCoinDrawable != null) { 384 if (mCoinDrawable.getWidth() == 0) { 385 // first time init 386 Texture tex = mCoinDrawable.getTexture(); 387 mCoinDrawable.resize(tex.width, tex.height); 388 mCoinLocation.x = (params.gameWidth / 2.0f) - tex.width / 2.0f; 389 mCoinLocation.y = params.gameHeight - tex.height - COLLECTABLE_EDGE_PADDING; 390 } 391 392 render.scheduleForDraw(mCoinDrawable, mCoinLocation, SortConstants.HUD, false); 393 if (mCoinDigitsChanged) { 394 intToDigitArray(mCoinCount, mCoinDigits); 395 mCoinDigitsChanged = false; 396 } 397 final float offset = mCoinDrawable.getWidth() * 0.75f; 398 mCoinLocation.x += offset; 399 drawNumber(mCoinLocation, mCoinDigits, true); 400 mCoinLocation.x -= offset; 401 } 402 403 if (mRubyDrawable != null) { 404 if (mRubyDrawable.getWidth() == 0) { 405 // first time init 406 Texture tex = mRubyDrawable.getTexture(); 407 mRubyDrawable.resize(tex.width, tex.height); 408 mRubyLocation.x = (params.gameWidth / 2.0f) + 100.0f; 409 mRubyLocation.y = params.gameHeight - tex.height - COLLECTABLE_EDGE_PADDING; 410 } 411 render.scheduleForDraw(mRubyDrawable, mRubyLocation, SortConstants.HUD, false); 412 if (mRubyDigitsChanged) { 413 intToDigitArray(mRubyCount, mRubyDigits); 414 mRubyDigitsChanged = false; 415 } 416 final float offset = mRubyDrawable.getWidth() * 0.75f; 417 mRubyLocation.x += offset; 418 drawNumber(mRubyLocation, mRubyDigits, true); 419 mRubyLocation.x -= offset; 420 } 421 } 422 423 if (mShowFPS) { 424 if (mFPSDigitsChanged) { 425 int count = intToDigitArray(mFPS, mFPSDigits); 426 mFPSDigitsChanged = false; 427 mFPSLocation.set(params.gameWidth - 10.0f - ((count + 1) * (mDigitDrawables[0].getWidth() / 2.0f)), 10.0f); 428 429 } 430 drawNumber(mFPSLocation, mFPSDigits, false); 431 } 432 433 if (mFading && factory != null) { 434 435 final float time = sSystemRegistry.timeSystem.getRealTime(); 436 final float fadeDelta = (time - mFadeStartTime); 437 438 float percentComplete = 1.0f; 439 if (fadeDelta < mFadeDuration) { 440 percentComplete = fadeDelta / mFadeDuration; 441 } else if (mFadeIn) { 442 // We've faded in. Turn fading off. 443 mFading = false; 444 } 445 446 if (percentComplete < 1.0f || !mFadeIn) { 447 float opacityValue = percentComplete; 448 if (mFadeIn) { 449 opacityValue = 1.0f - percentComplete; 450 } 451 452 DrawableBitmap bitmap = factory.allocateDrawableBitmap(); 453 if (bitmap != null) { 454 bitmap.setWidth(params.gameWidth); 455 bitmap.setHeight(params.gameHeight); 456 bitmap.setTexture(mFadeTexture); 457 bitmap.setCrop(0, mFadeTexture.height, mFadeTexture.width, mFadeTexture.height); 458 bitmap.setOpacity(opacityValue); 459 render.scheduleForDraw(bitmap, Vector2.ZERO, SortConstants.FADE, false); 460 } 461 } 462 463 if (percentComplete >= 1.0f && mFadePendingEventType != GameFlowEvent.EVENT_INVALID) { 464 LevelSystem level = sSystemRegistry.levelSystem; 465 if (level != null) { 466 level.sendGameEvent(mFadePendingEventType, mFadePendingEventIndex, false); 467 mFadePendingEventType = GameFlowEvent.EVENT_INVALID; 468 mFadePendingEventIndex = 0; 469 } 470 } 471 } 472 } 473 474 private void drawNumber(Vector2 location, int[] digits, boolean drawX) { 475 final RenderSystem render = sSystemRegistry.renderSystem; 476 477 if (mDigitDrawables[0].getWidth() == 0) { 478 // first time init 479 for (int x = 0; x < mDigitDrawables.length; x++) { 480 Texture tex = mDigitDrawables[x].getTexture(); 481 mDigitDrawables[x].resize(tex.width, tex.height); 482 } 483 } 484 485 if (mXDrawable.getWidth() == 0) { 486 // first time init 487 Texture tex = mXDrawable.getTexture(); 488 mXDrawable.resize(tex.width, tex.height); 489 } 490 491 final float characterWidth = mDigitDrawables[0].getWidth() / 2.0f; 492 float offset = 0.0f; 493 494 if (mXDrawable != null && drawX) { 495 render.scheduleForDraw(mXDrawable, location, SortConstants.HUD, false); 496 location.x += characterWidth; 497 offset += characterWidth; 498 } 499 500 for (int x = 0; x < digits.length && digits[x] != -1; x++) { 501 int index = digits[x]; 502 DrawableBitmap digit = mDigitDrawables[index]; 503 if (digit != null) { 504 render.scheduleForDraw(digit, location, SortConstants.HUD, false); 505 location.x += characterWidth; 506 offset += characterWidth; 507 } 508 } 509 510 location.x -= offset; 511 512 513 } 514 515 public int intToDigitArray(int value, int[] digits) { 516 int characterCount = 1; 517 if (value >= 1000) { 518 characterCount = 4; 519 } else if (value >= 100) { 520 characterCount = 3; 521 } else if (value >= 10) { 522 characterCount = 2; 523 } 524 525 int remainingValue = value; 526 int count = 0; 527 do { 528 int index = remainingValue != 0 ? remainingValue % 10 : 0; 529 remainingValue /= 10; 530 digits[characterCount - 1 - count] = index; 531 count++; 532 } while (remainingValue > 0 && count < digits.length); 533 534 if (count < digits.length) { 535 digits[count] = -1; 536 } 537 return characterCount; 538 } 539 540 public void sendGameEventOnFadeComplete(int eventType, int eventIndex) { 541 mFadePendingEventType = eventType; 542 mFadePendingEventIndex = eventIndex; 543 } 544 545 546} 547