<commit_before>/*
 *  SkeletalCreature.cpp
 *  openc2e
 *
 *  Created by <NAME> on Thu 10 Mar 2005.
 *  Copyright (c) 2005-2006 <NAME>. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */
/* 20th feb 2005, by fuzzie
   with thanks to vadim for helping out

   TODO:
	* hair tidiness/untidiness (offset by 0 to 2 multiplied by 16)
	* render hairs/ears .. at the moment we avoid them, we lack zorder sanity too
*/

#include "SkeletalCreature.h"
#include "Creature.h"
#include "World.h"
#include "Engine.h"
#include "Backend.h"

#include <typeinfo> // TODO: remove when genome system is fixed
#include <boost/format.hpp>

struct bodypartinfo {
	char letter;
	int parent;
	int attorder;
};

bodypartinfo cee_bodyparts[17] = {
	{ 'b', -1, -1 }, // body
	{ 'a', 0, 0 }, // head - attached to body
	{ 'c', 0, 1 }, // left thigh - attached to body
	{ 'f', 0, 2 }, // right thigh - attached to body
	{ 'i', 0, 3 }, // left humerus - attached to body
	{ 'k', 0, 4 }, // right humerus - attached to body
	{ 'm', 0, 5 }, // tail root - attached to body
	{ 'd', 2, 1 }, // left shin - attached to left thigh
	{ 'g', 3, 1 }, // right shin - attached to right thigh
	{ 'j', 4, 1 }, // left radius - attached to left humerus
	{ 'l', 5, 1 }, // right radius - attached to right humerus
	{ 'e', 7, 1 }, // left foot - attached to left shin
	{ 'h', 8, 1 }, // right foot - attached to right shin
	{ 'n', 6, 1 }, // tail tip - attached to tail root
	// Creatures Village only:
	{ 'o', 1, -1 }, // left ear - attached to head
	{ 'p', 1, -1 }, // right ear - attached to head
	{ 'q', 1, -1 } // hair - attached to head
};

unsigned int cee_zorder[4][14] = {
	{ 6, 13, 2, 7, 11, 4, 9, 0, 1, 5, 3, 8, 10, 12 },
	{ 6, 13, 3, 8, 12, 5, 10, 0, 1, 2, 7, 11, 4, 9 },
	{ 6, 13, 2, 4, 9, 5, 3, 7, 8, 10, 0, 11, 12, 1 },
	{ 2, 4, 9, 1, 5, 3, 7, 8, 10, 11, 12, 0, 6, 13 }
};

// needed for setPose(string) at least .. maybe cee_bodyparts should be indexed by letter
unsigned int cee_lookup[17] = { 1, 0, 2, 7, 11, 3, 8, 12, 4, 9, 5, 10, 6, 13, 14, 15, 16 };

std::string SkeletalCreature::dataString(unsigned int _stage, bool sprite, unsigned int dataspecies, unsigned int databreed) {
	// TODO: dataspecies is nonsense in c1
	char _postfix[4] = "XXX";
	_postfix[0] = '0' + dataspecies + ((sprite && creature->isFemale()) ? 4 : 0);
	_postfix[1] = '0' + _stage;
	if (engine.version == 1)
		_postfix[2] = '0' + databreed;
	else
		_postfix[2] = 'a' + databreed;
	return _postfix;
}

SkeletalCreature::SkeletalCreature(unsigned char _family, Creature *c) : CreatureAgent(_family, c) {
	facialexpression = 0;
	pregnancy = 0;
	eyesclosed = false;

	for (int i = 0; i < 14; i++) {
		images[i] = 0;
	}
	
	skeletonInit();

	// needs to go last for now, so we can throw exceptions from skeletonInit
	skeleton = new SkeletonPart(this);
}

void SkeletalCreature::skeletonInit() {
	//TODO: the exception throwing in here needs some more thought

	creatureAppearance *appearance[5] = { 0, 0, 0, 0, 0 };
	for (vector<gene *>::iterator i = creature->getGenome()->genes.begin(); i != creature->getGenome()->genes.end(); i++) {
		if (typeid(*(*i)) == typeid(creatureAppearance)) {
			creatureAppearance *x = (creatureAppearance *)(*i);
			if (x->part > 4)
				throw creaturesException(boost::str(boost::format("SkeletalCreature didn't understand a gene with a part# of %d") % (int)x->part));
			if (appearance[x->part])
				throw creaturesException(boost::str(boost::format("SkeletalCreature got a duplicated gene for part# %d") % (int)x->part));
			appearance[x->part] = x;
		}
	}

	for (int i = 0; i < 14; i++) {
		if (engine.version == 1) // TODO: this is hackery to skip tails for C1
			if (i == 6 || i == 13) continue;

		// try this stage and the stages below it to find data which worksforus
		if (images[i])
			world.gallery.delImage(images[i]);
		images[i] = 0;
		char x = cee_bodyparts[i].letter;
		int stage_to_try = creature->getStage();
		creatureAppearance *partapp = 0;
		if (x == 'a' || x >= 'o') {
			// head
			partapp = appearance[0];
		} else if (x == 'b') {
			// body
			partapp = appearance[1];
		} else if (x >= 'c' && x <= 'h') {
			// legs
			partapp = appearance[2];
		} else if (x >= 'i' && x <= 'm') {
			// arms
			partapp = appearance[3];
		} else if (x == 'n') {
			// tail
			partapp = appearance[4];
		}
		// TODO: this exception won't necessary be handled, neither will the one below
		if (!partapp)
			throw creaturesException(boost::str(boost::format("SkeletalCreature doesn't understand appearance id '%c'") % (unsigned char)x));
	
		if (engine.version == 1) partapp->species = 0; // TODO: don't stomp over the gene? :P
		while (stage_to_try > -1 && images[i] == 0) {
			images[i] = world.gallery.getImage(x + dataString(stage_to_try, true, partapp->species, partapp->variant));
			if (images[i] == 0) stage_to_try--;
		}
		if (images[i] == 0)
			throw creaturesException(boost::str(boost::format("SkeletalCreature couldn't find an image for species %d, variant %d, stage %d") % (int)partapp->species % (int)partapp->variant % (int)creature->getStage()));
		std::ifstream in(std::string(world.findFile(std::string("/Body Data/") + x + dataString(stage_to_try, false, partapp->species, partapp->variant) + ".att")).c_str());
		in >> att[i];
	}

	setPose(0);
}

SkeletalCreature::~SkeletalCreature() {
	delete skeleton;
}

void SkeletalCreature::render(Surface *renderer, int xoffset, int yoffset) {
	for (int j = 0; j < 14; j++) {
		int i = cee_zorder[direction][j];

		if (engine.version == 1) // TODO: this is hackery to skip tails for C1
			if (i == 6 || i == 13) continue;<commit_msg>add some untested error handling when opening ATT files<commit_after>/*
 *  SkeletalCreature.cpp
 *  openc2e
 *
 *  Created by <NAME> on Thu 10 Mar 2005.
 *  Copyright (c) 2005-2006 <NAME>. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */
/* 20th feb 2005, by fuzzie
   with thanks to vadim for helping out

   TODO:
	* hair tidiness/untidiness (offset by 0 to 2 multiplied by 16)
	* render hairs/ears .. at the moment we avoid them, we lack zorder sanity too
*/

#include "SkeletalCreature.h"
#include "Creature.h"
#include "World.h"
#include "Engine.h"
#include "Backend.h"

#include <typeinfo> // TODO: remove when genome system is fixed
#include <boost/format.hpp>

struct bodypartinfo {
	char letter;
	int parent;
	int attorder;
};

bodypartinfo cee_bodyparts[17] = {
	{ 'b', -1, -1 }, // body
	{ 'a', 0, 0 }, // head - attached to body
	{ 'c', 0, 1 }, // left thigh - attached to body
	{ 'f', 0, 2 }, // right thigh - attached to body
	{ 'i', 0, 3 }, // left humerus - attached to body
	{ 'k', 0, 4 }, // right humerus - attached to body
	{ 'm', 0, 5 }, // tail root - attached to body
	{ 'd', 2, 1 }, // left shin - attached to left thigh
	{ 'g', 3, 1 }, // right shin - attached to right thigh
	{ 'j', 4, 1 }, // left radius - attached to left humerus
	{ 'l', 5, 1 }, // right radius - attached to right humerus
	{ 'e', 7, 1 }, // left foot - attached to left shin
	{ 'h', 8, 1 }, // right foot - attached to right shin
	{ 'n', 6, 1 }, // tail tip - attached to tail root
	// Creatures Village only:
	{ 'o', 1, -1 }, // left ear - attached to head
	{ 'p', 1, -1 }, // right ear - attached to head
	{ 'q', 1, -1 } // hair - attached to head
};

unsigned int cee_zorder[4][14] = {
	{ 6, 13, 2, 7, 11, 4, 9, 0, 1, 5, 3, 8, 10, 12 },
	{ 6, 13, 3, 8, 12, 5, 10, 0, 1, 2, 7, 11, 4, 9 },
	{ 6, 13, 2, 4, 9, 5, 3, 7, 8, 10, 0, 11, 12, 1 },
	{ 2, 4, 9, 1, 5, 3, 7, 8, 10, 11, 12, 0, 6, 13 }
};

// needed for setPose(string) at least .. maybe cee_bodyparts should be indexed by letter
unsigned int cee_lookup[17] = { 1, 0, 2, 7, 11, 3, 8, 12, 4, 9, 5, 10, 6, 13, 14, 15, 16 };

std::string SkeletalCreature::dataString(unsigned int _stage, bool sprite, unsigned int dataspecies, unsigned int databreed) {
	// TODO: dataspecies is nonsense in c1
	char _postfix[4] = "XXX";
	_postfix[0] = '0' + dataspecies + ((sprite && creature->isFemale()) ? 4 : 0);
	_postfix[1] = '0' + _stage;
	if (engine.version == 1)
		_postfix[2] = '0' + databreed;
	else
		_postfix[2] = 'a' + databreed;
	return _postfix;
}

SkeletalCreature::SkeletalCreature(unsigned char _family, Creature *c) : CreatureAgent(_family, c) {
	facialexpression = 0;
	pregnancy = 0;
	eyesclosed = false;

	for (int i = 0; i < 14; i++) {
		images[i] = 0;
	}
	
	skeletonInit();

	// needs to go last for now, so we can throw exceptions from skeletonInit
	skeleton = new SkeletonPart(this);
}

void SkeletalCreature::skeletonInit() {
	//TODO: the exception throwing in here needs some more thought

	creatureAppearance *appearance[5] = { 0, 0, 0, 0, 0 };
	for (vector<gene *>::iterator i = creature->getGenome()->genes.begin(); i != creature->getGenome()->genes.end(); i++) {
		if (typeid(*(*i)) == typeid(creatureAppearance)) {
			creatureAppearance *x = (creatureAppearance *)(*i);
			if (x->part > 4)
				throw creaturesException(boost::str(boost::format("SkeletalCreature didn't understand a gene with a part# of %d") % (int)x->part));
			if (appearance[x->part])
				throw creaturesException(boost::str(boost::format("SkeletalCreature got a duplicated gene for part# %d") % (int)x->part));
			appearance[x->part] = x;
		}
	}

	for (int i = 0; i < 14; i++) {
		if (engine.version == 1) // TODO: this is hackery to skip tails for C1
			if (i == 6 || i == 13) continue;

		// try this stage and the stages below it to find data which worksforus
		if (images[i])
			world.gallery.delImage(images[i]);
		images[i] = 0;
		char x = cee_bodyparts[i].letter;
		int stage_to_try = creature->getStage();
		creatureAppearance *partapp = 0;
		if (x == 'a' || x >= 'o') {
			// head
			partapp = appearance[0];
		} else if (x == 'b') {
			// body
			partapp = appearance[1];
		} else if (x >= 'c' && x <= 'h') {
			// legs
			partapp = appearance[2];
		} else if (x >= 'i' && x <= 'm') {
			// arms
			partapp = appearance[3];
		} else if (x == 'n') {
			// tail
			partapp = appearance[4];
		}
		// TODO: this exception won't necessary be handled, neither will the one below
		if (!partapp)
			throw creaturesException(boost::str(boost::format("SkeletalCreature doesn't understand appearance id '%c'") % (unsigned char)x));
	
		if (engine.version == 1) partapp->species = 0; // TODO: don't stomp over the gene? :P
		while (stage_to_try > -1 && images[i] == 0) {
			images[i] = world.gallery.getImage(x + dataString(stage_to_try, true, partapp->species, partapp->variant));
			if (images[i] == 0) stage_to_try--;
		}
		if (images[i] == 0)
			throw creaturesException(boost::str(boost::format("SkeletalCreature couldn't find an image for species %d, variant %d, stage %d") % (int)partapp->species % (int)partapp->variant % (int)creature->getStage()));

		std::string attfilename = world.findFile(std::string("/Body Data/") + x + dataString(stage_to_try, false, partapp->species, partapp->variant) + ".att");
		if (attfilename.empty())
			throw creaturesException(boost::str(boost::format("SkeletalCreature couldn't find body data for species %d, variant %d, stage %d") % (int)partapp->species % (int)partapp->variant % stage_to_try));
		std::ifstream in(attfilename.c_str());
		if (in.fail())
			throw creaturesException(boost::str(boost::format("SkeletalCreature couldn't load body data for species %d, variant %d, stage %d") % (int)partapp->species % (int)partapp->variant % stage_to_try));
		in >> att[i];
	}

	setPose(0);
}

SkeletalCreature::~SkeletalCreature() {
	delete skeleton;
}

void SkeletalCreature::render(Surface *renderer, int xoffset, int yoffset) {
	for (int j = 0; j < 14; j++) {
		int i = cee_zorder[direction][j];

		if (engine.version == 1) // TODO: this is hackery to skip tails for C1
			if (i == 6 || i == 13) continue;
{'fraction_non_alphanumeric': 0.16120628404515672, 'fraction_numerical': 0.0344201468382411, 'mean_word_length': 3.4794908062234793, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '767745', 'n_tokens_mistral': 5106, 'n_tokens_neox': 4435, 'n_words': 1552}