otter.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. var Otter = function(options) {
  2. var defaults = {
  3. maxBreath: 7,
  4. breath: 7,
  5. drownRate: 20,
  6. // wiggle while swimming
  7. wiggleTheta: 0,
  8. wiggleAngle: 0,
  9. maxWiggle: .1,
  10. wiggleSpeed: 15.4,
  11. theta: 0,
  12. position: pt(500, 330),
  13. nextpos: pt(500, 330),
  14. speed: 0,
  15. maxSpeed: 300,
  16. acc: 200.5,
  17. drag: 100,
  18. dragSurface: 300,
  19. angularAcc: 4.1,
  20. surfaceSpeed: 0,
  21. uiAcc: false,
  22. uiDirection: pt(0,0),
  23. onSurface: 1,
  24. force: pt(0,0),
  25. bouyancy: 2,
  26. terminalVelocity: 5,
  27. ghost: false,
  28. colliders: [],
  29. minBound: 20,
  30. bumping: 0,
  31. hp: 100,
  32. eatRate: 11,
  33. mouthOpen: false,
  34. curHolding: null,
  35. holdVector: pt(30, -30),
  36. holdVectorFloating: pt(30, -20),
  37. holdVectorSwimming: pt(30, 30),
  38. noseVector: pt(100, 0),
  39. bubbleEmitter: new Emitter({
  40. objConst: ImageSprite,
  41. constOpts: {image: images.bubble},
  42. position: pt(500, 200),
  43. }),
  44. state: 'floating',
  45. };
  46. var e = $.extend({}, defaults, options);
  47. for(x in e) this[x] = e[x];
  48. this.type = 'Otter';
  49. this.init();
  50. }
  51. var pi_2 = Math.PI/2;
  52. function pointedLeft(th) {
  53. if(th > pi_2 && th <= 3*pi_2) return true;
  54. return false;
  55. }
  56. Otter.prototype.render = function(ctx) {
  57. ctx.save();
  58. ctx.translate(this.position.x ,
  59. this.position.y );
  60. // ctx.scale(-1, this.uiDirection.x > 0 ? 1 : -1);
  61. ctx.rotate(this.theta + this.wiggleAngle);
  62. var img = images.otter_swimming;
  63. if(this.state == 'eating' || this.state == 'floating') {
  64. img = this.mouthOpen ? images.otter_eating : images.otter_floating;
  65. if(pointedLeft(this.theta)) ctx.scale(1,-1);
  66. }
  67. ctx.translate( - images.otter_swimming.width / 2,
  68. - images.otter_swimming.height / 2);
  69. // ctx.beginPath();
  70. // ctx.rect(0, 0, 60, 20);
  71. // ctx.lineWidth = 6;
  72. // ctx.closePath();
  73. // ctx.fillStyle = "brown";
  74. // ctx.fill();
  75. ctx.drawImage(img, 0, 0);
  76. ctx.restore();
  77. this.bubbleEmitter.render(ctx);
  78. };
  79. Otter.prototype.frameMoveSurface = function(te) {
  80. // on the surface, theta really means nothing
  81. // movement is lateral, pressing down will dive
  82. this.theta = (this.theta + (Math.PI*2)) % (Math.PI*2);
  83. var dt = 0; // super hacky
  84. if(this.theta < pi_2 && this.theta > 0) {
  85. // going right, pointed down
  86. dt = 0 - this.theta;
  87. }
  88. else if(this.theta > 3*pi_2) {
  89. // still going right, pointed up
  90. dt = (Math.PI*2) - this.theta;
  91. }
  92. else if(this.theta > pi_2 && this.theta < Math.PI) {
  93. // going left, pointed down
  94. dt = this.theta - Math.PI;
  95. }
  96. else {
  97. // going left, pointed up
  98. dt = Math.PI - this.theta;
  99. }
  100. this.theta += dt * te * 3;
  101. this.theta = (this.theta + (Math.PI*2)) % (Math.PI*2);
  102. // need to level out...
  103. // this.theta = (this.theta + Math.PI*2) % Math.PI*2;
  104. if(0 == this.uiDirection.x) {
  105. if(this.surfaceSpeed > 0)
  106. this.surfaceSpeed = Math.min(300,this.surfaceSpeed - this.dragSurface*te);
  107. else
  108. this.surfaceSpeed = Math.max(-300, this.surfaceSpeed + this.dragSurface*te);
  109. }
  110. else {
  111. //this.surfaceSpeed += this.uiDirection.x * te * this.acc;
  112. if(this.surfaceSpeed < 0 && !pointedLeft(this.theta)) {
  113. this.theta = (this.theta + (Math.PI)) % (Math.PI*2);
  114. }
  115. if(this.surfaceSpeed > 0 && pointedLeft(this.theta)) {
  116. this.theta = (this.theta + (Math.PI)) % (Math.PI*2);
  117. }
  118. this.surfaceSpeed = Math.max(-this.maxSpeed, Math.min(this.maxSpeed, this.surfaceSpeed + this.uiDirection.x *2* this.acc*te));
  119. }
  120. // if(this.surfaceSpeed > .01) {
  121. // this.theta = -0.1;
  122. //
  123. // }
  124. // else if(this.surfaceSpeed < -.01) {
  125. // this.theta = Math.PI;
  126. //
  127. // }
  128. //
  129. // console.log(this.surfaceSpeed + ' ' + this.speed)
  130. this.force.x = this.surfaceSpeed;
  131. this.force.y = 0;
  132. //this.speed * Math.sin(this.theta);
  133. if(this.uiDirection.y > 0) {
  134. this.force.y = (this.speed) / 3;
  135. }
  136. }
  137. Otter.prototype.frameMoveSwimming = function(te) {
  138. // update speed
  139. if(this.uiAcc) {
  140. this.speed = Math.min(this.speed + te * this.acc, this.maxSpeed);
  141. }
  142. else {
  143. this.speed = Math.max(this.speed - te * this.drag, 0);
  144. }
  145. // update wiggle theta
  146. this.wiggleTheta = (this.wiggleTheta
  147. + this.wiggleSpeed * te
  148. * (this.speed / this.maxSpeed)) % (2*Math.PI);
  149. this.wiggleAngle = this.maxWiggle * Math.sin(this.wiggleTheta);
  150. // update theta
  151. var tt = (Math.atan2(this.uiDirection.y, this.uiDirection.x) + (2*Math.PI)) % (2*Math.PI);
  152. var pi2 = Math.PI*2;
  153. function getShortAngle2(at, target) {
  154. // rotate at to 0
  155. target -= at;
  156. // normalize target
  157. target = (target + pi2) % pi2;
  158. if(target > Math.PI) { // quicker to take the back route
  159. return target - pi2;
  160. }
  161. else { // target is small and positive, the shortest distance
  162. return target;
  163. }
  164. }
  165. dt = getShortAngle2(this.theta, tt);
  166. if(this.uiAcc && Math.abs(dt) > .1) {
  167. this.theta =
  168. (this.theta + te * this.angularAcc * (dt > 0 ? 1 : -1))
  169. % (2*Math.PI);
  170. }
  171. // update swimming force
  172. this.force.x += this.speed * Math.cos(this.theta);
  173. this.force.y += this.speed * Math.sin(this.theta);
  174. }
  175. Otter.prototype.frameMove = function(te) {
  176. this.checkState(te);
  177. this.force.x = 0;
  178. this.force.y = 0;
  179. // bouyancy
  180. if(this.position.y > 50) {
  181. this.force.y = -10;
  182. }
  183. else {
  184. this.force.y = 50;
  185. }
  186. //
  187. if(this.state == 'eating' || this.state == 'floating') {
  188. if(pointedLeft(this.theta)) this.holdVector.y = Math.abs(this.holdVectorFloating.y);
  189. else this.holdVector.y = -Math.abs(this.holdVectorFloating.y);
  190. }
  191. if(this.state == 'swimming') {
  192. this.frameMoveSwimming(te);
  193. }
  194. else {
  195. this.frameMoveSurface(te);
  196. }
  197. this.nextpos.y = this.position.y + this.force.y * te;
  198. this.nextpos.x = this.position.x + this.force.x * te;
  199. // move teh bubble emitter
  200. var tmpx = this.noseVector.x * Math.cos(this.theta) - this.noseVector.y * Math.sin(this.theta);
  201. var tmpy = this.noseVector.x * Math.sin(this.theta) + this.noseVector.y * Math.cos(this.theta);
  202. this.bubbleEmitter.position.x = this.position.x + tmpx;
  203. this.bubbleEmitter.position.y = this.position.y + tmpy;
  204. this.bubbleEmitter.frameMove(te);
  205. this.colliders = [];
  206. };
  207. Otter.prototype.checkState = function(te) {
  208. if(this.breath == 0) {
  209. this.subHP(te * this.drownRate);
  210. // console.log('otter is drowning! ' + this.hp);
  211. }
  212. if(this.hp == 0) {
  213. this.state = 'dead';
  214. }
  215. if(this.state == 'swimming') {
  216. // subtract breath
  217. this.subBreath(te);
  218. this.holdVector = this.holdVectorSwimming;
  219. // check collisions
  220. if(!this.curHolding) {
  221. for(var i = 0; i < this.colliders.length; i++) {
  222. if(this.canEat(this.colliders[i])) {
  223. //console.log('otter catches urchin');
  224. this.curHolding = this.colliders[i];
  225. this.curHolding.state = 'caught';
  226. this.curHolding.caughtBy = this;
  227. break;
  228. };
  229. }
  230. }
  231. if(this.position.y <= 55) {
  232. this.state = 'floating';
  233. this.surfaceSpeed = this.speed * Math.cos(this.theta);
  234. console.log('otter is floating ' + this.surfaceSpeed);
  235. }
  236. }
  237. // check for when the player dives
  238. if(this.state == "floating" || this.state == 'eating') {
  239. this.subBreath(te * -4);
  240. this.holdVector = this.holdVectorFloating;
  241. if(this.position.y > 80) {
  242. this.state = 'swimming';
  243. console.log('otter is swimming');
  244. return;
  245. }
  246. }
  247. if(this.state == 'floating' && this.canEat(this.curHolding)) {
  248. this.state = 'eating';
  249. console.log('otter starts eating');
  250. return;
  251. }
  252. if(this.state == 'eating') {
  253. // this.eat(te);
  254. // console.log('urchin hp: ' + this.curHolding.hp);
  255. }
  256. };
  257. var foodvalue = {
  258. Urchin: .1,
  259. };
  260. Otter.prototype.canEat = function(obj) {
  261. if(!obj) return false;
  262. return foodvalue[obj.type] > 0;
  263. };
  264. Otter.prototype.eat = function(amount) {
  265. var t = this.curHolding.type;
  266. if(!this.canEat(this.curHolding)) {
  267. this.subHP(5);
  268. return;
  269. };
  270. var hplost = this.curHolding.subHP(amount);
  271. this.subHP(-hplost * foodvalue[t]);
  272. if(this.curHolding.hp == 0) {
  273. this.dropItem();
  274. this.state = 'floating';
  275. }
  276. };
  277. Otter.prototype.dropItem = function() {
  278. if(!this.curHolding) return;
  279. this.curHolding.state = 'default';
  280. this.curHolding.caughtBy = null;
  281. this.curHolding = null;
  282. this.state = 'swimming';
  283. };
  284. Otter.prototype.takeBite = function() {
  285. if(this.state == 'swimming') {
  286. this.dropItem();
  287. return;
  288. }
  289. if(this.state == 'eating') {
  290. this.eat(this.eatRate);
  291. }
  292. };
  293. Otter.prototype.subHP = function(amount) {
  294. var old = this.hp;
  295. this.hp -= amount;
  296. if(this.hp < 0) this.hp = 0;
  297. if(this.hp > 100) this.hp = 100;
  298. return old - this.hp;
  299. }
  300. Otter.prototype.subBreath = function(amount) {
  301. var old = this.breath;
  302. this.breath -= amount;
  303. if(this.breath < 0) this.breath = 0;
  304. if(this.breath > this.maxBreath) this.breath = this.maxBreath;
  305. return old - this.breath;
  306. }
  307. Otter.prototype.init = function() {
  308. };