Huge overhaul.
- fix flaws with videotube's QTE-finding algorithm which caused breakage when seeking around in the video, qtes were added, or anyone sneezed - switch from arbitrary polygons to simple circles, simplifying the editing interface considerably - support for deletes and overwrites of QTEs - new server protocol - pause/resume when focus lost - some degree of error handling - restart key - videotube API no longer autostarts; 'play' becomes 'enqueue' and is called once
This commit is contained in:
parent
bc576d08b5
commit
46e54c44df
|
@ -1,14 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project>
|
||||
<project version="2">
|
||||
<!-- Output SWF options -->
|
||||
<output>
|
||||
<movie disabled="False" />
|
||||
<movie outputType="Application" />
|
||||
<movie input="" />
|
||||
<movie path="bin\LaserTube.swf" />
|
||||
<movie fps="30" />
|
||||
<movie width="800" />
|
||||
<movie height="600" />
|
||||
<movie version="10" />
|
||||
<movie minorVersion="0" />
|
||||
<movie platform="Flash Player" />
|
||||
<movie background="#FFFFFF" />
|
||||
</output>
|
||||
<!-- Other classes to be compiled into your SWF -->
|
||||
|
@ -24,6 +26,7 @@
|
|||
<option locale="" />
|
||||
<option loadConfig="" />
|
||||
<option optimize="True" />
|
||||
<option omitTraces="True" />
|
||||
<option showActionScriptWarnings="True" />
|
||||
<option showBindingWarnings="True" />
|
||||
<option showInvalidCSS="True" />
|
||||
|
@ -39,7 +42,7 @@
|
|||
<option staticLinkRSL="True" />
|
||||
<option additional="" />
|
||||
<option compilerConstants="" />
|
||||
<option customSDK="" />
|
||||
<option minorVersion="" />
|
||||
</build>
|
||||
<!-- SWC Include Libraries -->
|
||||
<includeLibraries>
|
||||
|
@ -82,4 +85,6 @@
|
|||
<option showHiddenPaths="False" />
|
||||
<option testMovie="Default" />
|
||||
</options>
|
||||
<!-- Plugin storage -->
|
||||
<storage />
|
||||
</project>
|
|
@ -13,38 +13,26 @@ package
|
|||
private var shape:Shape;
|
||||
private var shapeHidden:Shape;
|
||||
private var fHidden:Boolean;
|
||||
public function ClickArea(rgpoint: Array, color:uint, alpha:Number = 1)
|
||||
public function ClickArea(center: Point, radius:Number, color:uint, alpha:Number = 1)
|
||||
{
|
||||
super();
|
||||
shape = drawShape(new Shape(), rgpoint, color, null, alpha);
|
||||
shapeHidden = drawShape(new Shape(), rgpoint, color, null, 0);
|
||||
shape = drawShape(new Shape(), new Point(0,0), radius, color, null, alpha);
|
||||
shapeHidden = drawShape(new Shape(), new Point(0,0), radius, color, null, 0);
|
||||
fHidden = false;
|
||||
moveTo(center);
|
||||
addChild(shape);
|
||||
}
|
||||
public static function drawShape(shape:Shape, rgpoint:Array, color:uint, colorLine:* = null, alpha:Number = 1):Shape
|
||||
public function moveTo(centerNew: Point):void
|
||||
{
|
||||
x = centerNew.x;
|
||||
y = centerNew.y;
|
||||
}
|
||||
public static function drawShape(shape:Shape, center:Point, radius:Number, color:uint, colorLine:* = null, alpha:Number = 1):Shape
|
||||
{
|
||||
shape.graphics.clear();
|
||||
if (rgpoint.length > 0)
|
||||
{
|
||||
if (colorLine != null)
|
||||
shape.graphics.lineStyle(1, colorLine);
|
||||
shape.graphics.beginFill(color, alpha);
|
||||
var fFirstPoint:Boolean = true;
|
||||
for each (var point:Point in rgpoint)
|
||||
{
|
||||
if (fFirstPoint)
|
||||
{
|
||||
shape.graphics.moveTo(point.x, point.y);
|
||||
fFirstPoint = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
shape.graphics.lineTo(point.x, point.y);
|
||||
}
|
||||
}
|
||||
shape.graphics.lineTo(rgpoint[0].x, rgpoint[0].y);
|
||||
shape.graphics.endFill();
|
||||
}
|
||||
shape.graphics.beginFill(color, alpha);
|
||||
shape.graphics.drawCircle(center.x, center.y, radius);
|
||||
shape.graphics.endFill();
|
||||
return shape;
|
||||
}
|
||||
public function Show():void
|
||||
|
|
115
src/Game.as
Normal file
115
src/Game.as
Normal file
|
@ -0,0 +1,115 @@
|
|||
package
|
||||
{
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFormat;
|
||||
|
||||
/**
|
||||
* ...
|
||||
* @author Jeremy Penner
|
||||
*/
|
||||
public class Game extends Sprite
|
||||
{
|
||||
public static var hasFocus:Boolean = false;
|
||||
|
||||
protected var videotube:Videotube;
|
||||
protected var gamedisc:Gamedisc;
|
||||
protected var clickarea:ClickArea;
|
||||
protected var text:TextField;
|
||||
protected var textValues: Array;
|
||||
public function Game(videotube:Videotube, gamedisc:Gamedisc)
|
||||
{
|
||||
this.videotube = videotube;
|
||||
this.gamedisc = gamedisc;
|
||||
addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
}
|
||||
protected function init(e:Event):void {
|
||||
trace("init");
|
||||
removeEventListener(Event.ADDED_TO_STAGE, init);
|
||||
addEventListener(Event.REMOVED_FROM_STAGE, cleanup);
|
||||
|
||||
stage.addEventListener(Event.ACTIVATE, onFocus);
|
||||
stage.addEventListener(Event.DEACTIVATE, onLoseFocus);
|
||||
|
||||
clickarea = null;
|
||||
if (text != null)
|
||||
removeChild(text);
|
||||
text = null;
|
||||
textValues = [];
|
||||
|
||||
if (hasFocus) {
|
||||
onFocus(null);
|
||||
} else {
|
||||
onLoseFocus(null);
|
||||
}
|
||||
}
|
||||
protected function cleanup(e:Event):void {
|
||||
trace("cleanup");
|
||||
removeEventListener(Event.REMOVED_FROM_STAGE, cleanup);
|
||||
|
||||
stage.removeEventListener(Event.ACTIVATE, onFocus);
|
||||
stage.removeEventListener(Event.DEACTIVATE, onLoseFocus);
|
||||
|
||||
if (hasFocus)
|
||||
onPause();
|
||||
clearClickarea();
|
||||
}
|
||||
protected function onFocus(e:Event):void {
|
||||
trace("received focus");
|
||||
popText();
|
||||
hasFocus = true;
|
||||
stage.addEventListener(Event.EXIT_FRAME, onFocusEnded);
|
||||
}
|
||||
protected function onFocusEnded(e:Event): void {
|
||||
stage.removeEventListener(Event.EXIT_FRAME, onFocusEnded);
|
||||
onResume();
|
||||
}
|
||||
protected function onLoseFocus(e:Event): void {
|
||||
trace("lost focus");
|
||||
pushText("Paused\nCLICK TO PLAY", 0x000000, 0xFFFFFF);
|
||||
hasFocus = false;
|
||||
onPause();
|
||||
}
|
||||
|
||||
protected function onPause(): void {
|
||||
trace("onPause");
|
||||
videotube.pause();
|
||||
}
|
||||
protected function onResume(): void {
|
||||
trace("onResume");
|
||||
if (fPlaying())
|
||||
videotube.resume();
|
||||
}
|
||||
protected function fPlaying(): Boolean {
|
||||
return false;
|
||||
}
|
||||
public function clearClickarea(): void
|
||||
{
|
||||
if (clickarea != null) {
|
||||
removeChild(clickarea);
|
||||
clickarea = null;
|
||||
}
|
||||
}
|
||||
protected function pushText(html:String, bgcolor:int, fgcolor:int): void {
|
||||
if (text == null)
|
||||
text = Util.addTextFieldFullScreen(this);
|
||||
textValues.push( { 'html': html, 'bgcolor':bgcolor, 'fgcolor':fgcolor } );
|
||||
trace("saying: " + html);
|
||||
Util.setText(text, html, 164, bgcolor, fgcolor);
|
||||
}
|
||||
protected function popText(): void {
|
||||
if (textValues.length > 0) {
|
||||
textValues.pop();
|
||||
if (textValues.length > 0) {
|
||||
var val:Object = textValues.pop();
|
||||
pushText(val['html'], val['bgcolor'], val['fgcolor']);
|
||||
} else {
|
||||
removeChild(text);
|
||||
text = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -4,69 +4,111 @@ package
|
|||
import flash.events.Event;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.geom.Point;
|
||||
/**
|
||||
* ...
|
||||
* @author jjp
|
||||
*/
|
||||
public class GameEditor extends Sprite
|
||||
public class GameEditor extends Game
|
||||
{
|
||||
private var videotube:Videotube;
|
||||
private var gamedisc:Gamedisc;
|
||||
private var sketchShape:SketchShape;
|
||||
private var clickarea:ClickArea;
|
||||
|
||||
public function GameEditor(videotube:Videotube, gamedisc:Gamedisc)
|
||||
{
|
||||
this.videotube = videotube;
|
||||
this.gamedisc = gamedisc;
|
||||
addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
protected var qte:Qte;
|
||||
public override function GameEditor(videotube:Videotube, gamedisc:Gamedisc) {
|
||||
super(videotube, gamedisc);
|
||||
qte = null;
|
||||
}
|
||||
public function init(e: Event):void
|
||||
protected override function onResume():void
|
||||
{
|
||||
removeEventListener(Event.ADDED_TO_STAGE, init);
|
||||
addEventListener(Event.REMOVED_FROM_STAGE, cleanup);
|
||||
super.onResume();
|
||||
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
|
||||
|
||||
sketchShape = new SketchShape();
|
||||
addChild(sketchShape);
|
||||
sketchShape.addEventListener(SketchShape.DRAW_BEGIN, onDrawBegin);
|
||||
sketchShape.addEventListener(SketchShape.DRAW_END, onDrawEnd);
|
||||
|
||||
clickarea = null;
|
||||
stage.addEventListener(MouseEvent.MOUSE_DOWN, onClick);
|
||||
stage.addEventListener(MouseEvent.MOUSE_MOVE, onDrag);
|
||||
|
||||
videotube.addEventListener(EventQte.QTE, onQteBegin);
|
||||
videotube.addEventListener(EventQte.QTE_TIMEOUT, onQteEnd);
|
||||
videotube.addEventListener(EventQte.QTE_CANCEL, onQteEnd);
|
||||
}
|
||||
public function cleanup(e: Event):void
|
||||
protected override function onPause():void
|
||||
{
|
||||
removeEventListener(Event.REMOVED_FROM_STAGE, cleanup);
|
||||
super.onPause();
|
||||
stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp);
|
||||
sketchShape.removeEventListener(SketchShape.DRAW_BEGIN, onDrawBegin);
|
||||
sketchShape.removeEventListener(SketchShape.DRAW_END, onDrawEnd);
|
||||
stage.removeEventListener(MouseEvent.MOUSE_DOWN, onClick);
|
||||
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDrag);
|
||||
|
||||
videotube.removeEventListener(EventQte.QTE, onQteBegin);
|
||||
videotube.removeEventListener(EventQte.QTE_TIMEOUT, onQteEnd);
|
||||
videotube.removeEventListener(EventQte.QTE_CANCEL, onQteEnd);
|
||||
}
|
||||
protected override function fPlaying():Boolean {
|
||||
return true;
|
||||
}
|
||||
public function onQteBegin(e: EventQte):void
|
||||
{
|
||||
clickarea = new ClickArea(e.qte.rgpoint, 0x4444ee, 0.4);
|
||||
addChild(clickarea);
|
||||
trace("editor qte begin");
|
||||
if (e.qte != qte) {
|
||||
Util.assert(qte == null);
|
||||
if (qte != null) {
|
||||
gamedisc.repostQte(qte);
|
||||
clearClickarea();
|
||||
qte = null;
|
||||
}
|
||||
addClickArea(e.qte);
|
||||
}
|
||||
}
|
||||
public function onQteEnd(e: EventQte):void
|
||||
{
|
||||
removeChild(clickarea);
|
||||
clickarea = null;
|
||||
}
|
||||
public function onKeyUp(key: KeyboardEvent):void
|
||||
{
|
||||
|
||||
}
|
||||
private function onDrawBegin(e: Event): void
|
||||
{
|
||||
videotube.pause();
|
||||
}
|
||||
private function onDrawEnd(e: Event): void
|
||||
{
|
||||
gamedisc.AddQte(new Qte(sketchShape.rgpoint, videotube.time()));
|
||||
videotube.resume();
|
||||
trace("editor qte end");
|
||||
Util.assert(qte == e.qte);
|
||||
gamedisc.repostQte(qte);
|
||||
clearClickarea();
|
||||
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;
|
||||
}
|
||||
}
|
||||
private function addClickArea(qteNew: Qte):void
|
||||
{
|
||||
qte = qteNew;
|
||||
clickarea = new ClickArea(qte.center, qte.radius, 0x4444ee, 0.4);
|
||||
addChild(clickarea);
|
||||
}
|
||||
private function moveClickArea(point:Point): void
|
||||
{
|
||||
if (qte) {
|
||||
qte.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);
|
||||
}
|
||||
}
|
||||
public function onDrag(e: MouseEvent):void
|
||||
{
|
||||
if (e.buttonDown)
|
||||
moveClickArea(new Point(e.stageX, e.stageY));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ package
|
|||
import flash.display.Shape;
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.geom.Point;
|
||||
import flash.text.StyleSheet;
|
||||
|
@ -13,56 +14,45 @@ package
|
|||
* ...
|
||||
* @author jjp
|
||||
*/
|
||||
public class GamePlayer extends Sprite
|
||||
public class GamePlayer extends Game
|
||||
{
|
||||
private var videotube:Videotube;
|
||||
private var gamedisc:Gamedisc;
|
||||
private var clickarea:ClickArea;
|
||||
private var textDeath:TextField;
|
||||
|
||||
public function GamePlayer(videotube:Videotube, gamedisc:Gamedisc)
|
||||
{
|
||||
this.videotube = videotube;
|
||||
this.gamedisc = gamedisc;
|
||||
clickarea = null;
|
||||
textDeath = null;
|
||||
addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
private var fAlive: Boolean;
|
||||
public override function GamePlayer(videotube:Videotube, gamedisc:Gamedisc) {
|
||||
super(videotube, gamedisc);
|
||||
fAlive = true;
|
||||
}
|
||||
private function init(e:Event):void
|
||||
protected override function onResume():void
|
||||
{
|
||||
removeEventListener(Event.ADDED_TO_STAGE, init);
|
||||
addEventListener(Event.REMOVED_FROM_STAGE, cleanup);
|
||||
|
||||
super.onResume();
|
||||
addEventListener(MouseEvent.CLICK, onClick);
|
||||
videotube.addEventListener(EventQte.QTE, onQte);
|
||||
videotube.addEventListener(EventQte.QTE_TIMEOUT, onTimeout);
|
||||
stage.addEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
}
|
||||
private function cleanup(e:Event):void
|
||||
protected override function onPause():void
|
||||
{
|
||||
removeEventListener(Event.REMOVED_FROM_STAGE, cleanup);
|
||||
super.onPause();
|
||||
removeEventListener(MouseEvent.CLICK, onClick);
|
||||
videotube.removeEventListener(EventQte.QTE, onQte);
|
||||
videotube.removeEventListener(EventQte.QTE_TIMEOUT, onTimeout);
|
||||
stage.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
}
|
||||
protected override function fPlaying():Boolean {
|
||||
return fAlive;
|
||||
}
|
||||
private function onQte(e:EventQte):void
|
||||
{
|
||||
trace(e.qte.secTrigger() + "gameplayer start" + e.qte.secTimeout());
|
||||
clearClickarea();
|
||||
clickarea = new ClickArea(e.qte.rgpoint, 0xffff00, 0.7);
|
||||
clickarea = new ClickArea(e.qte.center, e.qte.radius, 0x4444ee, 0.4);
|
||||
addChild(clickarea);
|
||||
}
|
||||
private function onTimeout(e:EventQte):void
|
||||
{
|
||||
if (clickarea != null)
|
||||
{
|
||||
videotube.pause();
|
||||
textDeath = new TextField();
|
||||
textDeath.htmlText = "<p align='center'>YOU ARE DEAD</p>";
|
||||
textDeath.wordWrap = true;
|
||||
textDeath.background = true;
|
||||
textDeath.backgroundColor = 0x0000FF;
|
||||
textDeath.width = stage.stageWidth;
|
||||
textDeath.height = stage.stageHeight;
|
||||
textDeath.setTextFormat(new TextFormat(null, 164, 0xFF0000));
|
||||
addChild(textDeath);
|
||||
trace("gameplayer timeout");
|
||||
if (clickarea != null) {
|
||||
pushText("OH SHIT\n\nhit R to restart", 0x0000FF, 0xFF0000);
|
||||
fAlive = false;
|
||||
}
|
||||
clearClickarea();
|
||||
}
|
||||
|
@ -71,13 +61,14 @@ package
|
|||
if (clickarea != null && clickarea.FHit(new Point(mouse.stageX, mouse.stageY)))
|
||||
clearClickarea();
|
||||
}
|
||||
private function clearClickarea():void
|
||||
{
|
||||
if (clickarea != null)
|
||||
{
|
||||
removeChild(clickarea);
|
||||
clickarea = null;
|
||||
private function onKey(event:KeyboardEvent):void {
|
||||
if (!fAlive && event.keyCode == 82) {
|
||||
fAlive = true;
|
||||
popText();
|
||||
videotube.seek(0);
|
||||
videotube.resume();
|
||||
}
|
||||
trace("key:", event.keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
130
src/Gamedisc.as
130
src/Gamedisc.as
|
@ -1,8 +1,12 @@
|
|||
package
|
||||
{
|
||||
import com.adobe.serialization.json.JSON;
|
||||
import flash.display.IBitmapDrawable;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.net.sendToURL;
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLRequest;
|
||||
import flash.net.URLRequestHeader;
|
||||
import flash.net.URLRequestMethod;
|
||||
|
@ -17,29 +21,121 @@ package
|
|||
public static const VIDEOTUBE_YOUTUBE:String = "yt";
|
||||
|
||||
public var urlVideo:String;
|
||||
public var urlPostQte:String;
|
||||
public var headerPostQte:Object;
|
||||
public var typeVideotube:String;
|
||||
public var rgqte:Array;
|
||||
|
||||
protected var urlPostQte:String;
|
||||
protected var csrf:String;
|
||||
protected var queuePost:Array;
|
||||
|
||||
public function Gamedisc(urlVideo:String = null, typeVideotube:String = null)
|
||||
{
|
||||
this.urlVideo = urlVideo;
|
||||
this.typeVideotube = typeVideotube;
|
||||
this.rgqte = [];
|
||||
this.urlPostQte = null;
|
||||
this.csrf = null;
|
||||
this.queuePost = [];
|
||||
}
|
||||
public function AddQte(qte:Qte):void
|
||||
public function setUrlPost(urlPostQte:String, csrf:String):void {
|
||||
trace("setting url", urlPostQte, csrf);
|
||||
this.urlPostQte = urlPostQte;
|
||||
this.csrf = csrf;
|
||||
}
|
||||
public function fCanEdit():Boolean {
|
||||
return urlPostQte != null;
|
||||
}
|
||||
public function AddQte(qte:Qte, videotube:Videotube):void
|
||||
{
|
||||
rgqte.splice(Math.abs(Util.binarySearch(rgqte, qte, Qte.compare)), 0, qte);
|
||||
if (urlPostQte != null)
|
||||
{
|
||||
var iqte:int = Math.abs(Util.binarySearch(rgqte, qte, Qte.compare));
|
||||
var cqteDrop:int = 0;
|
||||
while (true) {
|
||||
var iqteToDrop:int = iqte + cqteDrop;
|
||||
if (iqteToDrop >= rgqte.length)
|
||||
break;
|
||||
if (rgqte[iqteToDrop].secTrigger() <= qte.secTimeout()) {
|
||||
cqteDrop ++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iqte > 0 && rgqte[iqte - 1].secTimeout() >= qte.secTrigger()) {
|
||||
iqte --;
|
||||
cqteDrop ++;
|
||||
}
|
||||
rgqte.splice(iqte, cqteDrop, qte);
|
||||
|
||||
Util.assert(qte.secTrigger() == videotube.time());
|
||||
videotube.onQtesChanged(iqte + 1, qte);
|
||||
}
|
||||
public function DeleteQte(qte:Qte, videotube:Videotube):void
|
||||
{
|
||||
var iqte:int = Util.binarySearch(rgqte, qte, Qte.compare);
|
||||
Util.assert(iqte >= 0);
|
||||
rgqte.splice(iqte, 1);
|
||||
|
||||
post("delete", { 'ms_trigger': qte.msTrigger } );
|
||||
videotube.onQtesChanged(iqte, null);
|
||||
}
|
||||
protected function post(action: String, val:Object):void {
|
||||
if (urlPostQte != null) {
|
||||
val['action'] = action;
|
||||
val['csrf'] = csrf;
|
||||
queuePost.push(JSON.encode(val));
|
||||
if (queuePost.length == 1) {
|
||||
doNextPost();
|
||||
}
|
||||
}
|
||||
}
|
||||
protected function doNextPost(): void {
|
||||
if (queuePost.length > 0) {
|
||||
var req:URLRequest = new URLRequest(urlPostQte);
|
||||
req.method = URLRequestMethod.POST;
|
||||
for (var key:String in headerPostQte)
|
||||
req.requestHeaders.push(new URLRequestHeader(key, headerPostQte[key]));
|
||||
var data:URLVariables = new URLVariables();
|
||||
data.qte = JSON.encode(qte.ToJson());
|
||||
req.data = data;
|
||||
sendToURL(req);
|
||||
req.data = queuePost.shift();
|
||||
req.contentType = 'application/json';
|
||||
var loader:URLLoader = new URLLoader();
|
||||
loader.addEventListener(Event.COMPLETE, onPostComplete);
|
||||
loader.addEventListener(IOErrorEvent.IO_ERROR, onPostFailed);
|
||||
loader.load(req);
|
||||
}
|
||||
}
|
||||
protected function onPostComplete(event:Event): void {
|
||||
event.target.removeEventListener(Event.COMPLETE, onPostComplete);
|
||||
event.target.removeEventListener(IOErrorEvent.IO_ERROR, onPostFailed);
|
||||
try {
|
||||
var result:Object = JSON.decode(event.target.data);
|
||||
csrf = result.csrf;
|
||||
|
||||
if (result.err != 'ok') {
|
||||
if (result.err == 'invalid') {
|
||||
reportError("Sorry, another browser has begun editing the video.");
|
||||
} else if (result.err == 'expired') {
|
||||
reportError("Sorry, your editing session has timed out.");
|
||||
} else {
|
||||
reportError();
|
||||
}
|
||||
} else {
|
||||
doNextPost();
|
||||
}
|
||||
} catch (e:Error) {
|
||||
trace(e);
|
||||
reportError();
|
||||
}
|
||||
}
|
||||
protected function onPostFailed(event:Event): void {
|
||||
event.target.removeEventListener(Event.COMPLETE, onPostComplete);
|
||||
event.target.removeEventListener(IOErrorEvent.IO_ERROR, onPostFailed);
|
||||
reportError();
|
||||
}
|
||||
protected function reportError(error:String = "Sorry, something weird happened. It's my fault. Reload the page to try again."): void {
|
||||
Main.instance.fatalError(error);
|
||||
}
|
||||
public function repostQte(qte:Qte): void
|
||||
{
|
||||
if (qte.fDirty && urlPostQte != null)
|
||||
{
|
||||
post("put", { 'qte': qte.ToJson() } );
|
||||
qte.fDirty = false;
|
||||
}
|
||||
}
|
||||
public function CreateVideotube():Videotube
|
||||
|
@ -60,19 +156,17 @@ package
|
|||
json.urlPostQte = urlPostQte;
|
||||
return json;
|
||||
}
|
||||
public function FromJson(json:Object, jsonPostHeaders:Object):void
|
||||
public function FromJson(json:Object):void
|
||||
{
|
||||
rgqte = [];
|
||||
for each (var jsonQte:Object in json.rgqte)
|
||||
for each (var jsonQte:Object in json.qtes)
|
||||
{
|
||||
var qte:Qte = new Qte();
|
||||
qte.FromJson(jsonQte);
|
||||
rgqte.push(qte);
|
||||
}
|
||||
urlVideo = json.urlVideo;
|
||||
typeVideotube = json.typeVideotube;
|
||||
urlPostQte = json.urlPostQte;
|
||||
headerPostQte = jsonPostHeaders;
|
||||
urlVideo = json.url;
|
||||
typeVideotube = json.ktube;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
42
src/Main.as
42
src/Main.as
|
@ -7,6 +7,7 @@ package
|
|||
import flash.events.MouseEvent;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.geom.Point;
|
||||
import flash.text.TextField;
|
||||
import flash.ui.Keyboard;
|
||||
|
||||
/**
|
||||
|
@ -19,11 +20,25 @@ package
|
|||
private var gamedisc:Gamedisc;
|
||||
private var gameeditor:GameEditor;
|
||||
private var gameplayer:GamePlayer;
|
||||
private var debug:Boolean;
|
||||
|
||||
public static var instance:Main;
|
||||
|
||||
public function Main():void
|
||||
{
|
||||
gamedisc = new Gamedisc();
|
||||
gamedisc.FromJson(JSON.decode(loaderInfo.parameters.jsonDisc), JSON.decode(loaderInfo.parameters.jsonPostHeaders));
|
||||
instance = this;
|
||||
if (loaderInfo.parameters.jsonDisc) {
|
||||
gamedisc = new Gamedisc();
|
||||
gamedisc.FromJson(JSON.decode(loaderInfo.parameters.jsonDisc));
|
||||
trace("+++LOADING+++", loaderInfo.parameters.urlPostQte, loaderInfo.parameters.csrf);
|
||||
if (loaderInfo.parameters.urlPostQte && loaderInfo.parameters.csrf) {
|
||||
gamedisc.setUrlPost(JSON.decode(loaderInfo.parameters.urlPostQte), JSON.decode(loaderInfo.parameters.csrf));
|
||||
}
|
||||
} else {
|
||||
// debugging
|
||||
gamedisc = new Gamedisc("The%20Last%20Eichhof%20-%20Longplay.flv", Gamedisc.VIDEOTUBE_FLV);
|
||||
debug = true;
|
||||
}
|
||||
videotube = gamedisc.CreateVideotube();
|
||||
if (stage) init();
|
||||
else addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
|
@ -61,19 +76,34 @@ package
|
|||
gameplayer = new GamePlayer(videotube, gamedisc);
|
||||
addChild(gameplayer);
|
||||
}
|
||||
videotube.seek(0);
|
||||
videotube.enqueue();
|
||||
}
|
||||
private function onVideotubeReady(event:Event = null):void
|
||||
{
|
||||
toggleGame();
|
||||
if (gamedisc.urlPostQte == null)
|
||||
if (!gamedisc.fCanEdit())
|
||||
toggleGame();
|
||||
videotube.play();
|
||||
}
|
||||
private function onKey(key:KeyboardEvent):void
|
||||
{
|
||||
if (key.keyCode == Keyboard.SPACE && gamedisc.urlPostQte != null)
|
||||
if (key.keyCode == Keyboard.SPACE && debug)
|
||||
toggleGame();
|
||||
}
|
||||
public function fatalError(message:String):void {
|
||||
var text:TextField = Util.addTextFieldFullScreen(this);
|
||||
Util.setText(text, message, 64, 0xffffff, 0x440000);
|
||||
if (gameplayer)
|
||||
removeChild(gameplayer);
|
||||
if (gameeditor)
|
||||
removeChild(gameeditor);
|
||||
if (videotube) {
|
||||
videotube.pause();
|
||||
removeChild(videotube);
|
||||
}
|
||||
gameplayer = null;
|
||||
gameeditor = null;
|
||||
videotube = null;
|
||||
stage.removeEventListener(KeyboardEvent.KEY_UP, onKey);
|
||||
}
|
||||
}
|
||||
}
|
55
src/Qte.as
55
src/Qte.as
|
@ -7,39 +7,62 @@ package
|
|||
*/
|
||||
public class Qte
|
||||
{
|
||||
public var rgpoint:Array;
|
||||
public var secTrigger:Number;
|
||||
public var center:Point;
|
||||
public var radius:Number;
|
||||
public var msTrigger:int;
|
||||
public var msTimeout:int;
|
||||
public var fDirty:Boolean;
|
||||
|
||||
public function Qte(rgpoint: Array = null, secTrigger:Number = -1)
|
||||
public function Qte(center: Point = null, radius: Number = -1, secTrigger:Number = -1, secTimeout:Number = -1, fDirty: Boolean = false)
|
||||
{
|
||||
this.rgpoint = rgpoint;
|
||||
this.secTrigger = secTrigger;
|
||||
this.center = center;
|
||||
this.radius = radius;
|
||||
if (secTrigger >= 0)
|
||||
this.msTrigger = int(secTrigger * 1000);
|
||||
else
|
||||
this.msTrigger = -1;
|
||||
|
||||
if (secTimeout >= 0) {
|
||||
this.msTimeout = int(secTimeout * 1000);
|
||||
Util.assert(msTimeout > msTrigger);
|
||||
} else if (msTrigger >= 0) {
|
||||
msTimeout = msTrigger + 1000; // 1 second timeout by default
|
||||
} else {
|
||||
msTimeout = -1;
|
||||
}
|
||||
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.secTrigger == qte2.secTrigger)
|
||||
if (qte1.msTrigger == qte2.msTrigger)
|
||||
return 0;
|
||||
else if (qte1.secTrigger < qte2.secTrigger)
|
||||
else if (qte1.msTrigger < qte2.msTrigger)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
public function secTrigger():Number
|
||||
{
|
||||
return msTrigger / 1000.0;
|
||||
}
|
||||
public function secTimeout():Number
|
||||
{
|
||||
return secTrigger + 1.0;
|
||||
return secTrigger() + 1.0;
|
||||
}
|
||||
public function ToJson():Object
|
||||
{
|
||||
var jsonRgpoint:Array = [];
|
||||
for each(var point:Point in rgpoint)
|
||||
jsonRgpoint.push([point.x, point.y]);
|
||||
return { rgpoint: jsonRgpoint, secTrigger: secTrigger };
|
||||
return { shape: {center: [center.x, center.y], radius: radius}, ms_trigger: msTrigger, ms_finish: msTimeout };
|
||||
}
|
||||
public function FromJson(json:Object):void
|
||||
{
|
||||
rgpoint = []
|
||||
for each(var jsonPoint:Array in json.rgpoint)
|
||||
rgpoint.push(new Point(jsonPoint[0], jsonPoint[1]));
|
||||
secTrigger = json.secTrigger;
|
||||
center = new Point(json.shape.center[0], json.shape.center[1]);
|
||||
radius = json.shape.radius;
|
||||
msTrigger = json.ms_trigger;
|
||||
msTimeout = json.ms_finish;
|
||||
}
|
||||
}
|
||||
}
|
35
src/Util.as
35
src/Util.as
|
@ -1,7 +1,15 @@
|
|||
package
|
||||
{
|
||||
import com.adobe.serialization.json.JSON;
|
||||
import flash.display.Bitmap;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.DisplayObjectContainer;
|
||||
import flash.display.Shape;
|
||||
import flash.display.Sprite;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFormat;
|
||||
/**
|
||||
* ...
|
||||
* @author jjp
|
||||
|
@ -33,6 +41,31 @@ package
|
|||
{
|
||||
ExternalInterface.call("alert", JSON.encode(rgo));
|
||||
}
|
||||
public static function assert(cond:Boolean, msg:String = "Assertion failed"): void
|
||||
{
|
||||
if (!cond) {
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
public static function BitmapFromSprite(sprite: DisplayObject):Bitmap
|
||||
{
|
||||
var bitmapData:BitmapData = new BitmapData(sprite.width, sprite.height);
|
||||
bitmapData.draw(sprite);
|
||||
return new Bitmap(bitmapData);
|
||||
}
|
||||
public static function addTextFieldFullScreen(parent: DisplayObjectContainer): TextField {
|
||||
var text:TextField = new TextField();
|
||||
text.wordWrap = true;
|
||||
text.background = true;
|
||||
text.width = parent.stage.stageWidth;
|
||||
text.height = parent.stage.stageHeight;
|
||||
parent.addChild(text);
|
||||
return text;
|
||||
}
|
||||
public static function setText(text:TextField, html:String, size:int, bgcolor:int, fgcolor:int):void {
|
||||
text.htmlText = "<p align='center'>" + html + "</p>";
|
||||
text.backgroundColor = bgcolor;
|
||||
text.setTextFormat(new TextFormat(null, size, fgcolor));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@ package
|
|||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFormat;
|
||||
|
||||
/**
|
||||
* ...
|
||||
|
@ -11,9 +13,10 @@ package
|
|||
public class Videotube extends Sprite
|
||||
{
|
||||
public static const READY:String = "videotube-ready";
|
||||
private static const DEBUG_VIDEO:Boolean = false;
|
||||
|
||||
public function fready():Boolean { throw "not implemented"; }
|
||||
public function play():void { throw "not implemented"; }
|
||||
public function enqueue():void { throw "not implemented"; }
|
||||
public function pause():void { throw "not implemented"; }
|
||||
public function resume():void { throw "not implemented"; }
|
||||
public function time():Number { throw "not implemented"; }
|
||||
|
@ -21,84 +24,132 @@ package
|
|||
|
||||
private var secPrev:Number;
|
||||
private var iqte:int;
|
||||
private var iqtePrev:int;
|
||||
private var qtePrev:Qte;
|
||||
protected var gamedisc:Gamedisc;
|
||||
private var textDebug:TextField;
|
||||
public function Videotube(gamedisc: Gamedisc)
|
||||
{
|
||||
this.gamedisc = gamedisc;
|
||||
secPrev = 0;
|
||||
iqte = 0;
|
||||
iqtePrev = 0;
|
||||
qtePrev = null;
|
||||
if (DEBUG_VIDEO) {
|
||||
textDebug = new TextField();
|
||||
textDebug.x = 0;
|
||||
textDebug.y = 0;
|
||||
textDebug.width = 640;
|
||||
textDebug.height = 70;
|
||||
}
|
||||
}
|
||||
|
||||
public function seek(sec:Number):void
|
||||
{
|
||||
trace("seeking");
|
||||
seekI(sec);
|
||||
if (iqtePrev != -1)
|
||||
{
|
||||
dispatchEvent(new EventQte(EventQte.QTE_CANCEL, gamedisc.rgqte[iqtePrev]));
|
||||
iqtePrev = -1;
|
||||
}
|
||||
cancelPrev();
|
||||
iqte = -1;
|
||||
trace("seek to " + sec + ":" + iqte);
|
||||
}
|
||||
// returns true if triggered
|
||||
private function Trigger(iqte:int, ev:String, secNow:Number): Boolean
|
||||
private function Trigger(qte:Qte, ev:String, secNow:Number): Boolean
|
||||
{
|
||||
if (iqte >= 0 && iqte < gamedisc.rgqte.length)
|
||||
if (qte != null)
|
||||
{
|
||||
var qte:Qte = gamedisc.rgqte[iqte];
|
||||
var secQte:Number;
|
||||
if (ev == EventQte.QTE)
|
||||
secQte = qte.secTrigger;
|
||||
secQte = qte.secTrigger();
|
||||
else
|
||||
secQte = qte.secTimeout();
|
||||
//trace("testing " + iqte + " for " + ev + " " + secQte + " in " + secPrev + ":" + secNow);
|
||||
if (Util.FInTimespan(secQte, secPrev, secNow))
|
||||
{
|
||||
trace("triggered " + ev);
|
||||
dispatchEvent(new EventQte(ev, qte));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected function cancelPrev():void {
|
||||
if (qtePrev != null) {
|
||||
dispatchEvent(new EventQte(EventQte.QTE_CANCEL, qtePrev));
|
||||
qtePrev = null;
|
||||
}
|
||||
}
|
||||
protected function tick(e: Event):void
|
||||
{
|
||||
var secNow:Number = time();
|
||||
var fPrevQteProcessed:Boolean = false;
|
||||
var fQteProcessed:Boolean = false;
|
||||
if (secNow < secPrev) {
|
||||
iqte = -1;
|
||||
cancelPrev();
|
||||
secPrev = secNow;
|
||||
}
|
||||
if (iqte < 0)
|
||||
{
|
||||
iqte = Util.binarySearch(gamedisc.rgqte, secNow, function(qte:Qte, secNow:Number):int {
|
||||
if (qte.secTrigger < secNow)
|
||||
trace("finding qte for time " + secNow);
|
||||
iqte = Math.abs(Util.binarySearch(gamedisc.rgqte, secNow, function(qte:Qte, secNow:Number):int {
|
||||
if (qte.secTrigger() < secNow)
|
||||
return -1;
|
||||
if (qte.secTrigger > secNow)
|
||||
if (qte.secTrigger() > secNow)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
iqtePrev = iqte;
|
||||
}));
|
||||
cancelPrev();
|
||||
if (iqte < gamedisc.rgqte.length)
|
||||
trace("found: " + iqte + " at " + gamedisc.rgqte[iqte].secTrigger());
|
||||
else
|
||||
trace("at end of qtes");
|
||||
}
|
||||
else
|
||||
{
|
||||
// we loop here so that, in the event of being passed bad data, we still do something vaguely sensible.
|
||||
while (!fPrevQteProcessed && !fQteProcessed)
|
||||
{
|
||||
if (Trigger(iqtePrev, EventQte.QTE_TIMEOUT, secNow))
|
||||
//trace(secNow + ":: prev" + iqtePrev + ", iqte" + iqte);
|
||||
if (Trigger(qtePrev, EventQte.QTE_TIMEOUT, secNow))
|
||||
{
|
||||
fPrevQteProcessed = false;
|
||||
iqtePrev ++;
|
||||
qtePrev = null;
|
||||
}
|
||||
else
|
||||
fPrevQteProcessed = true;
|
||||
|
||||
if (Trigger(iqte, EventQte.QTE, secNow))
|
||||
var qte:Qte = null;
|
||||
if (iqte >= 0 && iqte < gamedisc.rgqte.length)
|
||||
qte = gamedisc.rgqte[iqte];
|
||||
if (Trigger(qte, EventQte.QTE, secNow))
|
||||
{
|
||||
Util.assert(qtePrev == null);
|
||||
fQteProcessed = false;
|
||||
iqte ++;
|
||||
qtePrev = qte;
|
||||
}
|
||||
else
|
||||
fQteProcessed = true;
|
||||
}
|
||||
}
|
||||
if (DEBUG_VIDEO) {
|
||||
var txt:String = int(secNow * 1000) + "ms";
|
||||
if (iqte < gamedisc.rgqte.length)
|
||||
txt += " | next qte at " + gamedisc.rgqte[iqte].msTrigger;
|
||||
if (qtePrev != null)
|
||||
txt += " | qte over at " + qtePrev.msTimeout;
|
||||
textDebug.htmlText = txt;
|
||||
textDebug.backgroundColor = 0x000000;
|
||||
textDebug.setTextFormat(new TextFormat(null, 16, 0xffffff));
|
||||
if (textDebug.parent == this)
|
||||
removeChild(textDebug);
|
||||
addChild(textDebug);
|
||||
}
|
||||
|
||||
secPrev = secNow;
|
||||
}
|
||||
public function onQtesChanged(iqteNext: int, qtePrev:Qte): void {
|
||||
iqte = iqteNext;
|
||||
cancelPrev();
|
||||
this.qtePrev = qtePrev;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,11 +38,15 @@ package
|
|||
removeEventListener(Event.REMOVED_FROM_STAGE, cleanup);
|
||||
stage.removeEventListener(Event.ENTER_FRAME, tick);
|
||||
}
|
||||
public override function fready():Boolean { return true; }
|
||||
public override function play():void { stream.play(gamedisc.urlVideo); }
|
||||
public override function fready():Boolean { return true; }
|
||||
public override function pause():void { stream.pause(); }
|
||||
public override function resume():void { stream.resume(); }
|
||||
public override function time():Number { return stream.time; }
|
||||
public override function seek(sec:Number):void { stream.seek(sec); }
|
||||
protected override function seekI(sec:Number):void { stream.seek(sec); }
|
||||
public override function enqueue():void {
|
||||
seek(0);
|
||||
stream.play(gamedisc.urlVideo);
|
||||
stream.pause();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ package
|
|||
{
|
||||
super(gamedisc);
|
||||
Security.allowDomain("www.youtube.com");
|
||||
Security.allowDomain("*.ytimage.com");
|
||||
player = null;
|
||||
loader = new Loader();
|
||||
loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);
|
||||
|
@ -23,12 +24,23 @@ package
|
|||
}
|
||||
private function onLoaderInit(event:Event):void
|
||||
{
|
||||
trace("yt: loader init");
|
||||
addChild(loader);
|
||||
loader.contentLoaderInfo.removeEventListener(Event.INIT, onLoaderInit);
|
||||
loader.content.addEventListener("onReady", onPlayerReady);
|
||||
player = loader.content;
|
||||
player.addEventListener("onReady", onPlayerReady);
|
||||
player.addEventListener("onStateChange", onStateChange);
|
||||
player.addEventListener("onError", onError);
|
||||
}
|
||||
private function onStateChange(event:Event):void {
|
||||
trace("yt: state " + player.getPlayerState());
|
||||
}
|
||||
private function onError(event:Event):void {
|
||||
trace("yt: error", Object(event).data);
|
||||
}
|
||||
private function onPlayerReady(event:Event):void
|
||||
{
|
||||
trace("yt: player ready");
|
||||
player = loader.content;
|
||||
player.setSize(stage.stageWidth, stage.stageHeight);
|
||||
player.cueVideoById(gamedisc.urlVideo);
|
||||
|
@ -36,11 +48,11 @@ package
|
|||
stage.addEventListener(Event.ENTER_FRAME, tick);
|
||||
dispatchEvent(new Event(Videotube.READY));
|
||||
}
|
||||
public override function fready():Boolean { return player !== null; }
|
||||
public override function play():void { player.playVideo(); }
|
||||
public override function fready():Boolean { return player !== null && player.getPlayerState() >= 0; }
|
||||
public override function enqueue():void { seek(0); }
|
||||
public override function pause():void { player.pauseVideo(); }
|
||||
public override function resume():void { player.playVideo(); }
|
||||
public override function time():Number { return player.getCurrentTime(); }
|
||||
public override function seek(sec:Number):void { player.seekTo(sec, true); }
|
||||
protected override function seekI(sec:Number):void { player.seekTo(sec, true); }
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue