diff --git a/src/EventQte.as b/src/EventQte.as index 6c27f85..dc976bd 100644 --- a/src/EventQte.as +++ b/src/EventQte.as @@ -17,6 +17,11 @@ package super(type); this.qte = qte; } + public function qteCircle():QteCircle { + return qte as QteCircle; + } + public function qteText():QteText { + return qte as QteText; + } } - } \ No newline at end of file diff --git a/src/Game.as b/src/Game.as index ec86c53..e9feddb 100644 --- a/src/Game.as +++ b/src/Game.as @@ -1,9 +1,14 @@ package { + import flash.display.DisplayObject; + import flash.display.InteractiveObject; import flash.display.Sprite; import flash.events.Event; + import flash.events.FocusEvent; + import flash.geom.Point; import flash.text.TextField; import flash.text.TextFormat; + import flash.text.TextFormatAlign; /** * ... @@ -16,6 +21,7 @@ package protected var videotube:Videotube; protected var gamedisc:Gamedisc; protected var clickarea:ClickArea; + protected var typein:TextField; protected var text:TextField; protected var textValues: Array; public function Game(videotube:Videotube, gamedisc:Gamedisc) @@ -31,8 +37,11 @@ package stage.addEventListener(Event.ACTIVATE, onFocus); stage.addEventListener(Event.DEACTIVATE, onLoseFocus); + stage.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, preventWidgetFocusChange); + stage.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, preventWidgetFocusChange); clickarea = null; + typein = null; if (text != null) removeChild(text); text = null; @@ -50,13 +59,19 @@ package stage.removeEventListener(Event.ACTIVATE, onFocus); stage.removeEventListener(Event.DEACTIVATE, onLoseFocus); + stage.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, preventWidgetFocusChange); + stage.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, preventWidgetFocusChange); if (hasFocus) onPause(); clearClickarea(); } + protected function focussedChild():InteractiveObject { + return null; + } protected function onFocus(e:Event):void { trace("received focus"); + trace(stage.focus); popText(); hasFocus = true; stage.addEventListener(Event.EXIT_FRAME, onFocusEnded); @@ -67,11 +82,16 @@ package } protected function onLoseFocus(e:Event): void { trace("lost focus"); + trace(stage.focus); pushText("Paused\nCLICK TO PLAY", 0x000000, 0xFFFFFF); hasFocus = false; onPause(); } - + protected function preventWidgetFocusChange(e:Event): void { + trace("preventing focus change"); + e.preventDefault(); + stage.focus = focussedChild(); + } protected function onPause(): void { trace("onPause"); videotube.pause(); @@ -84,6 +104,24 @@ package protected function fPlaying(): Boolean { return false; } + public function addClickarea(center:Point, radius:Number):void { + clearClickarea(); + clickarea = new ClickArea(center, radius, 0x4444ee, 0.4) + addChild(clickarea); + } + public function addTypein(text:String):void { + clearTypein(); + typein = new TextField(); + typein.x = 0; + typein.y = stage.stageHeight / 2; + typein.width = stage.stageWidth; + typein.height = 64; + typein.background = true; + + Util.setText(typein, text, 40, 0x222222, 0xffffff); + + addChild(typein); + } public function clearClickarea(): void { if (clickarea != null) { @@ -91,9 +129,22 @@ package clickarea = null; } } + public function clearTypein(): void + { + if (typein != null) { + removeChild(typein); + typein = null; + } + } + public function clearQteUI(): void { + clearClickarea(); + clearTypein(); + } protected function pushText(html:String, bgcolor:int, fgcolor:int): void { - if (text == null) + if (text == null) { text = Util.addTextFieldFullScreen(this); + text.selectable = false; + } textValues.push( { 'html': html, 'bgcolor':bgcolor, 'fgcolor':fgcolor } ); trace("saying: " + html); Util.setText(text, html, 164, bgcolor, fgcolor); diff --git a/src/GameEditor.as b/src/GameEditor.as index e28d7f7..da2df3a 100644 --- a/src/GameEditor.as +++ b/src/GameEditor.as @@ -1,10 +1,14 @@ package { + import flash.display.Bitmap; + import flash.display.DisplayObject; + import flash.display.InteractiveObject; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.geom.Point; + import flash.text.TextFieldType; /** * ... * @author jjp @@ -12,6 +16,7 @@ package public class GameEditor extends Game { protected var qte:Qte; + protected var fTyping:Boolean; public override function GameEditor(videotube:Videotube, gamedisc:Gamedisc) { super(videotube, gamedisc); qte = null; @@ -20,6 +25,7 @@ package { super.onResume(); stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); + stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); stage.addEventListener(MouseEvent.MOUSE_DOWN, onClick); stage.addEventListener(MouseEvent.MOUSE_MOVE, onDrag); @@ -31,6 +37,7 @@ package { super.onPause(); stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp); + stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); stage.removeEventListener(MouseEvent.MOUSE_DOWN, onClick); stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDrag); @@ -38,8 +45,14 @@ package videotube.removeEventListener(EventQte.QTE_TIMEOUT, onQteEnd); videotube.removeEventListener(EventQte.QTE_CANCEL, onQteEnd); } + + override protected function focussedChild():InteractiveObject { + if (typein) + return typein; + return super.focussedChild(); + } protected override function fPlaying():Boolean { - return true; + return !fTyping; } public function onQteBegin(e: EventQte):void { @@ -51,7 +64,12 @@ package clearClickarea(); qte = null; } - addClickArea(e.qte); + qte = e.qte; + if (e.qteCircle()) { + addClickarea(e.qteCircle().center, e.qteCircle().radius); + } else if (e.qteText()) { + addTypein(e.qteText().text); + } } } public function onQteEnd(e: EventQte):void @@ -59,49 +77,87 @@ package trace("editor qte end"); Util.assert(qte == e.qte); gamedisc.repostQte(qte); - clearClickarea(); + clearQteUI(); qte = null; } public function onKeyUp(key: KeyboardEvent):void { - if (key.keyCode == 46 && qte != null) {// delete - gamedisc.DeleteQte(qte, videotube); - clearClickarea(); - qte = null; - } else if (key.keyCode == 37) { // leftArrow - videotube.seek(videotube.time() - 3); - clearClickarea(); - qte = null; - } else if (key.keyCode == 39) { // rightArrow - videotube.seek(videotube.time() + 3); - clearClickarea(); - qte = null; + if (!fTyping) { + if (key.keyCode == 46 && qte != null) {// delete + gamedisc.DeleteQte(qte, videotube); + clearQteUI(); + qte = null; + } else if (key.keyCode == 37) { // leftArrow + videotube.seek(videotube.time() - 3); + clearQteUI(); + qte = null; + } else if (key.keyCode == 39) { // rightArrow + videotube.seek(videotube.time() + 3); + clearQteUI(); + qte = null; + } + } else { + Util.assert(qte as QteText); + if (key.keyCode == 13) { // enter + var text:String = typein.text; + if (text.length > 0) { + trace("Creating QTE: " + text); + var qteText:QteText = qte as QteText; + qteText.text = text; + // 300ms per character + 500ms reaction time + qteText.msTimeout = ((text.length * 300) + 500) + qteText.msTrigger; + gamedisc.AddQte(qteText, videotube); + gamedisc.repostQte(qteText); + + } else { + gamedisc.DeleteQte(qte, videotube); + qte = null; + clearTypein(); + } + stage.focus = this; + fTyping = false; + videotube.resume(); + } } } - private function addClickArea(qteNew: Qte):void - { - qte = qteNew; - clickarea = new ClickArea(qte.center, qte.radius, 0x4444ee, 0.4); - addChild(clickarea); + private function onKeyDown(key:KeyboardEvent): void { + if (!fTyping && qte == null) { + if (key.charCode >= 0x20 && key.charCode <= 0x7e) { // printable character + var ch:String = String.fromCharCode(key.charCode); + trace("typed character: '" + ch + "'"); + qte = new QteText(ch, videotube.time(), videotube.time() + 0.1, /*fDirty*/true); + addTypein(ch); + typein.type = TextFieldType.INPUT; + trace("focusing"); + stage.focus = typein; + typein.setSelection(ch.length, ch.length); + fTyping = true; + videotube.pause(); + } + } } private function moveClickArea(point:Point): void { - if (qte) { - qte.moveTo(point); + var qteCircle:QteCircle = qte as QteCircle; + if (qteCircle) { + qteCircle.moveTo(point); clickarea.moveTo(point); } } public function onClick(e: MouseEvent):void { - trace("clicked"); - var point:Point = new Point(e.stageX, e.stageY); - if (qte) - moveClickArea(point); - else { - var qte:Qte = new Qte(point, 75, videotube.time(), -1, true /*fDirty*/); - addClickArea(qte); - gamedisc.AddQte(qte, videotube); + if (!fTyping) { + trace("clicked" + qte); + var point:Point = new Point(e.stageX, e.stageY); + if (qte) + moveClickArea(point); + else { + var qte:QteCircle = new QteCircle(point, 75, videotube.time(), -1, true /*fDirty*/); + addClickarea(qte.center, qte.radius); + this.qte = qte; + gamedisc.AddQte(qte, videotube); + } } } public function onDrag(e: MouseEvent):void diff --git a/src/GamePlayer.as b/src/GamePlayer.as index eeb9619..7263350 100644 --- a/src/GamePlayer.as +++ b/src/GamePlayer.as @@ -9,6 +9,7 @@ package import flash.text.StyleSheet; import flash.text.TextField; import flash.text.TextFieldAutoSize; + import flash.text.TextFieldType; import flash.text.TextFormat; /** * ... @@ -17,9 +18,11 @@ package public class GamePlayer extends Game { private var fAlive: Boolean; + private var ichNext: int; public override function GamePlayer(videotube:Videotube, gamedisc:Gamedisc) { super(videotube, gamedisc); fAlive = true; + ichNext = -1; } protected override function onResume():void { @@ -43,19 +46,26 @@ package private function onQte(e:EventQte):void { trace(e.qte.secTrigger() + "gameplayer start" + e.qte.secTimeout()); - clearClickarea(); - clickarea = new ClickArea(e.qte.center, e.qte.radius, 0x4444ee, 0.4); - addChild(clickarea); + trace(e.qte); + clearQteUI(); + if (e.qteCircle()) { + addClickarea(e.qteCircle().center, e.qteCircle().radius); + } else if (e.qteText()) { + addTypein(e.qteText().text); + typein.type = TextFieldType.DYNAMIC; + typein.selectable = false; + ichNext = 0; + } } private function onTimeout(e:EventQte):void { trace("gameplayer timeout"); - if (clickarea != null) { + if (clickarea != null || typein != null) { pushText("OH SHIT\n\nhit R to restart", 0x0000FF, 0xFF0000); videotube.pause(); fAlive = false; } - clearClickarea(); + clearQteUI(); } private function onClick(mouse:MouseEvent):void { @@ -69,8 +79,25 @@ package videotube.seek(0); videotube.resume(); } + if (typein != null) { + var text:String = typein.text; + var ch:String = String.fromCharCode(event.charCode).toLowerCase(); + var chToMatch:String = text.substr(ichNext, 1).toLowerCase(); + trace(chToMatch + ":" + ch); + if (ch == chToMatch) { + ichNext ++; + if (ichNext >= text.length) { + clearTypein(); + ichNext = -1; + } else { + var format:TextFormat = new TextFormat(); + format.size = 40; + format.color = 0xFFFF00; + typein.setTextFormat(format, 0, ichNext); + } + } + } trace("key:", event.keyCode); } } - } \ No newline at end of file diff --git a/src/Gamedisc.as b/src/Gamedisc.as index b40c270..6496774 100644 --- a/src/Gamedisc.as +++ b/src/Gamedisc.as @@ -161,9 +161,9 @@ package rgqte = []; for each (var jsonQte:Object in json.qtes) { - var qte:Qte = new Qte(); - qte.FromJson(jsonQte); - rgqte.push(qte); + var qte:Qte = Qte.FromJson(jsonQte); + if (qte) + rgqte.push(qte); } urlVideo = json.url; typeVideotube = json.ktube; diff --git a/src/Qte.as b/src/Qte.as index a60207b..a6c5b17 100644 --- a/src/Qte.as +++ b/src/Qte.as @@ -7,16 +7,12 @@ package */ public class Qte { - public var center:Point; - public var radius:Number; public var msTrigger:int; public var msTimeout:int; public var fDirty:Boolean; - public function Qte(center: Point = null, radius: Number = -1, secTrigger:Number = -1, secTimeout:Number = -1, fDirty: Boolean = false) + public function Qte(secTrigger:Number = -1, secTimeout:Number = -1, fDirty: Boolean = false) { - this.center = center; - this.radius = radius; if (secTrigger >= 0) this.msTrigger = int(secTrigger * 1000); else @@ -32,11 +28,7 @@ package } this.fDirty = fDirty; } - public function moveTo(center: Point): void - { - fDirty = true; - this.center = center; - } + public static function compare(qte1:Qte, qte2:Qte):int { if (qte1.msTrigger == qte2.msTrigger) @@ -51,18 +43,31 @@ package } public function secTimeout():Number { - return secTrigger() + 1.0; + return msTimeout / 1000.0; } + protected function shapeToJson():Object { + return null; + } + protected function setShapeFromJson(shape:Object):void {} public function ToJson():Object { - return { shape: {center: [center.x, center.y], radius: radius}, ms_trigger: msTrigger, ms_finish: msTimeout }; + return { shape: this.shapeToJson(), ms_trigger: msTrigger, ms_finish: msTimeout }; } - public function FromJson(json:Object):void + public static function FromJson(json:Object):Qte { - center = new Point(json.shape.center[0], json.shape.center[1]); - radius = json.shape.radius; - msTrigger = json.ms_trigger; - msTimeout = json.ms_finish; + var qte:Qte; + if (json.shape.radius) + qte = new QteCircle(); + else if (json.shape.text) + qte = new QteText(); + else + return null; + + qte.setShapeFromJson(json.shape); + qte.msTrigger = json.ms_trigger; + qte.msTimeout = json.ms_finish; + + return qte; } } } \ No newline at end of file diff --git a/src/QteCircle.as b/src/QteCircle.as new file mode 100644 index 0000000..3a69a29 --- /dev/null +++ b/src/QteCircle.as @@ -0,0 +1,31 @@ +package { + import flash.geom.Point; + + /** + * ... + * @author Jeremy Penner + */ + public class QteCircle extends Qte { + public var center:Point; + public var radius:Number; + public function QteCircle(center:Point = null, radius:Number = -1, secTrigger:Number = -1, secTimeout:Number = -1, fDirty:Boolean = false) { + this.center = center; + this.radius = radius; + super(secTrigger, secTimeout, fDirty); + } + public function moveTo(center: Point): void + { + fDirty = true; + this.center = center; + } + + protected override function shapeToJson(): Object { + return { center: [center.x, center.y], radius: radius }; + } + protected override function setShapeFromJson(shape:Object): void { + center = new Point(shape.center[0], shape.center[1]); + radius = shape.radius; + } + } + +} \ No newline at end of file diff --git a/src/QteText.as b/src/QteText.as new file mode 100644 index 0000000..b67778b --- /dev/null +++ b/src/QteText.as @@ -0,0 +1,21 @@ +package { + /** + * ... + * @author Jeremy Penner + */ + public class QteText extends Qte { + public var text:String; + public function QteText(text:String = null, secTrigger:Number=-1, secTimeout:Number=-1, fDirty:Boolean=false) { + super(secTrigger, secTimeout, fDirty); + this.text = text; + } + + override protected function shapeToJson():Object { + return { 'text': text }; + } + override protected function setShapeFromJson(shape:Object):void { + text = shape.text; + } + } + +} \ No newline at end of file