fixes for API renaming
[k8vacspelynky.git] / mapent / characters / damsel.vc
blob3554991a4d369a3406ccb4967eed4dc643d9a5e9
1 /**********************************************************************************
2  * Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC
3  * Copyright (c) 2010, Moloch
4  * Copyright (c) 2018, Ketmar Dark
5  *
6  * This file is part of Spelunky.
7  *
8  * You can redistribute and/or modify Spelunky, including its source code, under
9  * the terms of the Spelunky User License.
10  *
11  * Spelunky is distributed in the hope that it will be entertaining and useful,
12  * but WITHOUT WARRANTY.  Please see the Spelunky User License for more details.
13  *
14  * The Spelunky User License should be available in "Game Information", which
15  * can be found in the Resource Explorer, or as an external file called COPYING.
16  * If not, please obtain a new copy of Spelunky from <http://spelunkyworld.com/>
17  *
18  **********************************************************************************/
19 class MonsterDamselKiss['oDamselKiss'] : MapObject;
21 bool kissed = false;
24 override bool initialize () {
25   if (!::initialize()) return false;
26   setSprite(global.isDamsel ? 'sStandLeft' : 'sDamselLeft');
27   setCollisionBoundsFromFrame();
28   return true;
32 override void onAnimationLooped () {
33   auto spr = getSprite();
34   if (spr.Name == 'sDamselKissL' || spr.Name == 'sPKissL') {
35     setSprite(global.isDamsel ? 'sStandLeft' : 'sDamselLeft');
36   }
40 void kiss () {
41   if (kissed) return;
42   kissed = true;
43   setSprite(global.isDamsel ? 'sPKissL' : 'sDamselKissL');
47 override void thinkFrame () {
48   //writeln("DKS is thinking...");
49   auto spr = getSprite();
50   if ((spr.Name == 'sDamselKissL' || spr.Name == 'sPKissL') && trunc(imageFrame) == 7) {
51     level.MakeMapObject(ix-8, iy-8, 'oHeart');
52     playSound('sndKiss');
53     global.plife += level.damselSaved;
54     level.damselSaved = 0;
55   }
59 override void drawWithOfs (int xpos, int ypos, int scale, float currFrameDelta) {
60   ::drawWithOfs(xpos, ypos, scale, currFrameDelta);
61   if (kissed) {
62     auto oclr = GLVideo.color;
63     GLVideo.color = 0xff_ff_00;
64     level.sprStore.loadFont('sFontSmall');
65     //int scale = 3;
66     level.sprStore.renderText((ix-32)*scale-xpos, (iy-18)*scale-ypos, "MY HERO!", 3);
67     GLVideo.color = oclr;
68   }
71 defaultproperties {
72   imageSpeed = 0.5;
73   active = true;
74   spectral = false;
75   depth = 1;
79 // ////////////////////////////////////////////////////////////////////////// //
80 // dunno, she is an item in the original code
81 class MonsterDamsel['oDamsel'] : ObjCharacter;
83 bool startled;
84 bool calm;
85 bool forVending;
87 int kissCount = 2; // unhurt
89 int whiphits;
90 int stunMax;
93   IDLE = 0;
94   RUN = 1;
95   THROWN = 2;
96   YELL = 3;
97   EXIT = 4;
98   SLAVE = 5;
99   KISS = 6;
100   DEAD = 99;
103 override bool initialize () {
104   if (!::initialize()) return false;
105   setSprite('sDamselLeft');
106   initFixDirection();
107   if (!global.config.optDoubleKiss) kissCount = 1;
108   writeln("*** generated Damsel at tile (", ix/16, ",", iy/16, ")");
109   return true;
114 override void onDestroy () {
115   writeln("*** Damsel is dead");
120 final bool isStatusLessThanExit () {
121   return
122     status == IDLE ||
123     status == RUN ||
124     status == THROWN ||
125     status == YELL;
129 final bool isExiting () {
130   auto spr = getSprite();
131   return (spr.Name == 'sDamselExit2' || spr.Name == 'sPExit');
135 final bool isKissing () {
136   auto spr = getSprite();
137   return (spr.Name == 'sDamselKissL' || spr.Name == 'sPKissL');
141 final bool isStunned () {
142   auto spr = getSprite();
143   return (spr.Name == 'sDamselStunL');
147 override bool onCanBePickedUp (PlayerPawn plr) {
148   return !isExiting();
152 // return `true` to stop player from holding it
153 override bool onTryPickup (PlayerPawn plr) {
154   if (forSale) level.osdMessage("YOU MUST BE IN LOVE!", 3.33);
155   return false;
159 override void onAnimationLooped () {
160   if (isExiting()) {
161     instanceRemove();
162     return;
163   }
164   if (status == YELL) {
165     status = IDLE;
166     counter = 200;
167   } else if (status == KISS) {
168     status = SLAVE;
169   }
173 override bool onExplosionTouch (MapObject xplo) {
174   if (invincible) return false;
175   if (heldBy) heldBy.holdItem = none;
177   writeln("damsel touched by an explosion");
178   if (myGrav == 0) myGrav = 0.6; // spikes fix
180   kissCount = 0; // alas
182   if (xplo.fltx < fltx) xVel = global.randOther(4, 6); else xVel = -global.randOther(4, 6);
183   yVel = -6;
184   burning = 50;
185   hp -= global.config.explosionDmg;
186   spillBlood();
187   //if (hp <= 0 && !leavesBody) instanceRemove();
188   return true;
192 // return `false` to do standard weapon processing
193 override bool onTouchedByPlayerWeapon (PlayerPawn plr, PlayerWeapon wpn) {
194   if (heldBy) return true;
195   //writeln("damsel hit by weapon");
196   if (wpn.slashing) {
197     wpn.hitEnemy = true;
198     //damselDropped = true;
199     kissCount = min(1, kissCount);
200     if (hp > 0) playSound('sndDamsel');
201     yVel = -2;
202     plr.playSound('sndHit');
203     hp -= wpn.damage;
204     spillBlood();
205     if (forSale) {
206       level.scrShopkeeperAnger(GameLevel::SCAnger.DamselWhipped);
207       forSale = false;
208     }
209     if (!dead) {
210       status = THROWN;
211       calm = false;
212       counter = stunMax;
213       imageSpeed = 0.5;
214     }
215   } else if (status != THROWN && (isStatusLessThanExit() || status == SLAVE || status == KISS)) {
216     wpn.hitEnemy = true;
217     //damselDropped = true;
218     kissCount = min(1, kissCount);
219     yVel = -2;
220     plr.playSound('sndHit');
221     if (forSale) {
222       level.scrShopkeeperAnger(GameLevel::SCAnger.DamselWhipped);
223       forSale = false;
224     }
225     if (hp > 0) {
226       writeln("whiphits=", whiphits);
227       playSound('sndDamsel');
228       whiphits += 1;
229       if (global.randOther(1, 30) == 1 || whiphits > 10) {
230         status = THROWN;
231         calm = false;
232         counter = stunMax;
233         imageSpeed = 0.5;
234         //instance_create(x+8, y-8, oBlood);
235         hp -= 1;
236         spillBlood(); //k8: was iy-8; why?
237         whiphits = 0;
238       }
239     }
240   } else {
241     whiphits = 0;
242   }
244   return true;
248 override void onBulletHit (ObjBullet bullet) {
249   writeln("damsel hit by bullet");
250   if (heldBy) heldBy.holdItem = none;
251   countsAsKill = true;
252   spillBlood();
253   hp -= bullet.damage;
255   yVel = -6;
256   status = THROWN;
257   counter = stunMax;
258   //damselDropped = true;
259   kissCount = 0; // alas
261   xVel = bullet.xVel*0.3;
262   if (hp > 0) playSound('sndDamsel');
266 // return `false` to prevent
267 // owner is usually a player
268 override bool onLostAsHeldItem (MapObject owner, LostCause cause, optional float xvel, optional float yvel) {
269   resaleValue = 0;
271   if (cause == LostCause.Whoa) {
272     xVel = (owner.dir == Dir.Left ? -2 : 2);
273     playSound('sndDamsel');
274     return true;
275   }
277   if (cause == LostCause.Drop) {
278     visible = true;
279     resaleValue = 0;
280   } else if (cause == LostCause.Dead || cause == LostCause.Stunned) {
281     visible = true;
282     status = THROWN;
283     counter = stunMax;
284     calm = false;
285   }
287   if (specified_xvel) xVel = xvel;
288   if (specified_yvel) yVel = yvel;
290   return true;
294 override void onBeforeThrowBy (PlayerPawn plr) {
295   writeln("throwing a damsel!");
296   if (plr./*kDown*/scrPlayerIsDucking()) {
297     // drop gently - damsel remains calm after stun
298     status = THROWN;
299     calm = true; // prevents running after recover from stun
300     flty -= 2;
301     counter = round(stunMax/2); //it.stunMax
302   } else {
303     // throw - damsel freaks out after stun
304     status = THROWN;
305     counter = stunMax;
306     flty -= 4;
307     if (hp > 0) playSound('sndDamsel');
308   }
312 void exitAtDoor (MapTile door) {
313   if (!door) return;
314   if (heldBy) heldBy.holdItem = none;
315   if (hp > 0) {
316     // global.plife += 1; // in the original
317     //!if (isRealLevel()) global.damselsSavedTotal += 1;
318     /+!
319     global.damsels += 1;
320     global.xdamsels += 1;
321     global.xdamseldouble = (!hld.damselDropped);
322     door = instance_place(x, y, oExit);
323     +/
324     level.damselSaved = kissCount;
325     door.snapToExit(self);
326     setSprite(global.isDamsel ? 'sPExit' : 'sDamselExit2');
327     status = EXIT;
328     xVel = 0;
329     yVel = 0;
330     playSound('sndSteps');
331     depth = 999;
332     //active = false;
333     spectral = true;
334     canPickUp = false;
335     invincible = true;
336     level.addDamselSaved();
337   } else {
338     status = THROWN;
339   }
343 // return `true` if item hits the character
344 // this is called after item's event
345 override bool onHitByItem (MapItem item) {
346   // are we invincible?
347   if (invincible) return false;
348   // any items will fly thru stunned enemies (but not projectiles)
349   if ((status >= STUNNED || status == THROWN || status == DEAD || dead) && item !isa ItemProjectile) return false;
350   if (item isa ItemProjectile && !ItemProjectile(item).canHarm(self)) return false;
351   // item does no damage?
352   if (!item.damage) return false;
353   // speed check
354   if (fabs(item.xVel) < ItemSpeedToHitX && fabs(item.yVel) < ItemSpeedToHitY) return false;
355   // drop us
356   if (heldBy) heldBy.holdItem = none;
358   if (isStunned()) {
359     if (fabs(xVel) < 1) xVel = (item.xVel < 0 ? -1.5 : 1.5);
360     yVel = (self isa ItemProjectile ? -1.5 : -1.0);
361   } else {
362     xVel = item.xVel*0.3;
363     yVel = (self isa ItemProjectile ? -6.0 : -2.0);
364   }
365   hp -= damage;
366   if (hp <= 0) {
367     auto proj = ItemProjectile(item);
368     if (proj) countsAsKill = proj.launchedByPlayer;
369   }
370   // make some blood
371   if (!dead) spillBlood(amount:1);
372   yVel = -6;
373   status = THROWN;
374   counter = stunMax;
375   //damselDropped = true;
376   //kissCount = min(1, kissCount);
378   item.playSound('sndHit');
380   return true; // it hit us
384 override void thinkFrame () {
385   lightRadius = (!heldBy && forSale && level.isInShop(ix/16, iy/16) ? 64 : 0);
387   if (status == EXIT) {
388     //writeln("exiting!");
389     return;
390   }
392   // check if it is stolen
393   if (heldBy && forSale && !level.isInShop(ix/16, iy/16)) {
394     //writeln("STOLEN!");
395     level.scrShopkeeperAnger(GameLevel::SCAnger.ItemStolen);
396     forSale = false;
397   }
399   // check for exit
400   auto door = level.checkTileAtPoint(ix, iy, &level.cbCollisionExitTile);
401   if (active && door && hp > 0 && status != THROWN) {
402     exitAtDoor(door);
403   }
405   if (isExiting()) {
406     depth = 1000;
407     invincible = true;
408     status = EXIT;
409     return;
410   }
412   if (invincible > 0) invincible -= 1;
414   if (!heldBy) {
415     bool colTop = !!isCollisionTop(1);
416     bool colLeft = !!isCollisionLeft(1);
417     bool colRight = !!isCollisionRight(1);
418     bool colBot = !!isCollisionBottom(1);
420     if (!isCollisionAtPoint(ix, iy)) {
421       //if (self isa ItemProjectileArrow) writeln("xVel=", xVel, "; yVel=", yVel);
422       moveRel(xVel, yVel);
424       if (!colLeft && !colRight) stuck = false;
426       if (!flying /*&& !colBot && !stuck*/) yVel += myGrav;
427       //if (yVel > 8) yVel = 8;
428       yVel = fmin(yVelLimit, yVel);
430       if (colTop && yVel < 0) {
431              if (bounceTop) yVel = -yVel*0.8;
432         else if (fabs(xVel) < 0.0001) yVel = 0;
433       }
435       if (colLeft || colRight) {
436         xVel = (bounce ? -xVel*0.5 : 0.0);
437         myGrav = 0.6;
438       }
440       if (colBot) {
441         myGrav = 0.6;
442         // bounce
443         if (yVel > 1 && bounce) yVel = -yVel*bounceFactor; else yVel = 0;
444         // friction
445              if (fabs(xVel) < 0.1) xVel = 0;
446         else if (fabs(xVel) != 0) xVel *= frictionFactor;
447         if (fabs(yVel) < 1) {
448           shiftY(-1);
449           if (!isCollisionBottom(1)) shiftY(1);
450           yVel = 0;
451         }
452       }
454       if (colLeft && !stuck) {
455         if (!colRight) shiftX(1);
456         //yVel = 0;
457       } else if (colRight && !stuck) {
458         shiftX(-1);
459         //yVel = 0;
460       }
462       if (isCollisionTop(1)) {
463         if (yVel < 0) yVel = -yVel*0.8; else shiftY(1);
464         myGrav = 0.6;
465       }
467       if (isCollisionInRect(ix-3, iy-3, 7, 7, &level.cbCollisionLava)) {
468         myGrav = 0;
469         xVel = 0;
470         yVel = 0;
471         shiftY(0.05);
472       } else {
473         myGrav = 0.6;
474       }
475     } else {
476            if (colTop && !colBot) shiftY(1);
477       else if (colLeft && !colRight) shiftX(1);
478       else if (colRight && !colLeft) shiftX(-1);
479       else { xVel = 0; yVel = 0; }
480     }
481   }
482   // end of item physics
484   int x = ix, y = iy;
486   if (!heldBy) {
487     auto spf = getSpriteFrame();
488     // water
489     auto wtile = level.isWaterAtPoint(ix+spf.width/2, iy+spf.height/2/*, -1, -1*/);
490     if (wtile) {
491       if (!swimming) {
492         level.MakeMapObject(ix+spf.width/2, iy, 'oSplash');
493         swimming = true;
494         playSound('sndSplash');
495       }
496       myGrav = 0.2;
497       if (onFellInWater(wtile) || !isInstanceAlive) return;
498     } else {
499       auto wasSwimming = swimming;
500       swimming = false;
501       myGrav = 0.6;
502       if (wasSwimming) {
503         if (onOutOfWater() || !isInstanceAlive) return;
504       }
505     }
506   }
508   if (!invincible) {
509     if (!heldBy && level.isSolidAtPoint(x, y)) {
510       spillBlood();
511       if (hp > 0 && !dead) { if (countsAsKill) level.addKill(objName); }
512       playSound('sndDamsel');
513       instanceRemove();
514       return;
515     }
517     if (burning > 0) {
518       if (global.randOther(1, 5) == 1) level.MakeMapObject(x+global.randOther(4, 12), y+global.randOther(4, 12), 'oBurn');
519       burning -= 1;
520       //damselDropped = true;
521       kissCount = 0; // alas
522     }
524     if (level.isLavaAtPoint(x, y+6)) {
525       status = DEAD;
526       if (hp > 0 && !dead) { if (countsAsKill) level.addKill(objName); }
527       dead = true;
528       countsAsKill = false;
529       hp = 0;
530       burning = 1;
531       myGrav = 0;
532       xVel = 0;
533       yVel = 0.1;
534       depth = 999;
535       setSprite(global.isDamsel ? 'sDieL' : 'sDamselDieL');
536     }
538     if (yVel > 2) {
539       auto spikes = isCollisionAtPoint(x, y+6, &level.cbCollisionSpikes);
540       if (spikes) {
541         if (!bloodless) spikes.makeBloody();
542         if (hp > 0 && !dead) { if (countsAsKill) level.addKill(objName); }
543         spillBlood();
544         setSprite(global.isDamsel ? 'sDieL' : 'sDamselDieL');
545         status = DEAD;
546         dead = true;
547         countsAsKill = false;
548         hp = 0;
549         myGrav = 0;
550         xVel = 0;
551         yVel = 0.2;
552       }
553     }
554     if (myGrav == 0 && fabs(yVel) < 0.01) myGrav = 0.6;
556     /*!
557     if (collision_rectangle(x-3, y-3, x+3, y+3, oSpearsLeft, 0, 0) and (status != THROWN or isCollisionBottom(1))) {
558       //damselDropped = true;
559       kissCount = min(1, kissCount);
560       obj = instance_nearest(x, y, oSpearsLeft);
561       if (obj.image_index >= 19 and obj.image_index < 28) {
562         if (held) {
563           held = false;
564           with (oPlayer1) { holdItem = 0; pickupItemType = ""; }
565         }
566         hp -= 2;
567         status = THROWN;
568         calm = false;
569         counter = stunMax;
570         yVel = -6;
571         if (obj.x < x) xVel = 4; else xVel = -4;
572         image_speed = 0.5;
573         playSound(global.sndHit);
574         instance_create(other.x+8, other.y+8, oBlood);
575       }
576     }
577     */
578   }
580   if (!heldBy && yVel > 2 && status != THROWN) {
581     status = THROWN;
582     calm = false;
583     if (hp > 0) {
584       //writeln("SNDDAMSEL!");
585       playSound('sndDamsel');
586     }
587   }
589   if (heldBy) {
590     //setSprite('sDamselStunL');
591     /*if (!(dead || status == DEAD))*/ setSprite(global.isDamsel ? 'sDieLBounce' : 'sDamselBounceL');
592     //setSprite('sDamselFallL');
593     whiphits = 0;
594     if (!startled) {
595       //!if (isRealLevel()) global.damselsGrabbed += 1; // why was this in status == THROWN?
596       startled = true;
597     }
598     dir = level.player.dir;
599   } else if (status == SLAVE) {
600     dir = (level.player.ix < x ? Dir.Left : Dir.Right);
601     setSprite(global.isDamsel ? 'sStandLeft' : 'sDamselLeft');
602   } else if (status == KISS) {
603     whiphits = 0;
604     if (isKissing() && int(imageFrame) == 7) {
605       level.MakeMapObject(x+(dir == Dir.Left ? -8 : 8), y-8, 'oHeart');
606       playSound('sndKiss');
607     }
608   } else if (status == IDLE) {
609     setSprite(global.isDamsel ? 'sStandLeft' : 'sDamselLeft');
610     if (counter > 0) {
611       counter -= 1;
612     } else {
613       /*
614       int pan = 0;
615       if (global.isDamsel) sprite_index = sYellLeft; else sprite_index = sDamselYellL;
616            if (level.player.x < x) pan = 1;
617       else if (level.player.x > x) pan = -1;
618       else pan = 0;
619       playSound(global.sndDamsel, 1, pan);
620       */
621       status = YELL;
622       setSprite(global.isDamsel ? 'sYellLeft' : 'sDamselYellL');
623       // pan it slightly if damsel is too far away
624       auto plr = level.player;
625       if (distanceToEntityCenter(plr) <= 16*7) {
626         playSound('sndDamsel');
627       } else {
628         int px = plr.xCenter, py = plr.yCenter;
629         int dx = sign(xCenter-px);
630         int dy = sign(yCenter-py);
631         global.playSound(0, 96*dx, 96*dy, 'sndDamsel');
632       }
633     }
634   } else if (status == YELL) {
635     if (int(imageFrame) == 4) level.MakeMapObject(x, y-16, 'oYellHelp');
636   } else if (status == RUN) {
637     //damselDropped = true;
638     kissCount = min(1, kissCount);
639     whiphits = 0;
640     imageSpeed = 0.8;
641     setSprite(global.isDamsel ? 'sRunLeft' : 'sDamselRunL');
642     if (dir == Dir.Left && isCollisionLeft(2)) dir = Dir.Right;
643     if (dir == Dir.Right && isCollisionRight(2)) dir = Dir.Left;
644     xVel = (dir == Dir.Left ? -1.5 : 1.5);
645   } else if (status == THROWN) {
646     //damselDropped = true;
647     whiphits = 0;
648     // setCollisionBounds(-4, -2, 4, 2); // commented out in the original
649     if (yVel > 2) calm = false; // if player has tried to set damsel down gently but she falls, panic when recover from stun
650     if (!calm) kissCount = min(1, kissCount);
652     if (global.isDamsel) {
653       if (xVel == 0) {
654         setSprite('sStunL');
655       } else if (bounced) {
656         setSprite(yVel < 0 ? 'sDieLBounce' : 'sDieLFall');
657       } else {
658         setSprite(xVel < 0 ? 'sDieLL' : 'sDieLR');
659       }
660     } else {
661       if (xVel == 0) {
662         setSprite('sDamselStunL');
663       } else if (bounced) {
664         setSprite(yVel < 0 ? 'sDamselBounceL' : 'sDamselFallL');
665       } else {
666         setSprite(xVel < 0 ? 'sDamselDieLL' : 'sDamselDieLR');
667       }
668     }
670     if (isCollisionBottom(1) && !bounced) bounced = true;
672     if (isCollisionBottom(2) || level.isObjectInRect(x-4, y-6, 9, 15, &level.cbIsObjectWeb)) {
673       if (!dead) {
674         if (counter > 0) {
675           counter -= 1;
676         } else {
677           if (calm) {
678             if (cost > 0 && forSale && level.isInShop(x, y)) {
679               // kissing booth girl, resume SLAVE state
680               status = SLAVE;
681               calm = false;
682             } else {
683               // resume IDLE state after been dropped gently
684               status = IDLE;
685               counter = 200;
686               calm = false;
687             }
688           } else {
689             status = RUN;
690           }
691         }
692       }
694       if (hp <= 0) {
695         setSprite(global.isDamsel ? 'sDieL' : 'sDamselDieL');
696         status = DEAD;
697         if (!dead) {
698           dead = true;
699           if (countsAsKill) level.addKill(objName);
700         }
701       }
702     }
703     if (isStunned()) { // YASM 1.8.1
704       if (counter > 0 && counter < 30) imageSpeed = 0.8;
705     } else {
706       imageSpeed = 0.5;
707     }
708   } else if (status == DEAD) {
709     if (hp > 0) hp = 0;
710     if (!dead) {
711       dead = true;
712       if (countsAsKill) level.addKill(objName);
713     }
714     setSprite(global.isDamsel ? 'sDieL' : 'sDamselDieL');
715   }
716   // this fixes held sprite
717   if (hp <= 0 && status != THROWN && status != DEAD && status != STUNNED) status = THROWN;
719   // sacrifice
720   if (status == THROWN || status == DEAD) {
721     if (checkAndPerformSacrifice()) return;
722   } else {
723     sacCount = default.sacCount;
724   }
726   // moving projectile
727   if ((status == THROWN || status == DEAD || dead) && (fabs(xVel) > 2 || fabs(yVel) > 2) && !spectral) {
728     spectral = true;
729     level.isObjectInRect(x0, y0, width, height, delegate bool (MapObject o) {
730       if (o == self) return false;
731       if (!o.dead && !o.stunned && o.status < STUNNED && o.status != THROWN) {
732         if (!o.collidesWith(self)) return false;
733         onStunnedHitEnemy(MapEnemy(o));
734         return false;
735       }
736       return false;
737     }, precise:false, castClass:MapEnemy);
738     spectral = false;
739   }
741   // remove corpse
742   if (status == DEAD && removeCorpse) {
743     if (deathTimer > 0) {
744       deathTimer -= 1;
745     } else {
746       spillBlood();
747       instanceRemove();
748     }
749   }
753 defaultproperties {
754   objName = 'Damsel';
755   desc = "Damsel";
756   desc2 = "An unfortunate creature, underfed and driven half-insane by life in caverns and brothels. Perhaps you could free them.";
758   setCollisionBounds(-4, -4, 4, 8);
759   imageSpeed = 0.5;
760   trigger = true;
761   startled = false;
762   calm = false; // if calm, damsel will return to idle state after recovering from stun, otherwise she will run
763   invincible = false;
764   swimming = false;
765   heavy = true;
766   //damselDropped = false;
767   //!cost = getKissValue()*3;
768   //!buyMessage = "I'LL LET YOU HAVE H";
769   //!if (global.isDamsel) buyMessage = buyMessage+"IM"; else buyMessage = buyMessage+"ER";
770   //!buyMessage = buyMessage+" FOR $"+string(cost)+"!";
772   hp = 4;
773   bloodLeft = 4;
774   favor = 8;
775   sacCount = 20;
777   /*
778   IDLE = 0;
779   RUN = 1;
780   THROWN = 2;
781   YELL = 3;
782   EXIT = 4;
783   SLAVE = 5;
784   KISS = 6;
785   DEAD = 99;
786   */
788   dead = false;
789   status = IDLE;
790   whiphits = 0;
792   dir = Dir.Left;
794   bounced = false;
795   burning = false;
796   counter = 200;
797   stunMax = 120;
799   invincible = 0;
801   removeCorpse = false; // only applies to enemies that have corpses (Caveman, Yeti, etc.)
802   deathTimer = 200; // how many steps after death until corpse is removed
804   countsAsKill = true; // sometimes it's not the player's fault!
806   canPickUp = true;
808   canBeHitByBullet = true;
809   canBeNudged = false;