don't include OpenALBackend.h in qtgui/qtopenc2e.cpp
[openc2e.git] / SkeletalCreature.cpp
blobf516a75fc47f4dd46bc0c3dcbfe9395d0a3cfc67
1 /*
2 * SkeletalCreature.cpp
3 * openc2e
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
22 TODO:
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"
30 #include "Creature.h"
31 #include "World.h"
32 #include "Engine.h"
33 #include "Backend.h"
34 #include "Room.h"
35 #include "creaturesImage.h"
37 #include <typeinfo> // TODO: remove when genome system is fixed
38 #include <boost/format.hpp>
40 struct bodypartinfo {
41 char letter;
42 int parent;
43 int attorder;
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")
79 return 17;
80 else
81 return 14;
84 SkeletalCreature::SkeletalCreature(unsigned char _family) : CreatureAgent(_family) {
85 facialexpression = 0;
86 pregnancy = 0;
87 eyesclosed = false;
89 ticks = 0;
90 gaitgene = 0;
92 calculated = false;
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
99 size.setInt(224);
100 rest.setInt(70);
101 accg.setInt(15);
102 aero.setInt(10);
105 skeleton = new SkeletonPart(this);
108 SkeletalCreature::~SkeletalCreature() {
109 delete skeleton;
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;
118 else
119 _postfix[2] = 'a' + databreed;
120 return _postfix;
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') {
147 // head
148 return appearancegenes[0];
149 } else if (x == 'b') {
150 // body
151 return appearancegenes[1];
152 } else if (x >= 'c' && x <= 'h') {
153 // legs
154 return appearancegenes[2];
155 } else if (x >= 'i' && x <= 'l') {
156 // arms
157 return appearancegenes[3];
158 } else if (x == 'm' || x == 'n') {
159 // tail
160 return appearancegenes[4];
163 return 0;
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
175 images[i].reset();
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();
183 if (partapp) {
184 if (engine.version > 1) partspecies = partapp->species;
185 partvariant = partapp->variant;
186 } else {
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();
200 while (!images[i]) {
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));
206 stage_to_try--;
208 var--;
210 if (!tryfemale) break;
211 tryfemale = false;
213 if (!images[i])
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");
223 stage_to_try--;
225 var--;
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()));
230 // load ATT file
231 std::ifstream in(attfilename.c_str());
232 if (in.fail())
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));
234 in >> att[i];
236 images[i] = tintBodySprite(images[i]);
239 setPose(0);
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));
249 return newimage;
252 return s;
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];
267 bool mirror = false;
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) {
270 ourpose -= 4;
271 mirror = true;
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
282 assert(images[i]);
284 renderer->render(images[i], ourpose, partx[i] + adjustx + xoffset, party[i] + adjusty + yoffset, false, 0, mirror);
286 if (displaycore) {
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;
317 } else {
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];
323 int x, y;
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];
335 x = x - attachx;
336 y = y - attachy;
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]); }
347 adjustx = -lowestx;
348 adjusty = -lowesty;
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);
362 calculated = true;
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();
374 else
375 snapDownFoot();*/
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;
387 if (downfootroom) {
388 if (downfootroom->containsPoint(footx, footy)) {
389 newroom = downfootroom;
390 } else {
391 if (downfootroom->x_left <= footx && downfootroom->x_right >= footx) {
392 newroom = downfootroom; // TODO, we're just forcing for now
393 } else {
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) {
401 newroom = thisroom;
402 ydiff = thisydiff;
408 } else {
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);
415 newfooty--;
418 // TODO: give up here
420 footy = newfooty;
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>());
431 if (downfootroom) {
432 snapDownFoot();
433 return;
434 } else {
435 std::cout << "Creature out of room system at (" << footx << ", " << footy << ")!" << std::endl;
436 // TODO: exceptiony death?
437 return;
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;
444 // TODO: sucky code
445 x = lastgoodfootx - attachmentX(orig_footpart, 1);
446 footx = lastgoodfootx;
447 footy = lastgoodfooty;
448 downfootroom = world.map.roomAt(footx, footy);
449 queueScript(6);
451 if (!downfootroom) {
452 std::cout << "no down foot room! (" << footx << ", " << footy << ")" << std::endl;
453 // TODO: exceptiony death
454 return;
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
462 belowfloor = true;
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);
470 else falling = true;
471 return;
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()) {
484 grav.setInt(1);
485 return;
487 } else {
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);
494 else falling = true;
495 return;
502 void SkeletalCreature::setPose(unsigned int p) {
503 posedirection = 0;
504 for (int i = 0; i < SkeletalPartCount(); i++)
505 pose[i] = p;
506 recalculateSkeleton();
509 void SkeletalCreature::setPose(std::string s) {
510 switch (s[0]) {
511 case '?':
512 switch (direction) {
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);
519 break;
520 case '!':
521 switch (direction) {
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);
528 break;
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
534 default:
535 std::cout << "internal warning: SkeletalCreature::setPose didn't understand direction " << s[0] << " in pose '" << s << "'." << std::endl;
536 break;
539 for (int i = 0; i < 14; i++) {
540 int newpose;
542 switch (s[i + 1]) {
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;
556 else newpose = 1;
557 newpose += (posedirection * 4);
559 break;
560 // TODO: '!' also?
561 case 'X': continue; // do nothing
562 default:
563 std::cout << "internal warning: SkeletalCreature::setPose didn't understand " << s[i + 1] << " in pose '" << s << "'." << std::endl;
564 continue;
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
574 } else if (i == 1) {
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);
589 gaitgene = 0;
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
604 gaitgene = g;
605 gaiti = 0;
606 skeleton->animation.clear();
609 void SkeletalCreature::tick() {
610 CreatureAgent::tick();
612 if (paused) return;
614 eyesclosed = creature->isAsleep() || !creature->isAlive();
616 // TODO: every 2 ticks = correct? what about the engine var?
617 ticks++;
618 if (ticks % 2 == 0) return;
620 //if (eyesclosed) return; // TODO: hack, this is wrong :)
622 // TODO: hack!
623 if (!eyesclosed && !creature->isZombie()) {
624 if (approaching && approachtarget) {
625 // TODO: more sane approaching skillz
626 if (approachtarget->x < x)
627 direction = 3;
628 else
629 direction = 2;
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
637 gaitTick();
640 approaching = false;
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)
645 snapDownFoot();
646 else
647 downfootroom.reset();
650 void SkeletalCreature::physicsTick() {
651 // TODO: mmh
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))
656 snapDownFoot();
657 else downfootroom.reset();
658 } else downfootroom.reset();
661 void SkeletalCreature::gaitTick() {
662 if (!gaitgene) return;
663 uint8 pose = gaitgene->pose[gaiti];
664 if (pose == 0) {
665 if (gaiti == 0) return; // non-worky gait
667 gaiti = 0;
668 gaitTick();
669 return;
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) {
683 return skeleton;
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..
705 setFrameNo(f);
709 void SkeletonPart::setPose(unsigned int p) {
710 ((SkeletalCreature *)parent)->setPoseGene(p);
713 void SkeletonPart::setFrameNo(unsigned int p) {
714 assert(p < animation.size());
715 frameno = p;
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() {
725 Agent::finishInit();
727 processGenes();
728 skeletonInit();
731 void SkeletalCreature::creatureAged() {
732 // TODO: adjust position to account for any changes..
734 processGenes();
735 skeletonInit();
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);
746 if (x->part == 0) {
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) {
760 // TODO: muh, horror
762 clicky -= y;
763 if (clicky >= getSkelHeight() / 2.0) {
764 // slap
765 queueScript(0, (Agent *)world.hand());
766 } else {
767 // tickle
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);
779 return carrypoint;
782 /* vim: set noet: */