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