219 lines
No EOL
5.1 KiB
JavaScript
219 lines
No EOL
5.1 KiB
JavaScript
/********************************
|
|
|
|
Character Architecture:
|
|
|
|
* Layers: Body + Mouth + Eyes
|
|
* Can animate each layer independently
|
|
* Alternate mouth when talking
|
|
* Short animation loops
|
|
* [star] in body means show no eyes/mouth
|
|
|
|
*********************************/
|
|
|
|
Loader.addImages([
|
|
|
|
{ id:"fear_harm", src:"sprites/ui/fear_harm.png" },
|
|
{ id:"fear_alone", src:"sprites/ui/fear_alone.png" },
|
|
{ id:"fear_bad", src:"sprites/ui/fear_bad.png" }
|
|
|
|
]);
|
|
|
|
function Character(spriteConfig, animLoops){
|
|
|
|
var self = this;
|
|
self.spriteConfig = spriteConfig;
|
|
self.animLoops = animLoops || [];
|
|
|
|
// Sprite!
|
|
self.layers = {};
|
|
["body","mouth","eyes"].forEach(function(layerName){
|
|
self.layers[layerName] = new Sprite(spriteConfig);
|
|
});
|
|
|
|
// Attack sprites (Hong only, really)
|
|
self.fears = {};
|
|
["harm","alone","bad"].forEach( function(fearName){
|
|
self.fears[fearName] = new Sprite({
|
|
image: Library.images["fear_"+fearName],
|
|
grid:{ width:1, height:1 },
|
|
frame:{ width:200, height:200 },
|
|
anchor:{ x:100/2, y:100/2 },
|
|
scale:0.75
|
|
});
|
|
} );
|
|
|
|
// Go To Frames
|
|
self.bounce = 1;
|
|
self.bounceVel = 0;
|
|
self.bounceHookes = 0.2;
|
|
self.bounceDamp = 0.8;
|
|
self.characterFrames = {};
|
|
self.gotoFrames = function(args, bounce){
|
|
|
|
// Bounce?
|
|
if(bounce===undefined) bounce=0.03; // a LITTLE bit, by default
|
|
if(bounce!==undefined){
|
|
self.bounce += bounce;
|
|
}
|
|
|
|
// Gimme those args
|
|
if(args.body){
|
|
if(!self.layers.body.doesFrameNameExist("body_"+args.body)) args.body+="*"; // add * if missing
|
|
self.characterFrames.body = args.body;
|
|
}
|
|
if(args.mouth) self.characterFrames.mouth = args.mouth;
|
|
if(args.eyes) self.characterFrames.eyes = args.eyes;
|
|
|
|
if(args.body){
|
|
var bodyName = "body_"+args.body;
|
|
self.layers.body.gotoFrameByName(bodyName);
|
|
self.characterFrames.body = args.body;
|
|
}
|
|
|
|
if(args.mouth){
|
|
var mouthName = "mouth_"+args.mouth;
|
|
self.layers.mouth.gotoFrameByName(mouthName);
|
|
}
|
|
|
|
if(args.eyes){
|
|
var eyesName = "eyes_"+args.eyes;
|
|
self.layers.eyes.gotoFrameByName(eyesName);
|
|
}
|
|
|
|
// Go go go
|
|
var l = self.layers;
|
|
var c = self.characterFrames;
|
|
l.body.gotoFrameByName( "body_"+c.body );
|
|
l.mouth.gotoFrameByName( "mouth_"+c.mouth );
|
|
l.eyes.gotoFrameByName( "eyes_"+c.eyes );
|
|
|
|
};
|
|
|
|
// Draw
|
|
self.characterSpeakerID = "derp";
|
|
self.characterSquash = 1;
|
|
self.draw = function(ctx){
|
|
|
|
var fname;
|
|
var l = self.layers;
|
|
var c = self.characterFrames;
|
|
|
|
// Attacked? SHAKE WHOLE CONTEXT
|
|
ctx.save();
|
|
if(IVE_BEEN_ATTACKED){
|
|
var shakeDuration = 0.6;
|
|
if(attackedTimer<shakeDuration){
|
|
var shakeAmp = (shakeDuration-attackedTimer)/shakeDuration;
|
|
var shakeX = Math.sin(attackedTimer*Math.TAU*10)*shakeAmp*10;
|
|
ctx.translate(shakeX, 0);
|
|
}
|
|
}
|
|
|
|
// GLOBAL PARAMS: SQUASH * BOUNCE
|
|
self.bounce += self.bounceVel;
|
|
self.bounceVel += (1-self.bounce)*self.bounceHookes;
|
|
self.bounceVel *= self.bounceDamp;
|
|
var totalSquash = self.characterSquash * self.bounce;
|
|
l.body.squash = l.mouth.squash = l.eyes.squash = totalSquash;
|
|
|
|
// Anim Loop rules!
|
|
self.runAnimLoopRules();
|
|
|
|
// Body
|
|
l.body.draw(ctx);
|
|
|
|
// If body is NOT on a "*" frame...
|
|
if(l.body.currentFrameName.indexOf("*")<0){
|
|
|
|
// Mouth
|
|
if(Game.WHO_IS_SPEAKING==self.characterSpeakerID){
|
|
// If I'm talking, switch to a talking mouth!
|
|
var mouthTalkFrame = "mouth_"+c.mouth+"_talk";
|
|
if( l.mouth.doesFrameNameExist(mouthTalkFrame) ){
|
|
l.mouth.gotoFrameByName(mouthTalkFrame);
|
|
}
|
|
}else{
|
|
// If I'm not talking & my mouth is in the talk position, switch it back!
|
|
var isMyMouthTalking = (l.mouth.currentFrameName.indexOf("_talk")>=0);
|
|
if(isMyMouthTalking){
|
|
l.mouth.gotoFrameByName( "mouth_"+c.mouth );
|
|
}
|
|
}
|
|
l.mouth.draw(ctx);
|
|
|
|
// Eyes
|
|
l.eyes.draw(ctx);
|
|
|
|
}
|
|
|
|
// Draw attacked icon
|
|
if(IVE_BEEN_ATTACKED){
|
|
|
|
var icon = self.fears[attackedIconShown];
|
|
icon.draw(ctx);
|
|
|
|
attackedTimer += 1/60;
|
|
if(attackedTimer>1.75){
|
|
icon.y -= 1;
|
|
icon.alpha -= 1/15;
|
|
if(icon.alpha<0){
|
|
attackedIconShown = null;
|
|
IVE_BEEN_ATTACKED = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
ctx.restore();
|
|
|
|
};
|
|
|
|
// Anim Loop logic!
|
|
self.animLoops.forEach(function(rule){
|
|
rule.active = false;
|
|
rule.countdown = -1;
|
|
});
|
|
self.runAnimLoopRules = function(){
|
|
for(var i=0; i<self.animLoops.length; i++){
|
|
|
|
// Find target
|
|
var rule = self.animLoops[i];
|
|
var target = self.layers[rule.target];
|
|
|
|
// Activate rule if not already
|
|
if(target.currentFrameName == rule.target+"_"+rule.ifOnFrame){
|
|
if(!rule.active){
|
|
rule.active = true;
|
|
rule.countdown = rule.wait;
|
|
}
|
|
}else{
|
|
rule.active = false;
|
|
}
|
|
|
|
// Countdown... and ACTIVATE!
|
|
if(rule.active){
|
|
rule.countdown -= 1/60;
|
|
if(rule.countdown<=0){
|
|
target.gotoFrameByName(rule.target+"_"+rule.thenGoToFrame);
|
|
rule.active = false;
|
|
return; // DONE! No more.
|
|
}
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
// Show attacked icon!
|
|
var IVE_BEEN_ATTACKED = false;
|
|
var attackedIconShown = null;
|
|
var attackedTimer = 0;
|
|
self.showAttackedIcon = function(type){
|
|
IVE_BEEN_ATTACKED = true;
|
|
attackedIconShown = type;
|
|
var icon = self.fears[attackedIconShown];
|
|
icon.x = 82;
|
|
icon.y = 250;
|
|
icon.alpha = 1;
|
|
attackedTimer = 0;
|
|
};
|
|
|
|
} |