option to spawn ghost on the first level
[k8vacspelynky.git] / mapent / MapCharacter.vc
blobe9927b5357b1c4f27a9603b5a592df08621ed087
1 /**********************************************************************************
2  * Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC
3  * Copyright (c) 2018, Ketmar Dark
4  *
5  * This file is part of Spelunky.
6  *
7  * You can redistribute and/or modify Spelunky, including its source code, under
8  * the terms of the Spelunky User License.
9  *
10  * Spelunky is distributed in the hope that it will be entertaining and useful,
11  * but WITHOUT WARRANTY.  Please see the Spelunky User License for more details.
12  *
13  * The Spelunky User License should be available in "Game Information", which
14  * can be found in the Resource Explorer, or as an external file called COPYING.
15  * If not, please obtain a new copy of Spelunky from <http://spelunkyworld.com/>
16  *
17  **********************************************************************************/
18 // ////////////////////////////////////////////////////////////////////////// //
19 // dunno, she is an item in the original code
20 class ObjCharacter : MapObject;
22 int holdXOfs, holdYOfs;
25 // turn them around if they are looking at a wall
26 final void initFixDirection () {
27        if (level.isSolidAtPoint(ix-4, iy+8)) dir = Dir.Right;
28   else if (level.isSolidAtPoint(ix+16+4, iy+8)) dir = Dir.Left;
32 override bool onExplosionTouch (MapObject xplo) {
33   if (invincible) return false;
34   if (xplo.fltx < fltx) xVel = global.randOther(4, 6); else xVel = -global.randOther(4, 6);
35   yVel = -6;
36   burning = 50;
37   if (!bloodless && bloodLeft > 0) {
38     scrCreateBlood(ix, iy, 1);
39     if (hp <= 0) --bloodLeft;
40   }
41   hp -= global.config.explosionDmg;
42   //if (hp <= 0 && !leavesBody) instanceRemove();
43   return true;
47 // return `false` to do standard weapon processing
48 override bool onTouchedByPlayerWeapon (PlayerPawn plr, PlayerWeapon wpn) {
49   if (heldBy) return false;
50   if (!bloodless && bloodLeft > 0) {
51     wpn.hitEnemy = true;
52     scrCreateBlood(ix+8, iy-8, 1);
53     if (hp <= 0) --bloodLeft;
54   }
55   hp -= 1;
56   return true;
60 override void onBulletHit (ObjBullet bullet) {
61   if (heldBy) heldBy.holdItem = none;
62   countsAsKill = true;
63   spillBlood();
64   hp -= bullet.damage;
65   yVel = -6;
66   xVel = bullet.xVel*0.3;
70 override void fixHoldCoords () {
71   if (heldBy) {
72     xVel = 0;
73     yVel = 0;
74     xAcc = 0;
75     yAcc = 0;
76     int dx = (heldBy.dir == Dir.Left ? -4 : 4);
77     int dy = (heldBy.status == DUCKING && fabs(heldBy.xVel) < 2 ? 4 : 0);
78     dx += holdXOfs;
79     dy += holdYOfs;
80     setXY(heldBy.fltx+dx, heldBy.flty+dy);
81     prevFltX = heldBy.prevFltX+dx;
82     prevFltY = heldBy.prevFltY+dy;
83     updateGrid();
84     if (spriteRName) dir = heldBy.dir;
85   }
89 // virtual
90 // return `true` if item hits the character
91 // this is called after item's event
92 bool onItemHit (MapItem proj) {
93   return false;
97 // return `true` if this entity can be sacrificed
98 override bool canBeSacrificed (MapTile altar) {
99   return !bloodless;
103 override bool onSacrificed (MapTile altar) {
104   level.performSacrifice(self, altar);
105   return true;
109 override void onStunnedHitEnemy (MapEnemy enemy) {
110   if (enemy isa EnemyMagmaMan) return;
111   if (enemy.status < STUNNED) enemy.xVel = xVel;
112   enemy.countsAsKill = true;
113   enemy.spillBlood();
114   enemy.hp -= 1;
115   if (enemy.canBeStunned) {
116     enemy.status = STUNNED;
117     enemy.counter = 50;
118   }
119   //!enemy.origX = x;
120   //!enemy.origY = y;
121   enemy.playSound('sndHit');
122   //enemy.xVel = xVel*0.3; // was commented in the original
123   //!if (type == "Arrow" or type == "Fish Bone") instance_destroy();
127 override void onStunnedHitCharacter (ObjCharacter chr) {
128   if (chr.status < STUNNED) chr.xVel = xVel;
129   chr.countsAsKill = true;
130   chr.spillBlood();
131   chr.hp -= 1;
132   if (chr isa MonsterDamsel) {
133     chr.status = THROWN;
134     chr.counter = 50;
135   }
136   //!enemy.origX = x;
137   //!enemy.origY = y;
138   playSound('sndHit');
139   //enemy.xVel = xVel*0.3; // was commented in the original
140   //!if (type == "Arrow" or type == "Fish Bone") instance_destroy();
144 override void thinkFrame () {
145   // remove corpse
146   if (status == DEAD && removeCorpse) {
147     if (deathTimer > 0) {
148       deathTimer -= 1;
149     } else {
150       if (!bloodless && bloodLeft > 0) scrCreateBlood(ix+8, iy+8, 3);
151       instanceRemove();
152     }
153   }
157 defaultproperties {
158   objName = 'BaseCharacter';
160   //setCollisionBounds(-4, -4, 4, 8);
161   imageSpeed = 0.5;
162   //trigger = true;
163   //startled = false;
164   swimming = false;
165   heavy = true;
166   bloodLeft = 4;
167   dead = false;
168   status = IDLE;
169   dir = Dir.Left;
170   bounced = false;
171   burning = false;
172   removeCorpse = false; // only applies to enemies that have corpses (Caveman, Yeti, etc.)
173   deathTimer = 200; // how many steps after death until corpse is removed
174   canPickUp = true;
175   canBeHitByBullet = true;
176   canBeNudged = false;
178   activeWhenHeld = true; // for animation
179   spectralWhenHeld = false;