001package com.fs.starfarer.api.impl.campaign.eventide; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.Iterator; 006import java.util.List; 007 008import org.lwjgl.input.Keyboard; 009import org.lwjgl.opengl.GL11; 010import org.lwjgl.util.vector.Vector2f; 011 012import com.fs.starfarer.api.Global; 013import com.fs.starfarer.api.campaign.BaseCustomUIPanelPlugin; 014import com.fs.starfarer.api.campaign.CustomVisualDialogDelegate.DialogCallbacks; 015import com.fs.starfarer.api.campaign.InteractionDialogAPI; 016import com.fs.starfarer.api.input.InputEventAPI; 017import com.fs.starfarer.api.ui.CustomPanelAPI; 018import com.fs.starfarer.api.ui.PositionAPI; 019import com.fs.starfarer.api.util.FaderUtil; 020import com.fs.starfarer.api.util.Misc; 021 022public class DuelPanel extends BaseCustomUIPanelPlugin { 023 024 public static enum AttackResult { 025 NO_HIT, 026 BLOCK, 027 HIT, 028 } 029 030 public static float SOUND_LOC_MULT = 0.25f; 031 032 public static boolean DEBUG = true; 033 034 035 protected InteractionDialogAPI dialog; 036 protected DialogCallbacks callbacks; 037 protected CustomPanelAPI panel; 038 protected PositionAPI p; 039 040 protected Actor player; 041 protected Actor enemy; 042 protected List<QuadParticles> particles = new ArrayList<QuadParticles>(); 043 protected float floorLevel; 044 protected float leftBorder, rightBorder; 045 protected float viewAreaWidth = 512; 046 protected DuelEnemyAI ai; 047 protected DuelBackground background; 048 049 protected boolean tutorialMode = false; 050 protected DuelTutorialPanel prompt; 051 052 protected FaderUtil blinker = new FaderUtil(0f, 0.25f, 0.25f, true, true); 053 protected String ambienceLoopId = null; 054 055 public static DuelPanel createDefault(boolean playerSkilled, boolean enemySkilled, String ambienceLoopId) { 056 Actions.initActions(); 057 //playerSkilled = false; 058 059 Actor player = createActor(Actions.TEX, playerSkilled); 060// player.actionRemap1.put(Actions.IDLE, Actions.IDLE_HIGH); 061// player.actionRemap1.put(Actions.MOVE_FORWARD, Actions.MOVE_FORWARD_HIGH); 062// player.actionRemap1.put(Actions.MOVE_BACK, Actions.MOVE_BACK_HIGH); 063// player.actionRemap1.put(Actions.ATTACK, Actions.ATTACK_HIGH); 064// player.actionRemap1.put(Actions.BLOCK, Actions.BLOCK_LOW); 065 //player.actionRemap1.put(Actions.ATTACK, Actions.RIPOSTE_HIGH); 066 067// player.actionRemap1.put(Actions.BLOCK, Actions.ATTACK_RECOVERY); 068// player.actionRemap1.put(Actions.BLOCK, Actions.ATTACK_HIGH_RECOVERY); 069 //player.actionRemap1.put(Actions.ATTACK, Actions.ATTACK_HIGH); 070 //player.actionRemap1.put(Actions.ATTACK, Actions.ATTACK_RECOVERY); 071 //player.actionRemap1.put(Actions.BLOCK, Actions.BLOCK_LOW); 072 //player.actionRemap1.put(Actions.BLOCK, Actions.ATTACK_HIGH_RECOVERY); 073// player.actionRemap1.put(Actions.ATTACK, Actions.BLOCK_LOW); 074// player.actionRemap1.put(Actions.BLOCK, Actions.FALL); 075// player.actionRemap1.put(Actions.BLOCK, Actions.BLOCK_LOW); 076 //player.actionRemap1.put(Actions.ATTACK, Actions.ATTACK_HIGH); 077 //player.actionRemap1.put(Actions.ATTACK, Actions.ATTACK_RECOVERY); 078// player.actionRemap1.put(Actions.RIPOSTE, Actions.RIPOSTE_HIGH); 079// player.actionRemap1.put(Actions.IDLE, Actions.IDLE_HIGH); 080// player.actionRemap1.put(Actions.MOVE_FORWARD, Actions.MOVE_FORWARD_HIGH); 081 082 Actor enemy = createActor(Actions.TEX, enemySkilled); 083 enemy.actionRemap1.put(Actions.IDLE, Actions.IDLE_HIGH); 084 enemy.actionRemap1.put(Actions.MOVE_FORWARD, Actions.MOVE_FORWARD_HIGH); 085 enemy.actionRemap1.put(Actions.MOVE_BACK, Actions.MOVE_BACK_HIGH); 086 enemy.actionRemap1.put(Actions.ATTACK, Actions.ATTACK_HIGH); 087 enemy.actionRemap1.put(Actions.ATTACK_RECOVERY, Actions.ATTACK_HIGH_RECOVERY); 088 enemy.actionRemap1.put(Actions.RIPOSTE, Actions.RIPOSTE_HIGH); 089 enemy.actionRemap1.put(Actions.BLOCK, Actions.BLOCK_LOW); 090 091 DuelBackground bg = new EventideDuelBackground(); 092 DuelPanel panel = new DuelPanel(player, enemy, new DuelEnemyAIImpl(), bg); 093 //panel = new DuelPanel(player, enemy, null, bg); // this turns off enemy AI!!! 094// DO_CYCLE = true; // do block - attack - recover cycle automatically w/ no interaction 095// DO_CYCLE_HIGH = true; // do the enemy cycle, on the player actor 096 DO_CYCLE = false; 097 DO_CYCLE_HIGH = false; 098 099 panel.ambienceLoopId = ambienceLoopId; 100 101 return panel; 102 } 103 104 public static DuelPanel createTutorial(boolean playerSkilled, String ambienceLoopId) { 105 Actions.initActions(); 106 107 Actor player = createActor(Actions.TEX, playerSkilled); 108 109 Actor enemy = createActor(Actions.TEX, true); 110 111 DuelBackground bg = new EventideDuelBackground(); 112 DuelPanel panel = new DuelPanel(player, enemy, null, bg); 113 panel.tutorialMode = true; 114 panel.ambienceLoopId = ambienceLoopId; 115 return panel; 116 } 117 118 public static Actor createActor(String tex, boolean skilled) { 119 Actor actor = new Actor(tex); 120 int health = 5; 121 if (!skilled) { 122 health = 3; 123 actor.actionRemap2.put(Actions.RIPOSTE, Actions.ATTACK); 124 actor.actionRemap2.put(Actions.RIPOSTE_HIGH, Actions.ATTACK_HIGH); 125// float speedMult = 0.8f; 126// actor.actionSpeedMult.put(Actions.ATTACK, speedMult); 127// actor.actionSpeedMult.put(Actions.ATTACK_HIGH, speedMult); 128// actor.actionSpeedMult.put(Actions.BLOCK, speedMult); 129// actor.actionSpeedMult.put(Actions.BLOCK_LOW, speedMult); 130 } 131 132 //health = 15; 133 actor.maxHealth = health; 134 actor.health = health; 135 return actor; 136 } 137 138 139 public DuelPanel(Actor player, Actor enemy, DuelEnemyAI ai, DuelBackground background) { 140 this.player = player; 141 this.enemy = enemy; 142 this.ai = ai; 143 this.background = background; 144 } 145 146 public void init(CustomPanelAPI panel, DialogCallbacks callbacks, InteractionDialogAPI dialog) { 147 this.panel = panel; 148 this.callbacks = callbacks; 149 this.dialog = dialog; 150 151// Actions.initActions(); 152// player = new Actor(); 153// enemy = new Actor(); 154 155 blinker.fadeIn(); 156 157 if (tutorialMode) { 158 prompt = new DuelTutorialPanel(); 159 CustomPanelAPI p = panel.createCustomPanel(450, 120, prompt); 160 panel.addComponent(p).inTL(10, 30); 161 prompt.init(p, callbacks, dialog); 162 } 163 } 164 165 public CustomPanelAPI getPanel() { 166 return panel; 167 } 168 169 public PositionAPI getPosition() { 170 return p; 171 } 172 173 public float getFloorLevel() { 174 return floorLevel; 175 } 176 177 protected float desiredViewCenterX = 0f; 178 protected float viewCenterX = 0f; 179 public void positionChanged(PositionAPI position) { 180 this.p = position; 181 182 if (player != null) { 183 float cx = p.getCenterX(); 184 float cy = p.getCenterY(); 185 186 187 float charHeight = getCharacterHeight(); 188 189 floorLevel = (int)(cy - charHeight/2f); 190 leftBorder = (int)(cx - p.getWidth() / 2f + 50); 191 rightBorder = (int)(cx + p.getWidth() / 2f - 50); 192 193 float width = background.getStageWidth(); 194 float extra = width - p.getWidth(); 195 196 leftBorder -= (int)(extra/2f); 197 rightBorder += (int)(extra/2f); 198 199 float down = 5f; 200 player.loc.set(cx - 200, floorLevel + charHeight/2f - down); 201 player.facing = 1; 202 enemy.loc.set(cx + 200, floorLevel + charHeight/2f - down); 203 enemy.facing = -1; 204 205 viewAreaWidth = 200f; 206 desiredViewCenterX = player.loc.x + viewAreaWidth / 2f - 50f; 207 viewCenterX = desiredViewCenterX; 208 } 209 } 210 211 public void render(float alphaMult) { 212 213 } 214 215 public void renderBelow(float alphaMult) { 216 //if (tutorialMode) return; 217 if (p == null) return; 218 219 float x = p.getX(); 220 float y = p.getY(); 221 float cx = p.getCenterX(); 222 float cy = p.getCenterY(); 223 float w = p.getWidth(); 224 float h = p.getHeight(); 225 226 GL11.glDisable(GL11.GL_TEXTURE_2D); 227 GL11.glEnable(GL11.GL_SCISSOR_TEST); 228 229 float s = Global.getSettings().getScreenScaleMult(); 230 GL11.glScissor((int)(x * s), (int)(y * s), (int)(w * s), (int)(h * s)); 231 232// SpriteAPI s = Global.getSettings().getSprite(TEX); 233// s.render(x, y + h - s.getHeight()); 234 DEBUG = true; 235 DEBUG = false; 236 237 if (DEBUG && false) { 238 float w2 = player.currAction.curr.width * player.currAction.anim.scale; 239 float h2 = player.currAction.curr.height * player.currAction.anim.scale; 240 Misc.renderQuad(player.loc.x - w2/2f, player.loc.y - h2/2f, w2, h2, new Color(50,50,50,255), alphaMult); 241 } 242 243 float centerX = player.loc.x - (desiredViewCenterX - cx); 244 if (player.currAction != null && player.currAction.anim != null && 245 player.currAction.anim.hasAttackFrames()) { 246 centerX = cx; 247 } else if (player.currAction != null && player.currAction.anim != null && player.currAction.curr != null) { 248 if (!player.currAction.curr.hittableArea.isEmpty()) { 249 HitArea area = player.currAction.curr.hittableArea.get(0); 250 centerX = player.loc.x + (area.x + area.w / 2f) - (desiredViewCenterX - cx); 251 } 252 } 253 float leftX = cx - viewAreaWidth / 2f; 254 float rightX = cx + viewAreaWidth / 2f; 255 256 //System.out.println("[" + leftX + ", " + rightX + "] centerX: " + centerX + ", prev: " + prevOffsetX); 257 258 if (centerX < leftX) { 259 desiredViewCenterX -= leftX - centerX + 100f; 260 } else if (centerX > rightX) { 261 desiredViewCenterX += centerX - rightX + 100f; 262 } 263 float minVX = leftBorder + w / 2f - 50f; 264 float maxVX = rightBorder - w / 2f + 50f; 265 if (desiredViewCenterX < minVX) desiredViewCenterX = minVX; 266 if (desiredViewCenterX > maxVX) desiredViewCenterX = maxVX; 267 268 float offsetX = -(viewCenterX - cx); 269 270 if (background == null) { 271 GL11.glDisable(GL11.GL_TEXTURE_2D); 272 Misc.renderQuad(x, y, w, h, Color.black, alphaMult); 273 Misc.renderQuad(x, y, w, h, new Color(50,50,50,255), alphaMult); 274 } else { 275 background.render(this, offsetX, 0f, alphaMult); 276 } 277 278// GL11.glDisable(GL11.GL_TEXTURE_2D); 279// Misc.renderQuad(leftX, y, 5, h, Color.black, alphaMult); 280// Misc.renderQuad(rightX, y, 5, h, Color.black, alphaMult); 281// Misc.renderQuad(centerX, y, 5, h, Color.green, alphaMult); 282 283 GL11.glPushMatrix(); 284 GL11.glTranslatef(offsetX, 0, 0); 285// Misc.renderQuad(leftBorder, y, 5, h, Color.red, alphaMult); 286// Misc.renderQuad(rightBorder - 5, y, 5, h, Color.red, alphaMult); 287 288 for (QuadParticles p : particles) { 289 p.render(alphaMult, true); 290 } 291 292 if (enemy.health > 0 && player.health > 0) { 293 boolean playerAttack = player.currAction != null && player.currAction.anim != null && 294 player.currAction.anim.hasAttackFrames(); 295 if (playerAttack) { 296 if (!tutorialMode) enemy.render(alphaMult); 297 player.render(alphaMult); 298 } else { 299 player.render(alphaMult); 300 if (!tutorialMode) enemy.render(alphaMult); 301 } 302 } else if (enemy.health > 0) { 303 player.render(alphaMult); 304 if (!tutorialMode) enemy.render(alphaMult); 305 } else { 306 if (!tutorialMode) enemy.render(alphaMult); 307 player.render(alphaMult); 308 } 309 310 for (QuadParticles p : particles) { 311 p.render(alphaMult, false); 312 } 313 GL11.glPopMatrix(); 314 315 if (background != null) { 316 background.renderForeground(this, offsetX, 0f, alphaMult); 317 } 318 319 renderHealth(alphaMult); 320 321 if (ai != null) { 322 ai.render(alphaMult); 323 } 324 325 GL11.glDisable(GL11.GL_SCISSOR_TEST); 326 } 327 328 public void renderHealth(float alphaMult) { 329 float x = p.getX(); 330 float y = p.getY(); 331 float w = p.getWidth(); 332 float h = p.getHeight(); 333 334 float pipW = 20f; 335 float pipH = 20f / 1.6f; 336 float pad = 3f; 337 float opad = 10f; 338 339 GL11.glDisable(GL11.GL_TEXTURE_2D); 340 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 341 342 for (int i = 0; i < player.maxHealth; i++) { 343 boolean filled = i < player.health; 344 if (player.health == 1 && i == 0 && blinker.isFadingIn()) filled = false; 345 renderPip(x + opad + (pipW + pad) * i, y + h - opad - pipH, 346 pipW, pipH, Misc.getPositiveHighlightColor(), alphaMult, 347 filled); 348 } 349 350 if (!tutorialMode) { 351 for (int i = enemy.maxHealth - 1; i >= 0; i--) { 352 renderPip(x + w - opad - (pipW + pad) * (i + 1), y + h - opad - pipH, 353 pipW, pipH, Misc.getNegativeHighlightColor(), alphaMult, 354 i < enemy.health); 355 } 356 } 357 } 358 359 public void renderPip(float x, float y, float w, float h, Color c, float a, boolean filled) { 360 if (filled) { 361 Misc.renderQuad(x, y, w, h, c, a); 362 } else { 363 Misc.renderQuad(x, y, w, 1, c, a); 364 Misc.renderQuad(x, y + h - 1, w, 1, c, a); 365 Misc.renderQuad(x, y + 1, 1, h - 2, c, a); 366 Misc.renderQuad(x + w - 1, y + 1, 1, h - 2, c, a); 367 } 368 Misc.renderQuad(x + w, y - 1, 1, h, Color.black, a); 369 Misc.renderQuad(x + 1, y - 1, w - 1, 1, Color.black, a); 370 371 } 372 373 public static boolean DO_CYCLE = false; 374 public static boolean DO_CYCLE_HIGH = false; 375 public int cycleIndex = -1; 376 public static List<String> cycle = new ArrayList<String>(); 377 static { 378 cycle.add(Actions.BLOCK); 379 cycle.add(Actions.ATTACK); 380 cycle.add(Actions.ATTACK_RECOVERY); 381 } 382 public static List<String> cycleHigh = new ArrayList<String>(); 383 static { 384 cycleHigh.add(Actions.BLOCK_LOW); 385 cycleHigh.add(Actions.ATTACK_HIGH); 386 cycleHigh.add(Actions.ATTACK_HIGH_RECOVERY); 387 } 388 389 public void advance(float amount) { 390 if (p == null) return; 391 if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) && Global.getSettings().isDevMode()) { 392 amount *= 0.25f; 393 } 394 if (tutorialMode) { 395 panel.bringComponentToTop(prompt.getPanel()); 396 } 397 //amount *= 0.5f; 398 //System.out.println(player.loc.x); 399 400 viewCenterX = Misc.approach(viewCenterX, desiredViewCenterX, 3f, 1f, amount); 401 //Global.getSoundPlayer().setListenerPosOverrideOneFrame(new Vector2f(viewCenterX * 0f, player.loc.y)); 402 float soundOffset = viewCenterX - player.loc.x; 403 soundOffset *= SOUND_LOC_MULT; 404 Global.getSoundPlayer().setListenerPosOverrideOneFrame( 405 new Vector2f(soundOffset + (player.loc.x + enemy.loc.x)/2f * SOUND_LOC_MULT, player.loc.y)); 406 //System.out.println("VCX " + viewCenterX + ", playerLoc: " + player.loc.x); 407 408 if (ambienceLoopId != null) { 409 Global.getSoundPlayer().playUILoop(ambienceLoopId, 1f, 1f); 410 } 411 412 if (background != null) { 413 background.advance(amount); 414 } 415 416 if (DO_CYCLE || DO_CYCLE_HIGH) { 417 amount *= 1f;// 0.25f; 418 if (player.getActionId() != null && (player.getActionId().equals(Actions.ATTACK) || 419 player.getActionId().equals(Actions.ATTACK_HIGH))) { 420 float timeUntilAttack = 0f; 421 for (CharAnimFrame f : player.currAction.anim.frames) { 422 if (!f.attackArea.isEmpty()) break; 423 timeUntilAttack += f.dur; 424 } 425 //if (player.currAction.framesUntilAttackFrame() <= 1) { 426 if (timeUntilAttack <= player.currAction.progress + 0.05f) { 427 player.currAction = null; 428 } 429 } 430 if (player.getActionId().isEmpty() || player.getActionId().equals(Actions.IDLE) || 431 player.getActionId().equals(Actions.IDLE_HIGH)) { 432 cycleIndex++; 433 if (cycleIndex < 0) cycleIndex = 0; 434 if (DO_CYCLE_HIGH) { 435 cycleIndex = cycleIndex % cycleHigh.size(); 436 player.doAction(cycleHigh.get(cycleIndex), true); 437 } else { 438 cycleIndex = cycleIndex % cycle.size(); 439 player.doAction(cycle.get(cycleIndex), true); 440 } 441 } 442 } 443 444 //amount *= 0.2f; 445 blinker.advance(amount * 1.5f); 446 447 Iterator<QuadParticles> iter = particles.iterator(); 448 while (iter.hasNext()) { 449 QuadParticles curr = iter.next(); 450 curr.advance(amount); 451 if (curr.isDone()) { 452 iter.remove(); 453 } 454 } 455 player.advance(amount); 456 enemy.advance(amount); 457 458 if (!tutorialMode) { 459 if (player.loc.x - 100 > enemy.loc.x) { 460 player.facing = -1f; 461 enemy.facing = 1f; 462 } else if (player.loc.x + 100 < enemy.loc.x){ 463 player.facing = 1f; 464 enemy.facing = -1f; 465 } 466 } 467 468 float minDist = 40; 469 //minDist = 100; 470 if (getDistance() < minDist && player.health > 0 && enemy.health > 0 && !tutorialMode) { 471 player.doAction(Actions.MOVE_BACK, false); 472 enemy.doAction(Actions.MOVE_BACK, false); 473 } else { 474// if (player.loc.x < leftBorder || player.loc.x > rightBorder) { 475// player.doAction(Actions.MOVE_FORWARD, true); 476// } 477// if (enemy.loc.x < leftBorder || enemy.loc.x > rightBorder) { 478// enemy.doAction(Actions.MOVE_FORWARD, true); 479// } 480 if (player.loc.x < leftBorder) { 481 player.loc.x = leftBorder; 482 } 483 if (player.loc.x > rightBorder) { 484 player.loc.x = rightBorder; 485 } 486 if (enemy.loc.x < leftBorder) { 487 enemy.loc.x = leftBorder; 488 } 489 if (enemy.loc.x > rightBorder) { 490 enemy.loc.x = rightBorder; 491 } 492 } 493 494 checkBlocksAndHits(); 495 496 doAI(amount); 497 } 498 499 public float getDistance() { 500 return Math.abs(player.loc.x - enemy.loc.x); 501 } 502 503 protected boolean prevWasAttack; 504 505 public void doAI(float amount) { 506 if (ai != null) { 507 ai.advance(amount, this); 508 } 509// if (enemy.currAction != null && enemy.currAction.anim.action == Actions.IDLE) { 510// //float r = (float) Math.random(); 511// //System.out.println("PICKING " + rand); 512// if (prevWasAttack) { 513// //enemy.doAction(Actions.BLOCK, false); 514// enemy.doAction(Actions.ATTACK, false); 515// prevWasAttack = false; 516// } else { 517// enemy.doAction(Actions.ATTACK, false); 518// prevWasAttack = true; 519// } 520//// if (r < 0.5f && false) { 521//// enemy.doAction(Actions.ATTACK, false); 522//// } else { 523//// enemy.doAction(Actions.BLOCK, false); 524//// } 525// } 526 } 527 528 529 public void processInput(List<InputEventAPI> events) { 530 if (p == null) return; 531 532 for (InputEventAPI event : events) { 533 if (event.isConsumed()) continue; 534 if ((Global.getSettings().isDevMode() || tutorialMode) && 535 event.isKeyDownEvent() && event.getEventValue() == Keyboard.KEY_ESCAPE) { 536 event.consume(); 537 callbacks.dismissDialog(); 538 return; 539 } 540 541 if (event.isKeyDownEvent() && 542 (event.getEventValue() == Keyboard.KEY_UP || 543 event.getEventValue() == Keyboard.KEY_NUMPAD8 || 544 event.getEventValue() == Keyboard.KEY_W)) { 545 event.consume(); 546 player.doAction(Actions.BLOCK, false); 547 if (tutorialMode) prompt.reportAction(Actions.BLOCK); 548 continue; 549 } 550 if (event.isKeyDownEvent() && 551 (event.getEventValue() == Keyboard.KEY_LEFT || 552 event.getEventValue() == Keyboard.KEY_NUMPAD4 || 553 event.getEventValue() == Keyboard.KEY_A)) { 554 event.consume(); 555 if (player.facing > 0) { 556 player.doAction(Actions.MOVE_BACK, false); 557 if (tutorialMode) prompt.reportAction(Actions.MOVE_BACK); 558 } else { 559 player.doAction(Actions.MOVE_FORWARD, false); 560 if (tutorialMode) prompt.reportAction(Actions.MOVE_FORWARD); 561 } 562 continue; 563 } 564 if (event.isKeyDownEvent() && 565 (event.getEventValue() == Keyboard.KEY_RIGHT || 566 event.getEventValue() == Keyboard.KEY_NUMPAD6 || 567 event.getEventValue() == Keyboard.KEY_D)) { 568 event.consume(); 569 if (player.facing > 0) { 570 player.doAction(Actions.MOVE_FORWARD, false); 571 if (tutorialMode) prompt.reportAction(Actions.MOVE_FORWARD); 572 } else { 573 player.doAction(Actions.MOVE_BACK, false); 574 if (tutorialMode) prompt.reportAction(Actions.MOVE_BACK); 575 } 576 continue; 577 } 578 if (event.isKeyDownEvent() && 579 event.getEventValue() == Keyboard.KEY_SPACE) { 580 event.consume(); 581 player.doAction(Actions.ATTACK, false); 582 if (tutorialMode) prompt.reportAction(Actions.ATTACK); 583 continue; 584 } 585 if (Global.getSettings().isDevMode() && event.isKeyDownEvent() && 586 event.getEventValue() == Keyboard.KEY_F) { 587 event.consume(); 588 player.doAction(Actions.FALL, true); 589 590 //wasHit(player, null); 591 //wasHit(enemy, null); 592 continue; 593 } 594 if (event.isKeyDownEvent() && 595 event.getEventValue() == Keyboard.KEY_G) { 596 event.consume(); 597 addSparks(player, enemy); 598 continue; 599 } 600 } 601 } 602 603 public void checkBlocksAndHits() { 604 if (tutorialMode) return; 605 606 AttackResult playerAttackResult = checkAttackVsDefense(player, enemy); 607 AttackResult enemyAttackResult = checkAttackVsDefense(enemy, player); 608 609 if (playerAttackResult == AttackResult.HIT && enemyAttackResult == AttackResult.HIT) { 610 if ((float) Math.random() < 0.5f) { 611 playerAttackResult = AttackResult.NO_HIT; 612 } else { 613 enemyAttackResult = AttackResult.NO_HIT; 614 } 615 } 616 617 if (playerAttackResult == AttackResult.BLOCK) { 618 addSparks(player, enemy); 619 player.currAction.wasBlocked = true; 620 player.currAction.undoLastMove(); 621 enemy.currAction.performedBlock = true; 622 player.doAction(Actions.ATTACK_RECOVERY, true); 623 //enemy.endCurrentAnimation(); 624 } else if (playerAttackResult == AttackResult.HIT) { 625 wasHit(enemy, player); 626 player.currAction.scoredHit = true; 627 } 628 629 if (enemyAttackResult == AttackResult.BLOCK) { 630 addSparks(enemy, player); 631 enemy.currAction.wasBlocked = true; 632 enemy.currAction.undoLastMove(); 633 player.currAction.performedBlock = true; 634 enemy.doAction(Actions.ATTACK_RECOVERY, true); 635 //player.endCurrentAnimation(); 636 } else if (enemyAttackResult == AttackResult.HIT) { 637 wasHit(player, enemy); 638 enemy.currAction.scoredHit = true; 639 } 640 } 641 642 public AttackResult checkAttackVsDefense(Actor attacker, Actor defender) { 643 AnimAction a = attacker.currAction; 644 AnimAction d = defender.currAction; 645 if (a.curr == null || a.curr.attackArea.isEmpty()) return AttackResult.NO_HIT; 646 if (d.curr == null || d.curr.hittableArea.isEmpty()) return AttackResult.NO_HIT; 647 648 if (a.wasBlocked) return AttackResult.NO_HIT; 649 if (a.scoredHit) return AttackResult.NO_HIT; 650 651 for (HitArea attack : a.curr.attackArea) { 652 attack = attack.getAdjustedForAction(a); 653 for (HitArea block : d.curr.blockArea) { 654 block = block.getAdjustedForAction(d); 655 if (attack.intersects(block)) { 656 return AttackResult.BLOCK; 657 } 658 } 659 } 660 661 for (HitArea attack : a.curr.attackArea) { 662 attack = attack.getAdjustedForAction(a); 663 for (HitArea hit : d.curr.hittableArea) { 664 hit = hit.getAdjustedForAction(d); 665 if (attack.intersects(hit)) { 666 return AttackResult.HIT; 667 } 668 } 669 } 670 671 return AttackResult.NO_HIT; 672 } 673 674 public void addSparks(Actor attacker, Actor defender) { 675 //Global.getSoundPlayer().playUISound(Actions.SOUND_CLASH, 1f, 1f); 676 if (true) { 677// attacker.freeze(0.1f); 678// defender.freeze(0.1f); 679// attacker.freeze(1.9f); 680// defender.freeze(1.9f); 681 return; 682 } 683 QuadParticles p = new QuadParticles(); 684 p.additiveBlend = true; 685 p.minDur = 0.1f; 686 p.maxDur = 0.3f; 687 p.minSize = 1f; 688 p.maxSize = 2f; 689 p.fadeTime = 0.1f; 690 p.minColor = new Color(255, 200, 100, 155); 691 p.maxColor = new Color(255, 255, 150, 255); 692 693 694 Vector2f loc = Vector2f.add(attacker.loc, defender.loc, new Vector2f()); 695 loc.scale(0.5f); 696 loc.y += getCharacterHeight() * 0.18f; 697 698 AnimAction block = defender.currAction; 699 700 if (block != null && block.curr != null) { 701 if (!block.curr.blockArea.isEmpty()) { 702 HitArea area = block.curr.blockArea.get(0); 703 area = area.getAdjustedForAction(block); 704 Vector2f loc2 = new Vector2f(loc); 705 loc2.x = area.x + area.w / 2f + block.actor.facing * area.w * 0.25f; 706 float dist1 = Misc.getDistance(defender.loc, loc); 707 float dist2 = Misc.getDistance(defender.loc, loc2); 708 // if the "in between" location is farther than the one in the block area, use the block area 709 // otherwise opponents are close, use in-between location 710 if (dist1 > dist2) { 711 loc.set(loc2); 712 } 713 } 714 } 715 716 int num = 30 + Misc.random.nextInt(10); 717 for (int i = 0; i < num; i++) { 718 Vector2f pos = Misc.getPointWithinRadius(loc, 5f); 719 Vector2f vel = Misc.getPointWithinRadius(new Vector2f(), 200f); 720 p.addParticle(pos.x, pos.y, vel.x, vel.y); 721 } 722 723 p.gravity = getGravity() * 0.1f; 724 725 particles.add(p); 726 } 727 728 729 public void wasHit(Actor actor, Actor by) { 730 int damage = 1; 731 if (by != null && by.currAction != null && by.currAction.curr != null) { 732 damage = by.currAction.curr.hitDamage; 733 } 734 //damage = 0; 735 actor.health -= damage; 736 if (actor.health > 0) { 737 actor.doAction(Actions.GOT_HIT, true); 738 } else { 739 actor.doAction(Actions.FALL, true); 740 } 741 addBlood(actor); 742 } 743 public void addBlood(Actor actor) { 744 Vector2f loc = new Vector2f(actor.loc); 745 //loc.y += 3f * player.currAction.anim.scale; 746 loc.y += getCharacterHeight() * 0.07f; 747 addBlood(loc, actor); 748 } 749 public void addBlood(Vector2f loc, Actor actor) { 750// if (true) { 751// actor.freeze(1.9f); 752// return; 753// } 754 //Global.getSoundPlayer().playUISound(Actions.SOUND_RIP, 1f, 1f); 755 756 QuadParticles p = new QuadParticles(); 757 p.minDur = 5f; 758 p.maxDur = 8f; 759 p.minSize = 2f; 760 p.maxSize = 3f; 761 p.fadeTime = 3f; 762 p.minColor = new Color(136, 8, 8, 155); 763 p.maxColor = new Color(238, 75, 43, 255); 764 //p.maxFloorMod = 7f; 765 p.maxFloorMod = 30f; 766 767 int base = 100; 768 base -= actor.health * 15; 769 if (base < 20) base = 20; 770 //base = 20; 771 int num = base + Misc.random.nextInt((int) (base * 0.5f)); 772 num *= 2f; 773 for (int i = 0; i < num; i++) { 774 Vector2f pos = Misc.getPointWithinRadius(loc, 15f); 775 Vector2f vel = Misc.getPointWithinRadius(new Vector2f(), 150f); 776 p.addParticle(pos.x, pos.y, vel.x, vel.y); 777 } 778 779 p.gravity = getGravity(); 780 p.floor = floorLevel - 10f; 781 p.floorFriction = 100f; 782 particles.add(p); 783 } 784 785 786 public float getGravity() { 787 // assume player is about 2m tall 788 float h = getCharacterHeight(); 789 return h / 2f * 9.81f; 790 } 791 792 public float getCharacterHeight() { 793 CharAnim anim = Actions.ANIMATIONS.get(Actions.IDLE); 794 return anim.frameHeight * anim.scale; 795 } 796 797 public Actor getPlayer() { 798 return player; 799 } 800 801 public Actor getEnemy() { 802 return enemy; 803 } 804 805 806} 807 808 809