fix agentOnCamera to cope with the wrap
[openc2e.git] / SkeletalCreature.cpp
blob4b407ddbef8684f19434b54c77a39880cf28a7f0
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 for (unsigned int i = 0; i < 6; i++) appearancegenes[i] = 0;
107 skeleton = new SkeletonPart(this);
110 SkeletalCreature::~SkeletalCreature() {
111 delete skeleton;
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;
120 else
121 _postfix[2] = 'a' + databreed;
122 return _postfix;
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') {
149 // head
150 return appearancegenes[0];
151 } else if (x == 'b') {
152 // body
153 return appearancegenes[1];
154 } else if (x >= 'c' && x <= 'h') {
155 // legs
156 return appearancegenes[2];
157 } else if (x >= 'i' && x <= 'l') {
158 // arms
159 return appearancegenes[3];
160 } else if (x == 'm' || x == 'n') {
161 // tail
162 return appearancegenes[4];
165 return 0;
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
177 images[i].reset();
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();
185 if (partapp) {
186 if (engine.version > 1) partspecies = partapp->species;
187 partvariant = partapp->variant;
188 } else {
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();
202 while (!images[i]) {
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));
208 stage_to_try--;
210 var--;
212 if (!tryfemale) break;
213 tryfemale = false;
215 if (!images[i])
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");
225 stage_to_try--;
227 var--;
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()));
232 // load ATT file
233 std::ifstream in(attfilename.c_str());
234 if (in.fail())
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));
236 in >> att[i];
238 images[i] = tintBodySprite(images[i]);
241 setPose(0);
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));
251 return newimage;
254 return s;
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];
269 bool mirror = false;
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) {
272 ourpose -= 4;
273 mirror = true;
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
284 assert(images[i]);
286 renderer->render(images[i], ourpose, partx[i] + adjustx + xoffset, party[i] + adjusty + yoffset, false, 0, mirror);
288 if (displaycore) {
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;
319 } else {
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];
325 int x, y;
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];
337 x = x - attachx;
338 y = y - attachy;
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]); }
349 adjustx = -lowestx;
350 adjusty = -lowesty;
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);
364 calculated = true;
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();
376 else
377 snapDownFoot();*/
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;
389 if (downfootroom) {
390 if (downfootroom->containsPoint(footx, footy)) {
391 newroom = downfootroom;
392 } else {
393 if (downfootroom->x_left <= footx && downfootroom->x_right >= footx) {
394 newroom = downfootroom; // TODO, we're just forcing for now
395 } else {
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) {
403 newroom = thisroom;
404 ydiff = thisydiff;
410 } else {
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);
417 newfooty--;
420 // TODO: give up here
422 footy = newfooty;
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>());
433 if (downfootroom) {
434 snapDownFoot();
435 return;
436 } else {
437 std::cout << "Creature out of room system at (" << footx << ", " << footy << ")!" << std::endl;
438 // TODO: exceptiony death?
439 return;
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;
446 // TODO: sucky code
447 x = lastgoodfootx - attachmentX(orig_footpart, 1);
448 footx = lastgoodfootx;
449 footy = lastgoodfooty;
450 downfootroom = world.map.roomAt(footx, footy);
451 queueScript(6);
453 if (!downfootroom) {
454 std::cout << "no down foot room! (" << footx << ", " << footy << ")" << std::endl;
455 // TODO: exceptiony death
456 return;
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
464 belowfloor = true;
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);
472 else falling = true;
473 return;
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()) {
486 grav.setInt(1);
487 return;
489 } else {
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);
496 else falling = true;
497 return;
504 void SkeletalCreature::setPose(unsigned int p) {
505 posedirection = 0;
506 for (int i = 0; i < SkeletalPartCount(); i++)
507 pose[i] = p;
508 recalculateSkeleton();
511 void SkeletalCreature::setPose(std::string s) {
512 switch (s[0]) {
513 case '?':
514 switch (direction) {
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);
521 break;
522 case '!':
523 switch (direction) {
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);
530 break;
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
536 default:
537 std::cout << "internal warning: SkeletalCreature::setPose didn't understand direction " << s[0] << " in pose '" << s << "'." << std::endl;
538 break;
541 for (int i = 0; i < 14; i++) {
542 int newpose;
544 switch (s[i + 1]) {
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;
558 else newpose = 1;
559 newpose += (posedirection * 4);
561 break;
562 // TODO: '!' also?
563 case 'X': continue; // do nothing
564 default:
565 std::cout << "internal warning: SkeletalCreature::setPose didn't understand " << s[i + 1] << " in pose '" << s << "'." << std::endl;
566 continue;
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
576 } else if (i == 1) {
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);
591 gaitgene = 0;
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
606 gaitgene = g;
607 gaiti = 0;
608 skeleton->animation.clear();
611 void SkeletalCreature::tick() {
612 CreatureAgent::tick();
614 if (paused) return;
616 eyesclosed = creature->isAsleep() || !creature->isAlive();
618 // TODO: every 2 ticks = correct? what about the engine var?
619 ticks++;
620 if (ticks % 2 == 0) return;
622 //if (eyesclosed) return; // TODO: hack, this is wrong :)
624 // TODO: hack!
625 if (!eyesclosed && !creature->isZombie()) {
626 if (approaching && approachtarget) {
627 // TODO: more sane approaching skillz
628 if (approachtarget->x < x)
629 direction = 3;
630 else
631 direction = 2;
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
639 gaitTick();
642 approaching = false;
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)
647 snapDownFoot();
648 else
649 downfootroom.reset();
652 void SkeletalCreature::physicsTick() {
653 // TODO: mmh
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))
658 snapDownFoot();
659 else downfootroom.reset();
660 } else downfootroom.reset();
663 void SkeletalCreature::gaitTick() {
664 if (!gaitgene) return;
665 uint8 pose = gaitgene->pose[gaiti];
666 if (pose == 0) {
667 if (gaiti == 0) return; // non-worky gait
669 gaiti = 0;
670 gaitTick();
671 return;
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) {
685 return skeleton;
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..
707 setFrameNo(f);
711 void SkeletonPart::setPose(unsigned int p) {
712 ((SkeletalCreature *)parent)->setPoseGene(p);
715 void SkeletonPart::setFrameNo(unsigned int p) {
716 assert(p < animation.size());
717 frameno = p;
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() {
727 Agent::finishInit();
729 processGenes();
730 skeletonInit();
733 void SkeletalCreature::creatureAged() {
734 // TODO: adjust position to account for any changes..
736 processGenes();
737 skeletonInit();
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);
748 if (x->part == 0) {
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) {
762 // TODO: muh, horror
764 clicky -= y;
765 if (clicky >= getSkelHeight() / 2.0) {
766 return 0; // slap
767 } else {
768 return 1; // tickle
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: */