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 skeleton
= new SkeletonPart(this);
108 SkeletalCreature::~SkeletalCreature() {
112 std::string
SkeletalCreature::dataString(unsigned int _stage
, bool tryfemale
, unsigned int dataspecies
, unsigned int databreed
) {
113 char _postfix
[4] = "XXX";
114 _postfix
[0] = '0' + dataspecies
+ (tryfemale
? 4 : 0);
115 _postfix
[1] = '0' + _stage
;
116 if (engine
.version
== 1)
117 _postfix
[2] = '0' + databreed
;
119 _postfix
[2] = 'a' + databreed
;
123 void SkeletalCreature::processGenes() {
124 shared_ptr
<genomeFile
> genome
= creature
->getGenome();
126 for (vector
<gene
*>::iterator i
= genome
->genes
.begin(); i
!= genome
->genes
.end(); i
++) {
127 if (!creature
->shouldProcessGene(*i
)) continue;
129 if (typeid(*(*i
)) == typeid(creatureAppearanceGene
)) {
130 creatureAppearanceGene
*x
= (creatureAppearanceGene
*)(*i
);
131 if (x
->part
> 5) continue;
132 appearancegenes
[x
->part
] = x
;
133 } else if (typeid(*(*i
)) == typeid(creaturePoseGene
)) {
134 creaturePoseGene
*x
= (creaturePoseGene
*)(*i
);
135 posegenes
[x
->poseno
] = x
;
136 } else if (typeid(*(*i
)) == typeid(creatureGaitGene
)) {
137 creatureGaitGene
*x
= (creatureGaitGene
*)(*i
);
138 gaitgenes
[x
->drive
] = x
;
143 creatureAppearanceGene
*SkeletalCreature::appearanceGeneForPart(char x
) {
144 // TODO: tail madness?
146 if (x
== 'a' || x
>= 'o') {
148 return appearancegenes
[0];
149 } else if (x
== 'b') {
151 return appearancegenes
[1];
152 } else if (x
>= 'c' && x
<= 'h') {
154 return appearancegenes
[2];
155 } else if (x
>= 'i' && x
<= 'l') {
157 return appearancegenes
[3];
158 } else if (x
== 'm' || x
== 'n') {
160 return appearancegenes
[4];
166 void SkeletalCreature::skeletonInit() {
167 // TODO: the exception throwing in here needs some more thought
168 // TODO: if we throw an exception when we need to kill the creature off, else segfault :/
170 for (int i
= 0; i
< SkeletalPartCount(); i
++) { // CV hackery
171 if (engine
.version
== 1) // TODO: this is hackery to skip tails for C1
172 if (i
== 6 || i
== 13) continue;
174 // reset the existing image reference
177 // find the relevant gene
178 char x
= cee_bodyparts
[i
].letter
;
179 creatureAppearanceGene
*partapp
= appearanceGeneForPart(x
);
181 int partspecies
, partvariant
;
182 partspecies
= creature
->getGenus();
184 if (engine
.version
> 1) partspecies
= partapp
->species
;
185 partvariant
= partapp
->variant
;
187 partvariant
= creature
->getVariant();
190 int spe
= partspecies
;
193 * In order to find a sprite, we try our current stage first, then the stages below us,
194 * and then we try again with all lower variants, and if that fails for female sprites
195 * then we try the whole thing again but trying to find a male sprite.
198 // find relevant sprite
199 bool tryfemale
= creature
->isFemale();
201 int var
= partvariant
;
202 while (var
> -1 && !images
[i
]) {
203 int stage_to_try
= creature
->getStage();
204 while (stage_to_try
> -1 && !images
[i
]) {
205 images
[i
] = world
.gallery
.getImage(x
+ dataString(stage_to_try
, tryfemale
, spe
, var
));
210 if (!tryfemale
) break;
214 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()));
216 // find relevant ATT data
217 std::string attfilename
;
218 int var
= partvariant
;
219 while (var
> -1 && attfilename
.empty()) {
220 int stage_to_try
= creature
->getStage();
221 while (stage_to_try
> -1 && attfilename
.empty()) {
222 attfilename
= world
.findFile(std::string("/Body Data/") + x
+ dataString(stage_to_try
, false, spe
, var
) + ".att");
227 if (attfilename
.empty())
228 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()));
231 std::ifstream
in(attfilename
.c_str());
233 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
));
236 images
[i
] = tintBodySprite(images
[i
]);
242 shared_ptr
<creaturesImage
> SkeletalCreature::tintBodySprite(shared_ptr
<creaturesImage
> s
) {
243 // TODO: don't bother tinting if we don't need to
245 // TODO: work out tinting for other engine versions
246 if (engine
.version
> 2) {
247 shared_ptr
<creaturesImage
> newimage
= s
->mutableCopy();
248 newimage
->tint(creature
->getTint(0), creature
->getTint(1), creature
->getTint(2), creature
->getTint(3), creature
->getTint(4));
255 void SkeletalCreature::render(Surface
*renderer
, int xoffset
, int yoffset
) {
256 for (int j
= 0; j
< 17; j
++) {
257 int i
= cee_zorder
[posedirection
][j
];
258 if (i
>= SkeletalPartCount()) continue; // CV hackery
260 if (engine
.version
== 1) // TODO: this is hackery to skip tails for C1
261 if (i
== 6 || i
== 13) continue;
263 bodypartinfo
*part
= &cee_bodyparts
[i
];
265 unsigned int ourpose
= pose
[i
];
268 // TODO: ack, move this check out of the loop
269 if (i
!= 14 && i
!= 15 && world
.variables
["engine_mirror_creature_body_parts"] == 1 && ourpose
>= 4 && ourpose
<= 7) {
274 // adjust for pregnancy/facial expressions/etc as necessary
275 if (part
->parent
== -1) // body
276 ourpose
+= (pregnancy
* 16);
277 else if (i
== 1) // head
278 ourpose
+= (eyesclosed
? 16 : 0) + (facialexpression
* 32);
279 else if (i
== 16) // hair
280 ourpose
+= 0; // TODO: 16 * hair
284 renderer
->render(images
[i
], ourpose
, partx
[i
] + adjustx
+ xoffset
, party
[i
] + adjusty
+ yoffset
, false, 0, mirror
);
287 // TODO: we draw a lot of points twice here :)
288 int atx
= attachmentX(i
, 0) + xoffset
, aty
= attachmentY(i
, 0) + yoffset
;
289 renderer
->renderLine(atx
- 1, aty
, atx
+ 1, aty
, 0xFF0000CC);
290 renderer
->renderLine(atx
, aty
- 1, atx
, aty
+ 1, 0xFF0000CC);
291 atx
= attachmentX(i
, 1) + xoffset
; aty
= attachmentY(i
, 1) + yoffset
;
292 renderer
->renderLine(atx
- 1, aty
, atx
+ 1, aty
, 0xFF0000CC);
293 renderer
->renderLine(atx
, aty
- 1, atx
, aty
+ 1, 0xFF0000CC);
298 int SkeletalCreature::attachmentX(unsigned int part
, unsigned int id
) {
299 return partx
[part
] + att
[part
].attachments
[pose
[part
]][0 + (id
* 2)] + adjustx
;
302 int SkeletalCreature::attachmentY(unsigned int part
, unsigned int id
) {
303 return party
[part
] + att
[part
].attachments
[pose
[part
]][1 + (id
* 2)] + adjusty
;
306 void SkeletalCreature::recalculateSkeleton() {
307 int lowestx
= 0, lowesty
= 0, highestx
= 0, highesty
= 0;
309 for (int i
= 0; i
< SkeletalPartCount(); i
++) {
310 if (engine
.version
== 1) // TODO: this is hackery to skip tails for C1
311 if (i
== 6 || i
== 13) continue;
313 bodypartinfo
*part
= &cee_bodyparts
[i
];
315 if (part
->parent
== -1) {
316 partx
[i
] = 0; party
[i
] = 0;
318 attFile
&bodyattinfo
= att
[0];
319 attFile
&attinfo
= att
[i
];
321 int attachx
= att
[i
].attachments
[pose
[i
]][0];
322 int attachy
= att
[i
].attachments
[pose
[i
]][1];
325 if (part
->parent
== 0) { // linking to body
326 x
= bodyattinfo
.attachments
[pose
[0]][part
->attorder
* 2];
327 y
= bodyattinfo
.attachments
[pose
[0]][(part
->attorder
* 2) + 1];
328 } else { // extra limb
329 attFile
&parentattinfo
= att
[part
->parent
];
331 x
= partx
[part
->parent
] + parentattinfo
.attachments
[pose
[part
->parent
]][part
->attorder
* 2];
332 y
= party
[part
->parent
] + parentattinfo
.attachments
[pose
[part
->parent
]][(part
->attorder
* 2) + 1];
338 partx
[i
] = x
; party
[i
] = y
;
340 if (x
< lowestx
) { lowestx
= x
; }
341 if (y
< lowesty
) { lowesty
= y
; }
342 if (x
+ (int)images
[i
]->width(pose
[i
]) > highestx
) { highestx
= x
+ images
[i
]->width(pose
[i
]); }
343 if (y
+ (int)images
[i
]->height(pose
[i
]) > highesty
) { highesty
= y
+ images
[i
]->height(pose
[i
]); }
350 // TODO: muh, we should cooperate with physics system etc
351 if (!carriedby
&& !invehicle
&& calculated
) {
352 int orig_footpart
= (downfoot_left
? 11 : 12);
353 // adjust location to match foot
354 moveTo(x
- (attachmentX(orig_footpart
, 1) - oldfootx
), y
- (attachmentY(orig_footpart
, 1) - oldfooty
));
357 // work out which foot is down
358 int leftfoot
= attachmentY(11, 1);
359 int rightfoot
= attachmentY(12, 1);
360 downfoot_left
= (rightfoot
< leftfoot
);
363 int orig_footpart
= (downfoot_left
? 11 : 12);
364 oldfootx
= attachmentX(orig_footpart
, 1);
365 oldfooty
= attachmentY(orig_footpart
, 1);
367 // recalculate width/height
368 height
= downfoot_left
? leftfoot
: rightfoot
;
369 width
= 50; // TODO: arbitary values bad
371 // TODO: muh, we should cooperate with physics system etc
372 /*if (carriedby || invehicle)
373 downfootroom.reset();
378 void SkeletalCreature::snapDownFoot() {
379 // TODO: this isn't very well thought-out.
381 int orig_footpart
= (downfoot_left
? 11 : 12);
382 float footx
= x
+ attachmentX(orig_footpart
, 1);
383 float footy
= y
+ attachmentY(orig_footpart
, 1);
385 shared_ptr
<Room
> newroom
;
388 if (downfootroom
->containsPoint(footx
, footy
)) {
389 newroom
= downfootroom
;
391 if (downfootroom
->x_left
<= footx
&& downfootroom
->x_right
>= footx
) {
392 newroom
= downfootroom
; // TODO, we're just forcing for now
394 float ydiff
= 10000.0f
; // TODO: big number
395 for (std::map
<weak_ptr
<Room
>,RoomDoor
*>::iterator i
= downfootroom
->doors
.begin(); i
!= downfootroom
->doors
.end(); i
++) {
396 shared_ptr
<Room
> thisroom
= i
->first
.lock();
397 if (engine
.version
== 2 && size
.getInt() > i
->second
->perm
) continue;
398 if (thisroom
->x_left
<= footx
&& thisroom
->x_right
>= footx
) {
399 float thisydiff
= fabs(footy
- thisroom
->floorYatX(footx
));
400 if (thisydiff
< ydiff
) {
409 newroom
= bestRoomAt(footx
, footy
, 3, shared_ptr
<Room
>());
411 // insane emergency handling
412 float newfooty
= footy
;
413 while (!newroom
&& newfooty
> (footy
- 500.0f
)) {
414 newroom
= world
.map
.roomAt(footx
, newfooty
);
418 // TODO: give up here
423 bool newroomchosen
= (newroom
!= downfootroom
) && downfootroom
;
424 bool hadroom
= (downfootroom
);
425 downfootroom
= newroom
;
427 if (!downfootroom
/*|| !falling */) {
428 // TODO: hackery to cope with scripts moving us, this needs handling correctly somewhere
429 if (fabs(lastgoodfootx
- attachmentX(orig_footpart
, 1) - x
) > 50.0f
|| fabs(lastgoodfooty
- attachmentY(orig_footpart
, 1) - y
) > 50.0f
) {
430 downfootroom
= bestRoomAt(footx
, footy
, 3, shared_ptr
<Room
>());
435 std::cout
<< "Creature out of room system at (" << footx
<< ", " << footy
<< ")!" << std::endl
;
436 // TODO: exceptiony death?
441 // We fell out of the room system! How did that happen? Push ourselves back in, run collision script.
442 std::cout
<< "Creature out of room system at (" << footx
<< ", " << footy
<< "), pushing it back in." << std::endl
;
445 x
= lastgoodfootx
- attachmentX(orig_footpart
, 1);
446 footx
= lastgoodfootx
;
447 footy
= lastgoodfooty
;
448 downfootroom
= world
.map
.roomAt(footx
, footy
);
452 std::cout
<< "no down foot room! (" << footx
<< ", " << footy
<< ")" << std::endl
;
453 // TODO: exceptiony death
458 bool belowfloor
= false;
459 float newy
= downfootroom
->floorYatX(footx
);
460 if (engine
.version
== 2 && hadroom
&& y
> newy
) {
461 // TODO: hilar hack: cope with walking below floors
463 newy
= downfootroom
->bot
.pointAtX(footx
).y
;
466 if (engine
.version
> 1) {
467 // TODO: hilar hack: enable gravity if we're snapping by much
468 if (newroomchosen
&& abs(y
- (newy
- (footy
- y
))) > 20) {
469 if (engine
.version
== 2) grav
.setInt(1);
475 moveTo(x
, newy
- (footy
- y
));
477 lastgoodfootx
= footx
;
478 lastgoodfooty
= footy
;
480 if (engine
.version
> 2) {
481 if (engine
.version
== 2 && !belowfloor
&& downfootroom
->floorpoints
.size()) {
482 // TODO: hilar hack: same as above for floorvalue
483 if (size
.getInt() <= downfootroom
->floorvalue
.getInt()) {
488 // TODO: hilar hack: same as above for perm
489 shared_ptr
<Room
> downroom
= world
.map
.roomAt(footx
, downfootroom
->y_left_floor
+ 1);
490 if (downfootroom
->doors
.find(downroom
) != downfootroom
->doors
.end()) {
491 int permsize
= (engine
.version
== 2 ? size
.getInt() : perm
);
492 if (permsize
<= downfootroom
->doors
[downroom
]->perm
) {
493 if (engine
.version
== 2) grav
.setInt(1);
502 void SkeletalCreature::setPose(unsigned int p
) {
504 for (int i
= 0; i
< SkeletalPartCount(); i
++)
506 recalculateSkeleton();
509 void SkeletalCreature::setPose(std::string s
) {
513 case 0: break; // north, TODO
514 case 1: break; // south, TODO
515 case 2: posedirection
= 0; break; // right
516 case 3: posedirection
= 1; break; // left
517 default: assert(false);
522 case 0: break; // north, TODO
523 case 1: break; // south, TODO
524 case 2: posedirection
= 1; break; // right
525 case 3: posedirection
= 0; break; // left
526 default: assert(false);
529 case '0': posedirection
= 3; break;
530 case '1': posedirection
= 2; break;
531 case '2': posedirection
= 0; break;
532 case '3': posedirection
= 1; break;
533 case 'X': break; // do nothing
535 std::cout
<< "internal warning: SkeletalCreature::setPose didn't understand direction " << s
[0] << " in pose '" << s
<< "'." << std::endl
;
539 for (int i
= 0; i
< 14; i
++) {
543 case '0': newpose
= 0 + (posedirection
* 4); break;
544 case '1': newpose
= 1 + (posedirection
* 4); break;
545 case '2': newpose
= 2 + (posedirection
* 4); break;
546 case '3': newpose
= 3 + (posedirection
* 4); break;
547 case '?': assert(i
== 0); {
548 // make the head look in the posedirection of _IT_
549 float attachmenty
= attachmentY(1, 0) + y
; // head attachment point, which we'll use to 'look' from atm
551 // TODO: this is horrible, but i have no idea how the head angle is calculated
552 AgentRef attention
= creature
->getAttentionFocus();
553 if (attention
&& attention
->y
> (attachmenty
+ 30)) newpose
= 0;
554 else if (attention
&& attention
->y
< (attachmenty
- 70)) newpose
= 3;
555 else if (attention
&& attention
->y
< (attachmenty
- 30)) newpose
= 2;
557 newpose
+= (posedirection
* 4);
561 case 'X': continue; // do nothing
563 std::cout
<< "internal warning: SkeletalCreature::setPose didn't understand " << s
[i
+ 1] << " in pose '" << s
<< "'." << std::endl
;
567 pose
[cee_lookup
[i
]] = newpose
;
569 // TODO: this is some hackery for CV,
570 if (world
.gametype
!= "cv") continue;
571 if (i
== 0) { // head
572 pose
[14] = newpose
; pose
[15] = newpose
; // ears
573 pose
[16] = newpose
; // hair
575 pose
[6] = newpose
; // tail root
576 pose
[13] = newpose
; // tail tip
580 recalculateSkeleton();
583 void SkeletalCreature::setPoseGene(unsigned int poseno
) {
584 std::map
<unsigned int, creaturePoseGene
*>::iterator i
= posegenes
.find(poseno
);
585 if (i
== posegenes
.end()) return; // TODO: is there a better behaviour here?
587 creaturePoseGene
*g
= i
->second
;
588 assert(g
->poseno
== poseno
);
590 walking
= false; // TODO: doesn't belong here, does it? really the idea of a 'walking' bool is horrid
591 setPose(g
->getPoseString());
594 void SkeletalCreature::setGaitGene(unsigned int gaitdrive
) { // TODO: not sure if this is *useful*
595 std::map
<unsigned int, creatureGaitGene
*>::iterator i
= gaitgenes
.find(gaitdrive
);
596 if (i
== gaitgenes
.end()) return; // TODO: is there a better behaviour here?
598 creatureGaitGene
*g
= i
->second
;
599 assert(g
->drive
== gaitdrive
);
601 if (g
== gaitgene
) return;
603 // reset our gait details to default
606 skeleton
->animation
.clear();
609 void SkeletalCreature::tick() {
610 CreatureAgent::tick();
614 eyesclosed
= creature
->isAsleep() || !creature
->isAlive();
616 // TODO: every 2 ticks = correct? what about the engine var?
618 if (ticks
% 2 == 0) return;
620 //if (eyesclosed) return; // TODO: hack, this is wrong :)
623 if (!eyesclosed
&& !creature
->isZombie()) {
624 if (approaching
&& approachtarget
) {
625 // TODO: more sane approaching skillz
626 if (approachtarget
->x
< x
)
632 if (walking
|| approaching
) {
633 // TODO: we shouldn't bother with this unless it changed?
634 setGaitGene(creature
->getGait());
636 // TODO: we should only do this if we're moving :-P
643 // TODO: this kinda duplicates what physicsTick is doing below
644 if ((engine
.version
== 1 || (engine
.version
== 2 && grav
.getInt() == 0) || (engine
.version
== 3 && !falling
)) && !carriedby
&& !invehicle
)
647 downfootroom
.reset();
650 void SkeletalCreature::physicsTick() {
653 if (engine
.version
> 1 && falling
&& (engine
.version
== 2 || validInRoomSystem())) Agent::physicsTick();
654 if (!carriedby
&& !invehicle
) {
655 if (engine
.version
== 1 || (engine
.version
== 2 && grav
.getInt() == 0) || (engine
.version
== 3 && !falling
))
657 else downfootroom
.reset();
658 } else downfootroom
.reset();
661 void SkeletalCreature::gaitTick() {
662 if (!gaitgene
) return;
663 uint8 pose
= gaitgene
->pose
[gaiti
];
665 if (gaiti
== 0) return; // non-worky gait
672 std::map
<unsigned int, creaturePoseGene
*>::iterator i
= posegenes
.find(pose
);
673 if (i
!= posegenes
.end()) {
674 creaturePoseGene
*poseg
= i
->second
;
675 assert(poseg
->poseno
== pose
);
676 setPose(poseg
->getPoseString());
679 gaiti
++; if (gaiti
> 7) gaiti
= 0;
682 CompoundPart
*SkeletalCreature::part(unsigned int id
) {
686 void SkeletalCreature::setZOrder(unsigned int plane
) {
687 Agent::setZOrder(plane
);
688 skeleton
->zapZOrder();
689 skeleton
->addZOrder();
692 SkeletonPart::SkeletonPart(SkeletalCreature
*p
) : AnimatablePart(p
, 0, 0, 0, 0) {
695 void SkeletonPart::tick() {
696 if (animation
.empty()) return;
698 unsigned int f
= frameno
+ 1;
699 if (f
== animation
.size()) return;
700 if (animation
[f
] == 255) {
701 if (f
== (animation
.size() - 1)) f
= 0;
702 else f
= animation
[f
+ 1];
704 // TODO: check f is valid..
709 void SkeletonPart::setPose(unsigned int p
) {
710 ((SkeletalCreature
*)parent
)->setPoseGene(p
);
713 void SkeletonPart::setFrameNo(unsigned int p
) {
714 assert(p
< animation
.size());
716 setPose(animation
[p
]);
719 void SkeletonPart::partRender(class Surface
*renderer
, int xoffset
, int yoffset
) {
720 SkeletalCreature
*c
= dynamic_cast<SkeletalCreature
*>(parent
);
721 c
->render(renderer
, xoffset
, yoffset
);
724 void SkeletalCreature::finishInit() {
731 void SkeletalCreature::creatureAged() {
732 // TODO: adjust position to account for any changes..
738 std::string
SkeletalCreature::getFaceSpriteName() {
739 // TODO: we should store the face sprite when we first search for sprites (since it
740 // has to be the baby sprite), rather than this horrible hackery
741 for (vector
<gene
*>::iterator i
= creature
->getGenome()->genes
.begin(); i
!= creature
->getGenome()->genes
.end(); i
++) {
742 //if ((*i)->header.switchontime != creature->getStage()) continue;
744 if (typeid(*(*i
)) == typeid(creatureAppearanceGene
)) {
745 creatureAppearanceGene
*x
= (creatureAppearanceGene
*)(*i
);
747 return std::string("a") + dataString(0, creature
->isFemale(), x
->species
, x
->variant
);
752 caos_assert(false); // TODO: mmh
755 unsigned int SkeletalCreature::getFaceSpriteFrame() {
756 return 9 + (eyesclosed
? 16 : 0) + (facialexpression
* 32);
759 void SkeletalCreature::handleClick(float clickx
, float clicky
) {
763 if (clicky
>= getSkelHeight() / 2.0) {
765 queueScript(0, (Agent
*)world
.hand());
768 queueScript(1, (Agent
*)world
.hand());
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);