5 * Created by Alyssa Milburn on Thu 10 Mar 2005.
6 * Copyright (c) 2005-2006 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
19 /* 20th feb 2005, by fuzzie
20 with thanks to vadim for helping out
23 * hair tidiness/untidiness (offset by 0 to 2 multiplied by 16)
24 * render hairs/ears .. at the moment we avoid them, we lack zorder sanity too
25 * support clothes parts
26 * interpolate between differing poses, since c2e seems to
29 #include "SkeletalCreature.h"
35 #include "creaturesImage.h"
37 #include <typeinfo> // TODO: remove when genome system is fixed
38 #include <boost/format.hpp>
46 bodypartinfo cee_bodyparts
[17] = {
47 { 'b', -1, -1 }, // body
48 { 'a', 0, 0 }, // head - attached to body
49 { 'c', 0, 1 }, // left thigh - attached to body
50 { 'f', 0, 2 }, // right thigh - attached to body
51 { 'i', 0, 3 }, // left humerus - attached to body
52 { 'k', 0, 4 }, // right humerus - attached to body
53 { 'm', 0, 5 }, // tail root - attached to body
54 { 'd', 2, 1 }, // left shin - attached to left thigh
55 { 'g', 3, 1 }, // right shin - attached to right thigh
56 { 'j', 4, 1 }, // left radius - attached to left humerus
57 { 'l', 5, 1 }, // right radius - attached to right humerus
58 { 'e', 7, 1 }, // left foot - attached to left shin
59 { 'h', 8, 1 }, // right foot - attached to right shin
60 { 'n', 6, 1 }, // tail tip - attached to tail root
61 // Creatures Village only:
62 { 'o', 1, 2 }, // left ear - attached to head
63 { 'p', 1, 3 }, // right ear - attached to head
64 { 'q', 1, 4 } // hair - attached to head
67 unsigned int cee_zorder
[4][17] = {
68 { 6, 13, 2, 7, 11, 4, 9, 0, 14, 1, 16, 15, 5, 3, 8, 10, 12 },
69 { 6, 13, 3, 8, 12, 5, 10, 0, 15, 1, 16, 14, 2, 7, 11, 4, 9 },
70 { 6, 13, 2, 4, 9, 5, 3, 7, 8, 10, 0, 11, 12, 16, 14, 15, 1 },
71 { 2, 4, 9, 1, 14, 15, 16, 5, 3, 7, 8, 10, 11, 12, 0, 6, 13 }
74 // needed for setPose(string) at least .. maybe cee_bodyparts should be indexed by letter
75 unsigned int cee_lookup
[17] = { 1, 0, 2, 7, 11, 3, 8, 12, 4, 9, 5, 10, 6, 13, 14, 15, 16 };
77 int SkeletalPartCount() {
78 if (world
.gametype
== "cv")
84 SkeletalCreature::SkeletalCreature(unsigned char _family
) : CreatureAgent(_family
) {
94 if (engine
.version
== 1) {
95 setAttributes(64 + 4 + 2); // mouseable, activateable, groundbound(?!)
96 } else if (engine
.version
== 2) {
97 setAttributes(128 + 64 + 4 + 2); // mouseable, activateable, suffersphysics, sufferscollisions
98 // default values from original engine
105 for (unsigned int i
= 0; i
< 6; i
++) appearancegenes
[i
] = 0;
107 skeleton
= new SkeletonPart(this);
110 SkeletalCreature::~SkeletalCreature() {
114 std::string
SkeletalCreature::dataString(unsigned int _stage
, bool tryfemale
, unsigned int dataspecies
, unsigned int databreed
) {
115 char _postfix
[4] = "XXX";
116 _postfix
[0] = '0' + dataspecies
+ (tryfemale
? 4 : 0);
117 _postfix
[1] = '0' + _stage
;
118 if (engine
.version
== 1)
119 _postfix
[2] = '0' + databreed
;
121 _postfix
[2] = 'a' + databreed
;
125 void SkeletalCreature::processGenes() {
126 shared_ptr
<genomeFile
> genome
= creature
->getGenome();
128 for (vector
<gene
*>::iterator i
= genome
->genes
.begin(); i
!= genome
->genes
.end(); i
++) {
129 if (!creature
->shouldProcessGene(*i
)) continue;
131 if (typeid(*(*i
)) == typeid(creatureAppearanceGene
)) {
132 creatureAppearanceGene
*x
= (creatureAppearanceGene
*)(*i
);
133 if (x
->part
> 5) continue;
134 appearancegenes
[x
->part
] = x
;
135 } else if (typeid(*(*i
)) == typeid(creaturePoseGene
)) {
136 creaturePoseGene
*x
= (creaturePoseGene
*)(*i
);
137 posegenes
[x
->poseno
] = x
;
138 } else if (typeid(*(*i
)) == typeid(creatureGaitGene
)) {
139 creatureGaitGene
*x
= (creatureGaitGene
*)(*i
);
140 gaitgenes
[x
->drive
] = x
;
145 creatureAppearanceGene
*SkeletalCreature::appearanceGeneForPart(char x
) {
146 // TODO: tail madness?
148 if (x
== 'a' || x
>= 'o') {
150 return appearancegenes
[0];
151 } else if (x
== 'b') {
153 return appearancegenes
[1];
154 } else if (x
>= 'c' && x
<= 'h') {
156 return appearancegenes
[2];
157 } else if (x
>= 'i' && x
<= 'l') {
159 return appearancegenes
[3];
160 } else if (x
== 'm' || x
== 'n') {
162 return appearancegenes
[4];
168 void SkeletalCreature::skeletonInit() {
169 // TODO: the exception throwing in here needs some more thought
170 // TODO: if we throw an exception when we need to kill the creature off, else segfault :/
172 for (int i
= 0; i
< SkeletalPartCount(); i
++) { // CV hackery
173 if (engine
.version
== 1) // TODO: this is hackery to skip tails for C1
174 if (i
== 6 || i
== 13) continue;
176 // reset the existing image reference
179 // find the relevant gene
180 char x
= cee_bodyparts
[i
].letter
;
181 creatureAppearanceGene
*partapp
= appearanceGeneForPart(x
);
183 int partspecies
, partvariant
;
184 partspecies
= creature
->getGenus();
186 if (engine
.version
> 1) partspecies
= partapp
->species
;
187 partvariant
= partapp
->variant
;
189 partvariant
= creature
->getVariant();
192 int spe
= partspecies
;
195 * In order to find a sprite, we try our current stage first, then the stages below us,
196 * and then we try again with all lower variants, and if that fails for female sprites
197 * then we try the whole thing again but trying to find a male sprite.
200 // find relevant sprite
201 bool tryfemale
= creature
->isFemale();
203 int var
= partvariant
;
204 while (var
> -1 && !images
[i
]) {
205 int stage_to_try
= creature
->getStage();
206 while (stage_to_try
> -1 && !images
[i
]) {
207 images
[i
] = world
.gallery
.getImage(x
+ dataString(stage_to_try
, tryfemale
, spe
, var
));
212 if (!tryfemale
) break;
216 throw creaturesException(boost::str(boost::format("SkeletalCreature couldn't find an image for part %c of species %d, variant %d, stage %d") % x
% (int)partspecies
% (int)partvariant
% (int)creature
->getStage()));
218 // find relevant ATT data
219 std::string attfilename
;
220 int var
= partvariant
;
221 while (var
> -1 && attfilename
.empty()) {
222 int stage_to_try
= creature
->getStage();
223 while (stage_to_try
> -1 && attfilename
.empty()) {
224 attfilename
= world
.findFile(std::string("/Body Data/") + x
+ dataString(stage_to_try
, false, spe
, var
) + ".att");
229 if (attfilename
.empty())
230 throw creaturesException(boost::str(boost::format("SkeletalCreature couldn't find body data for part %c of species %d, variant %d, stage %d") % x
% (int)partspecies
% (int)partvariant
% creature
->getStage()));
233 std::ifstream
in(attfilename
.c_str());
235 throw creaturesException(boost::str(boost::format("SkeletalCreature couldn't load body data for part %c of species %d, variant %d, stage %d (tried file %s)") % x
% (int)partspecies
% (int)partvariant
% creature
->getStage() % attfilename
));
238 images
[i
] = tintBodySprite(images
[i
]);
244 shared_ptr
<creaturesImage
> SkeletalCreature::tintBodySprite(shared_ptr
<creaturesImage
> s
) {
245 // TODO: don't bother tinting if we don't need to
247 // TODO: work out tinting for other engine versions
248 if (engine
.version
> 2) {
249 shared_ptr
<creaturesImage
> newimage
= s
->mutableCopy();
250 newimage
->tint(creature
->getTint(0), creature
->getTint(1), creature
->getTint(2), creature
->getTint(3), creature
->getTint(4));
257 void SkeletalCreature::render(Surface
*renderer
, int xoffset
, int yoffset
) {
258 for (int j
= 0; j
< 17; j
++) {
259 int i
= cee_zorder
[posedirection
][j
];
260 if (i
>= SkeletalPartCount()) continue; // CV hackery
262 if (engine
.version
== 1) // TODO: this is hackery to skip tails for C1
263 if (i
== 6 || i
== 13) continue;
265 bodypartinfo
*part
= &cee_bodyparts
[i
];
267 unsigned int ourpose
= pose
[i
];
270 // TODO: ack, move this check out of the loop
271 if (i
!= 14 && i
!= 15 && world
.variables
["engine_mirror_creature_body_parts"] == 1 && ourpose
>= 4 && ourpose
<= 7) {
276 // adjust for pregnancy/facial expressions/etc as necessary
277 if (part
->parent
== -1) // body
278 ourpose
+= (pregnancy
* 16);
279 else if (i
== 1) // head
280 ourpose
+= (eyesclosed
? 16 : 0) + (facialexpression
* 32);
281 else if (i
== 16) // hair
282 ourpose
+= 0; // TODO: 16 * hair
286 renderer
->render(images
[i
], ourpose
, partx
[i
] + adjustx
+ xoffset
, party
[i
] + adjusty
+ yoffset
, false, 0, mirror
);
289 // TODO: we draw a lot of points twice here :)
290 int atx
= attachmentX(i
, 0) + xoffset
, aty
= attachmentY(i
, 0) + yoffset
;
291 renderer
->renderLine(atx
- 1, aty
, atx
+ 1, aty
, 0xFF0000CC);
292 renderer
->renderLine(atx
, aty
- 1, atx
, aty
+ 1, 0xFF0000CC);
293 atx
= attachmentX(i
, 1) + xoffset
; aty
= attachmentY(i
, 1) + yoffset
;
294 renderer
->renderLine(atx
- 1, aty
, atx
+ 1, aty
, 0xFF0000CC);
295 renderer
->renderLine(atx
, aty
- 1, atx
, aty
+ 1, 0xFF0000CC);
300 int SkeletalCreature::attachmentX(unsigned int part
, unsigned int id
) {
301 return partx
[part
] + att
[part
].attachments
[pose
[part
]][0 + (id
* 2)] + adjustx
;
304 int SkeletalCreature::attachmentY(unsigned int part
, unsigned int id
) {
305 return party
[part
] + att
[part
].attachments
[pose
[part
]][1 + (id
* 2)] + adjusty
;
308 void SkeletalCreature::recalculateSkeleton() {
309 int lowestx
= 0, lowesty
= 0, highestx
= 0, highesty
= 0;
311 for (int i
= 0; i
< SkeletalPartCount(); i
++) {
312 if (engine
.version
== 1) // TODO: this is hackery to skip tails for C1
313 if (i
== 6 || i
== 13) continue;
315 bodypartinfo
*part
= &cee_bodyparts
[i
];
317 if (part
->parent
== -1) {
318 partx
[i
] = 0; party
[i
] = 0;
320 attFile
&bodyattinfo
= att
[0];
321 attFile
&attinfo
= att
[i
];
323 int attachx
= att
[i
].attachments
[pose
[i
]][0];
324 int attachy
= att
[i
].attachments
[pose
[i
]][1];
327 if (part
->parent
== 0) { // linking to body
328 x
= bodyattinfo
.attachments
[pose
[0]][part
->attorder
* 2];
329 y
= bodyattinfo
.attachments
[pose
[0]][(part
->attorder
* 2) + 1];
330 } else { // extra limb
331 attFile
&parentattinfo
= att
[part
->parent
];
333 x
= partx
[part
->parent
] + parentattinfo
.attachments
[pose
[part
->parent
]][part
->attorder
* 2];
334 y
= party
[part
->parent
] + parentattinfo
.attachments
[pose
[part
->parent
]][(part
->attorder
* 2) + 1];
340 partx
[i
] = x
; party
[i
] = y
;
342 if (x
< lowestx
) { lowestx
= x
; }
343 if (y
< lowesty
) { lowesty
= y
; }
344 if (x
+ (int)images
[i
]->width(pose
[i
]) > highestx
) { highestx
= x
+ images
[i
]->width(pose
[i
]); }
345 if (y
+ (int)images
[i
]->height(pose
[i
]) > highesty
) { highesty
= y
+ images
[i
]->height(pose
[i
]); }
352 // TODO: muh, we should cooperate with physics system etc
353 if (!carriedby
&& !invehicle
&& calculated
) {
354 int orig_footpart
= (downfoot_left
? 11 : 12);
355 // adjust location to match foot
356 moveTo(x
- (attachmentX(orig_footpart
, 1) - oldfootx
), y
- (attachmentY(orig_footpart
, 1) - oldfooty
));
359 // work out which foot is down
360 int leftfoot
= attachmentY(11, 1);
361 int rightfoot
= attachmentY(12, 1);
362 downfoot_left
= (rightfoot
< leftfoot
);
365 int orig_footpart
= (downfoot_left
? 11 : 12);
366 oldfootx
= attachmentX(orig_footpart
, 1);
367 oldfooty
= attachmentY(orig_footpart
, 1);
369 // recalculate width/height
370 height
= downfoot_left
? leftfoot
: rightfoot
;
371 width
= 50; // TODO: arbitary values bad
373 // TODO: muh, we should cooperate with physics system etc
374 /*if (carriedby || invehicle)
375 downfootroom.reset();
380 void SkeletalCreature::snapDownFoot() {
381 // TODO: this isn't very well thought-out.
383 int orig_footpart
= (downfoot_left
? 11 : 12);
384 float footx
= x
+ attachmentX(orig_footpart
, 1);
385 float footy
= y
+ attachmentY(orig_footpart
, 1);
387 shared_ptr
<Room
> newroom
;
390 if (downfootroom
->containsPoint(footx
, footy
)) {
391 newroom
= downfootroom
;
393 if (downfootroom
->x_left
<= footx
&& downfootroom
->x_right
>= footx
) {
394 newroom
= downfootroom
; // TODO, we're just forcing for now
396 float ydiff
= 10000.0f
; // TODO: big number
397 for (std::map
<weak_ptr
<Room
>,RoomDoor
*>::iterator i
= downfootroom
->doors
.begin(); i
!= downfootroom
->doors
.end(); i
++) {
398 shared_ptr
<Room
> thisroom
= i
->first
.lock();
399 if (engine
.version
== 2 && size
.getInt() > i
->second
->perm
) continue;
400 if (thisroom
->x_left
<= footx
&& thisroom
->x_right
>= footx
) {
401 float thisydiff
= fabs(footy
- thisroom
->floorYatX(footx
));
402 if (thisydiff
< ydiff
) {
411 newroom
= bestRoomAt(footx
, footy
, 3, shared_ptr
<Room
>());
413 // insane emergency handling
414 float newfooty
= footy
;
415 while (!newroom
&& newfooty
> (footy
- 500.0f
)) {
416 newroom
= world
.map
.roomAt(footx
, newfooty
);
420 // TODO: give up here
425 bool newroomchosen
= (newroom
!= downfootroom
) && downfootroom
;
426 bool hadroom
= (downfootroom
);
427 downfootroom
= newroom
;
429 if (!downfootroom
/*|| !falling */) {
430 // TODO: hackery to cope with scripts moving us, this needs handling correctly somewhere
431 if (fabs(lastgoodfootx
- attachmentX(orig_footpart
, 1) - x
) > 50.0f
|| fabs(lastgoodfooty
- attachmentY(orig_footpart
, 1) - y
) > 50.0f
) {
432 downfootroom
= bestRoomAt(footx
, footy
, 3, shared_ptr
<Room
>());
437 std::cout
<< "Creature out of room system at (" << footx
<< ", " << footy
<< ")!" << std::endl
;
438 // TODO: exceptiony death?
443 // We fell out of the room system! How did that happen? Push ourselves back in, run collision script.
444 std::cout
<< "Creature out of room system at (" << footx
<< ", " << footy
<< "), pushing it back in." << std::endl
;
447 x
= lastgoodfootx
- attachmentX(orig_footpart
, 1);
448 footx
= lastgoodfootx
;
449 footy
= lastgoodfooty
;
450 downfootroom
= world
.map
.roomAt(footx
, footy
);
454 std::cout
<< "no down foot room! (" << footx
<< ", " << footy
<< ")" << std::endl
;
455 // TODO: exceptiony death
460 bool belowfloor
= false;
461 float newy
= downfootroom
->floorYatX(footx
);
462 if (engine
.version
== 2 && hadroom
&& y
> newy
) {
463 // TODO: hilar hack: cope with walking below floors
465 newy
= downfootroom
->bot
.pointAtX(footx
).y
;
468 if (engine
.version
> 1) {
469 // TODO: hilar hack: enable gravity if we're snapping by much
470 if (newroomchosen
&& abs(y
- (newy
- (footy
- y
))) > 20) {
471 if (engine
.version
== 2) grav
.setInt(1);
477 moveTo(x
, newy
- (footy
- y
));
479 lastgoodfootx
= footx
;
480 lastgoodfooty
= footy
;
482 if (engine
.version
> 2) {
483 if (engine
.version
== 2 && !belowfloor
&& downfootroom
->floorpoints
.size()) {
484 // TODO: hilar hack: same as above for floorvalue
485 if (size
.getInt() <= downfootroom
->floorvalue
.getInt()) {
490 // TODO: hilar hack: same as above for perm
491 shared_ptr
<Room
> downroom
= world
.map
.roomAt(footx
, downfootroom
->y_left_floor
+ 1);
492 if (downfootroom
->doors
.find(downroom
) != downfootroom
->doors
.end()) {
493 int permsize
= (engine
.version
== 2 ? size
.getInt() : perm
);
494 if (permsize
<= downfootroom
->doors
[downroom
]->perm
) {
495 if (engine
.version
== 2) grav
.setInt(1);
504 void SkeletalCreature::setPose(unsigned int p
) {
506 for (int i
= 0; i
< SkeletalPartCount(); i
++)
508 recalculateSkeleton();
511 void SkeletalCreature::setPose(std::string s
) {
515 case 0: break; // north, TODO
516 case 1: break; // south, TODO
517 case 2: posedirection
= 0; break; // right
518 case 3: posedirection
= 1; break; // left
519 default: assert(false);
524 case 0: break; // north, TODO
525 case 1: break; // south, TODO
526 case 2: posedirection
= 1; break; // right
527 case 3: posedirection
= 0; break; // left
528 default: assert(false);
531 case '0': posedirection
= 3; break;
532 case '1': posedirection
= 2; break;
533 case '2': posedirection
= 0; break;
534 case '3': posedirection
= 1; break;
535 case 'X': break; // do nothing
537 std::cout
<< "internal warning: SkeletalCreature::setPose didn't understand direction " << s
[0] << " in pose '" << s
<< "'." << std::endl
;
541 for (int i
= 0; i
< 14; i
++) {
545 case '0': newpose
= 0 + (posedirection
* 4); break;
546 case '1': newpose
= 1 + (posedirection
* 4); break;
547 case '2': newpose
= 2 + (posedirection
* 4); break;
548 case '3': newpose
= 3 + (posedirection
* 4); break;
549 case '?': assert(i
== 0); {
550 // make the head look in the posedirection of _IT_
551 float attachmenty
= attachmentY(1, 0) + y
; // head attachment point, which we'll use to 'look' from atm
553 // TODO: this is horrible, but i have no idea how the head angle is calculated
554 AgentRef attention
= creature
->getAttentionFocus();
555 if (attention
&& attention
->y
> (attachmenty
+ 30)) newpose
= 0;
556 else if (attention
&& attention
->y
< (attachmenty
- 70)) newpose
= 3;
557 else if (attention
&& attention
->y
< (attachmenty
- 30)) newpose
= 2;
559 newpose
+= (posedirection
* 4);
563 case 'X': continue; // do nothing
565 std::cout
<< "internal warning: SkeletalCreature::setPose didn't understand " << s
[i
+ 1] << " in pose '" << s
<< "'." << std::endl
;
569 pose
[cee_lookup
[i
]] = newpose
;
571 // TODO: this is some hackery for CV,
572 if (world
.gametype
!= "cv") continue;
573 if (i
== 0) { // head
574 pose
[14] = newpose
; pose
[15] = newpose
; // ears
575 pose
[16] = newpose
; // hair
577 pose
[6] = newpose
; // tail root
578 pose
[13] = newpose
; // tail tip
582 recalculateSkeleton();
585 void SkeletalCreature::setPoseGene(unsigned int poseno
) {
586 std::map
<unsigned int, creaturePoseGene
*>::iterator i
= posegenes
.find(poseno
);
587 if (i
== posegenes
.end()) return; // TODO: is there a better behaviour here?
589 creaturePoseGene
*g
= i
->second
;
590 assert(g
->poseno
== poseno
);
592 walking
= false; // TODO: doesn't belong here, does it? really the idea of a 'walking' bool is horrid
593 setPose(g
->getPoseString());
596 void SkeletalCreature::setGaitGene(unsigned int gaitdrive
) { // TODO: not sure if this is *useful*
597 std::map
<unsigned int, creatureGaitGene
*>::iterator i
= gaitgenes
.find(gaitdrive
);
598 if (i
== gaitgenes
.end()) return; // TODO: is there a better behaviour here?
600 creatureGaitGene
*g
= i
->second
;
601 assert(g
->drive
== gaitdrive
);
603 if (g
== gaitgene
) return;
605 // reset our gait details to default
608 skeleton
->animation
.clear();
611 void SkeletalCreature::tick() {
612 CreatureAgent::tick();
616 eyesclosed
= creature
->isAsleep() || !creature
->isAlive();
618 // TODO: every 2 ticks = correct? what about the engine var?
620 if (ticks
% 2 == 0) return;
622 //if (eyesclosed) return; // TODO: hack, this is wrong :)
625 if (!eyesclosed
&& !creature
->isZombie()) {
626 if (approaching
&& approachtarget
) {
627 // TODO: more sane approaching skillz
628 if (approachtarget
->x
< x
)
634 if (walking
|| approaching
) {
635 // TODO: we shouldn't bother with this unless it changed?
636 setGaitGene(creature
->getGait());
638 // TODO: we should only do this if we're moving :-P
645 // TODO: this kinda duplicates what physicsTick is doing below
646 if ((engine
.version
== 1 || (engine
.version
== 2 && grav
.getInt() == 0) || (engine
.version
== 3 && !falling
)) && !carriedby
&& !invehicle
)
649 downfootroom
.reset();
652 void SkeletalCreature::physicsTick() {
655 if (engine
.version
> 1 && falling
&& (engine
.version
== 2 || validInRoomSystem())) Agent::physicsTick();
656 if (!carriedby
&& !invehicle
) {
657 if (engine
.version
== 1 || (engine
.version
== 2 && grav
.getInt() == 0) || (engine
.version
== 3 && !falling
))
659 else downfootroom
.reset();
660 } else downfootroom
.reset();
663 void SkeletalCreature::gaitTick() {
664 if (!gaitgene
) return;
665 uint8 pose
= gaitgene
->pose
[gaiti
];
667 if (gaiti
== 0) return; // non-worky gait
674 std::map
<unsigned int, creaturePoseGene
*>::iterator i
= posegenes
.find(pose
);
675 if (i
!= posegenes
.end()) {
676 creaturePoseGene
*poseg
= i
->second
;
677 assert(poseg
->poseno
== pose
);
678 setPose(poseg
->getPoseString());
681 gaiti
++; if (gaiti
> 7) gaiti
= 0;
684 CompoundPart
*SkeletalCreature::part(unsigned int id
) {
688 void SkeletalCreature::setZOrder(unsigned int plane
) {
689 Agent::setZOrder(plane
);
690 skeleton
->zapZOrder();
691 skeleton
->addZOrder();
694 SkeletonPart::SkeletonPart(SkeletalCreature
*p
) : AnimatablePart(p
, 0, 0, 0, 0) {
697 void SkeletonPart::tick() {
698 if (animation
.empty()) return;
700 unsigned int f
= frameno
+ 1;
701 if (f
== animation
.size()) return;
702 if (animation
[f
] == 255) {
703 if (f
== (animation
.size() - 1)) f
= 0;
704 else f
= animation
[f
+ 1];
706 // TODO: check f is valid..
711 void SkeletonPart::setPose(unsigned int p
) {
712 ((SkeletalCreature
*)parent
)->setPoseGene(p
);
715 void SkeletonPart::setFrameNo(unsigned int p
) {
716 assert(p
< animation
.size());
718 setPose(animation
[p
]);
721 void SkeletonPart::partRender(class Surface
*renderer
, int xoffset
, int yoffset
) {
722 SkeletalCreature
*c
= dynamic_cast<SkeletalCreature
*>(parent
);
723 c
->render(renderer
, xoffset
, yoffset
);
726 void SkeletalCreature::finishInit() {
733 void SkeletalCreature::creatureAged() {
734 // TODO: adjust position to account for any changes..
740 std::string
SkeletalCreature::getFaceSpriteName() {
741 // TODO: we should store the face sprite when we first search for sprites (since it
742 // has to be the baby sprite), rather than this horrible hackery
743 for (vector
<gene
*>::iterator i
= creature
->getGenome()->genes
.begin(); i
!= creature
->getGenome()->genes
.end(); i
++) {
744 //if ((*i)->header.switchontime != creature->getStage()) continue;
746 if (typeid(*(*i
)) == typeid(creatureAppearanceGene
)) {
747 creatureAppearanceGene
*x
= (creatureAppearanceGene
*)(*i
);
749 return std::string("a") + dataString(0, creature
->isFemale(), x
->species
, x
->variant
);
754 caos_assert(false); // TODO: mmh
757 unsigned int SkeletalCreature::getFaceSpriteFrame() {
758 return 9 + (eyesclosed
? 16 : 0) + (facialexpression
* 32);
761 int SkeletalCreature::handleClick(float clickx
, float clicky
) {
765 if (clicky
>= getSkelHeight() / 2.0) {
772 std::pair
<int, int> SkeletalCreature::getCarryPoint() {
773 std::pair
<int, int> carrypoint
;
775 // TODO: 9 is left hand, how do we work out which hand a norn is carrying with?
776 carrypoint
.first
= attachmentX(9, 1);
777 carrypoint
.second
= attachmentY(9, 1);