Port over controller logic from Python.
[featherfall2.git] / src / main.js
1 "use strict";
2
3 var storage;
4
5 var PlayerController = new yT(yuu.C, {
6 constructor: function (body, left, right) {
7 this.body = body;
8 this.left = left;
9 this.right = right;
10 this.dleftLeft = this.dleftRight =
11 this.drightLeft = this.drightRight = 0;
12 this.up = 0;
13 this.leftPivot = 0;
14 this.rightPivot = 0;
15 this.x = this.lastX = body.x;
16 this.y = this.lastY = body.y;
17 this.commands = {
18 dleftLeft: yuu.propcmd(this, 'dleftLeft'),
19 dleftRight: yuu.propcmd(this, 'dleftRight'),
20 drightLeft: yuu.propcmd(this, 'drightLeft'),
21 drightRight: yuu.propcmd(this, 'drightRight'),
22 up: yuu.propcmd(this, 'up'),
23 };
24 },
25
26 _updatePivots: function () {
27 var PIVOT_SPEED = 0.03;
28 var leftSpeed = (this.dleftRight - this.dleftLeft) * PIVOT_SPEED;
29 var rightSpeed = (this.drightLeft - this.drightRight) * PIVOT_SPEED;
30 this.leftPivot = yf.clamp(this.leftPivot + leftSpeed, 0, 1);
31 this.rightPivot = yf.clamp(this.rightPivot + rightSpeed, 0, 1);
32 },
33
34 _updateTransforms: function () {
35 this.left.yaw = -this.leftPivot * Math.PI / 2;
36 this.right.yaw = this.rightPivot * Math.PI / 2;
37 this.body.x = this.x;
38 this.body.y = this.y;
39 },
40
41 tick: function () {
42 this._updatePivots();
43 var dt = 0.016;
44 var GRAVITY = 1;
45 var DRAG_MAX = 1;
46 var LIFT_MAX = 1;
47
48
49 var left = (1 - this.leftPivot) * Math.PI / 2;
50 var right = (1 - this.rightPivot) * Math.PI / 2;
51
52 var vx = (this.x - this.lastX) / dt;
53 var vy = (this.y - this.lastY) / dt;
54
55 var rotor = 1 * this.up;
56
57 var cleft = Math.cos(left);
58 var cright = Math.cos(right);
59 var sleft = Math.sin(left);
60 var sright = Math.sin(right);
61
62 var dvyleft = sleft * rotor;
63 var dvyright = sright * rotor;
64 var dvxleft = cleft * rotor;
65 var dvxright = cright * rotor;
66
67 var dvx = (dvxleft - dvxright);
68 var dvy = (dvyleft + dvyright) - GRAVITY;
69
70 var leftarea = DRAG_MAX * Math.abs(sleft);
71 var rightarea = DRAG_MAX * Math.abs(sright);
72 var dragy = vy * vy * (leftarea + rightarea);
73
74 leftarea = DRAG_MAX * Math.abs(cleft);
75 rightarea = DRAG_MAX * Math.abs(cright);
76 var dragx = vx * vx * (leftarea + rightarea);
77
78 leftarea = LIFT_MAX * Math.abs(cleft);
79 rightarea = LIFT_MAX * Math.abs(cright);
80 var liftx = vy * vy * (rightarea - leftarea);
81
82 leftarea = LIFT_MAX * Math.abs(sleft);
83 rightarea = LIFT_MAX * Math.abs(sright);
84 var lifty = vx * vx * (rightarea - leftarea);
85
86 if (vy > 0) {
87 dragy = -dragy;
88 liftx = -liftx;
89 }
90 if (vx > 0) {
91 dragx = -dragx;
92 lifty = -lifty;
93 }
94
95 var ax = dvx + liftx + dragx;
96 var ay = dvy + lifty + dragy;
97
98 var origX = this.x;
99 var origY = this.y;
100 this.x += (this.x - this.lastX) + ax * dt * dt;
101 this.y += (this.y - this.lastY) + ay * dt * dt;
102 this.y = Math.max(0, this.y);
103 this.lastX = origX;
104 this.lastY = origY;
105 this._updateTransforms();
106 },
107
108 TAPS: ['tick'],
109 });
110
111 var GameScene = yT(yuu.Scene, {
112 constructor: function () {
113 yuu.Scene.call(this);
114
115 this.layer0.resize(-1.3333333333/2, -0.2, 1.3333333333, 1);
116
117 var body, left, right;
118 this.player = new yuu.E(body = new yuu.Transform()
119 .setScale([0.081, 0.091, 1]),
120 new yuu.QuadC('@player')
121 .setAnchor('bottom')
122 .setPosition([0, 0]));
123 var leftWing = new yuu.E(left = new yuu.Transform()
124 .setPosition([-0.3, 0.65, 0])
125 .setScale([0.45, 0.22, 1]),
126 new yuu.QuadC('@left')
127 .setZ(-1)
128 .setAnchor("right")
129 .setPosition([0, 0]));
130 var rightWing = new yuu.E(right = new yuu.Transform()
131 .setPosition([0.3, 0.65, 0])
132 .setScale([0.45, 0.22, 1]),
133 new yuu.QuadC('@right')
134 .setZ(-1)
135 .setAnchor('left')
136 .setPosition([0, 0]));
137 this.player.addChildren(leftWing, rightWing);
138 this.entity0.addChild(this.player);
139
140 var ground = new yuu.E(new yuu.Transform()
141 .setPosition([0, -10, 1])
142 .setScale([100, 20, 1]),
143 new yuu.QuadC()
144 .setColor([0, 0.5, 0, 1]));
145 this.entity0.addChild(ground);
146
147 this.player.attach(
148 this.controller = new PlayerController(body, left, right));
149 Object.assign(this.commands, this.controller.commands);
150
151 this.ready = yuu.ready([
152 new yuu.Material('@player'),
153 new yuu.Material('@left'),
154 new yuu.Material('@right')]);
155 },
156
157 init: function () {
158 var audio0 = new Audio();
159 audio0.src = audio0.canPlayType('audio/ogg')
160 ? "data/sound/starting-line.ogg"
161 : "data/sound/starting-line.mp3";
162 audio0.autoplay = true;
163 audio0.loop = true;
164 document.body.appendChild(audio0);
165 var source = yuu.audio.createMediaElementSource(audio0);
166 source.connect(yuu.audio.music);
167 },
168
169 KEYBINDS: {
170 space: '+up',
171 up: '+up',
172 q: '+dleftLeft',
173 w: '+dleftRight',
174 o: '+drightLeft',
175 p: '+drightRight',
176 }
177 });
178
179 function start () {
180 yuu.director.start();
181 }
182
183 function load () {
184 storage = ystorage.getStorage();
185 yuu.audio.storage = storage;
186 var game = new GameScene();
187 yuu.director.pushScene(game);
188 return game.ready;
189 }
190
191 window.addEventListener("load", function() {
192 yuu.registerInitHook(load);
193 yuu.init({ backgroundColor: [0, 0, 0, 1], antialias: false })
194 .then(start);
195 });