From: Côme Chilliet Date: Sun, 11 Dec 2016 10:34:33 +0000 (+0700) Subject: Sorted all source files in folders X-Git-Url: https://git.jsancho.org/?p=lugaru.git;a=commitdiff_plain;h=b84825978803615f45a9f128232e62431042aec0 Sorted all source files in folders --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 087eafe..62a62f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,40 +48,41 @@ set(LUGARU_SRCS ${SRCDIR}/Animation/Joint.cpp ${SRCDIR}/Animation/Muscle.cpp ${SRCDIR}/Animation/Skeleton.cpp - ${SRCDIR}/Frustum.cpp - ${SRCDIR}/Account.cpp - ${SRCDIR}/Campaign.cpp - ${SRCDIR}/ConsoleCmds.cpp - ${SRCDIR}/Dialog.cpp - ${SRCDIR}/Hotspot.cpp + ${SRCDIR}/Audio/openal_wrapper.cpp + ${SRCDIR}/Audio/Sounds.cpp + ${SRCDIR}/Devtools/ConsoleCmds.cpp + ${SRCDIR}/Environment/Lights.cpp + ${SRCDIR}/Environment/Skybox.cpp + ${SRCDIR}/Environment/Terrain.cpp + ${SRCDIR}/Graphic/Models.cpp + ${SRCDIR}/Graphic/Sprite.cpp + ${SRCDIR}/Graphic/Stereo.cpp + ${SRCDIR}/Graphic/Text.cpp + ${SRCDIR}/Graphic/Texture.cpp + ${SRCDIR}/Level/Awards.cpp + ${SRCDIR}/Level/Campaign.cpp + ${SRCDIR}/Level/Dialog.cpp + ${SRCDIR}/Level/Hotspot.cpp + ${SRCDIR}/Math/Frustum.cpp + ${SRCDIR}/Math/Quaternions.cpp + ${SRCDIR}/Menu/Menu.cpp + ${SRCDIR}/Objects/Objects.cpp + ${SRCDIR}/Objects/Person.cpp + ${SRCDIR}/Objects/Weapons.cpp + ${SRCDIR}/User/Account.cpp + ${SRCDIR}/User/Settings.cpp + ${SRCDIR}/Utils/Folders.cpp + ${SRCDIR}/Utils/ImageIO.cpp + ${SRCDIR}/Utils/Input.cpp + ${SRCDIR}/Utils/pack.c + ${SRCDIR}/Utils/private.c + ${SRCDIR}/Utils/unpack.c ${SRCDIR}/Game.cpp ${SRCDIR}/GameDraw.cpp ${SRCDIR}/GameInitDispose.cpp ${SRCDIR}/GameTick.cpp ${SRCDIR}/Globals.cpp - ${SRCDIR}/Lights.cpp - ${SRCDIR}/Menu.cpp - ${SRCDIR}/Models.cpp - ${SRCDIR}/Objects.cpp - ${SRCDIR}/pack.c - ${SRCDIR}/Person.cpp - ${SRCDIR}/private.c - ${SRCDIR}/Quaternions.cpp - ${SRCDIR}/Skybox.cpp - ${SRCDIR}/Sprite.cpp - ${SRCDIR}/Terrain.cpp - ${SRCDIR}/Texture.cpp - ${SRCDIR}/Text.cpp - ${SRCDIR}/ImageIO.cpp - ${SRCDIR}/unpack.c - ${SRCDIR}/Weapons.cpp - ${SRCDIR}/openal_wrapper.cpp - ${SRCDIR}/Input.cpp - ${SRCDIR}/Settings.cpp - ${SRCDIR}/Stereo.cpp - ${SRCDIR}/Sounds.cpp - ${SRCDIR}/Awards.cpp - ${SRCDIR}/Utils/Folders.cpp + ) set(LUGARU_H @@ -89,38 +90,39 @@ set(LUGARU_H ${SRCDIR}/Animation/Joint.h ${SRCDIR}/Animation/Muscle.h ${SRCDIR}/Animation/Skeleton.h - ${SRCDIR}/Frustum.h - ${SRCDIR}/Account.h - ${SRCDIR}/Campaign.h - ${SRCDIR}/ConsoleCmds.h - ${SRCDIR}/Dialog.h - ${SRCDIR}/Hotspot.h - ${SRCDIR}/Game.h - ${SRCDIR}/Lights.h - ${SRCDIR}/Menu.h - ${SRCDIR}/Models.h - ${SRCDIR}/Objects.h - ${SRCDIR}/Person.h - ${SRCDIR}/PhysicsMath.h - ${SRCDIR}/Quaternions.h - ${SRCDIR}/Random.h - ${SRCDIR}/Skybox.h - ${SRCDIR}/Sprite.h - ${SRCDIR}/ImageIO.h - ${SRCDIR}/Terrain.h - ${SRCDIR}/Texture.h - ${SRCDIR}/Text.h - ${SRCDIR}/Weapons.h - ${SRCDIR}/Input.h - ${SRCDIR}/binio.h - ${SRCDIR}/openal_wrapper.h - ${SRCDIR}/optionparser.h - ${SRCDIR}/gamegl.h - ${SRCDIR}/private.h - ${SRCDIR}/Settings.h - ${SRCDIR}/Stereo.h - ${SRCDIR}/Sounds.h + ${SRCDIR}/Audio/openal_wrapper.h + ${SRCDIR}/Audio/Sounds.h + ${SRCDIR}/Devtools/ConsoleCmds.h + ${SRCDIR}/Environment/Lights.h + ${SRCDIR}/Environment/Skybox.h + ${SRCDIR}/Environment/Terrain.h + ${SRCDIR}/Graphic/gamegl.h + ${SRCDIR}/Graphic/Models.h + ${SRCDIR}/Graphic/Sprite.h + ${SRCDIR}/Graphic/Stereo.h + ${SRCDIR}/Graphic/Text.h + ${SRCDIR}/Graphic/Texture.h + ${SRCDIR}/Level/Campaign.h + ${SRCDIR}/Level/Dialog.h + ${SRCDIR}/Level/Hotspot.h + ${SRCDIR}/Math/Frustum.h + ${SRCDIR}/Math/PhysicsMath.h + ${SRCDIR}/Math/Quaternions.h + ${SRCDIR}/Math/Random.h + ${SRCDIR}/Menu/Menu.h + ${SRCDIR}/Objects/Objects.h + ${SRCDIR}/Objects/Person.h + ${SRCDIR}/Objects/Weapons.h + ${SRCDIR}/Thirdparty/optionparser.h + ${SRCDIR}/User/Account.h + ${SRCDIR}/User/Settings.h + ${SRCDIR}/Utils/binio.h ${SRCDIR}/Utils/Folders.h + ${SRCDIR}/Utils/ImageIO.h + ${SRCDIR}/Utils/Input.h + ${SRCDIR}/Utils/private.h + ${SRCDIR}/Game.h + ) if(UNIX) diff --git a/Source/Account.cpp b/Source/Account.cpp deleted file mode 100644 index 6a1d3bb..0000000 --- a/Source/Account.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Account.h" -#include "binio.h" -#include -#include "MacCompatibility.h" -#include "string.h" -#include - -using namespace std; - -extern bool devtools; - -vector Account::accounts; -int Account::i_active = -1; - -Account::Account(const string& name) : name(name), campaignProgress() -{ - difficulty = 0; - progress = 0; - points = 0; - memset(highscore, 0, sizeof(highscore)); - memset(fasttime, 0, sizeof(fasttime)); - memset(unlocked, 0, sizeof(unlocked)); - - setCurrentCampaign("main"); -} - -Account::Account(FILE* tfile) : Account("") -{ - funpackf(tfile, "Bi", &difficulty); - funpackf(tfile, "Bi", &progress); - int nbCampaigns; - funpackf(tfile, "Bi", &nbCampaigns); - - for (int k = 0; k < nbCampaigns; ++k) { - string campaignName = ""; - int t; - char c; - funpackf(tfile, "Bi", &t); - for (int j = 0; j < t; j++) { - funpackf(tfile, "Bb", &c); - campaignName.append(1, c); - } - funpackf(tfile, "Bf", &(campaignProgress[campaignName].time)); - funpackf(tfile, "Bf", &(campaignProgress[campaignName].score)); - funpackf(tfile, "Bf", &(campaignProgress[campaignName].fasttime)); - funpackf(tfile, "Bf", &(campaignProgress[campaignName].highscore)); - int campaignchoicesmade, campaignchoice; - funpackf(tfile, "Bi", &campaignchoicesmade); - for (int j = 0; j < campaignchoicesmade; j++) { - funpackf(tfile, "Bi", &campaignchoice); - if (campaignchoice >= 10) { // what is that for? - campaignchoice = 0; - } - campaignProgress[campaignName].choices.push_back(campaignchoice); - } - } - - currentCampaign = ""; - int t; - char c; - funpackf(tfile, "Bi", &t); - for (int i = 0; i < t; i++) { - funpackf(tfile, "Bb", &c); - currentCampaign.append(1, c); - } - - funpackf(tfile, "Bf", &points); - for (int i = 0; i < 50; i++) { - funpackf(tfile, "Bf", &(highscore[i])); - funpackf(tfile, "Bf", &(fasttime[i])); - } - for (int i = 0; i < 60; i++) { - funpackf(tfile, "Bb", &(unlocked[i])); - } - int temp; - char ctemp; - funpackf(tfile, "Bi", &temp); - for (int i = 0; i < temp; i++) { - funpackf(tfile, "Bb", &ctemp); - name.append(1, ctemp); - } - if (name.empty()) { - name = "Lugaru Player"; // no empty player name security. - } -} - -void Account::save(FILE* tfile) -{ - fpackf(tfile, "Bi", difficulty); - fpackf(tfile, "Bi", progress); - fpackf(tfile, "Bi", campaignProgress.size()); - - map::const_iterator it; - for (it = campaignProgress.begin(); it != campaignProgress.end(); ++it) { - fpackf(tfile, "Bi", it->first.size()); - for (unsigned j = 0; j < it->first.size(); j++) { - fpackf(tfile, "Bb", it->first[j]); - } - fpackf(tfile, "Bf", it->second.time); - fpackf(tfile, "Bf", it->second.score); - fpackf(tfile, "Bf", it->second.fasttime); - fpackf(tfile, "Bf", it->second.highscore); - fpackf(tfile, "Bi", it->second.choices.size()); - for (unsigned j = 0; j < it->second.choices.size(); j++) { - fpackf(tfile, "Bi", it->second.choices[j]); - } - } - - fpackf(tfile, "Bi", getCurrentCampaign().size()); - for (unsigned j = 0; j < getCurrentCampaign().size(); j++) { - fpackf(tfile, "Bb", getCurrentCampaign()[j]); - } - - fpackf(tfile, "Bf", points); - for (unsigned j = 0; j < 50; j++) { - fpackf(tfile, "Bf", highscore[j]); - fpackf(tfile, "Bf", fasttime[j]); - } - for (unsigned j = 0; j < 60; j++) { - fpackf(tfile, "Bb", unlocked[j]); - } - fpackf(tfile, "Bi", name.size()); - for (unsigned j = 0; j < name.size(); j++) { - fpackf(tfile, "Bb", name[j]); - } -} - -void Account::setCurrentCampaign(const string& name) -{ - currentCampaign = name; -} - -void Account::add(const string& name) -{ - accounts.emplace_back(name); - i_active = accounts.size() - 1; -} - -Account& Account::get(int i) -{ - return accounts.at(i); -} - -int Account::getNbAccounts() -{ - return accounts.size(); -} - -bool Account::hasActive() -{ - return (i_active >= 0); -} - -Account& Account::active() -{ - return accounts.at(i_active); -} - -void Account::setActive(int i) -{ - if ((i >= 0) && (i < int(accounts.size()))) { - i_active = i; - } else { - cerr << "Tried to set active account to " << i << " but there is not such account" << endl; - i_active = -1; - } -} - -void Account::destroyActive() -{ - if ((i_active >= 0) && (i_active < int(accounts.size()))) { - accounts.erase(accounts.begin() + i_active); - i_active = -1; - } else { - cerr << "Tried to destroy active account " << i_active << " but there is not such account" << endl; - i_active = -1; - } -} - -int Account::getDifficulty() -{ - return difficulty; -} - -void Account::endGame() -{ - campaignProgress[currentCampaign].choices.clear(); - campaignProgress[currentCampaign].score = 0; - campaignProgress[currentCampaign].time = 0; -} - -void Account::winCampaignLevel(int choice, float score, float time) -{ - campaignProgress[currentCampaign].choices.push_back(choice); - setCampaignScore(campaignProgress[currentCampaign].score + score); - campaignProgress[currentCampaign].time = time; -} - -void Account::winLevel(int level, float score, float time) -{ - if (!devtools) { - if (score > highscore[level]) - highscore[level] = score; - if (time < fasttime[level] || fasttime[level] == 0) - fasttime[level] = time; - } - if (progress < level + 1) - progress = level + 1; -} - -void Account::loadFile(string filename) -{ - FILE *tfile; - int numaccounts; - int iactive; - errno = 0; - - tfile = fopen(filename.c_str(), "rb" ); - - if (tfile) { - funpackf(tfile, "Bi", &numaccounts); - funpackf(tfile, "Bi", &iactive); - printf("Loading %d accounts\n", numaccounts); - for (int i = 0; i < numaccounts; i++) { - printf("Loading account %d/%d\n", i, numaccounts); - accounts.emplace_back(tfile); - } - - fclose(tfile); - setActive(iactive); - } else { - perror(("Couldn't load users from " + filename).c_str()); - i_active = -1; - } -} - -void Account::saveFile(string filename) -{ - FILE *tfile; - errno = 0; - - tfile = fopen(filename.c_str(), "wb" ); - if (tfile) { - fpackf(tfile, "Bi", getNbAccounts()); - fpackf(tfile, "Bi", i_active); - - for (int i = 0; i < getNbAccounts(); i++) { - printf("writing account %d/%d (%s)\n", i + 1, getNbAccounts(), accounts[i].getName().c_str()); - accounts[i].save(tfile); - } - - fclose(tfile); - } else { - perror(("Couldn't save users in " + filename).c_str()); - } -} diff --git a/Source/Account.h b/Source/Account.h deleted file mode 100644 index 385c6fa..0000000 --- a/Source/Account.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _Account_H_ -#define _Account_H_ - -#include -#include -#include -#include - -struct CampaignProgress { - float highscore; - float fasttime; - float score; - float time; - std::vector choices; - CampaignProgress() { - highscore = 0; - fasttime = 0; - score = 0; - time = 0; - } -}; - -class Account -{ -public: - static void destroyActive(); - static void setActive(int i); - static void add(const std::string& name); - static Account& get(int i); - static void loadFile(std::string filename); - static void saveFile(std::string filename); - static int getNbAccounts(); - - static bool hasActive(); - static Account& active(); - - Account(const std::string& name = ""); - Account(FILE* tfile); - - void endGame(); - void winCampaignLevel(int choice, float score, float time); - void winLevel(int level, float score, float time); - - // getter and setters - int getDifficulty(); - void setDifficulty(int i) { - difficulty = i; - }; - const std::string& getName() { - return name; - }; - float getCampaignScore() { - return campaignProgress[currentCampaign].score; - }; - int getCampaignChoicesMade() { - return campaignProgress[currentCampaign].choices.size(); - }; - int getCampaignChoice(int i) { - return campaignProgress[currentCampaign].choices[i]; - }; - void setCampaignScore(int s) { - campaignProgress[currentCampaign].score = s; - if (s > campaignProgress[currentCampaign].highscore) - campaignProgress[currentCampaign].highscore = s; - }; - void setCampaignFinalTime(float t) { - campaignProgress[currentCampaign].time = t; - if ((t < campaignProgress[currentCampaign].fasttime) || ((campaignProgress[currentCampaign].fasttime == 0) && (t != 0))) - campaignProgress[currentCampaign].fasttime = t; - }; - float getCampaignFasttime() { - return campaignProgress[currentCampaign].fasttime; - }; - void resetFasttime() { - campaignProgress[currentCampaign].fasttime = 0; - }; - float getCampaignHighScore() { - return campaignProgress[currentCampaign].highscore; - }; - float getHighScore(int i) { - return highscore[i]; - }; - float getFastTime(int i) { - return fasttime[i]; - }; - int getProgress() { - return progress; - }; - std::string getCurrentCampaign() { - return currentCampaign; - }; - void setCurrentCampaign(const std::string& name); - -private: - //statics - static std::vector accounts; - static int i_active; - - void save(FILE* tfile); - - int difficulty; - int progress; // progress in challenge levels - float points; - float highscore[50]; - float fasttime[50]; - bool unlocked[60]; - std::string name; - - std::string currentCampaign; - std::map campaignProgress; -}; - -#endif diff --git a/Source/Animation/Animation.cpp b/Source/Animation/Animation.cpp index 83e2c0e..4433800 100644 --- a/Source/Animation/Animation.cpp +++ b/Source/Animation/Animation.cpp @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with Lugaru. If not, see . */ -#include "Animation/Skeleton.h" #include "Animation/Animation.h" +#include "Animation/Skeleton.h" #include "Utils/Folders.h" #include "Game.h" diff --git a/Source/Animation/Animation.h b/Source/Animation/Animation.h index 4a74c5c..91205ab 100644 --- a/Source/Animation/Animation.h +++ b/Source/Animation/Animation.h @@ -21,6 +21,7 @@ along with Lugaru. If not, see . #define ANIMATION_H #include +#include "Math/Quaternions.h" enum anim_attack_type { neutral, normalattack, reversed, reversal diff --git a/Source/Animation/Joint.cpp b/Source/Animation/Joint.cpp index 4d3dd7c..4428cb8 100644 --- a/Source/Animation/Joint.cpp +++ b/Source/Animation/Joint.cpp @@ -19,7 +19,7 @@ along with Lugaru. If not, see . */ #include "Animation/Joint.h" -#include "binio.h" +#include "Utils/binio.h" Joint::Joint() : blurred(0), diff --git a/Source/Animation/Joint.h b/Source/Animation/Joint.h index c4c8e3b..124d7a6 100644 --- a/Source/Animation/Joint.h +++ b/Source/Animation/Joint.h @@ -21,7 +21,7 @@ along with Lugaru. If not, see . #ifndef _JOINT_H_ #define _JOINT_H_ -#include "Quaternions.h" +#include "Math/Quaternions.h" #include enum bodypart { diff --git a/Source/Animation/Muscle.cpp b/Source/Animation/Muscle.cpp index 94ed5b1..c4f201a 100644 --- a/Source/Animation/Muscle.cpp +++ b/Source/Animation/Muscle.cpp @@ -19,7 +19,7 @@ along with Lugaru. If not, see . */ #include "Animation/Muscle.h" -#include "binio.h" +#include "Utils/binio.h" extern float multiplier; extern bool freeze; diff --git a/Source/Animation/Skeleton.cpp b/Source/Animation/Skeleton.cpp index ee247c5..f525240 100644 --- a/Source/Animation/Skeleton.cpp +++ b/Source/Animation/Skeleton.cpp @@ -20,9 +20,9 @@ along with Lugaru. If not, see . /**> HEADER FILES <**/ #include "Game.h" -#include "Animation/Skeleton.h" -#include "openal_wrapper.h" #include "Animation/Animation.h" +#include "Animation/Skeleton.h" +#include "Audio/openal_wrapper.h" #include "Utils/Folders.h" extern float multiplier; diff --git a/Source/Animation/Skeleton.h b/Source/Animation/Skeleton.h index db03e75..19db503 100644 --- a/Source/Animation/Skeleton.h +++ b/Source/Animation/Skeleton.h @@ -21,17 +21,16 @@ along with Lugaru. If not, see . #ifndef _SKELETON_H_ #define _SKELETON_H_ -#include "Models.h" - /**> HEADER FILES <**/ -#include "gamegl.h" -#include "Quaternions.h" -#include "Objects.h" -#include "Sprite.h" -#include "binio.h" #include "Animation/Animation.h" #include "Animation/Joint.h" #include "Animation/Muscle.h" +#include "Graphic/gamegl.h" +#include "Graphic/Models.h" +#include "Graphic/Sprite.h" +#include "Math/Quaternions.h" +#include "Objects/Objects.h" +#include "Utils/binio.h" const int max_joints = 50; diff --git a/Source/Audio/Sounds.cpp b/Source/Audio/Sounds.cpp new file mode 100644 index 0000000..2e47da0 --- /dev/null +++ b/Source/Audio/Sounds.cpp @@ -0,0 +1,123 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Audio/openal_wrapper.h" +#include "Audio/Sounds.h" +#include "Math/Quaternions.h" +#include "Utils/Folders.h" + +struct OPENAL_SAMPLE *samp[sounds_count]; + +extern XYZ envsound[30]; +extern float envsoundvol[30]; +extern int numenvsounds; +extern float envsoundlife[30]; + +int footstepsound, footstepsound2, footstepsound3, footstepsound4; + +int channels[100]; + +static const char *sound_data[sounds_count] = { +#define DECLARE_SOUND(id, filename) filename, +#include "Sounds.def" +#undef DECLARE_SOUND +}; + +// FIXME: dimensionality is not a property of the sound sample. +// This should be decided at the time of playback +static int snd_mode(int snd) +{ + switch (snd) { + case alarmsound: + case consolefailsound: + case consolesuccesssound: + case firestartsound: + case fireendsound: + return OPENAL_2D; + default: + return OPENAL_HW3D; + } +} + +void loadAllSounds() +{ + for (int i = 0; i < sounds_count; i++) { + std::string buf = std::string("Sounds/") + sound_data[i]; + samp[i] = OPENAL_Sample_Load(OPENAL_FREE, + Folders::getResourcePath(buf).c_str(), + snd_mode(i), + 0, 0); + } + footstepsound = footstepsn1; + footstepsound2 = footstepsn2; + footstepsound3 = footstepst1; + footstepsound4 = footstepst2; + // Huh? + // OPENAL_Sample_SetMode(samp[whooshsound], OPENAL_LOOP_NORMAL); + for (int i = stream_firesound; i <= stream_menutheme; i++) + OPENAL_Stream_SetMode(samp[i], OPENAL_LOOP_NORMAL); +} + +void addEnvSound(XYZ coords, float vol, float life) +{ + envsound[numenvsounds] = coords; + envsoundvol[numenvsounds] = vol; + envsoundlife[numenvsounds] = life; + numenvsounds++; +} + +void emit_sound_at(int soundid, const XYZ &pos, float vol) +{ + PlaySoundEx (soundid, samp[soundid], NULL, true); + OPENAL_3D_SetAttributes_ (channels[soundid], pos, NULL); + OPENAL_SetVolume (channels[soundid], vol); + OPENAL_SetPaused (channels[soundid], false); +} + +void emit_sound_np(int soundid, float vol) +{ + PlaySoundEx (soundid, samp[soundid], NULL, true); + OPENAL_SetVolume (channels[soundid], vol); + OPENAL_SetPaused (channels[soundid], false); +} + +void emit_stream_at(int soundid, const XYZ &pos, float vol) +{ + PlayStreamEx (soundid, samp[soundid], NULL, true); + OPENAL_3D_SetAttributes_ (channels[soundid], pos, NULL); + OPENAL_SetVolume (channels[soundid], vol); + OPENAL_SetPaused (channels[soundid], false); +} + +void emit_stream_np(int soundid, float vol) +{ + PlayStreamEx (soundid, samp[soundid], NULL, true); + OPENAL_SetVolume (channels[soundid], vol); + OPENAL_SetPaused (channels[soundid], false); +} + +void resume_stream(int soundid) +{ + OPENAL_SetPaused (channels[soundid], false); +} + +void pause_sound(int soundid) +{ + OPENAL_SetPaused (channels[soundid], true); +} diff --git a/Source/Audio/Sounds.def b/Source/Audio/Sounds.def new file mode 100644 index 0000000..6fde586 --- /dev/null +++ b/Source/Audio/Sounds.def @@ -0,0 +1,90 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +DECLARE_SOUND(footstepsn1, "FootStepSnow1.ogg") +DECLARE_SOUND(footstepsn2, "FootStepSnow2.ogg") +DECLARE_SOUND(footstepst1, "FootStepStone1.ogg") +DECLARE_SOUND(footstepst2, "FootStepStone2.ogg") +DECLARE_SOUND(footstepgr1, "FootStepGrass1.ogg") +DECLARE_SOUND(footstepgr2, "FootStepGrass2.ogg") +DECLARE_SOUND(landsound, "Land.ogg") +DECLARE_SOUND(jumpsound, "Jump.ogg") +DECLARE_SOUND(hawksound, "Hawk.ogg") +DECLARE_SOUND(whooshsound, "Whoosh.ogg") +DECLARE_SOUND(landsound1, "Land1.ogg") +DECLARE_SOUND(landsound2, "Land2.ogg") +DECLARE_SOUND(breaksound, "Broken.ogg") +DECLARE_SOUND(lowwhooshsound, "LowWhoosh.ogg") +DECLARE_SOUND(midwhooshsound, "MidWhoosh.ogg") +DECLARE_SOUND(highwhooshsound, "HighWhoosh.ogg") +DECLARE_SOUND(movewhooshsound, "MoveWhoosh.ogg") +DECLARE_SOUND(heavyimpactsound, "HeavyImpact.ogg") +DECLARE_SOUND(whooshhitsound, "WhooshHit.ogg") +DECLARE_SOUND(thudsound, "Thud.ogg") +DECLARE_SOUND(alarmsound, "Alarm.ogg") +DECLARE_SOUND(breaksound2, "Break.ogg") +DECLARE_SOUND(knifedrawsound, "KnifeDraw.ogg") +DECLARE_SOUND(knifesheathesound, "KnifeSheathe.ogg") +DECLARE_SOUND(fleshstabsound, "FleshStab.ogg") +DECLARE_SOUND(fleshstabremovesound, "FleshStabRemove.ogg") +DECLARE_SOUND(knifeswishsound, "KnifeSwish.ogg") +DECLARE_SOUND(knifeslicesound, "KnifeSlice.ogg") +DECLARE_SOUND(swordslicesound, "SwordSlice.ogg") +DECLARE_SOUND(skidsound, "Skid.ogg") +DECLARE_SOUND(snowskidsound, "SnowSkid.ogg") +DECLARE_SOUND(bushrustle, "BushRustle.ogg") +DECLARE_SOUND(clank1sound, "Clank1.ogg") +DECLARE_SOUND(clank2sound, "Clank2.ogg") +DECLARE_SOUND(clank3sound, "Clank3.ogg") +DECLARE_SOUND(clank4sound, "Clank4.ogg") +DECLARE_SOUND(consolesuccesssound, "ConsoleSuccess.ogg") +DECLARE_SOUND(consolefailsound, "ConsoleFail.ogg") +DECLARE_SOUND(metalhitsound, "MetalHit.ogg") +DECLARE_SOUND(clawslicesound, "ClawSlice.ogg") +DECLARE_SOUND(splattersound, "Splatter.ogg") +DECLARE_SOUND(growlsound, "Growl.ogg") +DECLARE_SOUND(growl2sound, "Growl2.ogg") +DECLARE_SOUND(barksound, "Bark.ogg") +DECLARE_SOUND(bark2sound, "Bark2.ogg") +DECLARE_SOUND(bark3sound, "Bark3.ogg") +DECLARE_SOUND(snarlsound, "Snarl.ogg") +DECLARE_SOUND(snarl2sound, "Snarl2.ogg") +DECLARE_SOUND(barkgrowlsound, "BarkGrowl.ogg") +DECLARE_SOUND(rabbitattacksound, "RabbitAttack.ogg") +DECLARE_SOUND(rabbitattack2sound, "RabbitAttack2.ogg") +DECLARE_SOUND(rabbitattack3sound, "RabbitAttack3.ogg") +DECLARE_SOUND(rabbitattack4sound, "RabbitAttack4.ogg") +DECLARE_SOUND(rabbitpainsound, "RabbitPain.ogg") +DECLARE_SOUND(rabbitpain1sound, "RabbitPain2.ogg") +DECLARE_SOUND(rabbitchitter, "RabbitChitter.ogg") +DECLARE_SOUND(rabbitchitter2, "RabbitChitter2.ogg") +DECLARE_SOUND(swordstaffsound, "SwordStaff.ogg") +DECLARE_SOUND(staffbodysound, "StaffBody.ogg") +DECLARE_SOUND(staffheadsound, "StaffHead.ogg") +DECLARE_SOUND(staffbreaksound, "StaffBreak.ogg") +DECLARE_SOUND(firestartsound, "FireStart.ogg") +DECLARE_SOUND(fireendsound, "FireEnd.ogg") +DECLARE_SOUND(stream_firesound, "Fire.ogg") +DECLARE_SOUND(stream_grasstheme, "Music1Grass.ogg") +DECLARE_SOUND(stream_snowtheme, "Music1Snow.ogg") +DECLARE_SOUND(stream_deserttheme, "Music1Desert.ogg") +DECLARE_SOUND(stream_wind, "Wind.ogg") +DECLARE_SOUND(stream_desertambient, "DesertAmbient.ogg") +DECLARE_SOUND(stream_fighttheme, "Music2.ogg") +DECLARE_SOUND(stream_menutheme, "Music3.ogg") diff --git a/Source/Audio/Sounds.h b/Source/Audio/Sounds.h new file mode 100644 index 0000000..7557993 --- /dev/null +++ b/Source/Audio/Sounds.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef SOUNDS_H +#define SOUNDS_H + +enum sound_types { +#define DECLARE_SOUND(id, filename) id, +#include "Sounds.def" +#undef DECLARE_SOUND + sounds_count +}; + +extern struct OPENAL_SAMPLE *samp[sounds_count]; +extern int channels[]; + +extern void loadAllSounds(); + +extern void addEnvSound(XYZ coords, float vol = 16, float life = .4); + +extern void emit_sound_at(int soundid, const XYZ &pos = XYZ(), float vol = 256.f); +extern void emit_sound_np(int soundid, float vol = 256.f); +extern void emit_stream_at(int soundid, const XYZ &pos = XYZ(), float vol = 256.f); +extern void emit_stream_np(int soundid, float vol = 256.f); +extern void resume_stream(int soundid); +extern void pause_sound(int soundid); + +extern int footstepsound, footstepsound2, footstepsound3, footstepsound4; +#endif diff --git a/Source/Audio/openal_wrapper.cpp b/Source/Audio/openal_wrapper.cpp new file mode 100644 index 0000000..e1e2346 --- /dev/null +++ b/Source/Audio/openal_wrapper.cpp @@ -0,0 +1,613 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include +#include +#include + +#include "Math/Quaternions.h" +#include "Audio/openal_wrapper.h" +#include "Audio/Sounds.h" +#include "Game.h" + +extern float slomofreq; + +// NOTE: +// FMOD uses a Left Handed Coordinate system, OpenAL uses a Right Handed +// one...so we just need to flip the sign on the Z axis when appropriate. + +typedef struct { + ALuint sid; + OPENAL_SAMPLE *sample; + bool startpaused; + float position[3]; +} OPENAL_Channels; + +typedef struct OPENAL_SAMPLE { + char *name; + ALuint bid; // buffer id. + int mode; + int is2d; +} OPENAL_SAMPLE; + +static size_t num_channels = 0; +static OPENAL_Channels *impl_channels = NULL; +static bool initialized = false; +static float listener_position[3]; + +static void set_channel_position(const int channel, const float x, + const float y, const float z) +{ + OPENAL_Channels *chan = &impl_channels[channel]; + + chan->position[0] = x; + chan->position[1] = y; + chan->position[2] = z; + + OPENAL_SAMPLE *sptr = chan->sample; + if (sptr == NULL) + return; + + const ALuint sid = chan->sid; + const bool no_attenuate = sptr->is2d; + + if (no_attenuate) { + alSourcei(sid, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(sid, AL_POSITION, 0.0f, 0.0f, 0.0f); + } else { + alSourcei(sid, AL_SOURCE_RELATIVE, AL_FALSE); + alSource3f(sid, AL_POSITION, x, y, z); + } +} + + +AL_API void OPENAL_3D_Listener_SetAttributes(const float *pos, const float *vel, float fx, float fy, float fz, float tx, float ty, float tz) +{ + if (!initialized) + return; + if (pos != NULL) { + alListener3f(AL_POSITION, pos[0], pos[1], -pos[2]); + listener_position[0] = pos[0]; + listener_position[1] = pos[1]; + listener_position[2] = -pos[2]; + } + + ALfloat vec[6] = { fx, fy, -fz, tz, ty, -tz }; + alListenerfv(AL_ORIENTATION, vec); + + // we ignore velocity, since doppler's broken in the Linux AL at the moment... + + // adjust existing positions... + for (int i = 0; i < num_channels; i++) { + const float *p = impl_channels[i].position; + set_channel_position(i, p[0], p[1], p[2]); + } +} + +AL_API signed char OPENAL_3D_SetAttributes(int channel, const float *pos, const float *vel) +{ + if (!initialized) + return false; + if ((channel < 0) || (channel >= num_channels)) + return false; + + if (pos != NULL) + set_channel_position(channel, pos[0], pos[1], -pos[2]); + + // we ignore velocity, since doppler's broken in the Linux AL at the moment... + + return true; +} + +AL_API signed char OPENAL_3D_SetAttributes_(int channel, const XYZ &pos, const float *vel) +{ + if (!initialized) + return false; + if ((channel < 0) || (channel >= num_channels)) + return false; + + set_channel_position(channel, pos.x, pos.y, -pos.z); + + return true; +} + +AL_API signed char OPENAL_Init(int mixrate, int maxsoftwarechannels, unsigned int flags) +{ + if (initialized) + return false; + if (maxsoftwarechannels == 0) + return false; + + if (flags != 0) // unsupported. + return false; + + ALCdevice *dev = alcOpenDevice(NULL); + if (!dev) + return false; + + ALint caps[] = { ALC_FREQUENCY, mixrate, 0 }; + ALCcontext *ctx = alcCreateContext(dev, caps); + if (!ctx) { + alcCloseDevice(dev); + return false; + } + + alcMakeContextCurrent(ctx); + alcProcessContext(ctx); + + if (commandLineOptions[OPENALINFO]) { + printf("AL_VENDOR: %s\n", (char *) alGetString(AL_VENDOR)); + printf("AL_RENDERER: %s\n", (char *) alGetString(AL_RENDERER)); + printf("AL_VERSION: %s\n", (char *) alGetString(AL_VERSION)); + printf("AL_EXTENSIONS: %s\n", (char *) alGetString(AL_EXTENSIONS)); + } + + num_channels = maxsoftwarechannels; + impl_channels = new OPENAL_Channels[maxsoftwarechannels]; + memset(impl_channels, '\0', sizeof (OPENAL_Channels) * num_channels); + for (int i = 0; i < num_channels; i++) + alGenSources(1, &impl_channels[i].sid); // !!! FIXME: verify this didn't fail! + + initialized = true; + return true; +} + +AL_API void OPENAL_Close() +{ + if (!initialized) + return; + + ALCcontext *ctx = alcGetCurrentContext(); + if (ctx) { + for (int i = 0; i < num_channels; i++) { + alSourceStop(impl_channels[i].sid); + alSourcei(impl_channels[i].sid, AL_BUFFER, 0); + alDeleteSources(1, &impl_channels[i].sid); + } + ALCdevice *dev = alcGetContextsDevice(ctx); + alcMakeContextCurrent(NULL); + alcSuspendContext(ctx); + alcDestroyContext(ctx); + alcCloseDevice(dev); + } + + num_channels = 0; + delete[] impl_channels; + impl_channels = NULL; + + initialized = false; +} + +static OPENAL_SAMPLE *OPENAL_GetCurrentSample(int channel) +{ + if (!initialized) + return NULL; + if ((channel < 0) || (channel >= num_channels)) + return NULL; + return impl_channels[channel].sample; +} + +static signed char OPENAL_GetPaused(int channel) +{ + if (!initialized) + return false; + if ((channel < 0) || (channel >= num_channels)) + return false; + if (impl_channels[channel].startpaused) + return(true); + + ALint state = 0; + alGetSourceiv(impl_channels[channel].sid, AL_SOURCE_STATE, &state); + return((state == AL_PAUSED) ? true : false); +} + +static unsigned int OPENAL_GetLoopMode(int channel) +{ + if (!initialized) + return 0; + if ((channel < 0) || (channel >= num_channels)) + return 0; + ALint loop = 0; + alGetSourceiv(impl_channels[channel].sid, AL_LOOPING, &loop); + if (loop) + return(OPENAL_LOOP_NORMAL); + return OPENAL_LOOP_OFF; +} + +static signed char OPENAL_IsPlaying(int channel) +{ + if (!initialized) + return false; + if ((channel < 0) || (channel >= num_channels)) + return false; + ALint state = 0; + alGetSourceiv(impl_channels[channel].sid, AL_SOURCE_STATE, &state); + return((state == AL_PLAYING) ? true : false); +} + +static int OPENAL_PlaySoundEx(int channel, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused) +{ + if (!initialized) + return -1; + if (sptr == NULL) + return -1; + if (dsp != NULL) + return -1; + if (channel == OPENAL_FREE) { + for (int i = 0; i < num_channels; i++) { + ALint state = 0; + alGetSourceiv(impl_channels[i].sid, AL_SOURCE_STATE, &state); + if ((state != AL_PLAYING) && (state != AL_PAUSED)) { + channel = i; + break; + } + } + } + + if ((channel < 0) || (channel >= num_channels)) + return -1; + alSourceStop(impl_channels[channel].sid); + impl_channels[channel].sample = sptr; + alSourcei(impl_channels[channel].sid, AL_BUFFER, sptr->bid); + alSourcei(impl_channels[channel].sid, AL_LOOPING, (sptr->mode == OPENAL_LOOP_OFF) ? AL_FALSE : AL_TRUE); + set_channel_position(channel, 0.0f, 0.0f, 0.0f); + + impl_channels[channel].startpaused = ((startpaused) ? true : false); + if (!startpaused) + alSourcePlay(impl_channels[channel].sid); + return channel; +} + + +static void *decode_to_pcm(const char *_fname, ALenum &format, ALsizei &size, ALuint &freq) +{ +#ifdef __POWERPC__ + const int bigendian = 1; +#else + const int bigendian = 0; +#endif + + // !!! FIXME: if it's not Ogg, we don't have a decoder. I'm lazy. :/ + char *fname = (char *) alloca(strlen(_fname) + 16); + strcpy(fname, _fname); + char *ptr = strchr(fname, '.'); + if (ptr) + *ptr = '\0'; + strcat(fname, ".ogg"); + + // just in case... + FILE *io = fopen(fname, "rb"); + if (io == NULL) + return NULL; + + ALubyte *retval = NULL; + +#if 0 // untested, so disable this! + // Can we just feed it to the AL compressed? + if (alIsExtensionPresent((const ALubyte *) "AL_EXT_vorbis")) { + format = alGetEnumValue((const ALubyte *) "AL_FORMAT_VORBIS_EXT"); + freq = 44100; + fseek(io, 0, SEEK_END); + size = ftell(io); + fseek(io, 0, SEEK_SET); + retval = (ALubyte *) malloc(size); + size_t rc = fread(retval, size, 1, io); + fclose(io); + if (rc != 1) { + free(retval); + return NULL; + } + return retval; + } +#endif + + // Uncompress and feed to the AL. + OggVorbis_File vf; + memset(&vf, '\0', sizeof (vf)); + if (ov_open(io, &vf, NULL, 0) == 0) { + int bitstream = 0; + vorbis_info *info = ov_info(&vf, -1); + size = 0; + format = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; + freq = info->rate; + + if ((info->channels != 1) && (info->channels != 2)) { + ov_clear(&vf); + return NULL; + } + + char buf[1024 * 16]; + long rc = 0; + size_t allocated = 64 * 1024; + retval = (ALubyte *) malloc(allocated); + while ( (rc = ov_read(&vf, buf, sizeof (buf), bigendian, 2, 1, &bitstream)) != 0 ) { + if (rc > 0) { + size += rc; + if (size >= allocated) { + allocated *= 2; + ALubyte *tmp = (ALubyte *) realloc(retval, allocated); + if (tmp == NULL) { + free(retval); + retval = NULL; + break; + } + retval = tmp; + } + memcpy(retval + (size - rc), buf, rc); + } + } + ov_clear(&vf); + return retval; + } + + fclose(io); + return NULL; +} + + +AL_API OPENAL_SAMPLE *OPENAL_Sample_Load(int index, const char *name_or_data, unsigned int mode, int offset, int length) +{ + if (!initialized) + return NULL; + if (index != OPENAL_FREE) + return NULL; // this is all the game does... + if (offset != 0) + return NULL; // this is all the game does... + if (length != 0) + return NULL; // this is all the game does... + if ((mode != OPENAL_HW3D) && (mode != OPENAL_2D)) + return NULL; // this is all the game does... + + OPENAL_SAMPLE *retval = NULL; + ALenum format = AL_NONE; + ALsizei size = 0; + ALuint frequency = 0; + void *data = decode_to_pcm(name_or_data, format, size, frequency); + if (data == NULL) + return NULL; + + ALuint bid = 0; + alGetError(); + alGenBuffers(1, &bid); + if (alGetError() == AL_NO_ERROR) { + alBufferData(bid, format, data, size, frequency); + retval = new OPENAL_SAMPLE; + retval->bid = bid; + retval->mode = OPENAL_LOOP_OFF; + retval->is2d = (mode == OPENAL_2D); + retval->name = new char[strlen(name_or_data) + 1]; + if (retval->name) + strcpy(retval->name, name_or_data); + } + + free(data); + return(retval); +} + +AL_API void OPENAL_Sample_Free(OPENAL_SAMPLE *sptr) +{ + if (!initialized) + return; + if (sptr) { + for (int i = 0; i < num_channels; i++) { + if (impl_channels[i].sample == sptr) { + alSourceStop(impl_channels[i].sid); + alSourcei(impl_channels[i].sid, AL_BUFFER, 0); + impl_channels[i].sample = NULL; + } + } + alDeleteBuffers(1, &sptr->bid); + delete[] sptr->name; + delete sptr; + } +} + +static signed char OPENAL_Sample_SetMode(OPENAL_SAMPLE *sptr, unsigned int mode) +{ + if (!initialized) + return false; + if ((mode != OPENAL_LOOP_NORMAL) && (mode != OPENAL_LOOP_OFF)) + return false; + if (!sptr) + return false; + sptr->mode = mode; + return true; +} + +AL_API signed char OPENAL_SetFrequency(int channel, bool slomo) +{ + if (!initialized) + return false; + if (channel == OPENAL_ALL) { + for (int i = 0; i < num_channels; i++) + OPENAL_SetFrequency(i, slomo); + return true; + } + + if ((channel < 0) || (channel >= num_channels)) + return false; + if (slomo) + alSourcef(impl_channels[channel].sid, AL_PITCH, ((ALfloat) slomofreq) / 44100.0f); + else + alSourcef(impl_channels[channel].sid, AL_PITCH, 1.0f); + return true; +} + +AL_API signed char OPENAL_SetVolume(int channel, int vol) +{ + if (!initialized) + return false; + + if (channel == OPENAL_ALL) { + for (int i = 0; i < num_channels; i++) + OPENAL_SetVolume(i, vol); + return true; + } + + if ((channel < 0) || (channel >= num_channels)) + return false; + + if (vol < 0) + vol = 0; + else if (vol > 255) + vol = 255; + ALfloat gain = ((ALfloat) vol) / 255.0f; + alSourcef(impl_channels[channel].sid, AL_GAIN, gain); + return true; +} + +AL_API signed char OPENAL_SetPaused(int channel, signed char paused) +{ + if (!initialized) + return false; + + if (channel == OPENAL_ALL) { + for (int i = 0; i < num_channels; i++) + OPENAL_SetPaused(i, paused); + return true; + } + + if ((channel < 0) || (channel >= num_channels)) + return false; + + ALint state = 0; + if (impl_channels[channel].startpaused) + state = AL_PAUSED; + else + alGetSourceiv(impl_channels[channel].sid, AL_SOURCE_STATE, &state); + + if ((paused) && (state == AL_PLAYING)) + alSourcePause(impl_channels[channel].sid); + else if ((!paused) && (state == AL_PAUSED)) { + alSourcePlay(impl_channels[channel].sid); + impl_channels[channel].startpaused = false; + } + return true; +} + +AL_API void OPENAL_SetSFXMasterVolume(int volume) +{ + if (!initialized) + return; + ALfloat gain = ((ALfloat) volume) / 255.0f; + alListenerf(AL_GAIN, gain); +} + +AL_API signed char OPENAL_StopSound(int channel) +{ + if (!initialized) + return false; + + if (channel == OPENAL_ALL) { + for (int i = 0; i < num_channels; i++) + OPENAL_StopSound(i); + return true; + } + + if ((channel < 0) || (channel >= num_channels)) + return false; + alSourceStop(impl_channels[channel].sid); + impl_channels[channel].startpaused = false; + return true; +} + +static OPENAL_SAMPLE *OPENAL_Stream_GetSample(OPENAL_STREAM *stream) +{ + if (!initialized) + return NULL; + return (OPENAL_SAMPLE *) stream; +} + +static int OPENAL_Stream_PlayEx(int channel, OPENAL_STREAM *stream, OPENAL_DSPUNIT *dsp, signed char startpaused) +{ + return OPENAL_PlaySoundEx(channel, (OPENAL_SAMPLE *) stream, dsp, startpaused); +} + +static signed char OPENAL_Stream_Stop(OPENAL_STREAM *stream) +{ + if (!initialized) + return false; + for (int i = 0; i < num_channels; i++) { + if (impl_channels[i].sample == (OPENAL_SAMPLE *) stream) { + alSourceStop(impl_channels[i].sid); + impl_channels[i].startpaused = false; + } + } + return true; +} + +AL_API signed char OPENAL_Stream_SetMode(OPENAL_STREAM *stream, unsigned int mode) +{ + return OPENAL_Sample_SetMode((OPENAL_SAMPLE *) stream, mode); +} + +AL_API void OPENAL_Update() +{ + if (!initialized) + return; + alcProcessContext(alcGetCurrentContext()); +} + +AL_API signed char OPENAL_SetOutput(int outputtype) +{ + return true; +} + +extern int channels[]; + +extern "C" void PlaySoundEx(int chan, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused) +{ + const OPENAL_SAMPLE * currSample = OPENAL_GetCurrentSample(channels[chan]); + if (currSample && currSample == samp[chan]) { + if (OPENAL_GetPaused(channels[chan])) { + OPENAL_StopSound(channels[chan]); + channels[chan] = OPENAL_FREE; + } else if (OPENAL_IsPlaying(channels[chan])) { + int loop_mode = OPENAL_GetLoopMode(channels[chan]); + if (loop_mode & OPENAL_LOOP_OFF) { + channels[chan] = OPENAL_FREE; + } + } + } else { + channels[chan] = OPENAL_FREE; + } + + channels[chan] = OPENAL_PlaySoundEx(channels[chan], sptr, dsp, startpaused); + if (channels[chan] < 0) { + channels[chan] = OPENAL_PlaySoundEx(OPENAL_FREE, sptr, dsp, startpaused); + } +} + +extern "C" void PlayStreamEx(int chan, OPENAL_STREAM *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused) +{ + const OPENAL_SAMPLE * currSample = OPENAL_GetCurrentSample(channels[chan]); + if (currSample && currSample == OPENAL_Stream_GetSample(sptr)) { + OPENAL_StopSound(channels[chan]); + OPENAL_Stream_Stop(sptr); + } else { + OPENAL_Stream_Stop(sptr); + channels[chan] = OPENAL_FREE; + } + + channels[chan] = OPENAL_Stream_PlayEx(channels[chan], sptr, dsp, startpaused); + if (channels[chan] < 0) { + channels[chan] = OPENAL_Stream_PlayEx(OPENAL_FREE, sptr, dsp, startpaused); + } +} diff --git a/Source/Audio/openal_wrapper.h b/Source/Audio/openal_wrapper.h new file mode 100644 index 0000000..b8de914 --- /dev/null +++ b/Source/Audio/openal_wrapper.h @@ -0,0 +1,113 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef OPENAL_WRAPPER_H +#define OPENAL_WRAPPER_H + +#ifdef _WIN32 +#include +#endif + +#include "AL/al.h" +#include "AL/alc.h" + +#include "ogg/ogg.h" +#include "vorbis/vorbisfile.h" + +#include "Math/Quaternions.h" +#include "MacCompatibility.h" + +#if 0 /* this should only be enable if OPENAL doesn't provide AL_API on all platforms */ +#if (!defined(WIN32) && !defined(_WIN32) && !defined(__WIN32__) && !defined(_WIN64) && !defined(_WIN32_WCE) && !defined(_XBOX)) || (defined(__GNUC__) && defined(WIN32)) +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __stdcall +#define __stdcall +#endif +#endif + +#if defined(_WIN32_WCE) +#define AL_API _cdecl +#define F_CALLBACKAPI _cdecl +#else +#define AL_API __stdcall +#define F_CALLBACKAPI __stdcall +#endif + +#ifdef DLL_EXPORTS +#define DLL_API __declspec(dllexport) +#else +#if defined(__LCC__) || defined(__MINGW32__) || defined(__CYGWIN32__) +#define DLL_API AL_API +#else +#define DLL_API +#endif /* __LCC__ || __MINGW32__ || __CYGWIN32__ */ +#endif /* DLL_EXPORTS */ +#endif /* if 0 */ + + +typedef struct OPENAL_SAMPLE OPENAL_SAMPLE; +typedef OPENAL_SAMPLE OPENAL_STREAM; +typedef struct OPENAL_DSPUNIT OPENAL_DSPUNIT; + +enum OPENAL_OUTPUTTYPES { + OPENAL_OUTPUT_NOSOUND, /* NoSound driver, all calls to this succeed but do nothing. */ + OPENAL_OUTPUT_OSS, /* Linux/Unix OSS (Open Sound System) driver, i.e. the kernel sound drivers. */ + OPENAL_OUTPUT_ALSA, /* Linux Alsa driver. */ +}; + +#define OPENAL_LOOP_OFF 0x00000001 /* For non looping samples. */ +#define OPENAL_LOOP_NORMAL 0x00000002 /* For forward looping samples. */ +#define OPENAL_HW3D 0x00001000 /* Attempts to make samples use 3d hardware acceleration. (if the card supports it) */ +#define OPENAL_2D 0x00002000 /* Tells software (not hardware) based sample not to be included in 3d processing. */ +#define OPENAL_FREE -1 /* value to play on any free channel, or to allocate a sample in a free sample slot. */ +#define OPENAL_ALL -3 /* for a channel index , this flag will affect ALL channels available! Not supported by every function. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#undef AL_API +#define AL_API + + AL_API void OPENAL_3D_Listener_SetAttributes(const float *pos, const float *vel, float fx, float fy, float fz, float tx, float ty, float tz); + AL_API signed char OPENAL_3D_SetAttributes(int channel, const float *pos, const float *vel); + AL_API signed char OPENAL_3D_SetAttributes_(int channel, const XYZ &pos, const float *vel); + AL_API signed char OPENAL_Init(int mixrate, int maxsoftwarechannels, unsigned int flags); + AL_API void OPENAL_Close(); + AL_API OPENAL_SAMPLE *OPENAL_Sample_Load(int index, const char *name_or_data, unsigned int mode, int offset, int length); + AL_API void OPENAL_Sample_Free(OPENAL_SAMPLE *sptr); + AL_API signed char OPENAL_SetFrequency(int channel, bool slomo = false); + AL_API signed char OPENAL_SetVolume(int channel, int vol); + AL_API signed char OPENAL_SetPaused(int channel, signed char paused); + AL_API void OPENAL_SetSFXMasterVolume(int volume); + AL_API signed char OPENAL_StopSound(int channel); + AL_API signed char OPENAL_Stream_SetMode(OPENAL_STREAM *stream, unsigned int mode); + AL_API void OPENAL_Update(); + AL_API signed char OPENAL_SetOutput(int outputtype); + void PlaySoundEx(int chan, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused); + void PlayStreamEx(int chan, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Source/Awards.cpp b/Source/Awards.cpp deleted file mode 100644 index 5b27563..0000000 --- a/Source/Awards.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Awards.h" -#include "Person.h" -#include "Game.h" - -int bonus; -float bonusvalue; -float bonustotal; -float startbonustotal; -float bonustime; -float bonusnum[100]; - -const char *bonus_names[bonus_count] = { -#define DECLARE_BONUS(id, name, ...) name, -#include "Bonuses.def" -#undef DECLARE_BONUS -}; - -const char *award_names[award_count] = { -#define DECLARE_AWARD(id, name) name, -#include "Awards.def" -#undef DECLARE_AWARD -}; - -static const int bonus_values[bonus_count] = { -#define DECLARE_BONUS(id, name, value) value, -#include "Bonuses.def" -#undef DECLARE_BONUS -}; - -void -award_bonus(int playerid, int bonusid, int alt_value) -{ - if (playerid != 0) - return; - bonus = bonusid; - bonustime = 0; - bonusvalue = alt_value ? alt_value : bonus_values[bonusid]; -} - -// FIXME: make these per-player -float damagetaken; -int numfalls; -int numflipfail; -int numseen; -int numresponded; -int numstaffattack; -int numswordattack; -int numknifeattack; -int numunarmedattack; -int numescaped; -int numflipped; -int numwallflipped; -int numthrowkill; -int numafterkill; -int numreversals; -int numattacks; -int maxalarmed; - -int award_awards(int *awards) -{ - int numawards = 0; - if (damagetaken == 0 && Person::players[0]->bloodloss == 0) { - awards[numawards] = awardflawless; - numawards++; - } - bool alldead = true; - for (unsigned i = 1; i < Person::players.size(); i++) { - if (Person::players[i]->dead != 2) - alldead = 0; - } - if (alldead) { - awards[numawards] = awardalldead; - numawards++; - } - alldead = 1; - for (unsigned i = 1; i < Person::players.size(); i++) { - if (Person::players[i]->dead != 1) - alldead = 0; - } - if (alldead) { - awards[numawards] = awardnodead; - numawards++; - } - if (numresponded == 0 && !numthrowkill) { - awards[numawards] = awardstealth; - numawards++; - } - if (numattacks == numstaffattack && numattacks > 0) { - awards[numawards] = awardbojutsu; - numawards++; - } - if (numattacks == numswordattack && numattacks > 0) { - awards[numawards] = awardswordsman; - numawards++; - } - if (numattacks == numknifeattack && numattacks > 0) { - awards[numawards] = awardknifefighter; - numawards++; - } - if (numattacks == numunarmedattack && numthrowkill == 0 && weapons.size() > 0) { - awards[numawards] = awardkungfu; - numawards++; - } - if (numescaped > 0) { - awards[numawards] = awardevasion; - numawards++; - } - if (numflipfail == 0 && numflipped + numwallflipped * 2 > 20) { - awards[numawards] = awardacrobat; - numawards++; - } - if (numthrowkill == (int(Person::players.size()) - 1)) { - awards[numawards] = awardlongrange; - numawards++; - } - alldead = 1; - for (unsigned i = 1; i < Person::players.size(); i++) { - if (Person::players[i]->dead != 2) - alldead = 0; - } - if (numafterkill > 0 && alldead) { - awards[numawards] = awardbrutal; - numawards++; - } - if (numreversals > ((float)numattacks)*.8 && numreversals > 3) { - awards[numawards] = awardaikido; - numawards++; - } - if (maxalarmed == 1 && Person::players.size() > 2) { - awards[numawards] = awardstrategy; - numawards++; - } - if (numflipfail > 3) { - awards[numawards] = awardklutz; - numawards++; - } - return numawards; -} diff --git a/Source/Awards.def b/Source/Awards.def deleted file mode 100644 index fd8e5c4..0000000 --- a/Source/Awards.def +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -DECLARE_AWARD(awardklutz, "Suicidal") -DECLARE_AWARD(awardflawless, "Flawless!") -DECLARE_AWARD(awardalldead, "Take no prisoners") -DECLARE_AWARD(awardnodead, "Merciful") -DECLARE_AWARD(awardstealth, "One with the shadows!") -DECLARE_AWARD(awardswordsman, "Swordsman") -DECLARE_AWARD(awardkungfu, "Unarmed!") -DECLARE_AWARD(awardknifefighter, "Knife fighter") -DECLARE_AWARD(awardcoward, "Coward") -DECLARE_AWARD(awardevasion, "Escape artist") -DECLARE_AWARD(awardacrobat, "Gymnast") -DECLARE_AWARD(awardlongrange, "Blade slinger") -DECLARE_AWARD(awardbrutal, "Brutal") -DECLARE_AWARD(awardhyper, "Hyper") -DECLARE_AWARD(awardaikido, "Aikido master!") -DECLARE_AWARD(awardrambo, "Rambo") -DECLARE_AWARD(awardfast, "Fast") -DECLARE_AWARD(awardrealfast, "Real fast") -DECLARE_AWARD(awarddamnfast, "Damn fast") -DECLARE_AWARD(awardstrategy, "Divide and conquer") -DECLARE_AWARD(awardbojutsu, "Bojutsu") diff --git a/Source/Awards.h b/Source/Awards.h deleted file mode 100644 index 882fbef..0000000 --- a/Source/Awards.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef AWARDS_H -#define AWARDS_H - -enum bonus_types { -#define DECLARE_BONUS(id, ...) id, -#include "Bonuses.def" -#undef DECLARE_BONUS - bonus_count -}; - -extern const char *bonus_names[bonus_count]; - -extern int bonus; -extern float bonusvalue; -extern float bonustotal; -extern float bonustime; -extern float startbonustotal; -extern float bonusnum[100]; - -extern void award_bonus(int playerid, int bonusid, int alt_value = 0); - -enum award_types { -#define DECLARE_AWARD(id, name) id, -#include "Awards.def" -#undef DECLARE_AWARD - award_count -}; - -extern const char *award_names[award_count]; - -extern int award_awards(int *); - -extern float damagetaken; -extern int numfalls; -extern int numflipfail; -extern int numseen; -extern int numresponded; -extern int numstaffattack; -extern int numswordattack; -extern int numknifeattack; -extern int numunarmedattack; -extern int numescaped; -extern int numflipped; -extern int numwallflipped; -extern int numthrowkill; -extern int numafterkill; -extern int numreversals; -extern int numattacks; -extern int maxalarmed; -#endif - diff --git a/Source/Bonuses.def b/Source/Bonuses.def deleted file mode 100644 index bcc1a95..0000000 --- a/Source/Bonuses.def +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -DECLARE_BONUS(nobonus, "", 0) -DECLARE_BONUS(tracheotomy, "Tracheotomy!", 100) -DECLARE_BONUS(backstab, "Backstabber!", 100) -DECLARE_BONUS(spinecrusher, "Spinecrusher!", 100) -DECLARE_BONUS(ninja, "Ninja Bonus!", 60) -DECLARE_BONUS(style, "Style Bonus!", 150) -DECLARE_BONUS(cannon, "Leg Cannon!", 100) -DECLARE_BONUS(aimbonus, "Nice Aim!", 150) -DECLARE_BONUS(deepimpact, "Heavy Impact!", 50) -DECLARE_BONUS(touchofdeath, "Touch of Death!", 150) -DECLARE_BONUS(swordreversebonus, "Sword Disarm!", 100) -DECLARE_BONUS(staffreversebonus, "Staff Disarm!", 100) -DECLARE_BONUS(reverseko, "Reversal KO!", 100) -// The following five should be kept in that order -DECLARE_BONUS(solidhit, "Solid Hit!", 10) -DECLARE_BONUS(twoxcombo, "2X Combo!", 20) -DECLARE_BONUS(threexcombo, "3X Combo!", 40) -DECLARE_BONUS(fourxcombo, "4X COMBO!", 80) -DECLARE_BONUS(megacombo, "MEGA COMBO!", 160) -DECLARE_BONUS(Reversal, "Reversal!", 60) -DECLARE_BONUS(Stabbonus, "Punctured!", 40) -DECLARE_BONUS(Slicebonus, "Sliced!", 10) -DECLARE_BONUS(Bullseyebonus, "Bullseye!", 30) -DECLARE_BONUS(Slashbonus, "Slashed!", 40) -DECLARE_BONUS(Wolfbonus, "WOLF SLAYER!", 300) -DECLARE_BONUS(FinishedBonus, "SLAIN!", 200) -DECLARE_BONUS(TackleBonus, "Tackle!", 5) -DECLARE_BONUS(AboveBonus, "Death from Above!", 50) diff --git a/Source/Campaign.cpp b/Source/Campaign.cpp deleted file mode 100644 index fd75198..0000000 --- a/Source/Campaign.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Campaign.h" -#include "Game.h" -#include "Utils/Folders.h" -#include - -using namespace Game; - -std::vector campaignlevels; - -bool campaign = false; - -int actuallevel = 0; - -std::vector ListCampaigns() -{ - errno = 0; - DIR *campaigns = opendir(Folders::getResourcePath("Campaigns").c_str()); - struct dirent *campaign = NULL; - if (!campaigns) { - perror(("Problem while loading campaigns from " + Folders::getResourcePath("Campaigns")).c_str()); - exit(EXIT_FAILURE); - } - std::vector campaignNames; - while ((campaign = readdir(campaigns)) != NULL) { - std::string name(campaign->d_name); - if (name.length() < 5) - continue; - if (!name.compare(name.length() - 4, 4, ".txt")) { - campaignNames.push_back(name.substr(0, name.length() - 4)); - } - } - closedir(campaigns); - return campaignNames; -} - -void LoadCampaign() -{ - if (!Account::hasActive()) { - return; - } - std::ifstream ipstream(Folders::getResourcePath("Campaigns/" + Account::active().getCurrentCampaign() + ".txt")); - if (!ipstream.good()) { - if (Account::active().getCurrentCampaign() == "main") { - cerr << "Could not found main campaign!" << endl; - return; - } - cerr << "Could not found campaign \"" << Account::active().getCurrentCampaign() << "\", falling back to main." << endl; - Account::active().setCurrentCampaign("main"); - return LoadCampaign(); - } - ipstream.ignore(256, ':'); - int numlevels; - ipstream >> numlevels; - campaignlevels.clear(); - for (int i = 0; i < numlevels; i++) { - CampaignLevel cl; - ipstream >> cl; - campaignlevels.push_back(cl); - } - ipstream.close(); - - std::ifstream test(Folders::getResourcePath("Textures/" + Account::active().getCurrentCampaign() + "/World.png")); - if (test.good()) { - Mainmenuitems[7].load("Textures/" + Account::active().getCurrentCampaign() + "/World.png", 0); - } else { - Mainmenuitems[7].load("Textures/World.png", 0); - } - - if (Account::active().getCampaignChoicesMade() == 0) { - Account::active().setCampaignScore(0); - Account::active().resetFasttime(); - } -} - -CampaignLevel::CampaignLevel() : - width(10), - choosenext(1) -{ - location.x = 0; - location.y = 0; -} - -int CampaignLevel::getStartX() { - return 30 + 120 + location.x * 400 / 512; -} - -int CampaignLevel::getStartY() { - return 30 + 30 + (512 - location.y) * 400 / 512; -} - -int CampaignLevel::getEndX() { - return getStartX() + width; -} - -int CampaignLevel::getEndY() { - return getStartY() + width; -} - -XYZ CampaignLevel::getCenter() { - XYZ center; - center.x = getStartX() + width / 2; - center.y = getStartY() + width / 2; - return center; -} - -int CampaignLevel::getWidth() { - return width; -} - -istream& CampaignLevel::operator<< (istream& is) { - is.ignore(256, ':'); - is.ignore(256, ':'); - is.ignore(256, ' '); - is >> mapname; - is.ignore(256, ':'); - is >> description; - for (size_t pos = description.find('_'); pos != string::npos; pos = description.find('_', pos)) { - description.replace(pos, 1, 1, ' '); - } - is.ignore(256, ':'); - is >> choosenext; - is.ignore(256, ':'); - int numnext, next; - is >> numnext; - for (int j = 0; j < numnext; j++) { - is.ignore(256, ':'); - is >> next; - nextlevel.push_back(next - 1); - } - is.ignore(256, ':'); - is >> location.x; - is.ignore(256, ':'); - is >> location.y; - return is; -} diff --git a/Source/Campaign.h b/Source/Campaign.h deleted file mode 100644 index 05c7859..0000000 --- a/Source/Campaign.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include -#include - -#include "Quaternions.h" - -extern bool campaign; - -extern int actuallevel; - -std::vector ListCampaigns(); -void LoadCampaign(); - -class CampaignLevel -{ -private: - int width; - struct Position { - int x, y; - }; -public: - std::string mapname; - std::string description; - int choosenext; - /* - 0 = Immediately load next level at the end of this one. - 1 = Go back to the world map. - 2 = Don't bring up the Fiery loading screen. Maybe other things, I've not investigated. - */ - //int numnext; // 0 on final level. As David said: he meant to add story branching, but he eventually hadn't. - std::vector nextlevel; - Position location; - CampaignLevel(); - int getStartX(); - int getStartY(); - int getEndX(); - int getEndY(); - XYZ getCenter(); - int getWidth(); - std::istream& operator<< (std::istream& is); - friend std::istream& operator>> (std::istream& is, CampaignLevel& cl) { - return cl << is; - } -}; - -extern std::vector campaignlevels; diff --git a/Source/ConsoleCmds.cpp b/Source/ConsoleCmds.cpp deleted file mode 100644 index df26f82..0000000 --- a/Source/ConsoleCmds.cpp +++ /dev/null @@ -1,739 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "ConsoleCmds.h" -#include "Game.h" -#include "Dialog.h" -#include "Hotspot.h" -#include "Utils/Folders.h" - -const char *cmd_names[cmd_count] = { -#define DECLARE_COMMAND(cmd) #cmd, -#include "ConsoleCmds.def" -#undef DECLARE_COMMAND -}; - -console_handler cmd_handlers[cmd_count] = { -#define DECLARE_COMMAND(cmd) ch_##cmd, -#include "ConsoleCmds.def" -#undef DECLARE_COMMAND -}; - -using namespace Game; - -/* globals */ - -extern bool campaign; -extern bool cellophane; -extern int editoractive; -extern int editorpathtype; -extern int environment; -extern float fadestart; -extern float slomospeed; -extern float slomofreq; -extern int tutoriallevel; -extern int hostile; -extern int maptype; -extern Objects objects; -extern int slomo; -extern float slomodelay; -extern bool skyboxtexture; -extern float skyboxr; -extern float skyboxg; -extern float skyboxb; -extern float skyboxlightr; -extern float skyboxlightg; -extern float skyboxlightb; -extern Terrain terrain; -extern float viewdistance; - -/* defined in GameTick.cpp */ - -extern int whichlevel; - -float tintr = 1, tintg = 1, tintb = 1; - -/* Helpers used in console commands */ - -/* Return true if PFX is a prefix of STR (case-insensitive). */ -static bool stripfx(const char *str, const char *pfx) -{ - return !strncasecmp(str, pfx, strlen(pfx)); -} - -static void set_proportion(int pnum, const char *args) -{ - float headprop, bodyprop, armprop, legprop; - - sscanf(args, "%f%f%f%f", &headprop, &bodyprop, &armprop, &legprop); - - if (Person::players[pnum]->creature == wolftype) { - Person::players[pnum]->proportionhead = 1.1 * headprop; - Person::players[pnum]->proportionbody = 1.1 * bodyprop; - Person::players[pnum]->proportionarms = 1.1 * armprop; - Person::players[pnum]->proportionlegs = 1.1 * legprop; - } else if (Person::players[pnum]->creature == rabbittype) { - Person::players[pnum]->proportionhead = 1.2 * headprop; - Person::players[pnum]->proportionbody = 1.05 * bodyprop; - Person::players[pnum]->proportionarms = 1.00 * armprop; - Person::players[pnum]->proportionlegs = 1.1 * legprop; - Person::players[pnum]->proportionlegs.y = 1.05 * legprop; - } -} - -static void set_protection(int pnum, const char *args) -{ - float head, high, low; - sscanf(args, "%f%f%f", &head, &high, &low); - - Person::players[pnum]->protectionhead = head; - Person::players[pnum]->protectionhigh = high; - Person::players[pnum]->protectionlow = low; -} - -static void set_armor(int pnum, const char *args) -{ - float head, high, low; - sscanf(args, "%f%f%f", &head, &high, &low); - - Person::players[pnum]->armorhead = head; - Person::players[pnum]->armorhigh = high; - Person::players[pnum]->armorlow = low; -} - -static void set_metal(int pnum, const char *args) -{ - float head, high, low; - sscanf(args, "%f%f%f", &head, &high, &low); - - Person::players[pnum]->metalhead = head; - Person::players[pnum]->metalhigh = high; - Person::players[pnum]->metallow = low; -} - -static void set_noclothes(int pnum, const char *args) -{ - Person::players[pnum]->numclothes = 0; - Person::players[pnum]->skeleton.drawmodel.textureptr.load( - creatureskin[Person::players[pnum]->creature][Person::players[pnum]->whichskin], 1, - &Person::players[pnum]->skeleton.skinText[0], &Person::players[pnum]->skeleton.skinsize); -} - -static void set_clothes(int pnum, const char *args) -{ - char buf[64]; - snprintf(buf, 63, "Textures/%s.png", args); - - int id = Person::players[pnum]->numclothes; - strcpy(Person::players[pnum]->clothes[id], buf); - Person::players[pnum]->clothestintr[id] = tintr; - Person::players[pnum]->clothestintg[id] = tintg; - Person::players[pnum]->clothestintb[id] = tintb; - Person::players[pnum]->numclothes++; - - if (!Person::players[pnum]->addClothes(id)) - return; - - Person::players[pnum]->DoMipmaps(); -} - -/* Console commands themselves */ - -void ch_quit(const char *args) -{ - tryquit = 1; -} - -void ch_map(const char *args) -{ - Loadlevel(args); - whichlevel = -2; - campaign = 0; -} - -void ch_save(const char *args) -{ - std::string map_path = Folders::getUserDataPath() + "/Maps/" + args; - - int mapvers = 12; - - FILE *tfile; - tfile = fopen( map_path.c_str(), "wb" ); - fpackf(tfile, "Bi", mapvers); - fpackf(tfile, "Bi", maptype); - fpackf(tfile, "Bi", hostile); - fpackf(tfile, "Bf Bf", viewdistance, fadestart); - fpackf(tfile, "Bb Bf Bf Bf", skyboxtexture, skyboxr, skyboxg, skyboxb); - fpackf(tfile, "Bf Bf Bf", skyboxlightr, skyboxlightg, skyboxlightb); - fpackf(tfile, "Bf Bf Bf Bf Bf Bi", Person::players[0]->coords.x, Person::players[0]->coords.y, Person::players[0]->coords.z, - Person::players[0]->yaw, Person::players[0]->targetyaw, Person::players[0]->num_weapons); - if (Person::players[0]->num_weapons > 0 && Person::players[0]->num_weapons < 5) - for (int j = 0; j < Person::players[0]->num_weapons; j++) - fpackf(tfile, "Bi", weapons[Person::players[0]->weaponids[j]].getType()); - - fpackf(tfile, "Bf Bf Bf", Person::players[0]->armorhead, Person::players[0]->armorhigh, Person::players[0]->armorlow); - fpackf(tfile, "Bf Bf Bf", Person::players[0]->protectionhead, Person::players[0]->protectionhigh, Person::players[0]->protectionlow); - fpackf(tfile, "Bf Bf Bf", Person::players[0]->metalhead, Person::players[0]->metalhigh, Person::players[0]->metallow); - fpackf(tfile, "Bf Bf", Person::players[0]->power, Person::players[0]->speedmult); - - fpackf(tfile, "Bi", Person::players[0]->numclothes); - - fpackf(tfile, "Bi Bi", Person::players[0]->whichskin, Person::players[0]->creature); - - Dialog::saveDialogs(tfile); - - for (int k = 0; k < Person::players[0]->numclothes; k++) { - int templength = strlen(Person::players[0]->clothes[k]); - fpackf(tfile, "Bi", templength); - for (int l = 0; l < templength; l++) - fpackf(tfile, "Bb", Person::players[0]->clothes[k][l]); - fpackf(tfile, "Bf Bf Bf", Person::players[0]->clothestintr[k], Person::players[0]->clothestintg[k], Person::players[0]->clothestintb[k]); - } - - fpackf(tfile, "Bi", environment); - - fpackf(tfile, "Bi", objects.numobjects); - - for (int k = 0; k < objects.numobjects; k++) - fpackf(tfile, "Bi Bf Bf Bf Bf Bf Bf", objects.type[k], objects.yaw[k], objects.pitch[k], - objects.position[k].x, objects.position[k].y, objects.position[k].z, objects.scale[k]); - - fpackf(tfile, "Bi", Hotspot::hotspots.size()); - for (int i = 0; i < Hotspot::hotspots.size(); i++) { - fpackf(tfile, "Bi Bf Bf Bf Bf", Hotspot::hotspots[i].type, Hotspot::hotspots[i].size, Hotspot::hotspots[i].position.x, Hotspot::hotspots[i].position.y, Hotspot::hotspots[i].position.z); - int templength = strlen(Hotspot::hotspots[i].text); - fpackf(tfile, "Bi", templength); - for (int l = 0; l < templength; l++) - fpackf(tfile, "Bb", Hotspot::hotspots[i].text[l]); - } - - fpackf(tfile, "Bi", Person::players.size()); - if (Person::players.size() > maxplayers) { - cout << "Warning: this level contains more players than allowed" << endl; - } - for (unsigned j = 1; j < Person::players.size(); j++) { - fpackf(tfile, "Bi Bi Bf Bf Bf Bi Bi Bf Bb Bf", Person::players[j]->whichskin, Person::players[j]->creature, - Person::players[j]->coords.x, Person::players[j]->coords.y, Person::players[j]->coords.z, - Person::players[j]->num_weapons, Person::players[j]->howactive, Person::players[j]->scale, Person::players[j]->immobile, Person::players[j]->yaw); - if (Person::players[j]->num_weapons < 5) - for (int k = 0; k < Person::players[j]->num_weapons; k++) - fpackf(tfile, "Bi", weapons[Person::players[j]->weaponids[k]].getType()); - if (Person::players[j]->numwaypoints < 30) { - fpackf(tfile, "Bi", Person::players[j]->numwaypoints); - for (int k = 0; k < Person::players[j]->numwaypoints; k++) { - fpackf(tfile, "Bf", Person::players[j]->waypoints[k].x); - fpackf(tfile, "Bf", Person::players[j]->waypoints[k].y); - fpackf(tfile, "Bf", Person::players[j]->waypoints[k].z); - fpackf(tfile, "Bi", Person::players[j]->waypointtype[k]); - } - fpackf(tfile, "Bi", Person::players[j]->waypoint); - } else { - Person::players[j]->numwaypoints = 0; - Person::players[j]->waypoint = 0; - fpackf(tfile, "Bi Bi Bi", Person::players[j]->numwaypoints, Person::players[j]->waypoint, Person::players[j]->waypoint); - } - - fpackf(tfile, "Bf Bf Bf", Person::players[j]->armorhead, Person::players[j]->armorhigh, Person::players[j]->armorlow); - fpackf(tfile, "Bf Bf Bf", Person::players[j]->protectionhead, Person::players[j]->protectionhigh, Person::players[j]->protectionlow); - fpackf(tfile, "Bf Bf Bf", Person::players[j]->metalhead, Person::players[j]->metalhigh, Person::players[j]->metallow); - fpackf(tfile, "Bf Bf", Person::players[j]->power, Person::players[j]->speedmult); - - float headprop, bodyprop, armprop, legprop; - if (Person::players[j]->creature == wolftype) { - headprop = Person::players[j]->proportionhead.x / 1.1; - bodyprop = Person::players[j]->proportionbody.x / 1.1; - armprop = Person::players[j]->proportionarms.x / 1.1; - legprop = Person::players[j]->proportionlegs.x / 1.1; - } else if (Person::players[j]->creature == rabbittype) { - headprop = Person::players[j]->proportionhead.x / 1.2; - bodyprop = Person::players[j]->proportionbody.x / 1.05; - armprop = Person::players[j]->proportionarms.x / 1.00; - legprop = Person::players[j]->proportionlegs.x / 1.1; - } - - fpackf(tfile, "Bf Bf Bf Bf", headprop, bodyprop, armprop, legprop); - - fpackf(tfile, "Bi", Person::players[j]->numclothes); - if (Person::players[j]->numclothes) - for (int k = 0; k < Person::players[j]->numclothes; k++) { - int templength; - templength = strlen(Person::players[j]->clothes[k]); - fpackf(tfile, "Bi", templength); - for (int l = 0; l < templength; l++) - fpackf(tfile, "Bb", Person::players[j]->clothes[k][l]); - fpackf(tfile, "Bf Bf Bf", Person::players[j]->clothestintr[k], Person::players[j]->clothestintg[k], Person::players[j]->clothestintb[k]); - } - } - - fpackf(tfile, "Bi", numpathpoints); - for (int j = 0; j < numpathpoints; j++) { - fpackf(tfile, "Bf Bf Bf Bi", pathpoint[j].x, pathpoint[j].y, pathpoint[j].z, numpathpointconnect[j]); - for (int k = 0; k < numpathpointconnect[j]; k++) - fpackf(tfile, "Bi", pathpointconnect[j][k]); - } - - fpackf(tfile, "Bf Bf Bf Bf", mapcenter.x, mapcenter.y, mapcenter.z, mapradius); - - fclose(tfile); -} - -void ch_cellar(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Furdarko.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_tint(const char *args) -{ - sscanf(args, "%f%f%f", &tintr, &tintg, &tintb); -} - -void ch_tintr(const char *args) -{ - tintr = atof(args); -} - -void ch_tintg(const char *args) -{ - tintg = atof(args); -} - -void ch_tintb(const char *args) -{ - tintb = atof(args); -} - -void ch_speed(const char *args) -{ - Person::players[0]->speedmult = atof(args); -} - -void ch_strength(const char *args) -{ - Person::players[0]->power = atof(args); -} - -void ch_power(const char *args) -{ - Person::players[0]->power = atof(args); -} - -void ch_size(const char *args) -{ - Person::players[0]->scale = atof(args) * .2; -} - -void ch_sizenear(const char *args) -{ - int closest = findClosestPlayer(); - if (closest >= 0) - Person::players[closest]->scale = atof(args) * .2; -} - -void ch_proportion(const char *args) -{ - set_proportion(0, args); -} - -void ch_proportionnear(const char *args) -{ - int closest = findClosestPlayer(); - if (closest >= 0) - set_proportion(closest, args); -} - -void ch_protection(const char *args) -{ - set_protection(0, args); -} - -void ch_protectionnear(const char *args) -{ - int closest = findClosestPlayer(); - if (closest >= 0) - set_protection(closest, args); -} - -void ch_armor(const char *args) -{ - set_armor(0, args); -} - -void ch_armornear(const char *args) -{ - int closest = findClosestPlayer(); - if (closest >= 0) - set_armor(closest, args); -} - -void ch_protectionreset(const char *args) -{ - set_protection(0, "1 1 1"); - set_armor(0, "1 1 1"); -} - -void ch_metal(const char *args) -{ - set_metal(0, args); -} - -void ch_noclothes(const char *args) -{ - set_noclothes(0, args); -} - -void ch_noclothesnear(const char *args) -{ - int closest = findClosestPlayer(); - if (closest >= 0) - set_noclothes(closest, args); -} - -void ch_clothes(const char *args) -{ - set_clothes(0, args); -} - -void ch_clothesnear(const char *args) -{ - int closest = findClosestPlayer(); - if (closest >= 0) - set_clothes(closest, args); -} - -void ch_belt(const char *args) -{ - Person::players[0]->skeleton.clothes = !Person::players[0]->skeleton.clothes; -} - - -void ch_cellophane(const char *args) -{ - cellophane = !cellophane; - float mul = (cellophane ? 0 : 1); - - for (auto player : Person::players) { - player->proportionhead.z = player->proportionhead.x * mul; - player->proportionbody.z = player->proportionbody.x * mul; - player->proportionarms.z = player->proportionarms.x * mul; - player->proportionlegs.z = player->proportionlegs.x * mul; - } -} - -void ch_funnybunny(const char *args) -{ - Person::players[0]->creature = rabbittype; - Person::players[0]->skeletonLoad(true); - Person::players[0]->scale = .2; - Person::players[0]->headless = 0; - Person::players[0]->damagetolerance = 200; - set_proportion(0, "1 1 1 1"); -} - -void ch_wolfie(const char *args) -{ - Person::players[0]->creature = wolftype; - Person::players[0]->skeletonLoad(); - Person::players[0]->damagetolerance = 300; - set_proportion(0, "1 1 1 1"); -} - -void ch_wolfieisgod(const char *args) -{ - ch_wolfie(args); -} - -void ch_wolf(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Wolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_snowwolf(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/SnowWolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_darkwolf(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/DarkWolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_lizardwolf(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/LizardWolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_white(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Fur.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_brown(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Fur3.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_black(const char *args) -{ - Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Fur2.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); -} - -void ch_sizemin(const char *args) -{ - for (unsigned i = 1; i < Person::players.size(); i++) - if (Person::players[i]->scale < 0.8 * 0.2) - Person::players[i]->scale = 0.8 * 0.2; -} - -void ch_tutorial(const char *args) -{ - tutoriallevel = atoi(args); -} - -void ch_hostile(const char *args) -{ - hostile = atoi(args); -} - -void ch_type(const char *args) -{ - int n = sizeof(editortypenames) / sizeof(editortypenames[0]); - for (int i = 0; i < n; i++) - if (stripfx(args, editortypenames[i])) { - editoractive = i; - break; - } -} - -void ch_path(const char *args) -{ - int n = sizeof(pathtypenames) / sizeof(pathtypenames[0]); - for (int i = 0; i < n; i++) - if (stripfx(args, pathtypenames[i])) { - editorpathtype = i; - break; - } -} - -void ch_hs(const char *args) -{ - float size; - int type, shift; - sscanf(args, "%f%d %n", &size, &type, &shift); - - Hotspot::hotspots.emplace_back(Person::players[0]->coords, type, size); - - strcpy(Hotspot::hotspots.back().text, args + shift); - strcat(Hotspot::hotspots.back().text, "\n"); -} - -void ch_dialogue(const char *args) -{ - int type; - char buf1[32]; - - sscanf(args, "%d %31s", &type, buf1); - std::string filename = std::string("Dialogues/") + buf1 + ".txt"; - - Dialog::dialogs.push_back(Dialog(type, filename)); - - Dialog::directing = true; - Dialog::indialogue = 0; - Dialog::whichdialogue = Dialog::dialogs.size(); -} - -void ch_fixdialogue(const char *args) -{ - char buf1[32]; - int whichdi; - - sscanf(args, "%d %31s", &whichdi, buf1); - std::string filename = std::string("Dialogues/") + buf1 + ".txt"; - - Dialog::dialogs[whichdi] = Dialog(Dialog::dialogs[whichdi].type, filename); -} - -void ch_fixtype(const char *args) -{ - int dlg; - sscanf(args, "%d", &dlg); - Dialog::dialogs[0].type = dlg; -} - -void ch_fixrotation(const char *args) -{ - int playerId = Dialog::currentScene().participantfocus; - Dialog::currentDialog().participantyaw[playerId] = Person::players[playerId]->yaw; -} - -void ch_ddialogue(const char *args) -{ - if (!Dialog::dialogs.empty()) { - Dialog::dialogs.pop_back(); - } -} - -void ch_dhs(const char *args) -{ - if (!Hotspot::hotspots.empty()) { - Hotspot::hotspots.pop_back(); - } -} - -void ch_immobile(const char *args) -{ - Person::players[0]->immobile = 1; -} - -void ch_allimmobile(const char *args) -{ - for (unsigned i = 1; i < Person::players.size(); i++) - Person::players[i]->immobile = 1; -} - -void ch_mobile(const char *args) -{ - Person::players[0]->immobile = 0; -} - -void ch_default(const char *args) -{ - Person::players[0]->armorhead = 1; - Person::players[0]->armorhigh = 1; - Person::players[0]->armorlow = 1; - Person::players[0]->protectionhead = 1; - Person::players[0]->protectionhigh = 1; - Person::players[0]->protectionlow = 1; - Person::players[0]->metalhead = 1; - Person::players[0]->metalhigh = 1; - Person::players[0]->metallow = 1; - Person::players[0]->power = 1; - Person::players[0]->speedmult = 1; - Person::players[0]->scale = 1; - - if (Person::players[0]->creature == wolftype) { - Person::players[0]->proportionhead = 1.1; - Person::players[0]->proportionbody = 1.1; - Person::players[0]->proportionarms = 1.1; - Person::players[0]->proportionlegs = 1.1; - } else if (Person::players[0]->creature == rabbittype) { - Person::players[0]->proportionhead = 1.2; - Person::players[0]->proportionbody = 1.05; - Person::players[0]->proportionarms = 1.00; - Person::players[0]->proportionlegs = 1.1; - Person::players[0]->proportionlegs.y = 1.05; - } - - Person::players[0]->numclothes = 0; - Person::players[0]->skeleton.drawmodel.textureptr.load( - creatureskin[Person::players[0]->creature][Person::players[0]->whichskin], 1, - &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); - - editoractive = typeactive; - Person::players[0]->immobile = 0; -} - -void ch_play(const char *args) -{ - int dlg; - sscanf(args, "%d", &dlg); - Dialog::whichdialogue = dlg; - - if (Dialog::whichdialogue >= Dialog::dialogs.size()) { - return; - } - - Dialog::currentDialog().play(); -} - -void ch_mapkilleveryone(const char *args) -{ - maptype = mapkilleveryone; -} - -void ch_mapkillmost(const char *args) -{ - maptype = mapkillmost; -} - -void ch_mapkillsomeone(const char *args) -{ - maptype = mapkillsomeone; -} - -void ch_mapgosomewhere(const char *args) -{ - maptype = mapgosomewhere; -} - -void ch_viewdistance(const char *args) -{ - viewdistance = atof(args) * 100; -} - -void ch_fadestart(const char *args) -{ - fadestart = atof(args); -} - -void ch_slomo(const char *args) -{ - slomospeed = atof(args); - slomo = !slomo; - slomodelay = 1000; -} - -void ch_slofreq(const char *args) -{ - slomofreq = atof(args); -} - -void ch_skytint(const char *args) -{ - sscanf(args, "%f%f%f", &skyboxr, &skyboxg, &skyboxb); - - skyboxlightr = skyboxr; - skyboxlightg = skyboxg; - skyboxlightb = skyboxb; - - SetUpLighting(); - - terrain.DoShadows(); - objects.DoShadows(); -} - -void ch_skylight(const char *args) -{ - sscanf(args, "%f%f%f", &skyboxlightr, &skyboxlightg, &skyboxlightb); - - SetUpLighting(); - - terrain.DoShadows(); - objects.DoShadows(); -} - -void ch_skybox(const char *args) -{ - skyboxtexture = !skyboxtexture; - - SetUpLighting(); - - terrain.DoShadows(); - objects.DoShadows(); -} diff --git a/Source/ConsoleCmds.def b/Source/ConsoleCmds.def deleted file mode 100644 index d3f1684..0000000 --- a/Source/ConsoleCmds.def +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -DECLARE_COMMAND(quit) -DECLARE_COMMAND(map) -DECLARE_COMMAND(save) - -DECLARE_COMMAND(cellar) -DECLARE_COMMAND(tint) -DECLARE_COMMAND(tintr) -DECLARE_COMMAND(tintg) -DECLARE_COMMAND(tintb) -DECLARE_COMMAND(speed) -DECLARE_COMMAND(strength) -DECLARE_COMMAND(power) -DECLARE_COMMAND(size) -DECLARE_COMMAND(sizenear) -DECLARE_COMMAND(proportion) -DECLARE_COMMAND(proportionnear) -DECLARE_COMMAND(protection) -DECLARE_COMMAND(protectionnear) -DECLARE_COMMAND(protectionreset) -DECLARE_COMMAND(armor) -DECLARE_COMMAND(armornear) -DECLARE_COMMAND(metal) -DECLARE_COMMAND(clothes) -DECLARE_COMMAND(clothesnear) -DECLARE_COMMAND(noclothes) -DECLARE_COMMAND(noclothesnear) -DECLARE_COMMAND(belt) -DECLARE_COMMAND(cellophane) -DECLARE_COMMAND(funnybunny) -DECLARE_COMMAND(wolfie) -DECLARE_COMMAND(wolfieisgod) -DECLARE_COMMAND(wolf) -DECLARE_COMMAND(snowwolf) -DECLARE_COMMAND(darkwolf) -DECLARE_COMMAND(lizardwolf) -DECLARE_COMMAND(white) -DECLARE_COMMAND(brown) -DECLARE_COMMAND(black) - -DECLARE_COMMAND(sizemin) -DECLARE_COMMAND(viewdistance) -DECLARE_COMMAND(fadestart) -DECLARE_COMMAND(slomo) -DECLARE_COMMAND(slofreq) - -DECLARE_COMMAND(tutorial) -DECLARE_COMMAND(hostile) -DECLARE_COMMAND(type) -DECLARE_COMMAND(path) -DECLARE_COMMAND(hs) -DECLARE_COMMAND(dhs) -DECLARE_COMMAND(dialogue) -DECLARE_COMMAND(fixdialogue) -DECLARE_COMMAND(ddialogue) -DECLARE_COMMAND(fixtype) -DECLARE_COMMAND(fixrotation) -DECLARE_COMMAND(immobile) -DECLARE_COMMAND(allimmobile) -DECLARE_COMMAND(mobile) -DECLARE_COMMAND(default) -DECLARE_COMMAND(play) - -DECLARE_COMMAND(mapkilleveryone) -DECLARE_COMMAND(mapkillmost) -DECLARE_COMMAND(mapkillsomeone) -DECLARE_COMMAND(mapgosomewhere) - -DECLARE_COMMAND(skytint) -DECLARE_COMMAND(skylight) -DECLARE_COMMAND(skybox) diff --git a/Source/ConsoleCmds.h b/Source/ConsoleCmds.h deleted file mode 100644 index 9670aa0..0000000 --- a/Source/ConsoleCmds.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -typedef void (*console_handler)(const char *args); - -#define DECLARE_COMMAND(cmd) void ch_##cmd(const char *args); -#include "ConsoleCmds.def" -#undef DECLARE_COMMAND - -/* FIXME - This is only to get cmd_count, not very clean */ -enum console_command { -#define DECLARE_COMMAND(cmd) cmd_##cmd, -#include "ConsoleCmds.def" -#undef DECLARE_COMMAND - cmd_count -}; - -extern const char *cmd_names[cmd_count]; - -extern console_handler cmd_handlers[cmd_count]; diff --git a/Source/Devtools/ConsoleCmds.cpp b/Source/Devtools/ConsoleCmds.cpp new file mode 100644 index 0000000..dc4c275 --- /dev/null +++ b/Source/Devtools/ConsoleCmds.cpp @@ -0,0 +1,739 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" +#include "Devtools/ConsoleCmds.h" +#include "Level/Dialog.h" +#include "Level/Hotspot.h" +#include "Utils/Folders.h" + +const char *cmd_names[cmd_count] = { +#define DECLARE_COMMAND(cmd) #cmd, +#include "ConsoleCmds.def" +#undef DECLARE_COMMAND +}; + +console_handler cmd_handlers[cmd_count] = { +#define DECLARE_COMMAND(cmd) ch_##cmd, +#include "ConsoleCmds.def" +#undef DECLARE_COMMAND +}; + +using namespace Game; + +/* globals */ + +extern bool campaign; +extern bool cellophane; +extern int editoractive; +extern int editorpathtype; +extern int environment; +extern float fadestart; +extern float slomospeed; +extern float slomofreq; +extern int tutoriallevel; +extern int hostile; +extern int maptype; +extern Objects objects; +extern int slomo; +extern float slomodelay; +extern bool skyboxtexture; +extern float skyboxr; +extern float skyboxg; +extern float skyboxb; +extern float skyboxlightr; +extern float skyboxlightg; +extern float skyboxlightb; +extern Terrain terrain; +extern float viewdistance; + +/* defined in GameTick.cpp */ + +extern int whichlevel; + +float tintr = 1, tintg = 1, tintb = 1; + +/* Helpers used in console commands */ + +/* Return true if PFX is a prefix of STR (case-insensitive). */ +static bool stripfx(const char *str, const char *pfx) +{ + return !strncasecmp(str, pfx, strlen(pfx)); +} + +static void set_proportion(int pnum, const char *args) +{ + float headprop, bodyprop, armprop, legprop; + + sscanf(args, "%f%f%f%f", &headprop, &bodyprop, &armprop, &legprop); + + if (Person::players[pnum]->creature == wolftype) { + Person::players[pnum]->proportionhead = 1.1 * headprop; + Person::players[pnum]->proportionbody = 1.1 * bodyprop; + Person::players[pnum]->proportionarms = 1.1 * armprop; + Person::players[pnum]->proportionlegs = 1.1 * legprop; + } else if (Person::players[pnum]->creature == rabbittype) { + Person::players[pnum]->proportionhead = 1.2 * headprop; + Person::players[pnum]->proportionbody = 1.05 * bodyprop; + Person::players[pnum]->proportionarms = 1.00 * armprop; + Person::players[pnum]->proportionlegs = 1.1 * legprop; + Person::players[pnum]->proportionlegs.y = 1.05 * legprop; + } +} + +static void set_protection(int pnum, const char *args) +{ + float head, high, low; + sscanf(args, "%f%f%f", &head, &high, &low); + + Person::players[pnum]->protectionhead = head; + Person::players[pnum]->protectionhigh = high; + Person::players[pnum]->protectionlow = low; +} + +static void set_armor(int pnum, const char *args) +{ + float head, high, low; + sscanf(args, "%f%f%f", &head, &high, &low); + + Person::players[pnum]->armorhead = head; + Person::players[pnum]->armorhigh = high; + Person::players[pnum]->armorlow = low; +} + +static void set_metal(int pnum, const char *args) +{ + float head, high, low; + sscanf(args, "%f%f%f", &head, &high, &low); + + Person::players[pnum]->metalhead = head; + Person::players[pnum]->metalhigh = high; + Person::players[pnum]->metallow = low; +} + +static void set_noclothes(int pnum, const char *args) +{ + Person::players[pnum]->numclothes = 0; + Person::players[pnum]->skeleton.drawmodel.textureptr.load( + creatureskin[Person::players[pnum]->creature][Person::players[pnum]->whichskin], 1, + &Person::players[pnum]->skeleton.skinText[0], &Person::players[pnum]->skeleton.skinsize); +} + +static void set_clothes(int pnum, const char *args) +{ + char buf[64]; + snprintf(buf, 63, "Textures/%s.png", args); + + int id = Person::players[pnum]->numclothes; + strcpy(Person::players[pnum]->clothes[id], buf); + Person::players[pnum]->clothestintr[id] = tintr; + Person::players[pnum]->clothestintg[id] = tintg; + Person::players[pnum]->clothestintb[id] = tintb; + Person::players[pnum]->numclothes++; + + if (!Person::players[pnum]->addClothes(id)) + return; + + Person::players[pnum]->DoMipmaps(); +} + +/* Console commands themselves */ + +void ch_quit(const char *args) +{ + tryquit = 1; +} + +void ch_map(const char *args) +{ + Loadlevel(args); + whichlevel = -2; + campaign = 0; +} + +void ch_save(const char *args) +{ + std::string map_path = Folders::getUserDataPath() + "/Maps/" + args; + + int mapvers = 12; + + FILE *tfile; + tfile = fopen( map_path.c_str(), "wb" ); + fpackf(tfile, "Bi", mapvers); + fpackf(tfile, "Bi", maptype); + fpackf(tfile, "Bi", hostile); + fpackf(tfile, "Bf Bf", viewdistance, fadestart); + fpackf(tfile, "Bb Bf Bf Bf", skyboxtexture, skyboxr, skyboxg, skyboxb); + fpackf(tfile, "Bf Bf Bf", skyboxlightr, skyboxlightg, skyboxlightb); + fpackf(tfile, "Bf Bf Bf Bf Bf Bi", Person::players[0]->coords.x, Person::players[0]->coords.y, Person::players[0]->coords.z, + Person::players[0]->yaw, Person::players[0]->targetyaw, Person::players[0]->num_weapons); + if (Person::players[0]->num_weapons > 0 && Person::players[0]->num_weapons < 5) + for (int j = 0; j < Person::players[0]->num_weapons; j++) + fpackf(tfile, "Bi", weapons[Person::players[0]->weaponids[j]].getType()); + + fpackf(tfile, "Bf Bf Bf", Person::players[0]->armorhead, Person::players[0]->armorhigh, Person::players[0]->armorlow); + fpackf(tfile, "Bf Bf Bf", Person::players[0]->protectionhead, Person::players[0]->protectionhigh, Person::players[0]->protectionlow); + fpackf(tfile, "Bf Bf Bf", Person::players[0]->metalhead, Person::players[0]->metalhigh, Person::players[0]->metallow); + fpackf(tfile, "Bf Bf", Person::players[0]->power, Person::players[0]->speedmult); + + fpackf(tfile, "Bi", Person::players[0]->numclothes); + + fpackf(tfile, "Bi Bi", Person::players[0]->whichskin, Person::players[0]->creature); + + Dialog::saveDialogs(tfile); + + for (int k = 0; k < Person::players[0]->numclothes; k++) { + int templength = strlen(Person::players[0]->clothes[k]); + fpackf(tfile, "Bi", templength); + for (int l = 0; l < templength; l++) + fpackf(tfile, "Bb", Person::players[0]->clothes[k][l]); + fpackf(tfile, "Bf Bf Bf", Person::players[0]->clothestintr[k], Person::players[0]->clothestintg[k], Person::players[0]->clothestintb[k]); + } + + fpackf(tfile, "Bi", environment); + + fpackf(tfile, "Bi", objects.numobjects); + + for (int k = 0; k < objects.numobjects; k++) + fpackf(tfile, "Bi Bf Bf Bf Bf Bf Bf", objects.type[k], objects.yaw[k], objects.pitch[k], + objects.position[k].x, objects.position[k].y, objects.position[k].z, objects.scale[k]); + + fpackf(tfile, "Bi", Hotspot::hotspots.size()); + for (int i = 0; i < Hotspot::hotspots.size(); i++) { + fpackf(tfile, "Bi Bf Bf Bf Bf", Hotspot::hotspots[i].type, Hotspot::hotspots[i].size, Hotspot::hotspots[i].position.x, Hotspot::hotspots[i].position.y, Hotspot::hotspots[i].position.z); + int templength = strlen(Hotspot::hotspots[i].text); + fpackf(tfile, "Bi", templength); + for (int l = 0; l < templength; l++) + fpackf(tfile, "Bb", Hotspot::hotspots[i].text[l]); + } + + fpackf(tfile, "Bi", Person::players.size()); + if (Person::players.size() > maxplayers) { + cout << "Warning: this level contains more players than allowed" << endl; + } + for (unsigned j = 1; j < Person::players.size(); j++) { + fpackf(tfile, "Bi Bi Bf Bf Bf Bi Bi Bf Bb Bf", Person::players[j]->whichskin, Person::players[j]->creature, + Person::players[j]->coords.x, Person::players[j]->coords.y, Person::players[j]->coords.z, + Person::players[j]->num_weapons, Person::players[j]->howactive, Person::players[j]->scale, Person::players[j]->immobile, Person::players[j]->yaw); + if (Person::players[j]->num_weapons < 5) + for (int k = 0; k < Person::players[j]->num_weapons; k++) + fpackf(tfile, "Bi", weapons[Person::players[j]->weaponids[k]].getType()); + if (Person::players[j]->numwaypoints < 30) { + fpackf(tfile, "Bi", Person::players[j]->numwaypoints); + for (int k = 0; k < Person::players[j]->numwaypoints; k++) { + fpackf(tfile, "Bf", Person::players[j]->waypoints[k].x); + fpackf(tfile, "Bf", Person::players[j]->waypoints[k].y); + fpackf(tfile, "Bf", Person::players[j]->waypoints[k].z); + fpackf(tfile, "Bi", Person::players[j]->waypointtype[k]); + } + fpackf(tfile, "Bi", Person::players[j]->waypoint); + } else { + Person::players[j]->numwaypoints = 0; + Person::players[j]->waypoint = 0; + fpackf(tfile, "Bi Bi Bi", Person::players[j]->numwaypoints, Person::players[j]->waypoint, Person::players[j]->waypoint); + } + + fpackf(tfile, "Bf Bf Bf", Person::players[j]->armorhead, Person::players[j]->armorhigh, Person::players[j]->armorlow); + fpackf(tfile, "Bf Bf Bf", Person::players[j]->protectionhead, Person::players[j]->protectionhigh, Person::players[j]->protectionlow); + fpackf(tfile, "Bf Bf Bf", Person::players[j]->metalhead, Person::players[j]->metalhigh, Person::players[j]->metallow); + fpackf(tfile, "Bf Bf", Person::players[j]->power, Person::players[j]->speedmult); + + float headprop, bodyprop, armprop, legprop; + if (Person::players[j]->creature == wolftype) { + headprop = Person::players[j]->proportionhead.x / 1.1; + bodyprop = Person::players[j]->proportionbody.x / 1.1; + armprop = Person::players[j]->proportionarms.x / 1.1; + legprop = Person::players[j]->proportionlegs.x / 1.1; + } else if (Person::players[j]->creature == rabbittype) { + headprop = Person::players[j]->proportionhead.x / 1.2; + bodyprop = Person::players[j]->proportionbody.x / 1.05; + armprop = Person::players[j]->proportionarms.x / 1.00; + legprop = Person::players[j]->proportionlegs.x / 1.1; + } + + fpackf(tfile, "Bf Bf Bf Bf", headprop, bodyprop, armprop, legprop); + + fpackf(tfile, "Bi", Person::players[j]->numclothes); + if (Person::players[j]->numclothes) + for (int k = 0; k < Person::players[j]->numclothes; k++) { + int templength; + templength = strlen(Person::players[j]->clothes[k]); + fpackf(tfile, "Bi", templength); + for (int l = 0; l < templength; l++) + fpackf(tfile, "Bb", Person::players[j]->clothes[k][l]); + fpackf(tfile, "Bf Bf Bf", Person::players[j]->clothestintr[k], Person::players[j]->clothestintg[k], Person::players[j]->clothestintb[k]); + } + } + + fpackf(tfile, "Bi", numpathpoints); + for (int j = 0; j < numpathpoints; j++) { + fpackf(tfile, "Bf Bf Bf Bi", pathpoint[j].x, pathpoint[j].y, pathpoint[j].z, numpathpointconnect[j]); + for (int k = 0; k < numpathpointconnect[j]; k++) + fpackf(tfile, "Bi", pathpointconnect[j][k]); + } + + fpackf(tfile, "Bf Bf Bf Bf", mapcenter.x, mapcenter.y, mapcenter.z, mapradius); + + fclose(tfile); +} + +void ch_cellar(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Furdarko.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_tint(const char *args) +{ + sscanf(args, "%f%f%f", &tintr, &tintg, &tintb); +} + +void ch_tintr(const char *args) +{ + tintr = atof(args); +} + +void ch_tintg(const char *args) +{ + tintg = atof(args); +} + +void ch_tintb(const char *args) +{ + tintb = atof(args); +} + +void ch_speed(const char *args) +{ + Person::players[0]->speedmult = atof(args); +} + +void ch_strength(const char *args) +{ + Person::players[0]->power = atof(args); +} + +void ch_power(const char *args) +{ + Person::players[0]->power = atof(args); +} + +void ch_size(const char *args) +{ + Person::players[0]->scale = atof(args) * .2; +} + +void ch_sizenear(const char *args) +{ + int closest = findClosestPlayer(); + if (closest >= 0) + Person::players[closest]->scale = atof(args) * .2; +} + +void ch_proportion(const char *args) +{ + set_proportion(0, args); +} + +void ch_proportionnear(const char *args) +{ + int closest = findClosestPlayer(); + if (closest >= 0) + set_proportion(closest, args); +} + +void ch_protection(const char *args) +{ + set_protection(0, args); +} + +void ch_protectionnear(const char *args) +{ + int closest = findClosestPlayer(); + if (closest >= 0) + set_protection(closest, args); +} + +void ch_armor(const char *args) +{ + set_armor(0, args); +} + +void ch_armornear(const char *args) +{ + int closest = findClosestPlayer(); + if (closest >= 0) + set_armor(closest, args); +} + +void ch_protectionreset(const char *args) +{ + set_protection(0, "1 1 1"); + set_armor(0, "1 1 1"); +} + +void ch_metal(const char *args) +{ + set_metal(0, args); +} + +void ch_noclothes(const char *args) +{ + set_noclothes(0, args); +} + +void ch_noclothesnear(const char *args) +{ + int closest = findClosestPlayer(); + if (closest >= 0) + set_noclothes(closest, args); +} + +void ch_clothes(const char *args) +{ + set_clothes(0, args); +} + +void ch_clothesnear(const char *args) +{ + int closest = findClosestPlayer(); + if (closest >= 0) + set_clothes(closest, args); +} + +void ch_belt(const char *args) +{ + Person::players[0]->skeleton.clothes = !Person::players[0]->skeleton.clothes; +} + + +void ch_cellophane(const char *args) +{ + cellophane = !cellophane; + float mul = (cellophane ? 0 : 1); + + for (auto player : Person::players) { + player->proportionhead.z = player->proportionhead.x * mul; + player->proportionbody.z = player->proportionbody.x * mul; + player->proportionarms.z = player->proportionarms.x * mul; + player->proportionlegs.z = player->proportionlegs.x * mul; + } +} + +void ch_funnybunny(const char *args) +{ + Person::players[0]->creature = rabbittype; + Person::players[0]->skeletonLoad(true); + Person::players[0]->scale = .2; + Person::players[0]->headless = 0; + Person::players[0]->damagetolerance = 200; + set_proportion(0, "1 1 1 1"); +} + +void ch_wolfie(const char *args) +{ + Person::players[0]->creature = wolftype; + Person::players[0]->skeletonLoad(); + Person::players[0]->damagetolerance = 300; + set_proportion(0, "1 1 1 1"); +} + +void ch_wolfieisgod(const char *args) +{ + ch_wolfie(args); +} + +void ch_wolf(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Wolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_snowwolf(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/SnowWolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_darkwolf(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/DarkWolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_lizardwolf(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/LizardWolf.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_white(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Fur.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_brown(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Fur3.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_black(const char *args) +{ + Person::players[0]->skeleton.drawmodel.textureptr.load("Textures/Fur2.jpg", 1, &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); +} + +void ch_sizemin(const char *args) +{ + for (unsigned i = 1; i < Person::players.size(); i++) + if (Person::players[i]->scale < 0.8 * 0.2) + Person::players[i]->scale = 0.8 * 0.2; +} + +void ch_tutorial(const char *args) +{ + tutoriallevel = atoi(args); +} + +void ch_hostile(const char *args) +{ + hostile = atoi(args); +} + +void ch_type(const char *args) +{ + int n = sizeof(editortypenames) / sizeof(editortypenames[0]); + for (int i = 0; i < n; i++) + if (stripfx(args, editortypenames[i])) { + editoractive = i; + break; + } +} + +void ch_path(const char *args) +{ + int n = sizeof(pathtypenames) / sizeof(pathtypenames[0]); + for (int i = 0; i < n; i++) + if (stripfx(args, pathtypenames[i])) { + editorpathtype = i; + break; + } +} + +void ch_hs(const char *args) +{ + float size; + int type, shift; + sscanf(args, "%f%d %n", &size, &type, &shift); + + Hotspot::hotspots.emplace_back(Person::players[0]->coords, type, size); + + strcpy(Hotspot::hotspots.back().text, args + shift); + strcat(Hotspot::hotspots.back().text, "\n"); +} + +void ch_dialogue(const char *args) +{ + int type; + char buf1[32]; + + sscanf(args, "%d %31s", &type, buf1); + std::string filename = std::string("Dialogues/") + buf1 + ".txt"; + + Dialog::dialogs.push_back(Dialog(type, filename)); + + Dialog::directing = true; + Dialog::indialogue = 0; + Dialog::whichdialogue = Dialog::dialogs.size(); +} + +void ch_fixdialogue(const char *args) +{ + char buf1[32]; + int whichdi; + + sscanf(args, "%d %31s", &whichdi, buf1); + std::string filename = std::string("Dialogues/") + buf1 + ".txt"; + + Dialog::dialogs[whichdi] = Dialog(Dialog::dialogs[whichdi].type, filename); +} + +void ch_fixtype(const char *args) +{ + int dlg; + sscanf(args, "%d", &dlg); + Dialog::dialogs[0].type = dlg; +} + +void ch_fixrotation(const char *args) +{ + int playerId = Dialog::currentScene().participantfocus; + Dialog::currentDialog().participantyaw[playerId] = Person::players[playerId]->yaw; +} + +void ch_ddialogue(const char *args) +{ + if (!Dialog::dialogs.empty()) { + Dialog::dialogs.pop_back(); + } +} + +void ch_dhs(const char *args) +{ + if (!Hotspot::hotspots.empty()) { + Hotspot::hotspots.pop_back(); + } +} + +void ch_immobile(const char *args) +{ + Person::players[0]->immobile = 1; +} + +void ch_allimmobile(const char *args) +{ + for (unsigned i = 1; i < Person::players.size(); i++) + Person::players[i]->immobile = 1; +} + +void ch_mobile(const char *args) +{ + Person::players[0]->immobile = 0; +} + +void ch_default(const char *args) +{ + Person::players[0]->armorhead = 1; + Person::players[0]->armorhigh = 1; + Person::players[0]->armorlow = 1; + Person::players[0]->protectionhead = 1; + Person::players[0]->protectionhigh = 1; + Person::players[0]->protectionlow = 1; + Person::players[0]->metalhead = 1; + Person::players[0]->metalhigh = 1; + Person::players[0]->metallow = 1; + Person::players[0]->power = 1; + Person::players[0]->speedmult = 1; + Person::players[0]->scale = 1; + + if (Person::players[0]->creature == wolftype) { + Person::players[0]->proportionhead = 1.1; + Person::players[0]->proportionbody = 1.1; + Person::players[0]->proportionarms = 1.1; + Person::players[0]->proportionlegs = 1.1; + } else if (Person::players[0]->creature == rabbittype) { + Person::players[0]->proportionhead = 1.2; + Person::players[0]->proportionbody = 1.05; + Person::players[0]->proportionarms = 1.00; + Person::players[0]->proportionlegs = 1.1; + Person::players[0]->proportionlegs.y = 1.05; + } + + Person::players[0]->numclothes = 0; + Person::players[0]->skeleton.drawmodel.textureptr.load( + creatureskin[Person::players[0]->creature][Person::players[0]->whichskin], 1, + &Person::players[0]->skeleton.skinText[0], &Person::players[0]->skeleton.skinsize); + + editoractive = typeactive; + Person::players[0]->immobile = 0; +} + +void ch_play(const char *args) +{ + int dlg; + sscanf(args, "%d", &dlg); + Dialog::whichdialogue = dlg; + + if (Dialog::whichdialogue >= Dialog::dialogs.size()) { + return; + } + + Dialog::currentDialog().play(); +} + +void ch_mapkilleveryone(const char *args) +{ + maptype = mapkilleveryone; +} + +void ch_mapkillmost(const char *args) +{ + maptype = mapkillmost; +} + +void ch_mapkillsomeone(const char *args) +{ + maptype = mapkillsomeone; +} + +void ch_mapgosomewhere(const char *args) +{ + maptype = mapgosomewhere; +} + +void ch_viewdistance(const char *args) +{ + viewdistance = atof(args) * 100; +} + +void ch_fadestart(const char *args) +{ + fadestart = atof(args); +} + +void ch_slomo(const char *args) +{ + slomospeed = atof(args); + slomo = !slomo; + slomodelay = 1000; +} + +void ch_slofreq(const char *args) +{ + slomofreq = atof(args); +} + +void ch_skytint(const char *args) +{ + sscanf(args, "%f%f%f", &skyboxr, &skyboxg, &skyboxb); + + skyboxlightr = skyboxr; + skyboxlightg = skyboxg; + skyboxlightb = skyboxb; + + SetUpLighting(); + + terrain.DoShadows(); + objects.DoShadows(); +} + +void ch_skylight(const char *args) +{ + sscanf(args, "%f%f%f", &skyboxlightr, &skyboxlightg, &skyboxlightb); + + SetUpLighting(); + + terrain.DoShadows(); + objects.DoShadows(); +} + +void ch_skybox(const char *args) +{ + skyboxtexture = !skyboxtexture; + + SetUpLighting(); + + terrain.DoShadows(); + objects.DoShadows(); +} diff --git a/Source/Devtools/ConsoleCmds.def b/Source/Devtools/ConsoleCmds.def new file mode 100644 index 0000000..d3f1684 --- /dev/null +++ b/Source/Devtools/ConsoleCmds.def @@ -0,0 +1,90 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +DECLARE_COMMAND(quit) +DECLARE_COMMAND(map) +DECLARE_COMMAND(save) + +DECLARE_COMMAND(cellar) +DECLARE_COMMAND(tint) +DECLARE_COMMAND(tintr) +DECLARE_COMMAND(tintg) +DECLARE_COMMAND(tintb) +DECLARE_COMMAND(speed) +DECLARE_COMMAND(strength) +DECLARE_COMMAND(power) +DECLARE_COMMAND(size) +DECLARE_COMMAND(sizenear) +DECLARE_COMMAND(proportion) +DECLARE_COMMAND(proportionnear) +DECLARE_COMMAND(protection) +DECLARE_COMMAND(protectionnear) +DECLARE_COMMAND(protectionreset) +DECLARE_COMMAND(armor) +DECLARE_COMMAND(armornear) +DECLARE_COMMAND(metal) +DECLARE_COMMAND(clothes) +DECLARE_COMMAND(clothesnear) +DECLARE_COMMAND(noclothes) +DECLARE_COMMAND(noclothesnear) +DECLARE_COMMAND(belt) +DECLARE_COMMAND(cellophane) +DECLARE_COMMAND(funnybunny) +DECLARE_COMMAND(wolfie) +DECLARE_COMMAND(wolfieisgod) +DECLARE_COMMAND(wolf) +DECLARE_COMMAND(snowwolf) +DECLARE_COMMAND(darkwolf) +DECLARE_COMMAND(lizardwolf) +DECLARE_COMMAND(white) +DECLARE_COMMAND(brown) +DECLARE_COMMAND(black) + +DECLARE_COMMAND(sizemin) +DECLARE_COMMAND(viewdistance) +DECLARE_COMMAND(fadestart) +DECLARE_COMMAND(slomo) +DECLARE_COMMAND(slofreq) + +DECLARE_COMMAND(tutorial) +DECLARE_COMMAND(hostile) +DECLARE_COMMAND(type) +DECLARE_COMMAND(path) +DECLARE_COMMAND(hs) +DECLARE_COMMAND(dhs) +DECLARE_COMMAND(dialogue) +DECLARE_COMMAND(fixdialogue) +DECLARE_COMMAND(ddialogue) +DECLARE_COMMAND(fixtype) +DECLARE_COMMAND(fixrotation) +DECLARE_COMMAND(immobile) +DECLARE_COMMAND(allimmobile) +DECLARE_COMMAND(mobile) +DECLARE_COMMAND(default) +DECLARE_COMMAND(play) + +DECLARE_COMMAND(mapkilleveryone) +DECLARE_COMMAND(mapkillmost) +DECLARE_COMMAND(mapkillsomeone) +DECLARE_COMMAND(mapgosomewhere) + +DECLARE_COMMAND(skytint) +DECLARE_COMMAND(skylight) +DECLARE_COMMAND(skybox) diff --git a/Source/Devtools/ConsoleCmds.h b/Source/Devtools/ConsoleCmds.h new file mode 100644 index 0000000..9670aa0 --- /dev/null +++ b/Source/Devtools/ConsoleCmds.h @@ -0,0 +1,37 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +typedef void (*console_handler)(const char *args); + +#define DECLARE_COMMAND(cmd) void ch_##cmd(const char *args); +#include "ConsoleCmds.def" +#undef DECLARE_COMMAND + +/* FIXME - This is only to get cmd_count, not very clean */ +enum console_command { +#define DECLARE_COMMAND(cmd) cmd_##cmd, +#include "ConsoleCmds.def" +#undef DECLARE_COMMAND + cmd_count +}; + +extern const char *cmd_names[cmd_count]; + +extern console_handler cmd_handlers[cmd_count]; diff --git a/Source/Dialog.cpp b/Source/Dialog.cpp deleted file mode 100644 index 1f3adc5..0000000 --- a/Source/Dialog.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Dialog.h" -#include "Person.h" -#include "Input.h" -#include "Game.h" -#include "binio.h" -#include "Utils/Folders.h" - -extern int hostile; - -int Dialog::indialogue; -int Dialog::whichdialogue; -bool Dialog::directing; -float Dialog::dialoguetime; -std::vector Dialog::dialogs; - -void Dialog::loadDialogs(FILE* tfile) -{ - int numdialogues; - funpackf(tfile, "Bi", &numdialogues); - for (int k = 0; k < numdialogues; k++) { - dialogs.push_back(Dialog(tfile)); - } -} - -Dialog::Dialog(FILE* tfile) : gonethrough(0) -{ - int numdialogscenes; - funpackf(tfile, "Bi", &numdialogscenes); - funpackf(tfile, "Bi", &type); - for (int l = 0; l < 10; l++) { - funpackf(tfile, "Bf Bf Bf", &participantlocation[l].x, &participantlocation[l].y, &participantlocation[l].z); - funpackf(tfile, "Bf", &participantyaw[l]); - } - for (int l = 0; l < numdialogscenes; l++) { - scenes.push_back(DialogScene(tfile)); - } -} - -std::string funpackf_string(FILE* tfile, int maxlength) -{ - int templength; - funpackf(tfile, "Bi", &templength); - if ((templength > maxlength) || (templength <= 0)) { - templength = maxlength; - } - int m; - char* text = new char[maxlength]; - for (m = 0; m < templength; m++) { - funpackf(tfile, "Bb", &text[m]); - if (text[m] == '\0') - break; - } - text[m] = 0; - std::string result(text); - delete[] text; - return result; -} - -void fpackf_string(FILE* tfile, std::string text) -{ - fpackf(tfile, "Bi", text.size()); - for (int m = 0; m < text.size(); m++) { - fpackf(tfile, "Bb", text[m]); - if (text[m] == '\0') - break; - } -} - -DialogScene::DialogScene(FILE* tfile) -{ - funpackf(tfile, "Bi", &location); - funpackf(tfile, "Bf", &color[0]); - funpackf(tfile, "Bf", &color[1]); - funpackf(tfile, "Bf", &color[2]); - funpackf(tfile, "Bi", &sound); - - text = funpackf_string(tfile, 128); - name = funpackf_string(tfile, 64); - - funpackf(tfile, "Bf Bf Bf", &camera.x, &camera.y, &camera.z); - funpackf(tfile, "Bi", &participantfocus); - funpackf(tfile, "Bi", &participantaction); - - for (int m = 0; m < 10; m++) - funpackf(tfile, "Bf Bf Bf", &participantfacing[m].x, &participantfacing[m].y, &participantfacing[m].z); - - funpackf(tfile, "Bf Bf", &camerayaw, &camerapitch); -} - -/* Load dialog from txt file, used by console */ -Dialog::Dialog(int type, std::string filename) : type(type) -{ - ifstream ipstream(Folders::getResourcePath(filename)); - ipstream.ignore(256, ':'); - int numscenes; - ipstream >> numscenes; - for (int i = 0; i < numscenes; i++) { - scenes.push_back(DialogScene(ipstream)); - for (unsigned j = 0; j < Person::players.size(); j++) { - scenes.back().participantfacing[j] = Person::players[j]->facing; - } - } - ipstream.close(); -} - -DialogScene::DialogScene(ifstream &ipstream) -{ - ipstream.ignore(256, ':'); - ipstream.ignore(256, ':'); - ipstream.ignore(256, ' '); - ipstream >> location; - ipstream.ignore(256, ':'); - ipstream >> color[0]; - ipstream >> color[1]; - ipstream >> color[2]; - ipstream.ignore(256, ':'); - getline(ipstream, name); - ipstream.ignore(256, ':'); - ipstream.ignore(256, ' '); - getline(ipstream, text); - for (int j = 0; j < 128; j++) { - if (text[j] == '\\') - text[j] = '\n'; - } - ipstream.ignore(256, ':'); - ipstream >> sound; -} - -void Dialog::tick(int id) -{ - unsigned playerId = type % 10; - bool special = (type > 9); - - if ((!hostile || (type > 40) && (type < 50)) && - (playerId < Person::players.size()) && - (playerId > 0) && - ((gonethrough == 0) || !special) && - (special || Input::isKeyPressed(Game::attackkey))) { - if ((distsq(&Person::players[0]->coords, &Person::players[playerId]->coords) < 6) || - (Person::players[playerId]->howactive >= typedead1) || - (type > 40) && (type < 50)) { - whichdialogue = id; - play(); - dialoguetime = 0; - gonethrough++; - } - } -} - -void Dialog::play() -{ - for (int i = 0; i < scenes.size(); i++) { - int playerId = scenes[i].participantfocus; - Person::players[playerId]->coords = participantlocation[playerId]; - Person::players[playerId]->yaw = participantyaw[playerId]; - Person::players[playerId]->targetyaw = participantyaw[playerId]; - Person::players[playerId]->velocity = 0; - Person::players[playerId]->animTarget = Person::players[playerId]->getIdle(); - Person::players[playerId]->frameTarget = 0; - } - - Dialog::directing = false; - Dialog::indialogue = 0; - - if (scenes[indialogue].sound != 0) { - Game::playdialoguescenesound(); - } -} - -void Dialog::saveDialogs(FILE* tfile) -{ - fpackf(tfile, "Bi", dialogs.size()); - - for (int k = 0; k < dialogs.size(); k++) { - dialogs[k].save(tfile); - } -} - -void Dialog::save(FILE* tfile) -{ - fpackf(tfile, "Bi", scenes.size()); - fpackf(tfile, "Bi", type); - for (int l = 0; l < 10; l++) { - fpackf(tfile, "Bf Bf Bf", participantlocation[l].x, participantlocation[l].y, participantlocation[l].z); - fpackf(tfile, "Bf", participantyaw[l]); - } - for (int l = 0; l < scenes.size(); l++) { - scenes[l].save(tfile); - } -} - -void DialogScene::save(FILE* tfile) -{ - fpackf(tfile, "Bi", location); - fpackf(tfile, "Bf", color[0]); - fpackf(tfile, "Bf", color[1]); - fpackf(tfile, "Bf", color[2]); - fpackf(tfile, "Bi", sound); - - fpackf_string(tfile, text); - fpackf_string(tfile, name); - - fpackf(tfile, "Bf Bf Bf", camera.x, camera.y, camera.z); - fpackf(tfile, "Bi", participantfocus); - fpackf(tfile, "Bi", participantaction); - - for (int m = 0; m < 10; m++) - fpackf(tfile, "Bf Bf Bf", participantfacing[m].x, participantfacing[m].y, participantfacing[m].z); - - fpackf(tfile, "Bf Bf", camerayaw, camerapitch); -} diff --git a/Source/Dialog.h b/Source/Dialog.h deleted file mode 100644 index 59c791e..0000000 --- a/Source/Dialog.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _DIALOG_H_ -#define _DIALOG_H_ - -#include "stdio.h" -#include "Quaternions.h" -#include - -class DialogScene -{ -public: - DialogScene(FILE* tfile); - DialogScene(ifstream &ipstream); - void save(FILE* tfile); - - int location; - float color[3]; - int sound; - std::string text; - std::string name; - XYZ camera; - float camerayaw; - float camerapitch; - int participantfocus; - int participantaction; - XYZ participantfacing[10]; -}; - -class Dialog -{ -public: - Dialog(FILE* tfile); - Dialog(int type, std::string filename); - void tick(int id); - void play(); - void save(FILE* tfile); - - int type; - int gonethrough; - std::vector scenes; - XYZ participantlocation[10]; - float participantyaw[10]; - - static void loadDialogs(FILE*); - static void saveDialogs(FILE*); - - static bool inDialog() { return (indialogue != -1); } - static Dialog& currentDialog() { return dialogs[whichdialogue]; } - static DialogScene& currentScene() { return currentDialog().scenes[indialogue]; } - - static int indialogue; - static int whichdialogue; - static bool directing; - static float dialoguetime; - static std::vector dialogs; -}; - -#endif /*_DIALOG_H_*/ diff --git a/Source/Environment/Lights.cpp b/Source/Environment/Lights.cpp new file mode 100644 index 0000000..17efb4c --- /dev/null +++ b/Source/Environment/Lights.cpp @@ -0,0 +1,72 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +/**> HEADER FILES <**/ +#include "Environment/Lights.h" + +void SetUpLight(Light* whichsource, int whichlight) +{ + static float qattenuation[] = {0.0002f}; + + //Initialize lights + if (whichlight == 0) { + GLfloat LightAmbient[] = { whichsource->ambient[0], whichsource->ambient[1], whichsource->ambient[2], 1.0f}; + GLfloat LightDiffuse[] = { whichsource->color[0], whichsource->color[1], whichsource->color[2], 1.0f }; + GLfloat LightPosition[] = { whichsource->location.x, whichsource->location.y, whichsource->location.z, 0.0f }; + + glLightfv(GL_LIGHT0, GL_POSITION, LightPosition); + glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient); + glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse); + glEnable(GL_LIGHT0); + } else { + GLenum lightselect = GL_LIGHT1; + switch (whichlight) { + case 2: + lightselect = GL_LIGHT2; + break; + case 3: + lightselect = GL_LIGHT3; + break; + case 4: + lightselect = GL_LIGHT4; + break; + case 5: + lightselect = GL_LIGHT5; + break; + case 6: + lightselect = GL_LIGHT6; + break; + case 7: + lightselect = GL_LIGHT7; + break; + } + + GLfloat LightAmbient[] = { 0, 0, 0, 1.0f}; + GLfloat LightDiffuse[] = { whichsource->color[0], whichsource->color[1], whichsource->color[2], 1.0f }; + GLfloat LightPosition[] = { whichsource->location.x, whichsource->location.y, whichsource->location.z, 1.0f }; + + glLightfv(lightselect, GL_QUADRATIC_ATTENUATION, qattenuation); + glLightfv(lightselect, GL_POSITION, LightPosition); + glLightfv(lightselect, GL_AMBIENT, LightAmbient); + glLightfv(lightselect, GL_DIFFUSE, LightDiffuse); + glEnable(lightselect); + + } +} diff --git a/Source/Environment/Lights.h b/Source/Environment/Lights.h new file mode 100644 index 0000000..ba1fd6c --- /dev/null +++ b/Source/Environment/Lights.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _LIGHTS_H_ +#define _LIGHTS_H_ + + +/**> HEADER FILES <**/ +#include "Graphic/gamegl.h" +#include "Math/Quaternions.h" + +class Light +{ +public: + GLint type; + GLfloat color[3]; + GLfloat ambient[3]; + int attach; + XYZ location; + inline void setColors(GLfloat cr, GLfloat cg, GLfloat cb, + GLfloat ar, GLfloat ag, GLfloat ab) { + color[0] = cr; + color[1] = cg; + color[2] = cb; + ambient[0] = ar; + ambient[1] = ag; + ambient[2] = ab; + } +}; + +void SetUpLight(Light* whichsource, int whichlight); + +#endif diff --git a/Source/Environment/Skybox.cpp b/Source/Environment/Skybox.cpp new file mode 100644 index 0000000..ce58adf --- /dev/null +++ b/Source/Environment/Skybox.cpp @@ -0,0 +1,170 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Environment/Skybox.h" +#include "Game.h" + +extern float viewdistance; +extern float blurness; +extern int environment; +extern bool skyboxtexture; +extern float skyboxr; +extern float skyboxg; +extern float skyboxb; + +void SkyBox::load (const std::string& ffront, const std::string& fleft, const std::string& fback, + const std::string& fright, const std::string& fup, const std::string& fdown) +{ + front.load(ffront, true); + left.load(fleft, true); + back.load(fback, true); + right.load(fright, true); + up.load(fup, true); + down.load(fdown, true); +} + +void SkyBox::draw() +{ + static float size = viewdistance / 4; + glPushMatrix(); + static GLfloat M[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, M); + M[12] = 0; + M[13] = 0; + M[14] = 0; + glLoadMatrixf(M); + if (environment == desertenvironment) { + glScalef(1 + blurness / 1000, 1, 1 + blurness / 1000); + glColor3f(1 * skyboxr, .95 * skyboxg, .95 * skyboxb); + } else { + glColor3f(.85 * skyboxr, .85 * skyboxg, .95 * skyboxb); + } + + if (!skyboxtexture) { + glDisable(GL_TEXTURE_2D); + glColor3f(skyboxr * .8, skyboxg * .8, skyboxb * .8); + } + glDepthMask(0); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + if (skyboxtexture) + glEnable(GL_TEXTURE_2D); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + front.bind(); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glBegin(GL_QUADS); + glNormal3f( 0.0f, 0.0f, -1); + glTexCoord2f(0, 0); + glVertex3f(-size, -size, size); + glTexCoord2f(1, 0); + glVertex3f( size, -size, size); + glTexCoord2f(1, 1); + glVertex3f( size, size, size); + glTexCoord2f(0, 1); + glVertex3f(-size, size, size); + glEnd(); + back.bind(); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glBegin(GL_QUADS); + glNormal3f( 0.0f, 0.0f, 1); + glTexCoord2f(1, 0); + glVertex3f(-size, -size, -size); + glTexCoord2f(1, 1); + glVertex3f(-size, size, -size); + glTexCoord2f(0, 1); + glVertex3f( size, size, -size); + glTexCoord2f(0, 0); + glVertex3f( size, -size, -size); + glEnd(); + up.bind(); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glBegin(GL_QUADS); + glNormal3f( 0.0f, -1.0f, 0); + glTexCoord2f(0, 1); + glVertex3f(-size, size, -size); + glTexCoord2f(0, 0); + glVertex3f(-size, size, size); + glTexCoord2f(1, 0); + glVertex3f( size, size, size); + glTexCoord2f(1, 1); + glVertex3f( size, size, -size); + glEnd(); + down.bind(); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glBegin(GL_QUADS); + glNormal3f( 0.0f, 1.0f, 0); + + glTexCoord2f(0, 0); + glVertex3f(-size, -size, -size); + glTexCoord2f(1, 0); + glVertex3f( size, -size, -size); + glTexCoord2f(1, 1); + glVertex3f( size, -size, size); + glTexCoord2f(0, 1); + glVertex3f(-size, -size, size); + glEnd(); + right.bind(); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glBegin(GL_QUADS); + glNormal3f( -1.0f, 0.0f, 0); + glTexCoord2f(1, 0); + glVertex3f( size, -size, -size); + glTexCoord2f(1, 1); + glVertex3f( size, size, -size); + glTexCoord2f(0, 1); + glVertex3f( size, size, size); + glTexCoord2f(0, 0); + glVertex3f( size, -size, size); + glEnd(); + left.bind(); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glBegin(GL_QUADS); + glNormal3f( 1.0f, 0.0f, 0); + glTexCoord2f(0, 0); + glVertex3f(-size, -size, -size); + glTexCoord2f(1, 0); + glVertex3f(-size, -size, size); + glTexCoord2f(1, 1); + glVertex3f(-size, size, size); + glTexCoord2f(0, 1); + glVertex3f(-size, size, -size); + glEnd(); + glEnable(GL_CULL_FACE); + glDepthMask(1); + glPopMatrix(); +} + +SkyBox::~SkyBox() +{ + front.destroy(); + left.destroy(); + back.destroy(); + right.destroy(); + up.destroy(); + down.destroy(); +}; + diff --git a/Source/Environment/Skybox.h b/Source/Environment/Skybox.h new file mode 100644 index 0000000..d2b5d72 --- /dev/null +++ b/Source/Environment/Skybox.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _SKYBOX_H_ +#define _SKYBOX_H_ + +#include "Graphic/gamegl.h" +#include "Graphic/Texture.h" +#include "Math/Quaternions.h" +#include "Math/Quaternions.h" +#include "Utils/ImageIO.h" + +class SkyBox +{ +public: + Texture front, left, back, right, up, down; + + void load(const std::string& ffront, const std::string& fleft, const std::string& fback, + const std::string& fright, const std::string& fup, const std::string& fdown); + void draw(); + + SkyBox() {} + ~SkyBox(); +}; + +#endif diff --git a/Source/Environment/Terrain.cpp b/Source/Environment/Terrain.cpp new file mode 100644 index 0000000..8c79cd4 --- /dev/null +++ b/Source/Environment/Terrain.cpp @@ -0,0 +1,1528 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" +#include "Environment/Terrain.h" +#include "Objects/Objects.h" +#include "Utils/Folders.h" + +extern XYZ viewer; +extern float viewdistance; +extern float fadestart; +extern int environment; +extern float texscale; +extern Light light; +extern float multiplier; +extern FRUSTUM frustum; +extern float texdetail; +extern int detail; +extern bool decals; +extern float blurness; +extern float targetblurness; +extern Objects objects; +extern bool visibleloading; +extern bool skyboxtexture; +extern int tutoriallevel; + +//Functions + +int Terrain::lineTerrain(XYZ p1, XYZ p2, XYZ *p) +{ + static int i, j, k; + static float distance; + static float olddistance; + static int intersecting; + static int firstintersecting; + static XYZ point; + static int startx, starty; + static int endx, endy; + static float highest, lowest; + + firstintersecting = -1; + olddistance = 10000; + distance = 1; + + XYZ triangles[3]; + + p1 /= scale; + p2 /= scale; + + startx = p1.x; + starty = p1.z; + endx = p2.x; + endy = p2.z; + + if (startx > endx) { + i = endx; + endx = startx; + startx = i; + } + if (starty > endy) { + i = endy; + endy = starty; + starty = i; + } + + if (startx < 0) + startx = 0; + if (starty < 0) + starty = 0; + if (endx > size - 1) + endx = size - 1; + if (endy > size - 1) + endy = size - 1; + + for (i = startx; i <= endx; i++) { + for (j = starty; j <= endy; j++) { + highest = -1000; + lowest = 1000; + for (k = 0; k < 2; k++) { + if (heightmap[i + k][j] > highest) + highest = heightmap[i + k][j]; + if (heightmap[i + k][j] < lowest) + lowest = heightmap[i + k][j]; + if (heightmap[i + k][j + 1] > highest) + highest = heightmap[i + k][j + 1]; + if (heightmap[i + k][j + 1] < lowest) + lowest = heightmap[i + k][j + 1]; + } + if ((p1.y <= highest || p2.y <= highest) && (p1.y >= lowest || p2.y >= lowest)) { + triangles[0].x = i; + triangles[0].y = heightmap[i][j]; + triangles[0].z = j; + + triangles[1].x = i; + triangles[1].y = heightmap[i][j + 1]; + triangles[1].z = j + 1; + + triangles[2].x = i + 1; + triangles[2].y = heightmap[i + 1][j]; + triangles[2].z = j; + + intersecting = LineFacet(p1, p2, triangles[0], triangles[1], triangles[2], &point); + distance = distsq(&p1, &point); + if ((distance < olddistance || firstintersecting == -1) && intersecting == 1) { + olddistance = distance; + firstintersecting = 1; + *p = point; + } + + triangles[0].x = i + 1; + triangles[0].y = heightmap[i + 1][j]; + triangles[0].z = j; + + triangles[1].x = i; + triangles[1].y = heightmap[i][j + 1]; + triangles[1].z = j + 1; + + triangles[2].x = i + 1; + triangles[2].y = heightmap[i + 1][j + 1]; + triangles[2].z = j + 1; + + intersecting = LineFacet(p1, p2, triangles[0], triangles[1], triangles[2], &point); + distance = distsq(&p1, &point); + if ((distance < olddistance || firstintersecting == -1) && intersecting == 1) { + olddistance = distance; + firstintersecting = 1; + *p = point; + } + } + } + } + return firstintersecting; +} + +void Terrain::UpdateTransparency(int whichx, int whichy) +{ + static XYZ vertex; + static int i, j, a, b, c, d, patch_size, stepsize; + static float distance; + + static float viewdistsquared; + + viewdistsquared = viewdistance * viewdistance; + patch_size = size / subdivision; + + stepsize = 1; + c = whichx * patch_elements + whichy * patch_elements * subdivision; + + for (i = patch_size * whichx; i < patch_size * (whichx + 1) + 1; i += stepsize) { + for (j = patch_size * whichy; j < patch_size * (whichy + 1) + 1; j += stepsize) { + if (i < size && j < size) { + vertex.x = i * scale; + vertex.z = j * scale; + vertex.y = heightmap[i][j] * scale; + distance = distsq(&viewer, &vertex); + if (distance > viewdistsquared) + distance = viewdistsquared; + colors[i][j][3] = (viewdistsquared - (distance - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; + } + } + } + + for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { + for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { + a = (i - (patch_size * whichx)) / stepsize; + b = (j - (patch_size * whichy)) / stepsize; + d = (a * 54) + (b * 54 * patch_size / stepsize); + vArray[d + c + 6] = colors[i][j][3]; + + vArray[d + c + 15] = colors[i][j + stepsize][3]; + + vArray[d + c + 24] = colors[i + stepsize][j][3]; + + vArray[d + c + 33] = colors[i + stepsize][j][3]; + + vArray[d + c + 42] = colors[i][j + stepsize][3]; + + vArray[d + c + 51] = colors[i + stepsize][j + stepsize][3]; + } + } +} + +void Terrain::UpdateTransparencyother(int whichx, int whichy) +{ + static int i, j, a, b, c, d, patch_size, stepsize; + + patch_size = size / subdivision; + + stepsize = 1; + c = whichx * patch_elements + whichy * patch_elements * subdivision; + + for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { + for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { + a = (i - (patch_size * whichx)) / stepsize; + b = (j - (patch_size * whichy)) / stepsize; + d = (a * 54) + (b * 54 * patch_size / stepsize); + vArray[d + c + 6] = colors[i][j][3] * opacityother[i][j]; + + vArray[d + c + 15] = colors[i][j + stepsize][3] * opacityother[i][j + stepsize]; + + vArray[d + c + 24] = colors[i + stepsize][j][3] * opacityother[i + stepsize][j]; + + vArray[d + c + 33] = colors[i + stepsize][j][3] * opacityother[i + stepsize][j]; + + vArray[d + c + 42] = colors[i][j + stepsize][3] * opacityother[i][j + stepsize]; + + vArray[d + c + 51] = colors[i + stepsize][j + stepsize][3] * opacityother[i + stepsize][j + stepsize]; + } + } +} + +void Terrain::UpdateTransparencyotherother(int whichx, int whichy) +{ + static XYZ vertex; + static int i, j, a, b, c, d, patch_size, stepsize; + static float distance; + + static float viewdistsquared; + + viewdistsquared = viewdistance * viewdistance; + patch_size = size / subdivision; + + stepsize = 1; + c = whichx * patch_elements + whichy * patch_elements * subdivision; + + for (i = patch_size * whichx; i < patch_size * (whichx + 1) + 1; i += stepsize) { + for (j = patch_size * whichy; j < patch_size * (whichy + 1) + 1; j += stepsize) { + if (i < size && j < size) { + vertex.x = i * scale; + vertex.z = j * scale; + vertex.y = heightmap[i][j] * scale; + distance = distsq(&viewer, &vertex); + if (distance > viewdistsquared) + distance = viewdistsquared; + colors[i][j][3] = (viewdistsquared - (distance - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; + } + } + } + + for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { + for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { + a = (i - (patch_size * whichx)) / stepsize; + b = (j - (patch_size * whichy)) / stepsize; + d = (a * 54) + (b * 54 * patch_size / stepsize); + vArray[d + c + 6] = colors[i][j][3]; + + vArray[d + c + 15] = colors[i][j + stepsize][3]; + + vArray[d + c + 24] = colors[i + stepsize][j][3]; + + vArray[d + c + 33] = colors[i + stepsize][j][3]; + + vArray[d + c + 42] = colors[i][j + stepsize][3]; + + vArray[d + c + 51] = colors[i + stepsize][j + stepsize][3]; + } + } +} + +void Terrain::UpdateVertexArray(int whichx, int whichy) +{ + static int i, j, a, b, c, patch_size, stepsize; + + + numtris[whichx][whichy] = 0; + + patch_size = size / subdivision; + + stepsize = 1; + c = whichx * patch_elements + whichy * patch_elements * subdivision; + for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { + for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { + a = (i - ((float)size / subdivision * (float)whichx)) / stepsize; + b = (j - ((float)size / subdivision * (float)whichy)) / stepsize; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 0] = i * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 1] = heightmap[i][j] * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 2] = j * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 3] = colors[i][j][0]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 4] = colors[i][j][1]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 5] = colors[i][j][2]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 6] = colors[i][j][3]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 7] = i * scale * texscale + texoffsetx[i][j]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 8] = j * scale * texscale + texoffsety[i][j]; + + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 9] = i * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 10] = heightmap[i][j + stepsize] * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 11] = j * scale + stepsize * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 12] = colors[i][j + stepsize][0]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 13] = colors[i][j + stepsize][1]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 14] = colors[i][j + stepsize][2]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 15] = colors[i][j + stepsize][3]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 16] = i * scale * texscale + texoffsetx[i][j + stepsize]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 17] = j * scale * texscale + stepsize * scale * texscale + texoffsety[i][j + stepsize]; + + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 18] = i * scale + stepsize * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 19] = heightmap[i + stepsize][j] * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 20] = j * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 21] = colors[i + stepsize][j][0]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 22] = colors[i + stepsize][j][1]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 23] = colors[i + stepsize][j][2]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 24] = colors[i + stepsize][j][3]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 25] = i * scale * texscale + stepsize * scale * texscale + texoffsetx[i + stepsize][j]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 26] = j * scale * texscale + texoffsety[i + stepsize][j]; + + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 27] = i * scale + stepsize * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 28] = heightmap[i + stepsize][j] * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 29] = j * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 30] = colors[i + stepsize][j][0]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 31] = colors[i + stepsize][j][1]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 32] = colors[i + stepsize][j][2]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 33] = colors[i + stepsize][j][3]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 34] = i * scale * texscale + stepsize * scale * texscale + texoffsetx[i + stepsize][j]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 35] = j * scale * texscale + texoffsety[i + stepsize][j]; + + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 36] = i * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 37] = heightmap[i][j + stepsize] * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 38] = j * scale + stepsize * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 39] = colors[i][j + stepsize][0]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 40] = colors[i][j + stepsize][1]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 41] = colors[i][j + stepsize][2]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 42] = colors[i][j + stepsize][3]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 43] = i * scale * texscale + texoffsetx[i][j + stepsize]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 44] = j * scale * texscale + stepsize * scale * texscale + texoffsety[i][j + stepsize]; + + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 45] = i * scale + stepsize * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 46] = heightmap[i + stepsize][j + stepsize] * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 47] = j * scale + stepsize * scale; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 48] = colors[i + stepsize][j + stepsize][0]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 49] = colors[i + stepsize][j + stepsize][1]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 50] = colors[i + stepsize][j + stepsize][2]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 51] = colors[i + stepsize][j + stepsize][3]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 52] = i * scale * texscale + stepsize * scale * texscale + texoffsetx[i + stepsize][j + stepsize]; + vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 53] = j * scale * texscale + stepsize * scale * texscale + texoffsety[i + stepsize][j + stepsize]; + numtris[whichx][whichy] += 2; + } + } + + maxypatch[whichx][whichy] = -10000; + minypatch[whichx][whichy] = 10000; + for (a = 0; a < size / subdivision; a++) { + for (b = 0; b < size / subdivision; b++) { + if (heightmap[(size / subdivision)*whichx + a][(size / subdivision)*whichy + b]*scale > maxypatch[whichx][whichy]) + maxypatch[whichx][whichy] = heightmap[(size / subdivision) * whichx + a][(size / subdivision) * whichy + b] * scale; + if (heightmap[(size / subdivision)*whichx + a][(size / subdivision)*whichy + b]*scale < minypatch[whichx][whichy]) + minypatch[whichx][whichy] = heightmap[(size / subdivision) * whichx + a][(size / subdivision) * whichy + b] * scale; + } + } + heightypatch[whichx][whichy] = (maxypatch[whichx][whichy] - minypatch[whichx][whichy]); + if (heightypatch[whichx][whichy] < size / subdivision * scale) + heightypatch[whichx][whichy] = size / subdivision * scale; + avgypatch[whichx][whichy] = (minypatch[whichx][whichy] + maxypatch[whichx][whichy]) / 2; + + for (i = whichx * size / subdivision; i < (whichx + 1)*size / subdivision - 1; i++) { + for (j = whichy * size / subdivision; j < (whichy + 1)*size / subdivision - 1; j++) { + triangles[(i * (size - 1) * 2) + (j * 2)][0].x = i * scale; + triangles[(i * (size - 1) * 2) + (j * 2)][0].y = heightmap[i][j] * scale; + triangles[(i * (size - 1) * 2) + (j * 2)][0].z = j * scale; + + triangles[(i * (size - 1) * 2) + (j * 2)][1].x = i * scale; + triangles[(i * (size - 1) * 2) + (j * 2)][1].y = heightmap[i][j + 1] * scale; + triangles[(i * (size - 1) * 2) + (j * 2)][1].z = j * scale + scale; + + triangles[(i * (size - 1) * 2) + (j * 2)][2].x = i * scale + 1 * scale; + triangles[(i * (size - 1) * 2) + (j * 2)][2].y = heightmap[i + 1][j] * scale; + triangles[(i * (size - 1) * 2) + (j * 2)][2].z = j * scale; + + triangles[(i * (size - 1) * 2) + (j * 2) + 1][0].x = i * scale + 1 * scale; + triangles[(i * (size - 1) * 2) + (j * 2) + 1][0].y = heightmap[i + 1][j] * scale; + triangles[(i * (size - 1) * 2) + (j * 2) + 1][0].z = j * scale; + + triangles[(i * (size - 1) * 2) + (j * 2) + 1][1].x = i * scale; + triangles[(i * (size - 1) * 2) + (j * 2) + 1][1].y = heightmap[i][j + 1] * scale; + triangles[(i * (size - 1) * 2) + (j * 2) + 1][1].z = j * scale + 1 * scale; + + triangles[(i * (size - 1) * 2) + (j * 2) + 1][2].x = i * scale + 1 * scale; + triangles[(i * (size - 1) * 2) + (j * 2) + 1][2].y = heightmap[i + 1][j + 1] * scale; + triangles[(i * (size - 1) * 2) + (j * 2) + 1][2].z = j * scale + 1 * scale; + } + } + +} + + +bool Terrain::load(const std::string& fileName) +{ + static long i, j; + static long x, y; + static float patch_size; + + float temptexdetail = texdetail; + + ImageRec texture; + + //Load Image + if (!load_image(Folders::getResourcePath(fileName).c_str(), texture)) { + return false; + } + + //Is it valid? + if (texture.bpp > 24) { + int bytesPerPixel = texture.bpp / 8; + + int tempnum = 0; + for (i = 0; i < (long)(texture.sizeY * texture.sizeX * bytesPerPixel); i++) { + if ((i + 1) % 4) { + texture.data[tempnum] = texture.data[i]; + tempnum++; + } + } + } + texture.bpp = 24; + if (visibleloading) + Game::LoadingScreen(); + + texdetail = temptexdetail; + + size = texture.sizeX; + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + heightmap[size - 1 - i][j] = (float)((texture.data[(i + (j * size)) * texture.bpp / 8])) / 5; + } + } + + if (visibleloading) + Game::LoadingScreen(); + + float slopeness; + + for (i = 0; i < subdivision; i++) { + for (j = 0; j < subdivision; j++) { + textureness[i][j] = -1; + } + } + if (visibleloading) + Game::LoadingScreen(); + + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + heightmap[i][j] *= .5; + + texoffsetx[i][j] = (float)abs(Random() % 100) / 1200 / scale * 3; + texoffsety[i][j] = (float)abs(Random() % 100) / 1200 / scale * 3; + + slopeness = 0; + if (environment == snowyenvironment) { + if (j != 0 && heightmap[i][j] - heightmap[i][j - 1] > slopeness) { + slopeness = heightmap[i][j] - heightmap[i][j - 1]; + } + opacityother[i][j] = slopeness * slopeness * 2; + if (opacityother[i][j] > 1) + opacityother[i][j] = 1; + opacityother[i][j] -= (float)abs(Random() % 100) / 300; + } + if (environment == desertenvironment) { + if (j != 0 && heightmap[i][j] - heightmap[i][j - 1] > slopeness) { + slopeness = heightmap[i][j] - heightmap[i][j - 1]; + } + opacityother[i][j] = slopeness * slopeness * 2; + if (opacityother[i][j] > 1) + opacityother[i][j] = 1; + opacityother[i][j] -= (float)abs(Random() % 100) / 300; + } + if (environment == grassyenvironment) { + if (i != 0 && heightmap[i][j] - heightmap[i - 1][j] > slopeness) { + slopeness = heightmap[i][j] - heightmap[i - 1][j]; + } + if (j != 0 && heightmap[i][j] - heightmap[i][j - 1] > slopeness) { + slopeness = heightmap[i][j] - heightmap[i][j - 1]; + } + if (i < size - 1 && heightmap[i][j] - heightmap[i + 1][j] > slopeness) { + slopeness = heightmap[i][j] - heightmap[i + 1][j]; + } + if (j < size - 1 && heightmap[i][j] - heightmap[i][j + 1] > slopeness) { + slopeness = heightmap[i][j] - heightmap[i][j + 1]; + } + opacityother[i][j] = slopeness * slopeness * 10; + if (opacityother[i][j] > 1) + opacityother[i][j] = 1; + opacityother[i][j] -= (float)abs(Random() % 100) / 100; + } + } + } + if (visibleloading) + Game::LoadingScreen(); + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + if (environment == snowyenvironment) { + heightmap[i][j] -= opacityother[i][j]; + } + if (environment == desertenvironment) { + heightmap[i][j] -= opacityother[i][j]; + } + } + } + if (visibleloading) + Game::LoadingScreen(); + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + if (opacityother[i][j] < .1) + opacityother[i][j] = 0; + if (textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == -1) { + if (!opacityother[i][j]) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = allfirst; + if (opacityother[i][j] == 1) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = allsecond; + } + if (opacityother[i][j] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + + x = i; + y = j; + if (i > 0) { + i--; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + + if (j > 0) { + j--; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + j++; + } + + if (j < size - 1) { + j++; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + j--; + } + i++; + } + + if (i < size - 1) { + i++; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + + if (j > 0) { + j--; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + j++; + } + + if (j < size - 1) { + j++; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + j--; + } + i--; + } + + if (j > 0) { + j--; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + j++; + } + + if (j < size - 1) { + j++; + if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) + textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; + if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) + textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; + j--; + + } + } + } + if (visibleloading) + Game::LoadingScreen(); + + patch_size = size / subdivision; + patch_elements = (patch_size) * (patch_size) * 54; + CalculateNormals(); + + return true; +} + +void Terrain::CalculateNormals() +{ + static int i, j; + static XYZ facenormal; + static XYZ p, q, a, b, c; + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + normals[i][j].x = 0; + normals[i][j].y = 0; + normals[i][j].z = 0; + } + } + + for (i = 0; i < size - 1; i++) { + for (j = 0; j < size - 1; j++) { + a.x = i; + a.y = heightmap[i][j]; + a.z = j; + b.x = i; + b.y = heightmap[i][j + 1]; + b.z = j + 1; + c.x = i + 1; + c.y = heightmap[i + 1][j]; + c.z = j; + + p.x = b.x - a.x; + p.y = b.y - a.y; + p.z = b.z - a.z; + q.x = c.x - a.x; + q.y = c.y - a.y; + q.z = c.z - a.z; + + CrossProduct(&p, &q, &facenormal); + + facenormals[i][j] = facenormal; + + normals[i][j] = normals[i][j] + facenormal; + normals[i][j + 1] = normals[i][j + 1] + facenormal; + normals[i + 1][j] = normals[i + 1][j] + facenormal; + + + a.x = i + 1; + a.y = heightmap[i + 1][j]; + a.z = j; + b.x = i; + b.y = heightmap[i][j + 1]; + b.z = j + 1; + c.x = i + 1; + c.y = heightmap[i + 1][j + 1]; + c.z = j + 1; + + p.x = b.x - a.x; + p.y = b.y - a.y; + p.z = b.z - a.z; + q.x = c.x - a.x; + q.y = c.y - a.y; + q.z = c.z - a.z; + + CrossProduct(&p, &q, &facenormal); + + normals[i + 1][j + 1] = normals[i + 1][j + 1] + facenormal; + normals[i][j + 1] = normals[i][j + 1] + facenormal; + normals[i + 1][j] = normals[i + 1][j] + facenormal; + + Normalise(&facenormals[i][j]); + } + } + + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + Normalise(&normals[i][j]); + } + } +} + +void Terrain::drawpatch(int whichx, int whichy, float opacity) +{ + if (opacity >= 1) + glDisable(GL_BLEND); + if (opacity < 1) { + glEnable(GL_BLEND); + UpdateTransparency(whichx, whichy); + } + glColor4f(1, 1, 1, 1); + //Set up vertex array + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[0 + whichx * patch_elements + whichy * patch_elements * subdivision]); + glColorPointer(4, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[3 + whichx * patch_elements + whichy * patch_elements * subdivision]); + glTexCoordPointer(2, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[7 + whichx * patch_elements + whichy * patch_elements * subdivision]); + + //Draw + glDrawArrays(GL_TRIANGLES, 0, numtris[whichx][whichy] * 3); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +void Terrain::drawpatchother(int whichx, int whichy, float opacity) +{ + glEnable(GL_BLEND); + if (opacity < 1) { + UpdateTransparency(whichx, whichy); + } + UpdateTransparencyother(whichx, whichy); + glColor4f(1, 1, 1, 1); + //Set up vertex array + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[0 + whichx * patch_elements + whichy * patch_elements * subdivision]); + glColorPointer(4, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[3 + whichx * patch_elements + whichy * patch_elements * subdivision]); + glTexCoordPointer(2, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[7 + whichx * patch_elements + whichy * patch_elements * subdivision]); + + //Draw + glDrawArrays(GL_TRIANGLES, 0, numtris[whichx][whichy] * 3); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +void Terrain::drawpatchotherother(int whichx, int whichy, float opacity) +{ + glEnable(GL_BLEND); + UpdateTransparencyotherother(whichx, whichy); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glScalef(6, 6, 6); + + glColor4f(1, 1, 1, 1); + + //Set up vertex array + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[0 + whichx * patch_elements + whichy * patch_elements * subdivision]); + glColorPointer(4, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[3 + whichx * patch_elements + whichy * patch_elements * subdivision]); + glTexCoordPointer(2, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[7 + whichx * patch_elements + whichy * patch_elements * subdivision]); + + //Draw + glDrawArrays(GL_TRIANGLES, 0, numtris[whichx][whichy] * 3); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} + + +float Terrain::getHeight(float pointx, float pointz) +{ + static int tilex, tiley; + static XYZ startpoint, endpoint, intersect, triangle[3]; + + pointx /= scale; + pointz /= scale; + + if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) + return 0; + + startpoint.x = pointx; + startpoint.y = -1000; + startpoint.z = pointz; + + endpoint = startpoint; + endpoint.y = 1000; + + tilex = pointx; + tiley = pointz; + + triangle[0].x = tilex; + triangle[0].z = tiley; + triangle[0].y = heightmap[tilex][tiley]; + + triangle[1].x = tilex + 1; + triangle[1].z = tiley; + triangle[1].y = heightmap[tilex + 1][tiley]; + + triangle[2].x = tilex; + triangle[2].z = tiley + 1; + triangle[2].y = heightmap[tilex][tiley + 1]; + + if (!LineFacetd(&startpoint, &endpoint, &triangle[0], &triangle[1], &triangle[2], &intersect)) { + triangle[0].x = tilex + 1; + triangle[0].z = tiley; + triangle[0].y = heightmap[tilex + 1][tiley]; + + triangle[1].x = tilex + 1; + triangle[1].z = tiley + 1; + triangle[1].y = heightmap[tilex + 1][tiley + 1]; + + triangle[2].x = tilex; + triangle[2].z = tiley + 1; + triangle[2].y = heightmap[tilex][tiley + 1]; + LineFacetd(&startpoint, &endpoint, &triangle[0], &triangle[1], &triangle[2], &intersect); + } + return intersect.y * scale + getOpacity(pointx * scale, pointz * scale) / 8; +} + +float Terrain::getOpacity(float pointx, float pointz) +{ + static float height1, height2; + static int tilex, tiley; + + pointx /= scale; + pointz /= scale; + + if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) + return 0; + + tilex = pointx; + tiley = pointz; + + height1 = opacityother[tilex][tiley] * (1 - (pointx - tilex)) + opacityother[tilex + 1][tiley] * (pointx - tilex); + height2 = opacityother[tilex][tiley + 1] * (1 - (pointx - tilex)) + opacityother[tilex + 1][tiley + 1] * (pointx - tilex); + + return height1 * (1 - (pointz - tiley)) + height2 * (pointz - tiley); +} + +XYZ Terrain::getNormal(float pointx, float pointz) +{ + static XYZ height1, height2, total; + static int tilex, tiley; + + pointx /= scale; + pointz /= scale; + + height1 = 0; + if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) + return height1; + tilex = pointx; + tiley = pointz; + + height1 = normals[tilex][tiley] * (1 - (pointx - tilex)) + normals[tilex + 1][tiley] * (pointx - tilex); + height2 = normals[tilex][tiley + 1] * (1 - (pointx - tilex)) + normals[tilex + 1][tiley + 1] * (pointx - tilex); + total = height1 * (1 - (pointz - tiley)) + height2 * (pointz - tiley); + Normalise(&total); + return total; +} + +XYZ Terrain::getLighting(float pointx, float pointz) +{ + static XYZ height1, height2; + static int tilex, tiley; + + pointx /= scale; + pointz /= scale; + + height1 = 0; + if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) + return height1; + tilex = pointx; + tiley = pointz; + + height1.x = colors[tilex][tiley][0] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley][0] * (pointx - tilex); + height1.y = colors[tilex][tiley][1] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley][1] * (pointx - tilex); + height1.z = colors[tilex][tiley][2] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley][2] * (pointx - tilex); + height2.x = colors[tilex][tiley + 1][0] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley + 1][0] * (pointx - tilex); + height2.y = colors[tilex][tiley + 1][1] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley + 1][1] * (pointx - tilex); + height2.z = colors[tilex][tiley + 1][2] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley + 1][2] * (pointx - tilex); + + return height1 * (1 - (pointz - tiley)) + height2 * (pointz - tiley); +} + +void Terrain::draw(int layer) +{ + static int i, j; + static float opacity; + static XYZ terrainpoint; + static float distance[subdivision][subdivision]; + + static int beginx, endx; + static int beginz, endz; + + static float patch_size = size / subdivision * scale; + static float viewdistsquared; + + viewdistsquared = viewdistance * viewdistance; + + //Only nearby blocks + beginx = (viewer.x - viewdistance) / (patch_size) - 1; + if (beginx < 0) + beginx = 0; + beginz = (viewer.z - viewdistance) / (patch_size) - 1; + if (beginz < 0) + beginz = 0; + + endx = (viewer.x + viewdistance) / (patch_size) + 1; + if (endx > subdivision) + endx = subdivision; + endz = (viewer.z + viewdistance) / (patch_size) + 1; + if (endz > subdivision) + endz = subdivision; + + if (!layer) { + for (i = beginx; i < endx; i++) { + for (j = beginz; j < endz; j++) { + terrainpoint.x = i * patch_size + (patch_size) / 2; + terrainpoint.y = viewer.y; //heightmap[i][j]*scale; + terrainpoint.z = j * patch_size + (patch_size) / 2; + distance[i][j] = distsq(&viewer, &terrainpoint); + } + } + } + for (i = beginx; i < endx; i++) { + for (j = beginz; j < endz; j++) { + if (distance[i][j] < (viewdistance + patch_size) * (viewdistance + patch_size)) { + opacity = 1; + if (distance[i][j] > viewdistsquared * fadestart - viewdistsquared) + opacity = 0; + if (opacity == 1 && i != subdivision) + if (distance[i + 1][j] > viewdistsquared * fadestart - viewdistsquared) + opacity = 0; + if (opacity == 1 && j != subdivision) + if (distance[i][j + 1] > viewdistsquared * fadestart - viewdistsquared) + opacity = 0; + if (opacity == 1 && j != subdivision && i != subdivision) + if (distance[i + 1][j + 1] > viewdistsquared * fadestart - viewdistsquared) + opacity = 0; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (frustum.CubeInFrustum(i * patch_size + patch_size * .5, avgypatch[i][j], j * patch_size + patch_size * .5, heightypatch[i][j] / 2)) { + if (environment == desertenvironment && distance[i][j] > viewdistsquared / 4) + glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, blurness); + else if (environment == desertenvironment) + glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); + if (!layer && textureness[i][j] != allsecond) + drawpatch(i, j, opacity); + if (layer == 1 && textureness[i][j] != allfirst) + drawpatchother(i, j, opacity); + if (layer == 2 && textureness[i][j] != allfirst) + drawpatchotherother(i, j, opacity); + } + glPopMatrix(); + } + } + } + if (environment == desertenvironment) + glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); +} + +void Terrain::drawdecals() +{ + if (decals) { + static int i; + static float distancemult; + static int lasttype; + + static float viewdistsquared; + static bool blend; + + viewdistsquared = viewdistance * viewdistance; + blend = 1; + + lasttype = -1; + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(0); + for (i = 0; i < numdecals; i++) { + if (decaltype[i] == blooddecalfast && decalalivetime[i] < 2) + decalalivetime[i] = 2; + if ((decaltype[i] == shadowdecal || decaltype[i] == shadowdecalpermanent) && decaltype[i] != lasttype) { + shadowtexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + if (decaltype[i] == footprintdecal && decaltype[i] != lasttype) { + footprinttexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + if (decaltype[i] == bodyprintdecal && decaltype[i] != lasttype) { + bodyprinttexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalslow) && decaltype[i] != lasttype) { + bloodtexture.bind(); + if (blend) { + blend = 0; + glAlphaFunc(GL_GREATER, 0.15); + glBlendFunc(GL_ONE, GL_ZERO); + } + } + if ((decaltype[i] == blooddecalfast) && decaltype[i] != lasttype) { + bloodtexture2.bind(); + if (blend) { + blend = 0; + glAlphaFunc(GL_GREATER, 0.15); + glBlendFunc(GL_ONE, GL_ZERO); + } + } + if (decaltype[i] == shadowdecal || decaltype[i] == shadowdecalpermanent) { + distancemult = (viewdistsquared - (distsq(&viewer, &decalposition[i]) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; + if (distancemult >= 1) + glColor4f(1, 1, 1, decalopacity[i]); + if (distancemult < 1) + glColor4f(1, 1, 1, decalopacity[i]*distancemult); + } + if (decaltype[i] == footprintdecal || decaltype[i] == bodyprintdecal) { + distancemult = (viewdistsquared - (distsq(&viewer, &decalposition[i]) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; + if (distancemult >= 1) { + glColor4f(1, 1, 1, decalopacity[i]); + if (decalalivetime[i] > 3) + glColor4f(1, 1, 1, decalopacity[i] * (5 - decalalivetime[i]) / 2); + } + if (distancemult < 1) { + glColor4f(1, 1, 1, decalopacity[i]*distancemult); + if (decalalivetime[i] > 3) + glColor4f(1, 1, 1, decalopacity[i] * (5 - decalalivetime[i]) / 2 * distancemult); + } + } + if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow)) { + distancemult = (viewdistsquared - (distsq(&viewer, &decalposition[i]) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; + if (distancemult >= 1) { + glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]); + if (decalalivetime[i] < 4) + glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]*decalalivetime[i]*.25); + if (decalalivetime[i] > 58) + glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i] * (60 - decalalivetime[i]) / 2); + } + if (distancemult < 1) { + glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]*distancemult); + if (decalalivetime[i] < 4) + glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]*decalalivetime[i]*distancemult * .25); + if (decalalivetime[i] > 58) + glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i] * (60 - decalalivetime[i]) / 2 * distancemult); + } + } + lasttype = decaltype[i]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glBegin(GL_TRIANGLES); + for (int j = 0; j < 3; j++) { + glTexCoord2f(decaltexcoords[i][j][0], decaltexcoords[i][j][1]); + glVertex3f(decalvertex[i][j].x, decalvertex[i][j].y, decalvertex[i][j].z); + } + glEnd(); + glPopMatrix(); + } + for (i = numdecals - 1; i >= 0; i--) { + decalalivetime[i] += multiplier; + if (decaltype[i] == blooddecalslow) + decalalivetime[i] -= multiplier * 2 / 3; + if (decaltype[i] == blooddecalfast) + decalalivetime[i] += multiplier * 4; + if (decaltype[i] == shadowdecal) + DeleteDecal(i); + if (decaltype[i] == footprintdecal && decalalivetime[i] >= 5) + DeleteDecal(i); + if (decaltype[i] == bodyprintdecal && decalalivetime[i] >= 5) + DeleteDecal(i); + if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow) && decalalivetime[i] >= 60) + DeleteDecal(i); + } + glAlphaFunc(GL_GREATER, 0.0001); + } +} + +void Terrain::AddObject(XYZ where, float radius, int id) +{ + bool done; + int i, j; + XYZ points[4]; + if (id >= 0 && id < 10000) + for (i = 0; i < subdivision; i++) { + for (j = 0; j < subdivision; j++) { + if (patchobjectnum[i][j] < 300 - 1) { + done = 0; + points[0].x = (size / subdivision) * i; + points[0].z = (size / subdivision) * j; + points[0].y = heightmap[(int)points[0].x][(int)points[0].z]; + points[1].x = (size / subdivision) * (i + 1); + points[1].z = (size / subdivision) * j; + points[1].y = heightmap[(int)points[1].x][(int)points[1].z]; + points[2].x = (size / subdivision) * (i + 1); + points[2].z = (size / subdivision) * (j + 1); + points[2].y = heightmap[(int)points[2].x][(int)points[2].z]; + points[3].x = (size / subdivision) * i; + points[3].z = (size / subdivision) * (j + 1); + points[3].y = heightmap[(int)points[3].x][(int)points[3].z]; + points[0] *= scale; + points[1] *= scale; + points[2] *= scale; + points[3] *= scale; + if (!done && where.x + radius > points[0].x && where.x - radius < points[2].x && where.z + radius > points[0].z && where.z - radius < points[2].z) { + patchobjects[i][j][patchobjectnum[i][j]] = id; + patchobjectnum[i][j]++; + done = 1; + } + } + } + } +} + +void Terrain::DeleteDecal(int which) +{ + if (decals) { + decaltype[which] = decaltype[numdecals - 1]; + decalposition[which] = decalposition[numdecals - 1]; + for (int i = 0; i < 3; i++) { + decalvertex[which][i] = decalvertex[numdecals - 1][i]; + decaltexcoords[which][i][0] = decaltexcoords[numdecals - 1][i][0]; + decaltexcoords[which][i][1] = decaltexcoords[numdecals - 1][i][1]; + } + decalrotation[which] = decalrotation[numdecals - 1]; + decalalivetime[which] = decalalivetime[numdecals - 1]; + decalopacity[which] = decalopacity[numdecals - 1]; + decalbrightness[which] = decalbrightness[numdecals - 1]; + numdecals--; + } +} + +void Terrain::MakeDecal(int type, XYZ where, float size, float opacity, float rotation) +{ + if (decals) { + if (opacity > 0 && size > 0) { + static int patchx[4]; + static int patchy[4]; + + decaltexcoords[numdecals][0][0] = 1; + decaltexcoords[numdecals][0][1] = 0; + + patchx[0] = (where.x + size) / scale; + patchx[1] = (where.x - size) / scale; + patchx[2] = (where.x - size) / scale; + patchx[3] = (where.x + size) / scale; + + patchy[0] = (where.z - size) / scale; + patchy[1] = (where.z - size) / scale; + patchy[2] = (where.z + size) / scale; + patchy[3] = (where.z + size) / scale; + + if ((patchx[0] != patchx[1] || patchy[0] != patchy[1]) && (patchx[0] != patchx[2] || patchy[0] != patchy[2]) && (patchx[0] != patchx[3] || patchy[0] != patchy[3])) { + MakeDecalLock(type, where, patchx[0], patchy[0], size, opacity, rotation); + } + + if ((patchx[1] != patchx[2] || patchy[1] != patchy[2]) && (patchx[1] != patchx[3] || patchy[1] != patchy[3])) { + MakeDecalLock(type, where, patchx[1], patchy[1], size, opacity, rotation); + } + + if ((patchx[2] != patchx[3] || patchy[2] != patchy[3])) { + MakeDecalLock(type, where, patchx[2], patchy[2], size, opacity, rotation); + } + MakeDecalLock(type, where, patchx[3], patchy[3], size, opacity, rotation); + } + } + //} +} + +void Terrain::MakeDecalLock(int type, XYZ where, int whichx, int whichy, float size, float opacity, float rotation) +{ + if (decals) { + static float placex, placez; + static XYZ rot; + + float decalbright; + + rot = getLighting(where.x, where.z); + decalbrightness[numdecals] = (rot.x + rot.y + rot.z) / 3; + if (decalbrightness[numdecals] < .4) + decalbrightness[numdecals] = .4; + + if (environment == grassyenvironment) { + decalbrightness[numdecals] *= .6; + } + + if (decalbrightness[numdecals] > 1) + decalbrightness[numdecals] = 1; + decalbright = decalbrightness[numdecals]; + + decalposition[numdecals] = where; + decaltype[numdecals] = type; + decalopacity[numdecals] = opacity; + decalrotation[numdecals] = rotation; + decalalivetime[numdecals] = 0; + + placex = (float)whichx * scale + scale; + placez = (float)whichy * scale; + + decaltexcoords[numdecals][0][0] = (placex - where.x) / size / 2 + .5; + decaltexcoords[numdecals][0][1] = (placez - where.z) / size / 2 + .5; + + decalvertex[numdecals][0].x = placex; + decalvertex[numdecals][0].z = placez; + decalvertex[numdecals][0].y = heightmap[whichx + 1][whichy] * scale + .01; + + + placex = (float)whichx * scale + scale; + placez = (float)whichy * scale + scale; + + decaltexcoords[numdecals][1][0] = (placex - where.x) / size / 2 + .5; + decaltexcoords[numdecals][1][1] = (placez - where.z) / size / 2 + .5; + + decalvertex[numdecals][1].x = placex; + decalvertex[numdecals][1].z = placez; + decalvertex[numdecals][1].y = heightmap[whichx + 1][whichy + 1] * scale + .01; + + + placex = (float)whichx * scale; + placez = (float)whichy * scale + scale; + + decaltexcoords[numdecals][2][0] = (placex - where.x) / size / 2 + .5; + decaltexcoords[numdecals][2][1] = (placez - where.z) / size / 2 + .5; + + decalvertex[numdecals][2].x = placex; + decalvertex[numdecals][2].z = placez; + decalvertex[numdecals][2].y = heightmap[whichx][whichy + 1] * scale + .01; + + if (decalrotation[numdecals]) { + for (int i = 0; i < 3; i++) { + rot.y = 0; + rot.x = decaltexcoords[numdecals][i][0] - .5; + rot.z = decaltexcoords[numdecals][i][1] - .5; + rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); + decaltexcoords[numdecals][i][0] = rot.x + .5; + decaltexcoords[numdecals][i][1] = rot.z + .5; + } + } + + if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) + if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) + if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) + if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) + if (numdecals < max_decals - 1) + numdecals++; + + decalbrightness[numdecals] = decalbright; + + decalposition[numdecals] = where; + decaltype[numdecals] = type; + decalopacity[numdecals] = opacity; + decalrotation[numdecals] = rotation; + decalalivetime[numdecals] = 0; + + placex = (float)whichx * scale + scale; + placez = (float)whichy * scale; + + decaltexcoords[numdecals][0][0] = (placex - where.x) / size / 2 + .5; + decaltexcoords[numdecals][0][1] = (placez - where.z) / size / 2 + .5; + + decalvertex[numdecals][0].x = placex; + decalvertex[numdecals][0].z = placez; + decalvertex[numdecals][0].y = heightmap[whichx + 1][whichy] * scale + .01; + + + placex = (float)whichx * scale; + placez = (float)whichy * scale; + + decaltexcoords[numdecals][1][0] = (placex - where.x) / size / 2 + .5; + decaltexcoords[numdecals][1][1] = (placez - where.z) / size / 2 + .5; + + decalvertex[numdecals][1].x = placex; + decalvertex[numdecals][1].z = placez; + decalvertex[numdecals][1].y = heightmap[whichx][whichy] * scale + .01; + + + placex = (float)whichx * scale; + placez = (float)whichy * scale + scale; + + decaltexcoords[numdecals][2][0] = (placex - where.x) / size / 2 + .5; + decaltexcoords[numdecals][2][1] = (placez - where.z) / size / 2 + .5; + + decalvertex[numdecals][2].x = placex; + decalvertex[numdecals][2].z = placez; + decalvertex[numdecals][2].y = heightmap[whichx][whichy + 1] * scale + .01; + + if (decalrotation[numdecals]) { + for (int i = 0; i < 3; i++) { + rot.y = 0; + rot.x = decaltexcoords[numdecals][i][0] - .5; + rot.z = decaltexcoords[numdecals][i][1] - .5; + rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); + decaltexcoords[numdecals][i][0] = rot.x + .5; + decaltexcoords[numdecals][i][1] = rot.z + .5; + } + } + + if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) + if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) + if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) + if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) + if (numdecals < max_decals - 1) + numdecals++; + } +} + +void Terrain::DoShadows() +{ + static int i, j, k, l, todivide; + static float brightness, total; + static XYZ testpoint, testpoint2, terrainpoint, lightloc, col; + lightloc = light.location; + if (!skyboxtexture) { + lightloc.x = 0; + lightloc.z = 0; + } + if (skyboxtexture && tutoriallevel) { + lightloc.x *= .4; + lightloc.z *= .4; + } + int patchx, patchz; + float shadowed; + Normalise(&lightloc); + //Calculate shadows + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + terrainpoint.x = (float)(i) * scale; + terrainpoint.z = (float)(j) * scale; + terrainpoint.y = heightmap[i][j] * scale; + + shadowed = 0; + patchx = (float)(i) * subdivision / size; + patchz = (float)(j) * subdivision / size; + if (patchobjectnum[patchx][patchz]) { + for (k = 0; k < patchobjectnum[patchx][patchz]; k++) { + l = patchobjects[patchx][patchz][k]; + if (objects.type[l] != treetrunktype) { + testpoint = terrainpoint; + testpoint2 = terrainpoint + lightloc * 50 * (1 - shadowed); + if (objects.model[l].LineCheck(&testpoint, &testpoint2, &col, &objects.position[l], &objects.yaw[l]) != -1) { + shadowed = 1 - (findDistance(&terrainpoint, &col) / 50); + } + } + } + if (visibleloading) + Game::LoadingScreen(); + } + brightness = dotproduct(&lightloc, &normals[i][j]); + if (shadowed) + brightness *= 1 - shadowed; + + if (brightness > 1) + brightness = 1; + if (brightness < 0) + brightness = 0; + + colors[i][j][0] = light.color[0] * brightness + light.ambient[0]; + colors[i][j][1] = light.color[1] * brightness + light.ambient[1]; + colors[i][j][2] = light.color[2] * brightness + light.ambient[2]; + + if (colors[i][j][0] > 1) colors[i][j][0] = 1; + if (colors[i][j][1] > 1) colors[i][j][1] = 1; + if (colors[i][j][2] > 1) colors[i][j][2] = 1; + if (colors[i][j][0] < 0) colors[i][j][0] = 0; + if (colors[i][j][1] < 0) colors[i][j][1] = 0; + if (colors[i][j][2] < 0) colors[i][j][2] = 0; + } + } + + if (visibleloading) + Game::LoadingScreen(); + + //Smooth shadows + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + for (k = 0; k < 3; k++) { + total = 0; + todivide = 0; + if (i != 0) { + total += colors[j][i - 1][k]; + todivide++; + } + if (i != size - 1) { + total += colors[j][i + 1][k]; + todivide++; + } + if (j != 0) { + total += colors[j - 1][i][k]; + todivide++; + } + if (j != size - 1) { + total += colors[j + 1][i][k]; + todivide++; + } + if (i != 0 && j != 0) { + total += colors[j - 1][i - 1][k]; + todivide++; + } + if (i != size - 1 && j != 0) { + total += colors[j - 1][i + 1][k]; + todivide++; + } + if (j != size - 1 && i != size - 1) { + total += colors[j + 1][i + 1][k]; + todivide++; + } + if (j != size - 1 && i != 0) { + total += colors[j + 1][i - 1][k]; + todivide++; + } + total += colors[j][i][k]; + todivide++; + + colors[j][i][k] = total / todivide; + } + } + } + + for (i = 0; i < subdivision; i++) { + for (j = 0; j < subdivision; j++) { + UpdateVertexArray(i, j); + } + } +} + +Terrain::Terrain() +{ + size = 0; + + memset(patchobjectnum, 0, sizeof(patchobjectnum)); + memset(patchobjects, 0, sizeof(patchobjects)); + + scale = 1.0f; + type = 0; + memset(heightmap, 0, sizeof(heightmap)); + memset(normals, 0, sizeof(normals)); + memset(facenormals, 0, sizeof(facenormals)); + memset(triangles, 0, sizeof(triangles)); + memset(colors, 0, sizeof(colors)); + memset(opacityother, 0, sizeof(opacityother)); + memset(texoffsetx, 0, sizeof(texoffsetx)); + memset(texoffsety, 0, sizeof(texoffsety)); + memset(numtris, 0, sizeof(numtris)); + memset(textureness, 0, sizeof(textureness)); + + memset(vArray, 0, sizeof(vArray)); + + memset(visible, 0, sizeof(visible)); + memset(avgypatch, 0, sizeof(avgypatch)); + memset(maxypatch, 0, sizeof(maxypatch)); + memset(minypatch, 0, sizeof(minypatch)); + memset(heightypatch, 0, sizeof(heightypatch)); + + patch_elements = 0; + + memset(decaltexcoords, 0, sizeof(decaltexcoords)); + memset(decalvertex, 0, sizeof(decalvertex)); + memset(decaltype, 0, sizeof(decaltype)); + memset(decalopacity, 0, sizeof(decalopacity)); + memset(decalrotation, 0, sizeof(decalrotation)); + memset(decalalivetime, 0, sizeof(decalalivetime)); + memset(decalbrightness, 0, sizeof(decalbrightness)); + memset(decalposition, 0, sizeof(decalposition)); + numdecals = 0; +} +Terrain::~Terrain() +{ + terraintexture.destroy(); + shadowtexture.destroy(); + bodyprinttexture.destroy(); + footprinttexture.destroy(); + bloodtexture.destroy(); + bloodtexture2.destroy(); + breaktexture.destroy(); +} + diff --git a/Source/Environment/Terrain.h b/Source/Environment/Terrain.h new file mode 100644 index 0000000..f9319f7 --- /dev/null +++ b/Source/Environment/Terrain.h @@ -0,0 +1,133 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _TERRAIN_H_ +#define _TERRAIN_H_ + +#include "Environment/Lights.h" +#include "Graphic/gamegl.h" +#include "Graphic/Texture.h" +#include "Math/Frustum.h" +#include "Math/Quaternions.h" +#include "Math/Quaternions.h" +#include "Utils/ImageIO.h" + +#define max_terrain_size 256 +#define curr_terrain_size size +#define subdivision 64 +#define max_patch_elements (max_terrain_size/subdivision)*(max_terrain_size/subdivision)*54 + +#define allfirst 0 +#define mixed 1 +#define allsecond 2 + +#define max_decals 1000 + +#define shadowdecal 0 +#define footprintdecal 1 +#define blooddecal 2 +#define blooddecalfast 3 +#define shadowdecalpermanent 4 +#define breakdecal 5 +#define blooddecalslow 6 +#define bodyprintdecal 7 + +#define snowyenvironment 0 +#define grassyenvironment 1 +#define desertenvironment 2 +// +// Model Structures +// + +class Terrain +{ +public: + Texture bloodtexture; + Texture bloodtexture2; + Texture shadowtexture; + Texture footprinttexture; + Texture bodyprinttexture; + Texture breaktexture; + Texture terraintexture; + short size; + + int patchobjectnum[subdivision][subdivision]; + int patchobjects[subdivision][subdivision][300]; + + float scale; + int type; + float heightmap[max_terrain_size + 1][max_terrain_size + 1]; + XYZ normals[max_terrain_size][max_terrain_size]; + XYZ facenormals[max_terrain_size][max_terrain_size]; + XYZ triangles[(max_terrain_size - 1) * (max_terrain_size - 1) * 2][3]; + float colors[max_terrain_size][max_terrain_size][4]; + float opacityother[max_terrain_size][max_terrain_size]; + float texoffsetx[max_terrain_size][max_terrain_size]; + float texoffsety[max_terrain_size][max_terrain_size]; + int numtris[subdivision][subdivision]; + int textureness[subdivision][subdivision]; + + GLfloat vArray[(max_patch_elements)*subdivision*subdivision]; + + bool visible[subdivision][subdivision]; + float avgypatch[subdivision][subdivision]; + float maxypatch[subdivision][subdivision]; + float minypatch[subdivision][subdivision]; + float heightypatch[subdivision][subdivision]; + + int patch_elements; + + float decaltexcoords[max_decals][3][2]; + XYZ decalvertex[max_decals][3]; + int decaltype[max_decals]; + float decalopacity[max_decals]; + float decalrotation[max_decals]; + float decalalivetime[max_decals]; + float decalbrightness[max_decals]; + XYZ decalposition[max_decals]; + int numdecals; + + void AddObject(XYZ where, float radius, int id); + void DeleteDecal(int which); + void MakeDecal(int type, XYZ where, float size, float opacity, float rotation); + void MakeDecalLock(int type, XYZ where, int whichx, int whichy, float size, float opacity, float rotation); + int lineTerrain(XYZ p1, XYZ p2, XYZ *p); + float getHeight(float pointx, float pointz); + float getOpacity(float pointx, float pointz); + XYZ getLighting(float pointx, float pointz); + XYZ getNormal(float pointx, float pointz); + void UpdateVertexArray(int whichx, int whichy); + void UpdateTransparency(int whichx, int whichy); + void UpdateTransparencyother(int whichx, int whichy); + void UpdateTransparencyotherother(int whichx, int whichy); + bool load(const std::string& fileName); + void CalculateNormals(); + void drawdecals(); + void draw(int layer); + void drawpatch(int whichx, int whichy, float opacity); + void drawpatchother(int whichx, int whichy, float opacity); + void drawpatchotherother(int whichx, int whichy, float opacity); + void DoShadows(); + + Terrain(); + ~Terrain(); +}; + +#endif diff --git a/Source/Frustum.cpp b/Source/Frustum.cpp deleted file mode 100644 index b5f2129..0000000 --- a/Source/Frustum.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Frustum.h" -#include - -#include "gamegl.h" - - -void FRUSTUM:: -GetFrustum() -{ - static float projmatrix[16]; - static float mvmatrix[16]; - static float clip[16]; - - glGetFloatv(GL_PROJECTION_MATRIX, projmatrix); - glGetFloatv(GL_MODELVIEW_MATRIX, mvmatrix); - - // Combine the matrices - clip[0] = mvmatrix[0] * projmatrix[0] + mvmatrix[1] * projmatrix[4] + mvmatrix[2] * projmatrix[8] + mvmatrix[3] * projmatrix[12]; - clip[1] = mvmatrix[0] * projmatrix[1] + mvmatrix[1] * projmatrix[5] + mvmatrix[2] * projmatrix[9] + mvmatrix[3] * projmatrix[13]; - clip[2] = mvmatrix[0] * projmatrix[2] + mvmatrix[1] * projmatrix[6] + mvmatrix[2] * projmatrix[10] + mvmatrix[3] * projmatrix[14]; - clip[3] = mvmatrix[0] * projmatrix[3] + mvmatrix[1] * projmatrix[7] + mvmatrix[2] * projmatrix[11] + mvmatrix[3] * projmatrix[15]; - - clip[4] = mvmatrix[4] * projmatrix[0] + mvmatrix[5] * projmatrix[4] + mvmatrix[6] * projmatrix[8] + mvmatrix[7] * projmatrix[12]; - clip[5] = mvmatrix[4] * projmatrix[1] + mvmatrix[5] * projmatrix[5] + mvmatrix[6] * projmatrix[9] + mvmatrix[7] * projmatrix[13]; - clip[6] = mvmatrix[4] * projmatrix[2] + mvmatrix[5] * projmatrix[6] + mvmatrix[6] * projmatrix[10] + mvmatrix[7] * projmatrix[14]; - clip[7] = mvmatrix[4] * projmatrix[3] + mvmatrix[5] * projmatrix[7] + mvmatrix[6] * projmatrix[11] + mvmatrix[7] * projmatrix[15]; - - clip[8] = mvmatrix[8] * projmatrix[0] + mvmatrix[9] * projmatrix[4] + mvmatrix[10] * projmatrix[8] + mvmatrix[11] * projmatrix[12]; - clip[9] = mvmatrix[8] * projmatrix[1] + mvmatrix[9] * projmatrix[5] + mvmatrix[10] * projmatrix[9] + mvmatrix[11] * projmatrix[13]; - clip[10] = mvmatrix[8] * projmatrix[2] + mvmatrix[9] * projmatrix[6] + mvmatrix[10] * projmatrix[10] + mvmatrix[11] * projmatrix[14]; - clip[11] = mvmatrix[8] * projmatrix[3] + mvmatrix[9] * projmatrix[7] + mvmatrix[10] * projmatrix[11] + mvmatrix[11] * projmatrix[15]; - - clip[12] = mvmatrix[12] * projmatrix[0] + mvmatrix[13] * projmatrix[4] + mvmatrix[14] * projmatrix[8] + mvmatrix[15] * projmatrix[12]; - clip[13] = mvmatrix[12] * projmatrix[1] + mvmatrix[13] * projmatrix[5] + mvmatrix[14] * projmatrix[9] + mvmatrix[15] * projmatrix[13]; - clip[14] = mvmatrix[12] * projmatrix[2] + mvmatrix[13] * projmatrix[6] + mvmatrix[14] * projmatrix[10] + mvmatrix[15] * projmatrix[14]; - clip[15] = mvmatrix[12] * projmatrix[3] + mvmatrix[13] * projmatrix[7] + mvmatrix[14] * projmatrix[11] + mvmatrix[15] * projmatrix[15]; - - // Right plane - frustum[0][0] = clip[3] - clip[0]; - frustum[0][1] = clip[7] - clip[4]; - frustum[0][2] = clip[11] - clip[8]; - frustum[0][3] = clip[15] - clip[12]; - - // Left plane - frustum[1][0] = clip[3] + clip[0]; - frustum[1][1] = clip[7] + clip[4]; - frustum[1][2] = clip[11] + clip[8]; - frustum[1][3] = clip[15] + clip[12]; - - // Bottom plane - frustum[2][0] = clip[3] + clip[1]; - frustum[2][1] = clip[7] + clip[5]; - frustum[2][2] = clip[11] + clip[9]; - frustum[2][3] = clip[15] + clip[13]; - - // Top plane - frustum[3][0] = clip[3] - clip[1]; - frustum[3][1] = clip[7] - clip[5]; - frustum[3][2] = clip[11] - clip[9]; - frustum[3][3] = clip[15] - clip[13]; - - // Far plane - frustum[4][0] = clip[3] - clip[2]; - frustum[4][1] = clip[7] - clip[6]; - frustum[4][2] = clip[11] - clip[10]; - frustum[4][3] = clip[15] - clip[14]; - - // Near plane - frustum[5][0] = clip[3] + clip[2]; - frustum[5][1] = clip[7] + clip[6]; - frustum[5][2] = clip[11] + clip[10]; - frustum[5][3] = clip[15] + clip[14]; -} - -int FRUSTUM:: -CubeInFrustum(float x, float y, float z, float size) -{ - static int c, c2; - - for (int i = 0; i < 6; i++) { - c = 0; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y - size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y - size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y + size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y + size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y - size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y - size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y + size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y + size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (c == 0) - return 0; - if (c == 8) - c2++; - } - if (c2 >= 6) - return 2; - else - return 1; -} - -int FRUSTUM:: -CubeInFrustum(float x, float y, float z, float size, float height) -{ - static int c, c2; - - for (int i = 0; i < 6; i++) { - c = 0; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y - height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y - height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y + height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y + height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y - height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y - height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x - size) + frustum[i][1] * (y + height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (frustum[i][0] * (x + size) + frustum[i][1] * (y + height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) - c++; - if (c == 0) - return 0; - if (c == 8) - c2++; - } - if (c2 >= 6) - return 2; - else - return 1; -} - -int FRUSTUM:: -SphereInFrustum(float x, float y, float z, float radius) -{ - static int c2; - - for (int i = 0; i < 6; i++) { - if (frustum[i][0] * x + frustum[i][1] * y + frustum[i][2] * z + frustum[i][3] > -1 * radius) - c2++; - else - return 0; - } - if (c2 >= 6) - return 2; - else - return 1; -} diff --git a/Source/Frustum.h b/Source/Frustum.h deleted file mode 100644 index 918a047..0000000 --- a/Source/Frustum.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef FRUSTUM_H -#define FRUSTUM_H - -class FRUSTUM -{ -public: - float frustum[6][4]; - void GetFrustum(); - int CubeInFrustum(float, float, float, float); - int CubeInFrustum(float, float, float, float, float); - int SphereInFrustum(float, float, float, float); -}; - -#endif diff --git a/Source/Game.cpp b/Source/Game.cpp index d05bb44..e740aa6 100644 --- a/Source/Game.cpp +++ b/Source/Game.cpp @@ -18,10 +18,11 @@ You should have received a copy of the GNU General Public License along with Lugaru. If not, see . */ -#include "Game.h" -#include "openal_wrapper.h" #include "SDL_thread.h" -#include "Dialog.h" +#include "Game.h" +#include "Audio/openal_wrapper.h" +#include "Level/Dialog.h" + extern int mainmenu; diff --git a/Source/Game.h b/Source/Game.h index 2dbc9af..176d245 100644 --- a/Source/Game.h +++ b/Source/Game.h @@ -23,26 +23,26 @@ along with Lugaru. If not, see . #include "SDL.h" -#include "ImageIO.h" - -#include "Terrain.h" -#include "Skybox.h" #include "Animation/Skeleton.h" -#include "Models.h" -#include "Lights.h" -#include "Person.h" -#include "Sprite.h" -#include "Text.h" -#include "Objects.h" -#include "Weapons.h" -#include "binio.h" +#include "Audio/Sounds.h" +#include "Environment/Lights.h" +#include "Environment/Skybox.h" +#include "Environment/Terrain.h" +#include "Graphic/gamegl.h" +#include "Graphic/Models.h" +#include "Graphic/Sprite.h" +#include "Graphic/Stereo.h" +#include "Graphic/Text.h" +#include "Graphic/Texture.h" +#include "Objects/Objects.h" +#include "Objects/Person.h" +#include "Objects/Weapons.h" +#include "Thirdparty/optionparser.h" +#include "User/Account.h" +#include "Utils/binio.h" +#include "Utils/ImageIO.h" + #include -#include "gamegl.h" -#include "Stereo.h" -#include "Account.h" -#include "Sounds.h" -#include "Texture.h" -#include "optionparser.h" #define NB_CAMPAIGN_MENU_ITEM 7 diff --git a/Source/GameDraw.cpp b/Source/GameDraw.cpp index a346f23..d46b9b6 100644 --- a/Source/GameDraw.cpp +++ b/Source/GameDraw.cpp @@ -19,12 +19,12 @@ along with Lugaru. If not, see . */ #include "Game.h" -#include "openal_wrapper.h" -#include "Input.h" -#include "Awards.h" -#include "Menu.h" -#include "Dialog.h" -#include "Hotspot.h" +#include "Audio/openal_wrapper.h" +#include "Level/Awards.h" +#include "Level/Dialog.h" +#include "Level/Hotspot.h" +#include "Menu/Menu.h" +#include "Utils/Input.h" extern XYZ viewer; extern int environment; diff --git a/Source/GameInitDispose.cpp b/Source/GameInitDispose.cpp index c070d29..036f80f 100644 --- a/Source/GameInitDispose.cpp +++ b/Source/GameInitDispose.cpp @@ -19,11 +19,11 @@ along with Lugaru. If not, see . */ #include "Game.h" -#include "openal_wrapper.h" #include "Animation/Animation.h" -#include "Texture.h" +#include "Audio/openal_wrapper.h" +#include "Graphic/Texture.h" +#include "Menu/Menu.h" #include "Utils/Folders.h" -#include "Menu.h" extern float screenwidth, screenheight; extern float viewdistance; diff --git a/Source/GameTick.cpp b/Source/GameTick.cpp index 1b865f1..37a29f5 100644 --- a/Source/GameTick.cpp +++ b/Source/GameTick.cpp @@ -35,17 +35,17 @@ along with Lugaru. If not, see . #include #include #include "Game.h" -#include "openal_wrapper.h" -#include "Settings.h" -#include "Input.h" #include "Animation/Animation.h" -#include "Awards.h" -#include "Menu.h" -#include "ConsoleCmds.h" -#include "Dialog.h" +#include "Audio/openal_wrapper.h" +#include "Devtools/ConsoleCmds.h" +#include "Level/Awards.h" +#include "Level/Campaign.h" +#include "Level/Dialog.h" +#include "Level/Hotspot.h" +#include "Menu/Menu.h" +#include "User/Settings.h" #include "Utils/Folders.h" -#include "Hotspot.h" -#include "Campaign.h" +#include "Utils/Input.h" #include #include diff --git a/Source/Globals.cpp b/Source/Globals.cpp index d8d430d..66c6c9b 100644 --- a/Source/Globals.cpp +++ b/Source/Globals.cpp @@ -20,21 +20,11 @@ along with Lugaru. If not, see . #include "SDL.h" -#include "gamegl.h" -#include "Quaternions.h" -#include "Lights.h" -#include "Animation/Animation.h" -#include "Animation/Skeleton.h" -#include "Terrain.h" -#include "Sprite.h" -#include "Frustum.h" -#include "Objects.h" -#include "Weapons.h" -#include "Person.h" -#include "ImageIO.h" -#include "openal_wrapper.h" -#include "Stereo.h" +#include +#include "Graphic/Stereo.h" +#include "Math/Quaternions.h" +#include "Objects/Weapons.h" bool visibleloading = 0; diff --git a/Source/Graphic/Models.cpp b/Source/Graphic/Models.cpp new file mode 100644 index 0000000..a9833d0 --- /dev/null +++ b/Source/Graphic/Models.cpp @@ -0,0 +1,1487 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" +#include "Graphic/Models.h" +#include "Utils/Folders.h" + +extern float multiplier; +extern float viewdistance; +extern XYZ viewer; +extern float fadestart; +extern float texdetail; +extern bool decals; + +extern bool visibleloading; + +int Model::LineCheck(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate) +{ + static int j; + static float distance; + static float olddistance; + static int intersecting; + static int firstintersecting; + static XYZ point; + + *p1 = *p1 - *move; + *p2 = *p2 - *move; + if (*rotate) + *p1 = DoRotation(*p1, 0, -*rotate, 0); + if (*rotate) + *p2 = DoRotation(*p2, 0, -*rotate, 0); + if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) + return -1; + firstintersecting = -1; + + for (j = 0; j < TriangleNum; j++) { + intersecting = LineFacetd(p1, p2, &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], &facenormals[j], &point); + distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); + if ((distance < olddistance || firstintersecting == -1) && intersecting) { + olddistance = distance; + firstintersecting = j; + *p = point; + } + } + + if (*rotate) + *p = DoRotation(*p, 0, *rotate, 0); + *p = *p + *move; + return firstintersecting; +} + +int Model::LineCheckPossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate) +{ + static int j; + static float distance; + static float olddistance; + static int intersecting; + static int firstintersecting; + static XYZ point; + + *p1 = *p1 - *move; + *p2 = *p2 - *move; + if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) + return -1; + firstintersecting = -1; + if (*rotate) + *p1 = DoRotation(*p1, 0, -*rotate, 0); + if (*rotate) + *p2 = DoRotation(*p2, 0, -*rotate, 0); + + if (numpossible > 0 && numpossible < TriangleNum) + for (j = 0; j < numpossible; j++) { + if (possible[j] >= 0 && possible[j] < TriangleNum) { + intersecting = LineFacetd(p1, p2, &vertex[Triangles[possible[j]].vertex[0]], &vertex[Triangles[possible[j]].vertex[1]], &vertex[Triangles[possible[j]].vertex[2]], &facenormals[possible[j]], &point); + distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); + if ((distance < olddistance || firstintersecting == -1) && intersecting) { + olddistance = distance; + firstintersecting = possible[j]; + *p = point; + } + } + } + + if (*rotate) + *p = DoRotation(*p, 0, *rotate, 0); + *p = *p + *move; + return firstintersecting; +} + +int Model::LineCheckSlidePossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate) +{ + static int j; + static float distance; + static float olddistance; + static int intersecting; + static int firstintersecting; + static XYZ point; + + *p1 = *p1 - *move; + *p2 = *p2 - *move; + if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) + return -1; + firstintersecting = -1; + if (*rotate) + *p1 = DoRotation(*p1, 0, -*rotate, 0); + if (*rotate) + *p2 = DoRotation(*p2, 0, -*rotate, 0); + + if (numpossible) + for (j = 0; j < numpossible; j++) { + if (possible[j] >= 0 && possible[j] < TriangleNum) { + intersecting = LineFacetd(p1, p2, &vertex[Triangles[possible[j]].vertex[0]], &vertex[Triangles[possible[j]].vertex[1]], &vertex[Triangles[possible[j]].vertex[2]], &facenormals[possible[j]], &point); + distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); + if ((distance < olddistance || firstintersecting == -1) && intersecting) { + olddistance = distance; + firstintersecting = possible[j]; + } + } + } + + if (firstintersecting > 0) { + distance = abs((facenormals[firstintersecting].x * p2->x) + (facenormals[firstintersecting].y * p2->y) + (facenormals[firstintersecting].z * p2->z) - ((facenormals[firstintersecting].x * vertex[Triangles[firstintersecting].vertex[0]].x) + (facenormals[firstintersecting].y * vertex[Triangles[firstintersecting].vertex[0]].y) + (facenormals[firstintersecting].z * vertex[Triangles[firstintersecting].vertex[0]].z))); + *p2 -= facenormals[firstintersecting] * distance; + } + + if (*rotate) + *p2 = DoRotation(*p2, 0, *rotate, 0); + *p2 = *p2 + *move; + return firstintersecting; +} + +int Model::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate) +{ + static int i, j; + static float distance; + static float olddistance; + static int intersecting; + static int firstintersecting; + static XYZ point; + static XYZ oldp1; + + firstintersecting = -1; + + oldp1 = *p1; + *p1 = *p1 - *move; + if (*rotate) + *p1 = DoRotation(*p1, 0, -*rotate, 0); + if (distsq(p1, &boundingspherecenter) > radius * radius + boundingsphereradius * boundingsphereradius) + return -1; + + for (i = 0; i < 4; i++) { + for (j = 0; j < TriangleNum; j++) { + intersecting = 0; + distance = abs((facenormals[j].x * p1->x) + (facenormals[j].y * p1->y) + (facenormals[j].z * p1->z) - ((facenormals[j].x * vertex[Triangles[j].vertex[0]].x) + (facenormals[j].y * vertex[Triangles[j].vertex[0]].y) + (facenormals[j].z * vertex[Triangles[j].vertex[0]].z))); + if (distance < radius) { + point = *p1 - facenormals[j] * distance; + if (PointInTriangle( &point, facenormals[j], &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]])) + intersecting = 1; + if (!intersecting) + intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], p1, &radius); + if (!intersecting) + intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], p1, &radius); + if (!intersecting) + intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[2]], p1, &radius); + if (intersecting) { + *p1 += facenormals[j] * (distance - radius); + } + } + if ((distance < olddistance || firstintersecting == -1) && intersecting) { + olddistance = distance; + firstintersecting = j; + *p = point; + } + } + } + if (*rotate) + *p = DoRotation(*p, 0, *rotate, 0); + *p = *p + *move; + if (*rotate) + *p1 = DoRotation(*p1, 0, *rotate, 0); + *p1 += *move; + return firstintersecting; +} + +int Model::SphereCheckPossible(XYZ *p1, float radius, XYZ *move, float *rotate) +{ + static int j; + static float distance; + static float olddistance; + static int intersecting; + static int firstintersecting; + static XYZ point; + static XYZ oldp1; + + firstintersecting = -1; + + oldp1 = *p1; + *p1 = *p1 - *move; + + numpossible = 0; + + if (*rotate) + *p1 = DoRotation(*p1, 0, -*rotate, 0); + if (distsq(p1, &boundingspherecenter) > radius * radius + boundingsphereradius * boundingsphereradius) { + *p1 = oldp1; + return -1; + } + + for (j = 0; j < TriangleNum; j++) { + intersecting = 0; + distance = abs((facenormals[j].x * p1->x) + (facenormals[j].y * p1->y) + (facenormals[j].z * p1->z) - ((facenormals[j].x * vertex[Triangles[j].vertex[0]].x) + (facenormals[j].y * vertex[Triangles[j].vertex[0]].y) + (facenormals[j].z * vertex[Triangles[j].vertex[0]].z))); + if (distance < radius) { + point = *p1 - facenormals[j] * distance; + if (PointInTriangle( &point, facenormals[j], &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]])) + intersecting = 1; + if (!intersecting) + intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], p1, &radius); + if (!intersecting) + intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], p1, &radius); + if (!intersecting) + intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[2]], p1, &radius); + if (intersecting) { + possible[numpossible] = j; + numpossible++; + } + } + if ((distance < olddistance || firstintersecting == -1) && intersecting) { + olddistance = distance; + firstintersecting = j; + } + } + if (*rotate) + *p1 = DoRotation(*p1, 0, *rotate, 0); + *p1 += *move; + return firstintersecting; +} + + +void Model::UpdateVertexArray() +{ + if (type != normaltype && type != decalstype) + return; + static int i; + static int j; + if (!flat) + for (i = 0; i < TriangleNum; i++) { + j = i * 24; + vArray[j + 0] = Triangles[i].gx[0]; + vArray[j + 1] = Triangles[i].gy[0]; + vArray[j + 2] = normals[Triangles[i].vertex[0]].x; + vArray[j + 3] = normals[Triangles[i].vertex[0]].y; + vArray[j + 4] = normals[Triangles[i].vertex[0]].z; + vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; + vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; + vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; + + vArray[j + 8] = Triangles[i].gx[1]; + vArray[j + 9] = Triangles[i].gy[1]; + vArray[j + 10] = normals[Triangles[i].vertex[1]].x; + vArray[j + 11] = normals[Triangles[i].vertex[1]].y; + vArray[j + 12] = normals[Triangles[i].vertex[1]].z; + vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; + vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; + vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; + + vArray[j + 16] = Triangles[i].gx[2]; + vArray[j + 17] = Triangles[i].gy[2]; + vArray[j + 18] = normals[Triangles[i].vertex[2]].x; + vArray[j + 19] = normals[Triangles[i].vertex[2]].y; + vArray[j + 20] = normals[Triangles[i].vertex[2]].z; + vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; + vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; + vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; + } + if (flat) + for (i = 0; i < TriangleNum; i++) { + j = i * 24; + vArray[j + 0] = Triangles[i].gx[0]; + vArray[j + 1] = Triangles[i].gy[0]; + vArray[j + 2] = facenormals[i].x * -1; + vArray[j + 3] = facenormals[i].y * -1; + vArray[j + 4] = facenormals[i].z * -1; + vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; + vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; + vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; + + vArray[j + 8] = Triangles[i].gx[1]; + vArray[j + 9] = Triangles[i].gy[1]; + vArray[j + 10] = facenormals[i].x * -1; + vArray[j + 11] = facenormals[i].y * -1; + vArray[j + 12] = facenormals[i].z * -1; + vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; + vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; + vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; + + vArray[j + 16] = Triangles[i].gx[2]; + vArray[j + 17] = Triangles[i].gy[2]; + vArray[j + 18] = facenormals[i].x * -1; + vArray[j + 19] = facenormals[i].y * -1; + vArray[j + 20] = facenormals[i].z * -1; + vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; + vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; + vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; + + } +} + +void Model::UpdateVertexArrayNoTex() +{ + if (type != normaltype && type != decalstype) + return; + static int i; + static int j; + if (!flat) + for (i = 0; i < TriangleNum; i++) { + j = i * 24; + vArray[j + 2] = normals[Triangles[i].vertex[0]].x; + vArray[j + 3] = normals[Triangles[i].vertex[0]].y; + vArray[j + 4] = normals[Triangles[i].vertex[0]].z; + vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; + vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; + vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; + + vArray[j + 10] = normals[Triangles[i].vertex[1]].x; + vArray[j + 11] = normals[Triangles[i].vertex[1]].y; + vArray[j + 12] = normals[Triangles[i].vertex[1]].z; + vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; + vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; + vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; + + vArray[j + 18] = normals[Triangles[i].vertex[2]].x; + vArray[j + 19] = normals[Triangles[i].vertex[2]].y; + vArray[j + 20] = normals[Triangles[i].vertex[2]].z; + vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; + vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; + vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; + } + if (flat) + for (i = 0; i < TriangleNum; i++) { + j = i * 24; + vArray[j + 2] = facenormals[i].x * -1; + vArray[j + 3] = facenormals[i].y * -1; + vArray[j + 4] = facenormals[i].z * -1; + vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; + vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; + vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; + + vArray[j + 10] = facenormals[i].x * -1; + vArray[j + 11] = facenormals[i].y * -1; + vArray[j + 12] = facenormals[i].z * -1; + vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; + vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; + vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; + + vArray[j + 18] = facenormals[i].x * -1; + vArray[j + 19] = facenormals[i].y * -1; + vArray[j + 20] = facenormals[i].z * -1; + vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; + vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; + vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; + } +} + +void Model::UpdateVertexArrayNoTexNoNorm() +{ + if (type != normaltype && type != decalstype) + return; + static int i; + static int j; + for (i = 0; i < TriangleNum; i++) { + j = i * 24; + vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; + vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; + vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; + + vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; + vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; + vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; + + vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; + vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; + vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; + } +} + +bool Model::loadnotex(const std::string& filename ) +{ + FILE *tfile; + long i; + + type = notextype; + color = 0; + + tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); + + // read model settings + + fseek(tfile, 0, SEEK_SET); + funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); + + // read the model data + deallocate(); + + numpossible = 0; + + owner = (int*)malloc(sizeof(int) * vertexNum); + possible = (int*)malloc(sizeof(int) * TriangleNum); + vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); + Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); + vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); + + for (i = 0; i < vertexNum; i++) { + funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); + } + + for (i = 0; i < TriangleNum; i++) { + short vertex[ 6]; + funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); + Triangles[i].vertex[ 0] = vertex[ 0]; + Triangles[i].vertex[ 1] = vertex[ 2]; + Triangles[i].vertex[ 2] = vertex[ 4]; + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); + } + + fclose(tfile); + + UpdateVertexArray(); + + for (i = 0; i < vertexNum; i++) { + owner[i] = -1; + } + + static int j; + boundingsphereradius = 0; + for (i = 0; i < vertexNum; i++) { + for (j = 0; j < vertexNum; j++) { + if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { + boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; + boundingspherecenter = (vertex[i] + vertex[j]) / 2; + } + } + } + boundingsphereradius = fast_sqrt(boundingsphereradius); + + return true; +} + + +bool Model::load(const std::string& filename, bool texture ) +{ + FILE *tfile; + long i; + + LOGFUNC; + + LOG(std::string("Loading model...") + filename); + + if (visibleloading) + Game::LoadingScreen(); + + type = normaltype; + color = 0; + + tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); + + // read model settings + + fseek(tfile, 0, SEEK_SET); + funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); + + // read the model data + deallocate(); + + numpossible = 0; + + owner = (int*)malloc(sizeof(int) * vertexNum); + possible = (int*)malloc(sizeof(int) * TriangleNum); + vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); + normals = (XYZ*)malloc(sizeof(XYZ) * vertexNum); + facenormals = (XYZ*)malloc(sizeof(XYZ) * TriangleNum); + Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); + vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); + + for (i = 0; i < vertexNum; i++) { + funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); + } + + for (i = 0; i < TriangleNum; i++) { + short vertex[ 6]; + funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); + Triangles[i].vertex[ 0] = vertex[ 0]; + Triangles[i].vertex[ 1] = vertex[ 2]; + Triangles[i].vertex[ 2] = vertex[ 4]; + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); + } + + modelTexture.xsz = 0; + + fclose(tfile); + + UpdateVertexArray(); + + for (i = 0; i < vertexNum; i++) { + owner[i] = -1; + } + + static int j; + boundingsphereradius = 0; + for (i = 0; i < vertexNum; i++) { + for (j = 0; j < vertexNum; j++) { + if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { + boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; + boundingspherecenter = (vertex[i] + vertex[j]) / 2; + } + } + } + boundingsphereradius = fast_sqrt(boundingsphereradius); + + return true; +} + +bool Model::loaddecal(const std::string& filename, bool texture ) +{ + FILE *tfile; + long i, j; + + LOGFUNC; + + LOG(std::string("Loading decal...") + Folders::getResourcePath(filename)); + + type = decalstype; + numdecals = 0; + color = 0; + + tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); + + // read model settings + + fseek(tfile, 0, SEEK_SET); + funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); + + // read the model data + + deallocate(); + + numpossible = 0; + + owner = (int*)malloc(sizeof(int) * vertexNum); + possible = (int*)malloc(sizeof(int) * TriangleNum); + vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); + normals = (XYZ*)malloc(sizeof(XYZ) * vertexNum); + facenormals = (XYZ*)malloc(sizeof(XYZ) * TriangleNum); + Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); + vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); + + + for (i = 0; i < vertexNum; i++) { + funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); + } + + for (i = 0; i < TriangleNum; i++) { + short vertex[ 6]; + funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); + Triangles[i].vertex[ 0] = vertex[ 0]; + Triangles[i].vertex[ 1] = vertex[ 2]; + Triangles[i].vertex[ 2] = vertex[ 4]; + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); + } + + + modelTexture.xsz = 0; + + fclose(tfile); + + UpdateVertexArray(); + + for (i = 0; i < vertexNum; i++) { + owner[i] = -1; + } + + boundingsphereradius = 0; + for (i = 0; i < vertexNum; i++) { + for (j = 0; j < vertexNum; j++) { + if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { + boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; + boundingspherecenter = (vertex[i] + vertex[j]) / 2; + } + } + } + boundingsphereradius = fast_sqrt(boundingsphereradius); + + //allow decals + if (!decaltexcoords) { + decaltexcoords = (float***)malloc(sizeof(float**)*max_model_decals); + for (i = 0; i < max_model_decals; i++) { + decaltexcoords[i] = (float**)malloc(sizeof(float*) * 3); + for (j = 0; j < 3; j++) { + decaltexcoords[i][j] = (float*)malloc(sizeof(float) * 2); + } + } + decalvertex = (XYZ**)malloc(sizeof(XYZ*)*max_model_decals); + for (i = 0; i < max_model_decals; i++) { + decalvertex[i] = (XYZ*)malloc(sizeof(XYZ) * 3); + } + + decaltype = (int*)malloc(sizeof(int) * max_model_decals); + decalopacity = (float*)malloc(sizeof(float) * max_model_decals); + decalrotation = (float*)malloc(sizeof(float) * max_model_decals); + decalalivetime = (float*)malloc(sizeof(float) * max_model_decals); + decalposition = (XYZ*)malloc(sizeof(XYZ) * max_model_decals); + } + + return true; +} + +bool Model::loadraw(const std::string& filename) +{ + FILE *tfile; + long i; + + LOGFUNC; + + LOG(std::string("Loading raw...") + filename); + + type = rawtype; + color = 0; + + tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); + + // read model settings + + fseek(tfile, 0, SEEK_SET); + funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); + + // read the model data + deallocate(); + + numpossible = 0; + + owner = (int*)malloc(sizeof(int) * vertexNum); + possible = (int*)malloc(sizeof(int) * TriangleNum); + vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); + Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); + vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); + + + for (i = 0; i < vertexNum; i++) { + funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); + } + + for (i = 0; i < TriangleNum; i++) { + short vertex[ 6]; + funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); + Triangles[i].vertex[ 0] = vertex[ 0]; + Triangles[i].vertex[ 1] = vertex[ 2]; + Triangles[i].vertex[ 2] = vertex[ 4]; + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); + funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); + } + + + fclose(tfile); + + for (i = 0; i < vertexNum; i++) { + owner[i] = -1; + } + + return true; +} + + +void Model::UniformTexCoords() +{ + static int i; + for (i = 0; i < TriangleNum; i++) { + Triangles[i].gy[0] = vertex[Triangles[i].vertex[0]].y; + Triangles[i].gy[1] = vertex[Triangles[i].vertex[1]].y; + Triangles[i].gy[2] = vertex[Triangles[i].vertex[2]].y; + Triangles[i].gx[0] = vertex[Triangles[i].vertex[0]].x; + Triangles[i].gx[1] = vertex[Triangles[i].vertex[1]].x; + Triangles[i].gx[2] = vertex[Triangles[i].vertex[2]].x; + } + UpdateVertexArray(); +} + + +void Model::FlipTexCoords() +{ + static int i; + for (i = 0; i < TriangleNum; i++) { + Triangles[i].gy[0] = -Triangles[i].gy[0]; + Triangles[i].gy[1] = -Triangles[i].gy[1]; + Triangles[i].gy[2] = -Triangles[i].gy[2]; + } + UpdateVertexArray(); +} + +void Model::ScaleTexCoords(float howmuch) +{ + static int i; + for (i = 0; i < TriangleNum; i++) { + Triangles[i].gx[0] *= howmuch; + Triangles[i].gx[1] *= howmuch; + Triangles[i].gx[2] *= howmuch; + Triangles[i].gy[0] *= howmuch; + Triangles[i].gy[1] *= howmuch; + Triangles[i].gy[2] *= howmuch; + } + UpdateVertexArray(); +} + +void Model::Scale(float xscale, float yscale, float zscale) +{ + static int i; + for (i = 0; i < vertexNum; i++) { + vertex[i].x *= xscale; + vertex[i].y *= yscale; + vertex[i].z *= zscale; + } + UpdateVertexArray(); + + static int j; + + boundingsphereradius = 0; + for (i = 0; i < vertexNum; i++) { + for (j = 0; j < vertexNum; j++) { + if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { + boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; + boundingspherecenter = (vertex[i] + vertex[j]) / 2; + } + } + } + boundingsphereradius = fast_sqrt(boundingsphereradius); +} + +void Model::ScaleNormals(float xscale, float yscale, float zscale) +{ + if (type != normaltype && type != decalstype) + return; + static int i; + for (i = 0; i < vertexNum; i++) { + normals[i].x *= xscale; + normals[i].y *= yscale; + normals[i].z *= zscale; + } + for (i = 0; i < TriangleNum; i++) { + facenormals[i].x *= xscale; + facenormals[i].y *= yscale; + facenormals[i].z *= zscale; + } + UpdateVertexArray(); +} + +void Model::Translate(float xtrans, float ytrans, float ztrans) +{ + static int i; + for (i = 0; i < vertexNum; i++) { + vertex[i].x += xtrans; + vertex[i].y += ytrans; + vertex[i].z += ztrans; + } + UpdateVertexArray(); + + static int j; + boundingsphereradius = 0; + for (i = 0; i < vertexNum; i++) { + for (j = 0; j < vertexNum; j++) { + if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { + boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; + boundingspherecenter = (vertex[i] + vertex[j]) / 2; + } + } + } + boundingsphereradius = fast_sqrt(boundingsphereradius); +} + +void Model::Rotate(float xang, float yang, float zang) +{ + static int i; + for (i = 0; i < vertexNum; i++) { + vertex[i] = DoRotation(vertex[i], xang, yang, zang); + } + UpdateVertexArray(); + + static int j; + boundingsphereradius = 0; + for (i = 0; i < vertexNum; i++) { + for (j = 0; j < vertexNum; j++) { + if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { + boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; + boundingspherecenter = (vertex[i] + vertex[j]) / 2; + } + } + } + boundingsphereradius = fast_sqrt(boundingsphereradius); +} + + +void Model::CalculateNormals(bool facenormalise) +{ + if (visibleloading) + Game::LoadingScreen(); + static int i; + if (type != normaltype && type != decalstype) + return; + + for (i = 0; i < vertexNum; i++) { + normals[i].x = 0; + normals[i].y = 0; + normals[i].z = 0; + } + + for (i = 0; i < TriangleNum; i++) { + CrossProduct(vertex[Triangles[i].vertex[1]] - vertex[Triangles[i].vertex[0]], vertex[Triangles[i].vertex[2]] - vertex[Triangles[i].vertex[0]], &facenormals[i]); + + normals[Triangles[i].vertex[0]].x += facenormals[i].x; + normals[Triangles[i].vertex[0]].y += facenormals[i].y; + normals[Triangles[i].vertex[0]].z += facenormals[i].z; + + normals[Triangles[i].vertex[1]].x += facenormals[i].x; + normals[Triangles[i].vertex[1]].y += facenormals[i].y; + normals[Triangles[i].vertex[1]].z += facenormals[i].z; + + normals[Triangles[i].vertex[2]].x += facenormals[i].x; + normals[Triangles[i].vertex[2]].y += facenormals[i].y; + normals[Triangles[i].vertex[2]].z += facenormals[i].z; + if (facenormalise) + Normalise(&facenormals[i]); + } + for (i = 0; i < vertexNum; i++) { + Normalise(&normals[i]); + normals[i] *= -1; + } + UpdateVertexArrayNoTex(); +} + +void Model::drawimmediate() +{ + textureptr.bind(); + glBegin(GL_TRIANGLES); + for (int i = 0; i < TriangleNum; i++) { + glTexCoord2f(Triangles[i].gx[0], Triangles[i].gy[0]); + if (color) + glColor3f(normals[Triangles[i].vertex[0]].x, normals[Triangles[i].vertex[0]].y, normals[Triangles[i].vertex[0]].z); + if (!color && !flat) + glNormal3f(normals[Triangles[i].vertex[0]].x, normals[Triangles[i].vertex[0]].y, normals[Triangles[i].vertex[0]].z); + if (!color && flat) + glNormal3f(facenormals[i].x, facenormals[i].y, facenormals[i].y); + glVertex3f(vertex[Triangles[i].vertex[0]].x, vertex[Triangles[i].vertex[0]].y, vertex[Triangles[i].vertex[0]].z); + + glTexCoord2f(Triangles[i].gx[1], Triangles[i].gy[1]); + if (color) + glColor3f(normals[Triangles[i].vertex[1]].x, normals[Triangles[i].vertex[1]].y, normals[Triangles[i].vertex[1]].z); + if (!color && !flat) + glNormal3f(normals[Triangles[i].vertex[1]].x, normals[Triangles[i].vertex[1]].y, normals[Triangles[i].vertex[1]].z); + if (!color && flat) + glNormal3f(facenormals[i].x, facenormals[i].y, facenormals[i].y); + glVertex3f(vertex[Triangles[i].vertex[1]].x, vertex[Triangles[i].vertex[1]].y, vertex[Triangles[i].vertex[1]].z); + + glTexCoord2f(Triangles[i].gx[2], Triangles[i].gy[2]); + if (color) + glColor3f(normals[Triangles[i].vertex[2]].x, normals[Triangles[i].vertex[2]].y, normals[Triangles[i].vertex[2]].z); + if (!color && !flat) + glNormal3f(normals[Triangles[i].vertex[2]].x, normals[Triangles[i].vertex[2]].y, normals[Triangles[i].vertex[2]].z); + if (!color && flat) + glNormal3f(facenormals[i].x, facenormals[i].y, facenormals[i].y); + glVertex3f(vertex[Triangles[i].vertex[2]].x, vertex[Triangles[i].vertex[2]].y, vertex[Triangles[i].vertex[2]].z); + } + glEnd(); +} + +void Model::draw() +{ + if (type != normaltype && type != decalstype) + return; + + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + if (!color) + glInterleavedArrays( GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); + if (color) + glInterleavedArrays( GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); + textureptr.bind(); + + glDrawArrays(GL_TRIANGLES, 0, TriangleNum * 3); + + if (!color) + glDisableClientState(GL_NORMAL_ARRAY); + if (color) + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +//TODO: phase out in favor of Texture +void Model::drawdifftex(GLuint texture) +{ + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if (!color) + glInterleavedArrays( GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); + if (color) + glInterleavedArrays( GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); + + glBindTexture(GL_TEXTURE_2D, (unsigned long)texture); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + + +#ifndef WIN32 + glLockArraysEXT( 0, TriangleNum * 3); +#endif + glDrawArrays(GL_TRIANGLES, 0, TriangleNum * 3); +#ifndef WIN32 + glUnlockArraysEXT(); +#endif + + + if (!color) + glDisableClientState(GL_NORMAL_ARRAY); + if (color) + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +void Model::drawdifftex(Texture texture) +{ + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if (!color) + glInterleavedArrays( GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); + if (color) + glInterleavedArrays( GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); + + texture.bind(); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + + +#ifndef WIN32 + glLockArraysEXT( 0, TriangleNum * 3); +#endif + glDrawArrays(GL_TRIANGLES, 0, TriangleNum * 3); +#ifndef WIN32 + glUnlockArraysEXT(); +#endif + + + if (!color) + glDisableClientState(GL_NORMAL_ARRAY); + if (color) + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +void Model::drawdecals(Texture shadowtexture, Texture bloodtexture, Texture bloodtexture2, Texture breaktexture) +{ + if (decals) { + if (type != decalstype) + return; + static int i; + static int lasttype; + static bool blend; + + blend = 1; + + lasttype = -1; + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(0); + if (numdecals > max_model_decals) + numdecals = max_model_decals; + for (i = 0; i < numdecals; i++) { + if (decaltype[i] == blooddecalfast && decalalivetime[i] < 2) + decalalivetime[i] = 2; + + if (decaltype[i] == shadowdecal && decaltype[i] != lasttype) { + shadowtexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + if (decaltype[i] == breakdecal && decaltype[i] != lasttype) { + breaktexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalslow) && decaltype[i] != lasttype) { + bloodtexture.bind(); + if (blend) { + blend = 0; + glAlphaFunc(GL_GREATER, 0.15); + glBlendFunc(GL_ONE, GL_ZERO); + } + } + if ((decaltype[i] == blooddecalfast) && decaltype[i] != lasttype) { + bloodtexture2.bind(); + if (blend) { + blend = 0; + glAlphaFunc(GL_GREATER, 0.15); + glBlendFunc(GL_ONE, GL_ZERO); + } + } + if (decaltype[i] == shadowdecal) { + glColor4f(1, 1, 1, decalopacity[i]); + } + if (decaltype[i] == breakdecal) { + glColor4f(1, 1, 1, decalopacity[i]); + if (decalalivetime[i] > 58) + glColor4f(1, 1, 1, decalopacity[i] * (60 - decalalivetime[i]) / 2); + } + if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow)) { + glColor4f(1, 1, 1, decalopacity[i]); + if (decalalivetime[i] < 4) + glColor4f(1, 1, 1, decalopacity[i]*decalalivetime[i]*.25); + if (decalalivetime[i] > 58) + glColor4f(1, 1, 1, decalopacity[i] * (60 - decalalivetime[i]) / 2); + } + lasttype = decaltype[i]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glBegin(GL_TRIANGLES); + for (int j = 0; j < 3; j++) { + glTexCoord2f(decaltexcoords[i][j][0], decaltexcoords[i][j][1]); + glVertex3f(decalvertex[i][j].x, decalvertex[i][j].y, decalvertex[i][j].z); + } + glEnd(); + glPopMatrix(); + } + for (i = numdecals - 1; i >= 0; i--) { + decalalivetime[i] += multiplier; + if (decaltype[i] == blooddecalslow) + decalalivetime[i] -= multiplier * 2 / 3; + if (decaltype[i] == blooddecalfast) + decalalivetime[i] += multiplier * 4; + if (decaltype[i] == shadowdecal) + DeleteDecal(i); + if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow) && decalalivetime[i] >= 60) + DeleteDecal(i); + } + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } +} + +void Model::DeleteDecal(int which) +{ + if (decals) { + if (type != decalstype) + return; + decaltype[which] = decaltype[numdecals - 1]; + decalposition[which] = decalposition[numdecals - 1]; + for (int i = 0; i < 3; i++) { + decalvertex[which][i] = decalvertex[numdecals - 1][i]; + decaltexcoords[which][i][0] = decaltexcoords[numdecals - 1][i][0]; + decaltexcoords[which][i][1] = decaltexcoords[numdecals - 1][i][1]; + } + decalrotation[which] = decalrotation[numdecals - 1]; + decalalivetime[which] = decalalivetime[numdecals - 1]; + decalopacity[which] = decalopacity[numdecals - 1]; + numdecals--; + } +} + +void Model::MakeDecal(int atype, XYZ *where, float *size, float *opacity, float *rotation) +{ + if (decals) { + if (type != decalstype) + return; + + static float placex, placez; + static XYZ rot; + static float distance; + static int i, j; + + if (*opacity > 0) + if (distsq(where, &boundingspherecenter) < (boundingsphereradius + *size) * (boundingsphereradius + *size)) + for (i = 0; i < TriangleNum; i++) { + if (facenormals[i].y < -.1 && (vertex[Triangles[i].vertex[0]].y < where->y || vertex[Triangles[i].vertex[1]].y < where->y || vertex[Triangles[i].vertex[2]].y < where->y)) { + decalposition[numdecals] = *where; + decaltype[numdecals] = atype; + decalrotation[numdecals] = *rotation; + decalalivetime[numdecals] = 0; + distance = abs(((facenormals[i].x * where->x) + (facenormals[i].y * where->y) + (facenormals[i].z * where->z) - ((facenormals[i].x * vertex[Triangles[i].vertex[0]].x) + (facenormals[i].y * vertex[Triangles[i].vertex[0]].y) + (facenormals[i].z * vertex[Triangles[i].vertex[0]].z))) / facenormals[i].y); + decalopacity[numdecals] = *opacity - distance / 10; + + if (decalopacity[numdecals > 0]) { + placex = vertex[Triangles[i].vertex[0]].x; + placez = vertex[Triangles[i].vertex[0]].z; + + decaltexcoords[numdecals][0][0] = (placex - where->x) / (*size) / 2 + .5; + decaltexcoords[numdecals][0][1] = (placez - where->z) / (*size) / 2 + .5; + + decalvertex[numdecals][0].x = placex; + decalvertex[numdecals][0].z = placez; + decalvertex[numdecals][0].y = vertex[Triangles[i].vertex[0]].y; + + + placex = vertex[Triangles[i].vertex[1]].x; + placez = vertex[Triangles[i].vertex[1]].z; + + decaltexcoords[numdecals][1][0] = (placex - where->x) / (*size) / 2 + .5; + decaltexcoords[numdecals][1][1] = (placez - where->z) / (*size) / 2 + .5; + + decalvertex[numdecals][1].x = placex; + decalvertex[numdecals][1].z = placez; + decalvertex[numdecals][1].y = vertex[Triangles[i].vertex[1]].y; + + + placex = vertex[Triangles[i].vertex[2]].x; + placez = vertex[Triangles[i].vertex[2]].z; + + decaltexcoords[numdecals][2][0] = (placex - where->x) / (*size) / 2 + .5; + decaltexcoords[numdecals][2][1] = (placez - where->z) / (*size) / 2 + .5; + + decalvertex[numdecals][2].x = placex; + decalvertex[numdecals][2].z = placez; + decalvertex[numdecals][2].y = vertex[Triangles[i].vertex[2]].y; + + if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) + if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) + if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) + if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { + if (decalrotation[numdecals]) { + for (j = 0; j < 3; j++) { + rot.y = 0; + rot.x = decaltexcoords[numdecals][j][0] - .5; + rot.z = decaltexcoords[numdecals][j][1] - .5; + rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); + decaltexcoords[numdecals][j][0] = rot.x + .5; + decaltexcoords[numdecals][j][1] = rot.z + .5; + } + } + if (numdecals < max_model_decals - 1) + numdecals++; + } + } + } + } + } +} + +void Model::MakeDecal(int atype, XYZ where, float size, float opacity, float rotation) +{ + if (decals) { + if (type != decalstype) + return; + + static float placex, placez; + static XYZ rot; + static float distance; + static int i, j; + + if (opacity > 0) + if (distsq(&where, &boundingspherecenter) < (boundingsphereradius + size) * (boundingsphereradius + size)) + for (i = 0; i < TriangleNum; i++) { + distance = abs(((facenormals[i].x * where.x) + (facenormals[i].y * where.y) + (facenormals[i].z * where.z) - ((facenormals[i].x * vertex[Triangles[i].vertex[0]].x) + (facenormals[i].y * vertex[Triangles[i].vertex[0]].y) + (facenormals[i].z * vertex[Triangles[i].vertex[0]].z)))); + if (distance < .02 && abs(facenormals[i].y) > abs(facenormals[i].x) && abs(facenormals[i].y) > abs(facenormals[i].z)) { + decalposition[numdecals] = where; + decaltype[numdecals] = atype; + decalrotation[numdecals] = rotation; + decalalivetime[numdecals] = 0; + decalopacity[numdecals] = opacity - distance / 10; + + if (decalopacity[numdecals > 0]) { + placex = vertex[Triangles[i].vertex[0]].x; + placez = vertex[Triangles[i].vertex[0]].z; + + decaltexcoords[numdecals][0][0] = (placex - where.x) / (size) / 2 + .5; + decaltexcoords[numdecals][0][1] = (placez - where.z) / (size) / 2 + .5; + + decalvertex[numdecals][0].x = placex; + decalvertex[numdecals][0].z = placez; + decalvertex[numdecals][0].y = vertex[Triangles[i].vertex[0]].y; + + + placex = vertex[Triangles[i].vertex[1]].x; + placez = vertex[Triangles[i].vertex[1]].z; + + decaltexcoords[numdecals][1][0] = (placex - where.x) / (size) / 2 + .5; + decaltexcoords[numdecals][1][1] = (placez - where.z) / (size) / 2 + .5; + + decalvertex[numdecals][1].x = placex; + decalvertex[numdecals][1].z = placez; + decalvertex[numdecals][1].y = vertex[Triangles[i].vertex[1]].y; + + + placex = vertex[Triangles[i].vertex[2]].x; + placez = vertex[Triangles[i].vertex[2]].z; + + decaltexcoords[numdecals][2][0] = (placex - where.x) / (size) / 2 + .5; + decaltexcoords[numdecals][2][1] = (placez - where.z) / (size) / 2 + .5; + + decalvertex[numdecals][2].x = placex; + decalvertex[numdecals][2].z = placez; + decalvertex[numdecals][2].y = vertex[Triangles[i].vertex[2]].y; + + if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) + if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) + if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) + if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { + if (decalrotation[numdecals]) { + for (j = 0; j < 3; j++) { + rot.y = 0; + rot.x = decaltexcoords[numdecals][j][0] - .5; + rot.z = decaltexcoords[numdecals][j][1] - .5; + rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); + decaltexcoords[numdecals][j][0] = rot.x + .5; + decaltexcoords[numdecals][j][1] = rot.z + .5; + } + } + if (numdecals < max_model_decals - 1) + numdecals++; + } + } + } else if (distance < .02 && abs(facenormals[i].x) > abs(facenormals[i].y) && abs(facenormals[i].x) > abs(facenormals[i].z)) { + decalposition[numdecals] = where; + decaltype[numdecals] = atype; + decalrotation[numdecals] = rotation; + decalalivetime[numdecals] = 0; + decalopacity[numdecals] = opacity - distance / 10; + + if (decalopacity[numdecals > 0]) { + placex = vertex[Triangles[i].vertex[0]].y; + placez = vertex[Triangles[i].vertex[0]].z; + + decaltexcoords[numdecals][0][0] = (placex - where.y) / (size) / 2 + .5; + decaltexcoords[numdecals][0][1] = (placez - where.z) / (size) / 2 + .5; + + decalvertex[numdecals][0].x = vertex[Triangles[i].vertex[0]].x; + decalvertex[numdecals][0].z = placez; + decalvertex[numdecals][0].y = placex; + + + placex = vertex[Triangles[i].vertex[1]].y; + placez = vertex[Triangles[i].vertex[1]].z; + + decaltexcoords[numdecals][1][0] = (placex - where.y) / (size) / 2 + .5; + decaltexcoords[numdecals][1][1] = (placez - where.z) / (size) / 2 + .5; + + decalvertex[numdecals][1].x = vertex[Triangles[i].vertex[1]].x; + decalvertex[numdecals][1].z = placez; + decalvertex[numdecals][1].y = placex; + + + placex = vertex[Triangles[i].vertex[2]].y; + placez = vertex[Triangles[i].vertex[2]].z; + + decaltexcoords[numdecals][2][0] = (placex - where.y) / (size) / 2 + .5; + decaltexcoords[numdecals][2][1] = (placez - where.z) / (size) / 2 + .5; + + decalvertex[numdecals][2].x = vertex[Triangles[i].vertex[2]].x; + decalvertex[numdecals][2].z = placez; + decalvertex[numdecals][2].y = placex; + + if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) + if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) + if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) + if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { + if (decalrotation[numdecals]) { + for (j = 0; j < 3; j++) { + rot.y = 0; + rot.x = decaltexcoords[numdecals][j][0] - .5; + rot.z = decaltexcoords[numdecals][j][1] - .5; + rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); + decaltexcoords[numdecals][j][0] = rot.x + .5; + decaltexcoords[numdecals][j][1] = rot.z + .5; + } + } + if (numdecals < max_model_decals - 1) + numdecals++; + } + } + } else if (distance < .02 && abs(facenormals[i].z) > abs(facenormals[i].y) && abs(facenormals[i].z) > abs(facenormals[i].x)) { + decalposition[numdecals] = where; + decaltype[numdecals] = atype; + decalrotation[numdecals] = rotation; + decalalivetime[numdecals] = 0; + decalopacity[numdecals] = opacity - distance / 10; + + if (decalopacity[numdecals > 0]) { + placex = vertex[Triangles[i].vertex[0]].x; + placez = vertex[Triangles[i].vertex[0]].y; + + decaltexcoords[numdecals][0][0] = (placex - where.x) / (size) / 2 + .5; + decaltexcoords[numdecals][0][1] = (placez - where.y) / (size) / 2 + .5; + + decalvertex[numdecals][0].x = placex; + decalvertex[numdecals][0].z = vertex[Triangles[i].vertex[0]].z; + decalvertex[numdecals][0].y = placez; + + + placex = vertex[Triangles[i].vertex[1]].x; + placez = vertex[Triangles[i].vertex[1]].y; + + decaltexcoords[numdecals][1][0] = (placex - where.x) / (size) / 2 + .5; + decaltexcoords[numdecals][1][1] = (placez - where.y) / (size) / 2 + .5; + + decalvertex[numdecals][1].x = placex; + decalvertex[numdecals][1].z = vertex[Triangles[i].vertex[1]].z; + decalvertex[numdecals][1].y = placez; + + + placex = vertex[Triangles[i].vertex[2]].x; + placez = vertex[Triangles[i].vertex[2]].y; + + decaltexcoords[numdecals][2][0] = (placex - where.x) / (size) / 2 + .5; + decaltexcoords[numdecals][2][1] = (placez - where.y) / (size) / 2 + .5; + + decalvertex[numdecals][2].x = placex; + decalvertex[numdecals][2].z = vertex[Triangles[i].vertex[2]].z; + decalvertex[numdecals][2].y = placez; + + if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) + if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) + if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) + if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { + if (decalrotation[numdecals]) { + for (j = 0; j < 3; j++) { + rot.y = 0; + rot.x = decaltexcoords[numdecals][j][0] - .5; + rot.z = decaltexcoords[numdecals][j][1] - .5; + rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); + decaltexcoords[numdecals][j][0] = rot.x + .5; + decaltexcoords[numdecals][j][1] = rot.z + .5; + } + } + if (numdecals < max_model_decals - 1) + numdecals++; + } + } + } + } + } +} + +Model::~Model() +{ + deallocate(); + textureptr.destroy(); +} + +void Model::deallocate() +{ + int i = 0, j = 0; + + if (owner) + free(owner); + owner = 0; + + if (possible) + free(possible); + possible = 0; + + if (vertex) + free(vertex); + vertex = 0; + + if (normals) + free(normals); + normals = 0; + + if (facenormals) + free(facenormals); + facenormals = 0; + + if (Triangles) + free(Triangles); + Triangles = 0; + + if (vArray) + free(vArray); + vArray = 0; + + + //allow decals + if (decaltexcoords) { + for (i = 0; i < max_model_decals; i++) { + for (j = 0; j < 3; j++) { + free(decaltexcoords[i][j]); + } + free(decaltexcoords[i]); + } + free(decaltexcoords); + } + decaltexcoords = 0; + + + if (decalvertex) { + for (i = 0; i < max_model_decals; i++) { + free(decalvertex[i]); + } + free(decalvertex); + } + decalvertex = 0; + + + free(decaltype); + decaltype = 0; + + free(decalopacity); + decalopacity = 0; + + free(decalrotation); + decalrotation = 0; + + free(decalalivetime); + decalalivetime = 0; + + free(decalposition); + decalposition = 0; + +}; + +Model::Model() +{ + vertexNum = 0, TriangleNum = 0; + hastexture = 0; + + type = 0, oldtype = 0; + + possible = 0; + owner = 0; + vertex = 0; + normals = 0; + facenormals = 0; + Triangles = 0; + vArray = 0; + + memset(&modelTexture, 0, sizeof(modelTexture)); + numpossible = 0; + color = 0; + + boundingspherecenter = 0; + boundingsphereradius = 0; + + decaltexcoords = 0; + decalvertex = 0; + decaltype = 0; + decalopacity = 0; + decalrotation = 0; + decalalivetime = 0; + decalposition = 0; + + numdecals = 0; + + flat = 0; + + type = nothing; +} + diff --git a/Source/Graphic/Models.h b/Source/Graphic/Models.h new file mode 100644 index 0000000..e14ebe6 --- /dev/null +++ b/Source/Graphic/Models.h @@ -0,0 +1,151 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _MODELS_H_ +#define _MODELS_H_ + +/**> Model Loading <**/ +// +// Model Maximums +// +#include "Graphic/gamegl.h" +#include +#include +#include +#include + +#include "Environment/Terrain.h" +#include "Graphic/Texture.h" +#include "Math/Quaternions.h" +#include "Utils/binio.h" + +// +// Textures List +// +typedef struct { + long xsz, ysz; + GLubyte *txt; +} ModelTexture; + +// +// Model Structures +// + +class TexturedTriangle +{ +public: + short vertex[3]; + float gx[3], gy[3]; +}; + +#define max_model_decals 300 + +#define nothing 0 +#define normaltype 4 +#define notextype 1 +#define rawtype 2 +#define decalstype 3 + +class Model +{ +public: + short vertexNum, TriangleNum; + bool hastexture; + + int type, oldtype; + + int* possible; + int* owner; + XYZ* vertex; + XYZ* normals; + XYZ* facenormals; + TexturedTriangle* Triangles; + GLfloat* vArray; + + /*int possible[max_model_vertex]; + int owner[max_textured_triangle]; + XYZ vertex[max_model_vertex]; + XYZ normals[max_model_vertex]; + XYZ facenormals[max_textured_triangle]; + TexturedTriangle Triangles[max_textured_triangle]; + GLfloat vArray[max_textured_triangle*24];*/ + + Texture textureptr; + ModelTexture modelTexture; + int numpossible; + bool color; + + XYZ boundingspherecenter; + float boundingsphereradius; + + float*** decaltexcoords; + XYZ** decalvertex; + int* decaltype; + float* decalopacity; + float* decalrotation; + float* decalalivetime; + XYZ* decalposition; + + /*float decaltexcoords[max_model_decals][3][2]; + XYZ decalvertex[max_model_decals][3]; + int decaltype[max_model_decals]; + float decalopacity[max_model_decals]; + float decalrotation[max_model_decals]; + float decalalivetime[max_model_decals]; + XYZ decalposition[max_model_decals];*/ + + int numdecals; + + bool flat; + + void DeleteDecal(int which); + void MakeDecal(int atype, XYZ *where, float *size, float *opacity, float *rotation); + void MakeDecal(int atype, XYZ where, float size, float opacity, float rotation); + void drawdecals(Texture shadowtexture, Texture bloodtexture, Texture bloodtexture2, Texture breaktexture); + int SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate); + int SphereCheckPossible(XYZ *p1, float radius, XYZ *move, float *rotate); + int LineCheck(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate); + int LineCheckPossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate); + int LineCheckSlidePossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate); + void UpdateVertexArray(); + void UpdateVertexArrayNoTex(); + void UpdateVertexArrayNoTexNoNorm(); + bool loadnotex(const std::string& filename); + bool loadraw(const std::string& filename); + bool load(const std::string& filename, bool texture); + bool loaddecal(const std::string& filename, bool texture); + void Scale(float xscale, float yscale, float zscale); + void FlipTexCoords(); + void UniformTexCoords(); + void ScaleTexCoords(float howmuch); + void ScaleNormals(float xscale, float yscale, float zscale); + void Translate(float xtrans, float ytrans, float ztrans); + void CalculateNormals(bool facenormalise); + void draw(); + void drawdifftex(GLuint texture); + void drawdifftex(Texture texture); + void drawimmediate(); + void Rotate(float xang, float yang, float zang); + ~Model(); + void deallocate(); + Model(); +}; + +#endif diff --git a/Source/Graphic/Sprite.cpp b/Source/Graphic/Sprite.cpp new file mode 100644 index 0000000..4276ce5 --- /dev/null +++ b/Source/Graphic/Sprite.cpp @@ -0,0 +1,480 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" +#include "Graphic/Sprite.h" +#include "Objects/Person.h" + +extern XYZ viewer; +extern float viewdistance; +extern float fadestart; +extern int environment; +extern float texscale; +extern Light light; +extern float multiplier; +extern float gravity; +extern Terrain terrain; +extern Objects objects; +extern int detail; +extern XYZ viewerfacing; +extern int bloodtoggle; +extern XYZ windvector; + +// init statics +Texture Sprite::cloudtexture; +Texture Sprite::cloudimpacttexture; +Texture Sprite::bloodtexture; +Texture Sprite::flametexture; +Texture Sprite::bloodflametexture; +Texture Sprite::smoketexture; +Texture Sprite::snowflaketexture; +Texture Sprite::shinetexture; +Texture Sprite::splintertexture; +Texture Sprite::leaftexture; +Texture Sprite::toothtexture; + +float Sprite::checkdelay = 0; + +vector Sprite::sprites = vector(); + +//Functions +void Sprite::Draw() +{ + int k; + static float M[16]; + static XYZ point; + static float distancemult; + static int lasttype; + static int lastspecial; + static int whichpatchx, whichpatchz; + static XYZ start, end, colpoint; + static bool check; + static bool blend; + static float tempmult; + static XYZ difference; + static float lightcolor[3]; + static float viewdistsquared = viewdistance * viewdistance; + static XYZ tempviewer; + + tempviewer = viewer + viewerfacing * 6; + check = 0; + + lightcolor[0] = light.color[0] * .5 + light.ambient[0]; + lightcolor[1] = light.color[1] * .5 + light.ambient[1]; + lightcolor[2] = light.color[2] * .5 + light.ambient[2]; + + checkdelay -= multiplier * 10; + + if (checkdelay <= 0) { + check = 1; + checkdelay = 1; + } + + lasttype = -1; + lastspecial = -1; + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + blend = 1; + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(0); + glAlphaFunc(GL_GREATER, 0.0001); + for (unsigned i = 0; i < sprites.size(); i++) { + if (lasttype != sprites[i]->type) { + switch (sprites[i]->type) { + case cloudsprite: + cloudtexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + break; + case breathsprite: + case cloudimpactsprite: + cloudimpacttexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + break; + case smoketype: + smoketexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + break; + case bloodsprite: + bloodtexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + break; + case splintersprite : + if (lastspecial != sprites[i]->special) { + if (sprites[i]->special == 0) + splintertexture.bind(); + if (sprites[i]->special == 1) + leaftexture.bind(); + if (sprites[i]->special == 2) + snowflaketexture.bind(); + if (sprites[i]->special == 3) + toothtexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + break; + case snowsprite: + snowflaketexture.bind(); + if (!blend) { + blend = 1; + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + break; + case weaponshinesprite: + shinetexture.bind(); + if (blend) { + blend = 0; + glAlphaFunc(GL_GREATER, 0.001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + break; + case flamesprite: + case weaponflamesprite: + flametexture.bind(); + if (blend || lasttype == bloodflamesprite) { + blend = 0; + glAlphaFunc(GL_GREATER, 0.3); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + break; + case bloodflamesprite: + bloodflametexture.bind(); + if (blend) { + blend = 0; + glAlphaFunc(GL_GREATER, 0.3); + glBlendFunc(GL_ONE, GL_ZERO); + } + break; + } + } + if (sprites[i]->type == snowsprite) + distancemult = (144 - (distsq(&tempviewer, &sprites[i]->position) - (144 * fadestart)) * (1 / (1 - fadestart))) / 144; + else + distancemult = (viewdistsquared - (distsq(&viewer, &sprites[i]->position) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; + if (sprites[i]->type == flamesprite) { + if (distancemult >= 1) + glColor4f(sprites[i]->color[0], sprites[i]->color[1], sprites[i]->color[2], sprites[i]->opacity); + else + glColor4f(sprites[i]->color[0], sprites[i]->color[1], sprites[i]->color[2], sprites[i]->opacity * distancemult); + } else { + if (distancemult >= 1) + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity); + else + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity * distancemult); + } + lasttype = sprites[i]->type; + lastspecial = sprites[i]->special; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(sprites[i]->position.x, sprites[i]->position.y, sprites[i]->position.z); + if ((sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite)) { + difference = viewer - sprites[i]->position; + Normalise(&difference); + glTranslatef(difference.x * sprites[i]->size / 4, difference.y * sprites[i]->size / 4, difference.z * sprites[i]->size / 4); + } + if (sprites[i]->type == snowsprite) { + glRotatef(sprites[i]->rotation * .2, 0, .3, 1); + glTranslatef(1, 0, 0); + } + glGetFloatv(GL_MODELVIEW_MATRIX, M); + point.x = M[12]; + point.y = M[13]; + point.z = M[14]; + glLoadIdentity(); + glTranslatef(point.x, point.y, point.z); + + glRotatef(sprites[i]->rotation, 0, 0, 1); + + if ((sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == bloodflamesprite)) { + if (sprites[i]->alivetime < .14) + glScalef(sprites[i]->alivetime / .14, sprites[i]->alivetime / .14, sprites[i]->alivetime / .14); + } + if (sprites[i]->type == smoketype || sprites[i]->type == snowsprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == breathsprite) { + if (sprites[i]->alivetime < .3) { + if (distancemult >= 1) + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity * sprites[i]->alivetime / .3); + if (distancemult < 1) + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity * distancemult * sprites[i]->alivetime / .3); + } + } + if (sprites[i]->type == splintersprite && sprites[i]->special > 0 && sprites[i]->special != 3) { + if (sprites[i]->alivetime < .2) { + if (distancemult >= 1) + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->alivetime / .2); + else + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], distancemult * sprites[i]->alivetime / .2); + } else { + if (distancemult >= 1) + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); + else + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); + } + } + if (sprites[i]->type == splintersprite && (sprites[i]->special == 0 || sprites[i]->special == 3)) { + if (distancemult >= 1) + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); + else + glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); + } + + glBegin(GL_TRIANGLES); + glTexCoord2f(1.0f, 1.0f); + glVertex3f( .5 * sprites[i]->size, .5 * sprites[i]->size, 0.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-.5 * sprites[i]->size, .5 * sprites[i]->size, 0.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f( .5 * sprites[i]->size, -.5 * sprites[i]->size, 0.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-.5 * sprites[i]->size, -.5 * sprites[i]->size, 0.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f( .5 * sprites[i]->size, -.5 * sprites[i]->size, 0.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-.5 * sprites[i]->size, .5 * sprites[i]->size, 0.0f); + glEnd(); + glPopMatrix(); + } + tempmult = multiplier; + for (int i = sprites.size() - 1; i >= 0; i--) { + multiplier = tempmult; + if (sprites[i]->type != snowsprite) { + sprites[i]->position += sprites[i]->velocity * multiplier; + sprites[i]->velocity += windvector * multiplier; + } + if (sprites[i]->type == flamesprite || sprites[i]->type == smoketype) + sprites[i]->position += windvector * multiplier / 2; + if ((sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == bloodflamesprite)) + multiplier *= sprites[i]->speed * .7; + sprites[i]->alivetime += multiplier; + + if (sprites[i]->type == cloudsprite || sprites[i]->type == cloudimpactsprite) { + sprites[i]->opacity -= multiplier / 2; + sprites[i]->size += multiplier / 2; + sprites[i]->velocity.y += gravity * multiplier * .25; + } + if (sprites[i]->type == breathsprite) { + sprites[i]->opacity -= multiplier / 2; + sprites[i]->size += multiplier / 2; + if (findLength(&sprites[i]->velocity) <= multiplier) { + sprites[i]->velocity = 0; + } else { + XYZ slowdown; + slowdown = sprites[i]->velocity * -1; + Normalise(&slowdown); + slowdown *= multiplier; + sprites[i]->velocity += slowdown; + } + } + if (sprites[i]->type == snowsprite) { + sprites[i]->size -= multiplier / 120; + sprites[i]->rotation += multiplier * 360; + sprites[i]->position.y -= multiplier; + sprites[i]->position += windvector * multiplier; + if (sprites[i]->position.y < tempviewer.y - 6) sprites[i]->position.y += 12; + if (sprites[i]->position.y > tempviewer.y + 6) sprites[i]->position.y -= 12; + if (sprites[i]->position.z < tempviewer.z - 6) sprites[i]->position.z += 12; + if (sprites[i]->position.z > tempviewer.z + 6) sprites[i]->position.z -= 12; + if (sprites[i]->position.x < tempviewer.x - 6) sprites[i]->position.x += 12; + if (sprites[i]->position.x > tempviewer.x + 6) sprites[i]->position.x -= 12; + } + if (sprites[i]->type == bloodsprite) { + bool spritehit = 0; + sprites[i]->rotation += multiplier * 100; + sprites[i]->velocity.y += gravity * multiplier; + if (check) { + XYZ where, startpoint, endpoint, movepoint, footpoint; + float rotationpoint; + int whichtri; + + for (unsigned j = 0; j < Person::players.size(); j++) { + if (!spritehit && Person::players[j]->dead && sprites[i]->alivetime > .1) { + where = sprites[i]->oldposition; + where -= Person::players[j]->coords; + if (!Person::players[j]->skeleton.free) + where = DoRotation(where, 0, -Person::players[j]->yaw, 0); + startpoint = where; + where = sprites[i]->position; + where -= Person::players[j]->coords; + if (!Person::players[j]->skeleton.free) + where = DoRotation(where, 0, -Person::players[j]->yaw, 0); + endpoint = where; + + movepoint = 0; + rotationpoint = 0; + whichtri = Person::players[j]->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &footpoint, &movepoint, &rotationpoint); + if (whichtri != -1) { + spritehit = 1; + Person::players[j]->DoBloodBigWhere(0, 160, sprites[i]->oldposition); + DeleteSprite(i); + } + } + } + + whichpatchx = sprites[i]->position.x / (terrain.size / subdivision * terrain.scale); + whichpatchz = sprites[i]->position.z / (terrain.size / subdivision * terrain.scale); + if (whichpatchx > 0 && whichpatchz > 0 && whichpatchx < subdivision && whichpatchz < subdivision) + if (terrain.patchobjectnum[whichpatchx][whichpatchz]) { + if (!spritehit) + for (int j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { + k = terrain.patchobjects[whichpatchx][whichpatchz][j]; + start = sprites[i]->oldposition; + end = sprites[i]->position; + if (!spritehit) + if (objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]) != -1) { + if (detail == 2 || (detail == 1 && abs(Random() % 4) == 0) || (detail == 0 && abs(Random() % 8) == 0)) + objects.model[k].MakeDecal(blooddecalfast, DoRotation(colpoint - objects.position[k], 0, -objects.yaw[k], 0), sprites[i]->size * 1.6, .5, Random() % 360); + DeleteSprite(i); + spritehit = 1; + } + } + } + if (!spritehit) + if (sprites[i]->position.y < terrain.getHeight(sprites[i]->position.x, sprites[i]->position.z)) { + terrain.MakeDecal(blooddecalfast, sprites[i]->position, sprites[i]->size * 1.6, .6, Random() % 360); + DeleteSprite(i); + } + } + } + if (sprites[i]->type == splintersprite) { + sprites[i]->rotation += sprites[i]->rotatespeed * multiplier; + sprites[i]->opacity -= multiplier / 2; + if (sprites[i]->special == 0 || sprites[i]->special == 2 || sprites[i]->special == 3) + sprites[i]->velocity.y += gravity * multiplier; + if (sprites[i]->special == 1) + sprites[i]->velocity.y += gravity * multiplier * .5; + } + if (sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == bloodflamesprite) { + sprites[i]->rotation += multiplier * sprites[i]->rotatespeed; + sprites[i]->opacity -= multiplier * 5 / 4; + if (sprites[i]->type != weaponshinesprite && sprites[i]->type != bloodflamesprite) + if (sprites[i]->opacity < .5 && sprites[i]->opacity + multiplier * 5 / 4 >= .5 && (abs(Random() % 4) == 0 || (sprites[i]->initialsize > 2 && Random() % 2 == 0))) + MakeSprite(smoketype, sprites[i]->position, sprites[i]->velocity, .9, .9, .6, sprites[i]->size * 1.2, .4); + if (sprites[i]->alivetime > .14 && (sprites[i]->type == flamesprite)) { + sprites[i]->velocity = 0; + sprites[i]->velocity.y = 1.5; + } + } + if (sprites[i]->type == smoketype) { + sprites[i]->opacity -= multiplier / 3 / sprites[i]->initialsize; + sprites[i]->color[0] -= multiplier; + sprites[i]->color[1] -= multiplier; + sprites[i]->color[2] -= multiplier; + if (sprites[i]->color[0] < .6) + sprites[i]->color[0] = .6; + if (sprites[i]->color[1] < .6) + sprites[i]->color[1] = .6; + if (sprites[i]->color[2] < .6) + sprites[i]->color[2] = .6; + sprites[i]->size += multiplier; + sprites[i]->velocity = 0; + sprites[i]->velocity.y = 1.5; + sprites[i]->rotation += multiplier * sprites[i]->rotatespeed / 5; + } + if (sprites[i]->opacity <= 0 || sprites[i]->size <= 0) + DeleteSprite(i); + } + if (check) + for (int i = sprites.size() - 1; i >= 0; i--) { + sprites[i]->oldposition = sprites[i]->position; + } + glAlphaFunc(GL_GREATER, 0.0001); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void Sprite::DeleteSprite(int i) +{ + sprites.erase(sprites.begin() + i); +} + +void Sprite::MakeSprite(int atype, XYZ where, XYZ avelocity, float red, float green, float blue, float asize, float aopacity) +{ + if (sprites.size() < max_sprites - 1) { + sprites.push_back(new Sprite()); + if ((atype != bloodsprite && atype != bloodflamesprite) || bloodtoggle) { + sprites.back()->special = 0; + sprites.back()->type = atype; + sprites.back()->position = where; + sprites.back()->oldposition = where; + sprites.back()->velocity = avelocity; + sprites.back()->alivetime = 0; + sprites.back()->opacity = aopacity; + sprites.back()->size = asize; + sprites.back()->initialsize = asize; + sprites.back()->color[0] = red; + sprites.back()->color[1] = green; + sprites.back()->color[2] = blue; + sprites.back()->rotatespeed = abs(Random() % 720) - 360; + sprites.back()->speed = float(abs(Random() % 100)) / 200 + 1.5; + } + } +} + +Sprite::Sprite() +{ + oldposition = 0; + position = 0; + velocity = 0; + size = 0; + initialsize = 0; + type = 0; + special = 0; + memset(color, 0, sizeof(color)); + opacity = 0; + rotation = 0; + alivetime = 0; + speed = 0; + rotatespeed = 0; +} + +void Sprite::clearTextures() +{ + toothtexture.destroy(); + cloudtexture.destroy(); + cloudimpacttexture.destroy(); + bloodtexture.destroy(); + flametexture.destroy(); + bloodflametexture.destroy(); + smoketexture.destroy(); + snowflaketexture.destroy(); + shinetexture.destroy(); + splintertexture.destroy(); + leaftexture.destroy(); +} + diff --git a/Source/Graphic/Sprite.h b/Source/Graphic/Sprite.h new file mode 100644 index 0000000..bed8439 --- /dev/null +++ b/Source/Graphic/Sprite.h @@ -0,0 +1,111 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _SPRITE_H_ +#define _SPRITE_H_ + +#include "Environment/Lights.h" +#include "Environment/Terrain.h" +#include "Graphic/gamegl.h" +#include "Graphic/Texture.h" +#include "Math/Frustum.h" +#include "Math/Quaternions.h" +#include "Math/Quaternions.h" +#include "Objects/Objects.h" +#include "Utils/ImageIO.h" + +#include + +#define max_sprites 20000 + +enum { + cloudsprite = 0, + bloodsprite, + flamesprite, + smoketype, + weaponflamesprite, + cloudimpactsprite, + snowsprite, + weaponshinesprite, + bloodflamesprite, + breathsprite, + splintersprite, + spritenumber +}; + +class Sprite +{ +private: + XYZ oldposition; + XYZ position; + XYZ velocity; + float size; + float initialsize; + int type; + int special; + float color[3]; + float opacity; + float rotation; + float alivetime; + float speed; + float rotatespeed; + + static float checkdelay; + + static vector sprites; + +public: + static void DeleteSprite(int which); + static void MakeSprite(int atype, XYZ where, XYZ avelocity, float red, float green, float blue, float asize, float aopacity); + static void Draw(); + static void deleteSprites() { + sprites.clear(); + } + static void setLastSpriteSpecial(int s) { + sprites.back()->special = s; + } + static void setLastSpriteSpeed(int s) { + sprites.back()->speed = s; + } + static void setLastSpriteAlivetime(float al) { + sprites.back()->alivetime = al; + } + static void clearTextures(); + + static Texture cloudtexture; + static Texture bloodtexture; + static Texture flametexture; + static Texture smoketexture; + + static Texture cloudimpacttexture; + static Texture snowflaketexture; + static Texture shinetexture; + static Texture bloodflametexture; + + static Texture splintertexture; + + static Texture leaftexture; + static Texture toothtexture; + + Sprite(); + ~Sprite(); +}; + +#endif diff --git a/Source/Graphic/Stereo.cpp b/Source/Graphic/Stereo.cpp new file mode 100644 index 0000000..efb300b --- /dev/null +++ b/Source/Graphic/Stereo.cpp @@ -0,0 +1,152 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" +#include "Graphic/Stereo.h" + + +extern int kContextWidth; +extern int kContextHeight; + +bool CanInitStereo(StereoMode mode) +{ + GLint stencilbits = 0; + + switch (mode) { + case stereoNone: + case stereoAnaglyph: + return true; + break; + case stereoHorizontalInterlaced: + case stereoVerticalInterlaced: + glGetIntegerv(GL_STENCIL_BITS, &stencilbits); + if ( stencilbits < 1 ) { + fprintf(stderr, "Failed to get a stencil buffer, interlaced stereo not available.\n"); + return false; + } else { + fprintf(stderr, "Stencil buffer has %i bits, good.\n", stencilbits); + } + return true; + break; + default: + return false; + } + +} + +void InitStereo(StereoMode mode) +{ + switch (mode) { + default: + case stereoNone: + case stereoAnaglyph: + glDisable(GL_STENCIL_TEST); + return; + case stereoHorizontalInterlaced: + case stereoVerticalInterlaced: + fprintf(stderr, "Screen width is %i, height is %i\n", kContextWidth, kContextHeight); + + // Setup stencil buffer + glDisable( GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + + glEnable( GL_STENCIL_TEST); + glClearStencil(0); + glClear( GL_STENCIL_BUFFER_BIT ); + glStencilFunc(GL_ALWAYS, 0x1, 0x1); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + + // Setup viewport + glViewport(0, 0, kContextWidth, kContextHeight); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho((GLdouble)0, (GLdouble)kContextWidth, (GLdouble)kContextHeight, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + glDisable(GL_LINE_SMOOTH); + + // Add 0.5 to the coordinates, because OpenGL considers a pixel should be + // turned on when a line passes through the center of it. + if ( mode == stereoHorizontalInterlaced ) { + for (int y = 0; y < kContextHeight; y += 2) { + glBegin(GL_LINES); + glVertex3f(0.5, y + 0.5, 0); + glVertex3f(kContextWidth + 0.5, y + 0.5, 0); + glEnd(); + } + } else { + for (int x = 0; x < kContextWidth; x += 2) { + glBegin(GL_LINES); + glVertex3f(x + 0.5, 0.5, 0); + glVertex3f(x + 0.5, kContextHeight + 0.5, 0); + glEnd(); + } + } + + glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glStencilFunc(GL_NOTEQUAL, 0x01, 0x01); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glEnable( GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + } + +} + +const std::string StereoModeName(StereoMode mode) +{ + switch (mode) { + case stereoNone: + return "None"; + break; + case stereoAnaglyph: + return "Anaglyph"; + break; + case stereoHorizontalInterlaced: + return "Horizontal interlacing"; + break; + case stereoVerticalInterlaced: + return "Vertical interlacing"; + break; + case stereoHorizontalSplit: + return "Horizontal split"; + break; + case stereoVerticalSplit: + return "Vertical split"; + break; + case stereoOpenGL: + return "OpenGL"; + break; + default: + return "(error)"; + break; + } +} diff --git a/Source/Graphic/Stereo.h b/Source/Graphic/Stereo.h new file mode 100644 index 0000000..8e70e5e --- /dev/null +++ b/Source/Graphic/Stereo.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef STEREO_H_ +#define STEREO_H_ + +enum StereoMode { + stereoNone, + stereoAnaglyph, /* red/cyan */ + stereoHorizontalInterlaced, /* some 3D monitors */ + stereoVerticalInterlaced, + stereoHorizontalSplit, /* cross-eyed view */ + stereoVerticalSplit, + stereoOpenGL, /* Whatever OpenGL does, if supported */ + stereoCount /* must be last element */ +}; + + +enum StereoSide { + // Code multiplies by StereoSide to calculate camera offsets + stereoLeft = -1, + stereoCenter = 0, + stereoRight = 1 +}; + +extern StereoMode stereomode; +extern StereoMode newstereomode; +extern float stereoseparation; +extern bool stereoreverse; + +bool CanInitStereo(StereoMode mode); +void InitStereo(StereoMode mode); +const std::string StereoModeName(StereoMode mode); + +#endif diff --git a/Source/Graphic/Text.cpp b/Source/Graphic/Text.cpp new file mode 100644 index 0000000..31bbe9f --- /dev/null +++ b/Source/Graphic/Text.cpp @@ -0,0 +1,154 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +/**> HEADER FILES <**/ +#include "Graphic/Text.h" +#include "Game.h" + +void Text::LoadFontTexture(const std::string& fileName) +{ + LOGFUNC; + + LOG(std::string("Loading font texture...") + fileName); + + FontTexture.load(fileName, false); + if (base) { + glDeleteLists(base, 512); + base = 0; + } +} + +void Text::BuildFont() // Build Our Font Display List +{ + float cx; // Holds Our X Character Coord + float cy; // Holds Our Y Character Coord + int loop; + + LOGFUNC; + + if (base) { + glDeleteLists(base, 512); + base = 0; + } + + base = glGenLists(512); // Creating 256 Display Lists + FontTexture.bind(); + for (loop = 0; loop < 512; loop++) { // Loop Through All 256 Lists + if (loop < 256) { + cx = float(loop % 16) / 16.0f; // X Position Of Current Character + cy = float(loop / 16) / 16.0f; // Y Position Of Current Character + } else { + cx = float((loop - 256) % 16) / 16.0f; // X Position Of Current Character + cy = float((loop - 256) / 16) / 16.0f; // Y Position Of Current Character + } + glNewList(base + loop, GL_COMPILE); // Start Building A List + glBegin(GL_QUADS); // Use A Quad For Each Character + glTexCoord2f(cx, 1 - cy - 0.0625f + .001); // Texture Coord (Bottom Left) + glVertex2i(0, 0); // Vertex Coord (Bottom Left) + glTexCoord2f(cx + 0.0625f, 1 - cy - 0.0625f + .001); // Texture Coord (Bottom Right) + glVertex2i(16, 0); // Vertex Coord (Bottom Right) + glTexCoord2f(cx + 0.0625f, 1 - cy - .001); // Texture Coord (Top Right) + glVertex2i(16, 16); // Vertex Coord (Top Right) + glTexCoord2f(cx, 1 - cy - +.001); // Texture Coord (Top Left) + glVertex2i(0, 16); // Vertex Coord (Top Left) + glEnd(); // Done Building Our Quad (Character) + if (loop < 256) + glTranslated(10, 0, 0); // Move To The Right Of The Character + else + glTranslated(8, 0, 0); // Move To The Right Of The Character + glEndList(); // Done Building The Display List + } // Loop Until All 256 Are Built +} + +void Text::glPrint(float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens +{ + glPrint(x, y, string, set, size, width, height, 0, strlen(string)); +} + +void Text::_glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end, int offset) // Where The Printing Happens +{ + if (set > 1) { + set = 1; + } + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + FontTexture.bind(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, width, 0, height, -100, 100); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslated(x, y, 0); + glScalef(size, size, 1); + glListBase(base - 32 + (128 * set) + offset); // Choose The Font Set (0 or 1) + glCallLists(end - start, GL_BYTE, &string[start]); // Write The Text To The Screen + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glEnable(GL_DEPTH_TEST); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); +} + +void Text::glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end) // Where The Printing Happens +{ + _glPrint(x, y, string, set, size, width, height, start, end, 0); +} + +void Text::glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens +{ + glPrintOutline(x, y, string, set, size, width, height, 0, strlen(string)); +} + +void Text::glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height, int start, int end) // Where The Printing Happens +{ + _glPrint(x, y, string, set, size, width, height, start, end, 256); +} +void Text::glPrintOutlined(float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens +{ + glPrintOutlined(1, 1, 1, x, y, string, set, size, width, height); +} + +void Text::glPrintOutlined(float r, float g, float b, float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens +{ + glColor4f(0, 0, 0, 1); + glPrintOutline( x - 2 * size, y - 2 * size, string, set, size * 2.5 / 2, width, height); + glColor4f(r, g, b, 1); + glPrint( x, y, string, set, size, width, height); +} + +Text::Text() +{ + base = 0; +} +Text::~Text() +{ + if (base) { + glDeleteLists(base, 512); + base = 0; + } + FontTexture.destroy(); +} + diff --git a/Source/Graphic/Text.h b/Source/Graphic/Text.h new file mode 100644 index 0000000..b5cc8a1 --- /dev/null +++ b/Source/Graphic/Text.h @@ -0,0 +1,52 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _TEXT_H_ +#define _TEXT_H_ + +/**> HEADER FILES <**/ +#include "Graphic/gamegl.h" +#include "Graphic/Texture.h" +#include "Math/Quaternions.h" +#include "Utils/ImageIO.h" + +class Text +{ +public: + Texture FontTexture; + GLuint base; + + void LoadFontTexture(const std::string& fileName); + void BuildFont(); + void glPrint(float x, float y, const char *string, int set, float size, float width, float height); + void glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height); + void glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end); + void glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height, int start, int end); + void glPrintOutlined(float x, float y, const char *string, int set, float size, float width, float height); + void glPrintOutlined(float r, float g, float b, float x, float y, const char *string, int set, float size, float width, float height); + + Text(); + ~Text(); + +private: + void _glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end, int offset); +}; + +#endif diff --git a/Source/Graphic/Texture.cpp b/Source/Graphic/Texture.cpp new file mode 100644 index 0000000..3f5211a --- /dev/null +++ b/Source/Graphic/Texture.cpp @@ -0,0 +1,137 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Graphic/gamegl.h" +#include "Graphic/Texture.h" +#include "Utils/Folders.h" +#include "Utils/ImageIO.h" + +using namespace std; + +extern bool trilinear; + +vector TextureRes::list; + +void TextureRes::load() +{ + ImageRec texture; + + //load image into 'texture' + if (!load_image(filename.c_str(), texture)) { + cerr << "Texture " << filename << " loading failed" << endl; + return; + } + + skinsize = texture.sizeX; + GLuint type = GL_RGBA; + if (texture.bpp == 24) + type = GL_RGB; + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glDeleteTextures(1, &id); + glGenTextures(1, &id); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (hasMipmap) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (trilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR_MIPMAP_NEAREST)); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + if (isSkin) { + free(data); + const int nb = texture.sizeY * texture.sizeX * (texture.bpp / 8); + data = (GLubyte*)malloc(nb * sizeof(GLubyte)); + datalen = 0; + for (int i = 0; i < nb; i++) + if ((i + 1) % 4 || type == GL_RGB) + data[datalen++] = texture.data[i]; + glTexImage2D(GL_TEXTURE_2D, 0, type, texture.sizeX, texture.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, type, texture.sizeX, texture.sizeY, 0, type, GL_UNSIGNED_BYTE, texture.data); + } +} + +void TextureRes::bind() +{ + glBindTexture(GL_TEXTURE_2D, id); +} + +TextureRes::TextureRes(const string& _filename, bool _hasMipmap): + id(0), filename(_filename), hasMipmap(_hasMipmap), isSkin(false), + skinsize(0), data(NULL), datalen(0) +{ + load(); + list.push_back(this); +} + +TextureRes::TextureRes(const string& _filename, bool _hasMipmap, GLubyte* array, int* skinsizep): + id(0), filename(_filename), hasMipmap(_hasMipmap), isSkin(true), + skinsize(0), data(NULL), datalen(0) +{ + load(); + *skinsizep = skinsize; + for (int i = 0; i < datalen; i++) + array[i] = data[i]; + list.push_back(this); +} + +TextureRes::~TextureRes() +{ + free(data); + glDeleteTextures(1, &id); + for (vector::iterator it = list.begin(); it != list.end(); it++) + if (*it == this) { + list.erase(it); + break; + } +} + +void Texture::load(const string& filename, bool hasMipmap) +{ + destroy(); + tex = new TextureRes(Folders::getResourcePath(filename), hasMipmap); +} + +void Texture::load(const string& filename, bool hasMipmap, GLubyte* array, int* skinsizep) +{ + destroy(); + tex = new TextureRes(Folders::getResourcePath(filename), hasMipmap, array, skinsizep); +} + +void Texture::destroy() +{ + if (tex) { + delete tex; + tex = NULL; + } +} + +void Texture::bind() +{ + if (tex) + tex->bind(); + else + glBindTexture(GL_TEXTURE_2D, 0); +} diff --git a/Source/Graphic/Texture.h b/Source/Graphic/Texture.h new file mode 100644 index 0000000..95547bf --- /dev/null +++ b/Source/Graphic/Texture.h @@ -0,0 +1,63 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _TEXTURE_H_ +#define _TEXTURE_H_ + +#include +#include +#include +using namespace std; + +class TextureRes +{ +private: + static vector list; + + GLuint id; + string filename; + bool hasMipmap; + bool isSkin; + int skinsize; + GLubyte* data; + int datalen; + + void load(); + +public: + TextureRes(const string& filename, bool hasMipmap); + TextureRes(const string& filename, bool hasMipmap, GLubyte* array, int* skinsize); + ~TextureRes(); + void bind(); +}; + +class Texture +{ +private: + TextureRes* tex; +public: + inline Texture(): tex(NULL) {} + void load(const string& filename, bool hasMipmap); + void load(const string& filename, bool hasMipmap, GLubyte* array, int* skinsizep); + void destroy(); + void bind(); +}; + +#endif diff --git a/Source/Graphic/gamegl.h b/Source/Graphic/gamegl.h new file mode 100644 index 0000000..9bee196 --- /dev/null +++ b/Source/Graphic/gamegl.h @@ -0,0 +1,53 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _LUGARU_GL_H_ +#define _LUGARU_GL_H_ + + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #define Polygon WinPolygon + #include + #undef Polygon +#endif + +#define GL_GLEXT_PROTOTYPES 1 +#include "GL/gl.h" +#include "GL/glu.h" +#include "GL/glext.h" +#include "MacCompatibility.h" + +using namespace std; + +/* !!! FIXME: until we replace logger better. --ryan. */ +#define LOGFUNC +void LOG(const std::string &fmt, ...); + +#endif + + diff --git a/Source/Hotspot.cpp b/Source/Hotspot.cpp deleted file mode 100644 index 03bf3a1..0000000 --- a/Source/Hotspot.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Hotspot.h" - -std::vector Hotspot::hotspots; -int Hotspot::current = 0; -int Hotspot::killhotspot = 0; - -Hotspot::Hotspot() : - position(), - type(0), - size(0) -{ -} - -Hotspot::Hotspot(XYZ p, int t, float s) : - position(p), - type(t), - size(s) -{ -} diff --git a/Source/Hotspot.h b/Source/Hotspot.h deleted file mode 100644 index 3139f98..0000000 --- a/Source/Hotspot.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _HOTSPOT_H_ -#define _HOTSPOT_H_ - -#include "Quaternions.h" -#include - -class Hotspot -{ -public: - static std::vector hotspots; - static int current; - static int killhotspot; - - Hotspot(); - Hotspot(XYZ position, int type, float size); - - XYZ position; - int type; - float size; - char text[256] = {0}; -}; - -#endif diff --git a/Source/ImageIO.cpp b/Source/ImageIO.cpp deleted file mode 100644 index 3e6a72e..0000000 --- a/Source/ImageIO.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -/**> HEADER FILES <**/ - -#include -#include -#include -#include - -#include "Game.h" -#include "ImageIO.h" -#include "Utils/Folders.h" - -extern bool visibleloading; - -/* These two are needed for screenshot */ -extern int kContextWidth; -extern int kContextHeight; - -static bool load_png(const char * fname, ImageRec & tex); -static bool load_jpg(const char * fname, ImageRec & tex); -static bool save_screenshot_png(const char * fname); - -ImageRec::ImageRec() -{ - data = ( GLubyte* )malloc( 1024 * 1024 * 4 ); -} - -ImageRec::~ImageRec() -{ - free(data); - data = NULL; -} - -bool load_image(const char *file_name, ImageRec &tex) -{ - if (visibleloading) - Game::LoadingScreen(); - - if ( tex.data == NULL ) - return false; - - const char *ptr = strrchr((char *)file_name, '.'); - if (ptr) { - if (strcasecmp(ptr + 1, "png") == 0) - return load_png(file_name, tex); - else if (strcasecmp(ptr + 1, "jpg") == 0) - return load_jpg(file_name, tex); - } - - STUBBED("Unsupported image type"); - return false; -} - -bool save_screenshot(const char *file_name) -{ - const char *ptr = strrchr((char *)file_name, '.'); - if (ptr) { - if (strcasecmp(ptr + 1, "png") == 0) - return save_screenshot_png((Folders::getScreenshotDir() + '/' + file_name).c_str()); - } - - STUBBED("Unsupported image type"); - return false; -} - -struct my_error_mgr { - struct jpeg_error_mgr pub; /* "public" fields */ - jmp_buf setjmp_buffer; /* for return to caller */ -}; -typedef struct my_error_mgr * my_error_ptr; - -static void my_error_exit(j_common_ptr cinfo) -{ - struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err; - longjmp(err->setjmp_buffer, 1); -} - -/* stolen from public domain example.c code in libjpg distribution. */ -static bool load_jpg(const char *file_name, ImageRec &tex) -{ - struct jpeg_decompress_struct cinfo; - struct my_error_mgr jerr; - JSAMPROW buffer[1]; /* Output row buffer */ - int row_stride; /* physical row width in output buffer */ - FILE *infile = fopen(file_name, "rb"); - - if (infile == NULL) - return false; - - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = my_error_exit; - if (setjmp(jerr.setjmp_buffer)) { - jpeg_destroy_decompress(&cinfo); - fclose(infile); - return false; - } - - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, infile); - (void) jpeg_read_header(&cinfo, TRUE); - - cinfo.out_color_space = JCS_RGB; - cinfo.quantize_colors = 0; - (void) jpeg_calc_output_dimensions(&cinfo); - (void) jpeg_start_decompress(&cinfo); - - row_stride = cinfo.output_width * cinfo.output_components; - tex.sizeX = cinfo.output_width; - tex.sizeY = cinfo.output_height; - tex.bpp = 24; - - while (cinfo.output_scanline < cinfo.output_height) { - buffer[0] = (JSAMPROW)(char *)tex.data + - ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride; - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - } - - (void) jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(infile); - - return true; -} - -/* stolen from public domain example.c code in libpng distribution. */ -static bool load_png(const char *file_name, ImageRec &tex) -{ - bool hasalpha = false; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - png_uint_32 width, height; - int bit_depth, color_type, interlace_type; - bool retval = false; - png_byte **row_pointers = NULL; - FILE *fp = fopen(file_name, "rb"); - - if (fp == NULL) { - cerr << file_name << " not found" << endl; - return false; - } - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL) - goto png_done; - - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) - goto png_done; - - if (setjmp(png_jmpbuf(png_ptr))) - goto png_done; - - png_init_io(png_ptr, fp); - png_read_png(png_ptr, info_ptr, - PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING, - NULL); - png_get_IHDR(png_ptr, info_ptr, &width, &height, - &bit_depth, &color_type, &interlace_type, NULL, NULL); - - if (bit_depth != 8) // transform SHOULD handle this... - goto png_done; - - if (color_type & PNG_COLOR_MASK_PALETTE) // !!! FIXME? - goto png_done; - - if ((color_type & PNG_COLOR_MASK_COLOR) == 0) // !!! FIXME? - goto png_done; - - hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0); - row_pointers = png_get_rows(png_ptr, info_ptr); - if (!row_pointers) - goto png_done; - - if (!hasalpha) { - png_byte *dst = tex.data; - for (int i = height - 1; i >= 0; i--) { - png_byte *src = row_pointers[i]; - for (unsigned j = 0; j < width; j++) { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = 0xFF; - src += 3; - dst += 4; - } - } - } - - else { - png_byte *dst = tex.data; - int pitch = width * 4; - for (int i = height - 1; i >= 0; i--, dst += pitch) - memcpy(dst, row_pointers[i], pitch); - } - - tex.sizeX = width; - tex.sizeY = height; - tex.bpp = 32; - retval = true; - -png_done: - if (!retval) { - cerr << "There was a problem loading " << file_name << endl; - } - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - if (fp) - fclose(fp); - return (retval); -} - -static bool save_screenshot_png(const char *file_name) -{ - FILE *fp = NULL; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - bool retval = false; - - fp = fopen(file_name, "wb"); - if (fp == NULL) - return false; - - png_bytep *row_pointers = new png_bytep[kContextHeight]; - png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3]; - if ((!screenshot) || (!row_pointers)) - goto save_png_done; - - glGetError(); - glReadPixels(0, 0, kContextWidth, kContextHeight, - GL_RGB, GL_UNSIGNED_BYTE, screenshot); - if (glGetError() != GL_NO_ERROR) - goto save_png_done; - - for (int i = 0; i < kContextHeight; i++) - row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3); - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL) - goto save_png_done; - - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) - goto save_png_done; - - if (setjmp(png_jmpbuf(png_ptr))) - goto save_png_done; - - png_init_io(png_ptr, fp); - - if (setjmp(png_jmpbuf(png_ptr))) - goto save_png_done; - - png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight, - 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_write_info(png_ptr, info_ptr); - - if (setjmp(png_jmpbuf(png_ptr))) - goto save_png_done; - - png_write_image(png_ptr, row_pointers); - - if (setjmp(png_jmpbuf(png_ptr))) - goto save_png_done; - - png_write_end(png_ptr, NULL); - retval = true; - -save_png_done: - png_destroy_write_struct(&png_ptr, &info_ptr); - delete[] screenshot; - delete[] row_pointers; - if (fp) - fclose(fp); - if (!retval) - unlink(file_name); - return retval; -} diff --git a/Source/ImageIO.h b/Source/ImageIO.h deleted file mode 100644 index b3d0a49..0000000 --- a/Source/ImageIO.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _IMAGE_IO_H_ -#define _IMAGE_IO_H_ - -#ifdef _MSC_VER -#pragma once -#endif - - -/**> HEADER FILES <**/ -#include -#include -#include -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#define Polygon WinPolygon -#include -#undef Polygon -#include "GL/gl.h" -#else -#include "gamegl.h" -#endif - -/**> DATA STRUCTURES <**/ -class ImageRec { -public: - GLubyte *data; // Image Data (Up To 32 Bits) - GLuint bpp; // Image Color Depth In Bits Per Pixel. - GLuint sizeX; - GLuint sizeY; - ImageRec(); - ~ImageRec(); -private: - /* Make sure this class cannot be copied to avoid memory problems */ - ImageRec(ImageRec const &); - ImageRec& operator=(ImageRec const &); -}; - -bool load_image(const char * fname, ImageRec & tex); -bool save_screenshot(const char * fname); - -#endif - diff --git a/Source/Input.cpp b/Source/Input.cpp deleted file mode 100644 index b18ec6e..0000000 --- a/Source/Input.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -/**> HEADER FILES <**/ -#include "Input.h" - -bool keyDown[SDL_NUM_SCANCODES + 6]; -bool keyPressed[SDL_NUM_SCANCODES + 6]; - -void Input::Tick() -{ - SDL_PumpEvents(); - int numkeys; - const Uint8 *keyState = SDL_GetKeyboardState(&numkeys); - for (int i = 0; i < numkeys; i++) { - keyPressed[i] = !keyDown[i] && keyState[i]; - keyDown[i] = keyState[i]; - } - Uint8 mb = SDL_GetMouseState(NULL, NULL); - for (int i = 1; i < 6; i++) { - keyPressed[SDL_NUM_SCANCODES + i] = !keyDown[SDL_NUM_SCANCODES + i] && (mb & SDL_BUTTON(i)); - keyDown[SDL_NUM_SCANCODES + i] = (mb & SDL_BUTTON(i)); - } -} - -bool Input::isKeyDown(int k) -{ - if (k >= SDL_NUM_SCANCODES + 6) // really useful? check that. - return false; - return keyDown[k]; -} - -bool Input::isKeyPressed(int k) -{ - if (k >= SDL_NUM_SCANCODES + 6) - return false; - return keyPressed[k]; -} - -const char* Input::keyToChar(unsigned short i) -{ - if (i < SDL_NUM_SCANCODES) - return SDL_GetScancodeName(SDL_Scancode(i)); - else if (i == MOUSEBUTTON1) - return "mouse1"; - else if (i == MOUSEBUTTON2) - return "mouse2"; - else if (i == MOUSEBUTTON3) - return "mouse3"; - else - return "unknown"; -} - -unsigned short Input::CharToKey(const char* which) -{ - for (unsigned short i = 0; i < SDL_NUM_SCANCODES; i++) { - if (!strcasecmp(which, SDL_GetScancodeName(SDL_Scancode(i)))) - return i; - } - if (!strcasecmp(which, "mouse1")) { - return MOUSEBUTTON1; - } - if (!strcasecmp(which, "mouse2")) { - return MOUSEBUTTON2; - } - if (!strcasecmp(which, "mouse3")) { - return MOUSEBUTTON3; - } - return SDL_NUM_SCANCODES; -} - -bool Input::MouseClicked() -{ - return isKeyPressed(SDL_NUM_SCANCODES + SDL_BUTTON_LEFT); -} diff --git a/Source/Input.h b/Source/Input.h deleted file mode 100644 index 3bf8d79..0000000 --- a/Source/Input.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _Input_H_ -#define _Input_H_ - -/**> HEADER FILES <**/ -#include "SDL.h" -#include "Game.h" - -/**> CONSTANT DECLARATIONS <**/ -#define MOUSEBUTTON1 (SDL_NUM_SCANCODES + SDL_BUTTON_LEFT) -#define MOUSEBUTTON2 (SDL_NUM_SCANCODES + SDL_BUTTON_RIGHT) -#define MOUSEBUTTON3 (SDL_NUM_SCANCODES + SDL_BUTTON_MIDDLE) - -/**> FUNCTION PROTOTYPES <**/ -class Input -{ -public: - static void Tick(); - static bool isKeyDown(int k); - static bool isKeyPressed(int k); - static const char* keyToChar(unsigned short which); - static unsigned short CharToKey(const char* which); - static bool MouseClicked(); -}; - -#endif diff --git a/Source/Level/Awards.cpp b/Source/Level/Awards.cpp new file mode 100644 index 0000000..0a561e5 --- /dev/null +++ b/Source/Level/Awards.cpp @@ -0,0 +1,157 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Level/Awards.h" +#include "Objects/Person.h" +#include "Game.h" + +int bonus; +float bonusvalue; +float bonustotal; +float startbonustotal; +float bonustime; +float bonusnum[100]; + +const char *bonus_names[bonus_count] = { +#define DECLARE_BONUS(id, name, ...) name, +#include "Bonuses.def" +#undef DECLARE_BONUS +}; + +const char *award_names[award_count] = { +#define DECLARE_AWARD(id, name) name, +#include "Awards.def" +#undef DECLARE_AWARD +}; + +static const int bonus_values[bonus_count] = { +#define DECLARE_BONUS(id, name, value) value, +#include "Bonuses.def" +#undef DECLARE_BONUS +}; + +void +award_bonus(int playerid, int bonusid, int alt_value) +{ + if (playerid != 0) + return; + bonus = bonusid; + bonustime = 0; + bonusvalue = alt_value ? alt_value : bonus_values[bonusid]; +} + +// FIXME: make these per-player +float damagetaken; +int numfalls; +int numflipfail; +int numseen; +int numresponded; +int numstaffattack; +int numswordattack; +int numknifeattack; +int numunarmedattack; +int numescaped; +int numflipped; +int numwallflipped; +int numthrowkill; +int numafterkill; +int numreversals; +int numattacks; +int maxalarmed; + +int award_awards(int *awards) +{ + int numawards = 0; + if (damagetaken == 0 && Person::players[0]->bloodloss == 0) { + awards[numawards] = awardflawless; + numawards++; + } + bool alldead = true; + for (unsigned i = 1; i < Person::players.size(); i++) { + if (Person::players[i]->dead != 2) + alldead = 0; + } + if (alldead) { + awards[numawards] = awardalldead; + numawards++; + } + alldead = 1; + for (unsigned i = 1; i < Person::players.size(); i++) { + if (Person::players[i]->dead != 1) + alldead = 0; + } + if (alldead) { + awards[numawards] = awardnodead; + numawards++; + } + if (numresponded == 0 && !numthrowkill) { + awards[numawards] = awardstealth; + numawards++; + } + if (numattacks == numstaffattack && numattacks > 0) { + awards[numawards] = awardbojutsu; + numawards++; + } + if (numattacks == numswordattack && numattacks > 0) { + awards[numawards] = awardswordsman; + numawards++; + } + if (numattacks == numknifeattack && numattacks > 0) { + awards[numawards] = awardknifefighter; + numawards++; + } + if (numattacks == numunarmedattack && numthrowkill == 0 && weapons.size() > 0) { + awards[numawards] = awardkungfu; + numawards++; + } + if (numescaped > 0) { + awards[numawards] = awardevasion; + numawards++; + } + if (numflipfail == 0 && numflipped + numwallflipped * 2 > 20) { + awards[numawards] = awardacrobat; + numawards++; + } + if (numthrowkill == (int(Person::players.size()) - 1)) { + awards[numawards] = awardlongrange; + numawards++; + } + alldead = 1; + for (unsigned i = 1; i < Person::players.size(); i++) { + if (Person::players[i]->dead != 2) + alldead = 0; + } + if (numafterkill > 0 && alldead) { + awards[numawards] = awardbrutal; + numawards++; + } + if (numreversals > ((float)numattacks)*.8 && numreversals > 3) { + awards[numawards] = awardaikido; + numawards++; + } + if (maxalarmed == 1 && Person::players.size() > 2) { + awards[numawards] = awardstrategy; + numawards++; + } + if (numflipfail > 3) { + awards[numawards] = awardklutz; + numawards++; + } + return numawards; +} diff --git a/Source/Level/Awards.def b/Source/Level/Awards.def new file mode 100644 index 0000000..fd8e5c4 --- /dev/null +++ b/Source/Level/Awards.def @@ -0,0 +1,40 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +DECLARE_AWARD(awardklutz, "Suicidal") +DECLARE_AWARD(awardflawless, "Flawless!") +DECLARE_AWARD(awardalldead, "Take no prisoners") +DECLARE_AWARD(awardnodead, "Merciful") +DECLARE_AWARD(awardstealth, "One with the shadows!") +DECLARE_AWARD(awardswordsman, "Swordsman") +DECLARE_AWARD(awardkungfu, "Unarmed!") +DECLARE_AWARD(awardknifefighter, "Knife fighter") +DECLARE_AWARD(awardcoward, "Coward") +DECLARE_AWARD(awardevasion, "Escape artist") +DECLARE_AWARD(awardacrobat, "Gymnast") +DECLARE_AWARD(awardlongrange, "Blade slinger") +DECLARE_AWARD(awardbrutal, "Brutal") +DECLARE_AWARD(awardhyper, "Hyper") +DECLARE_AWARD(awardaikido, "Aikido master!") +DECLARE_AWARD(awardrambo, "Rambo") +DECLARE_AWARD(awardfast, "Fast") +DECLARE_AWARD(awardrealfast, "Real fast") +DECLARE_AWARD(awarddamnfast, "Damn fast") +DECLARE_AWARD(awardstrategy, "Divide and conquer") +DECLARE_AWARD(awardbojutsu, "Bojutsu") diff --git a/Source/Level/Awards.h b/Source/Level/Awards.h new file mode 100644 index 0000000..882fbef --- /dev/null +++ b/Source/Level/Awards.h @@ -0,0 +1,70 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef AWARDS_H +#define AWARDS_H + +enum bonus_types { +#define DECLARE_BONUS(id, ...) id, +#include "Bonuses.def" +#undef DECLARE_BONUS + bonus_count +}; + +extern const char *bonus_names[bonus_count]; + +extern int bonus; +extern float bonusvalue; +extern float bonustotal; +extern float bonustime; +extern float startbonustotal; +extern float bonusnum[100]; + +extern void award_bonus(int playerid, int bonusid, int alt_value = 0); + +enum award_types { +#define DECLARE_AWARD(id, name) id, +#include "Awards.def" +#undef DECLARE_AWARD + award_count +}; + +extern const char *award_names[award_count]; + +extern int award_awards(int *); + +extern float damagetaken; +extern int numfalls; +extern int numflipfail; +extern int numseen; +extern int numresponded; +extern int numstaffattack; +extern int numswordattack; +extern int numknifeattack; +extern int numunarmedattack; +extern int numescaped; +extern int numflipped; +extern int numwallflipped; +extern int numthrowkill; +extern int numafterkill; +extern int numreversals; +extern int numattacks; +extern int maxalarmed; +#endif + diff --git a/Source/Level/Bonuses.def b/Source/Level/Bonuses.def new file mode 100644 index 0000000..bcc1a95 --- /dev/null +++ b/Source/Level/Bonuses.def @@ -0,0 +1,47 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +DECLARE_BONUS(nobonus, "", 0) +DECLARE_BONUS(tracheotomy, "Tracheotomy!", 100) +DECLARE_BONUS(backstab, "Backstabber!", 100) +DECLARE_BONUS(spinecrusher, "Spinecrusher!", 100) +DECLARE_BONUS(ninja, "Ninja Bonus!", 60) +DECLARE_BONUS(style, "Style Bonus!", 150) +DECLARE_BONUS(cannon, "Leg Cannon!", 100) +DECLARE_BONUS(aimbonus, "Nice Aim!", 150) +DECLARE_BONUS(deepimpact, "Heavy Impact!", 50) +DECLARE_BONUS(touchofdeath, "Touch of Death!", 150) +DECLARE_BONUS(swordreversebonus, "Sword Disarm!", 100) +DECLARE_BONUS(staffreversebonus, "Staff Disarm!", 100) +DECLARE_BONUS(reverseko, "Reversal KO!", 100) +// The following five should be kept in that order +DECLARE_BONUS(solidhit, "Solid Hit!", 10) +DECLARE_BONUS(twoxcombo, "2X Combo!", 20) +DECLARE_BONUS(threexcombo, "3X Combo!", 40) +DECLARE_BONUS(fourxcombo, "4X COMBO!", 80) +DECLARE_BONUS(megacombo, "MEGA COMBO!", 160) +DECLARE_BONUS(Reversal, "Reversal!", 60) +DECLARE_BONUS(Stabbonus, "Punctured!", 40) +DECLARE_BONUS(Slicebonus, "Sliced!", 10) +DECLARE_BONUS(Bullseyebonus, "Bullseye!", 30) +DECLARE_BONUS(Slashbonus, "Slashed!", 40) +DECLARE_BONUS(Wolfbonus, "WOLF SLAYER!", 300) +DECLARE_BONUS(FinishedBonus, "SLAIN!", 200) +DECLARE_BONUS(TackleBonus, "Tackle!", 5) +DECLARE_BONUS(AboveBonus, "Death from Above!", 50) diff --git a/Source/Level/Campaign.cpp b/Source/Level/Campaign.cpp new file mode 100644 index 0000000..2aa5f17 --- /dev/null +++ b/Source/Level/Campaign.cpp @@ -0,0 +1,156 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" +#include "Level/Campaign.h" +#include "Utils/Folders.h" + +#include + +using namespace Game; + +std::vector campaignlevels; + +bool campaign = false; + +int actuallevel = 0; + +std::vector ListCampaigns() +{ + errno = 0; + DIR *campaigns = opendir(Folders::getResourcePath("Campaigns").c_str()); + struct dirent *campaign = NULL; + if (!campaigns) { + perror(("Problem while loading campaigns from " + Folders::getResourcePath("Campaigns")).c_str()); + exit(EXIT_FAILURE); + } + std::vector campaignNames; + while ((campaign = readdir(campaigns)) != NULL) { + std::string name(campaign->d_name); + if (name.length() < 5) + continue; + if (!name.compare(name.length() - 4, 4, ".txt")) { + campaignNames.push_back(name.substr(0, name.length() - 4)); + } + } + closedir(campaigns); + return campaignNames; +} + +void LoadCampaign() +{ + if (!Account::hasActive()) { + return; + } + std::ifstream ipstream(Folders::getResourcePath("Campaigns/" + Account::active().getCurrentCampaign() + ".txt")); + if (!ipstream.good()) { + if (Account::active().getCurrentCampaign() == "main") { + cerr << "Could not found main campaign!" << endl; + return; + } + cerr << "Could not found campaign \"" << Account::active().getCurrentCampaign() << "\", falling back to main." << endl; + Account::active().setCurrentCampaign("main"); + return LoadCampaign(); + } + ipstream.ignore(256, ':'); + int numlevels; + ipstream >> numlevels; + campaignlevels.clear(); + for (int i = 0; i < numlevels; i++) { + CampaignLevel cl; + ipstream >> cl; + campaignlevels.push_back(cl); + } + ipstream.close(); + + std::ifstream test(Folders::getResourcePath("Textures/" + Account::active().getCurrentCampaign() + "/World.png")); + if (test.good()) { + Mainmenuitems[7].load("Textures/" + Account::active().getCurrentCampaign() + "/World.png", 0); + } else { + Mainmenuitems[7].load("Textures/World.png", 0); + } + + if (Account::active().getCampaignChoicesMade() == 0) { + Account::active().setCampaignScore(0); + Account::active().resetFasttime(); + } +} + +CampaignLevel::CampaignLevel() : + width(10), + choosenext(1) +{ + location.x = 0; + location.y = 0; +} + +int CampaignLevel::getStartX() { + return 30 + 120 + location.x * 400 / 512; +} + +int CampaignLevel::getStartY() { + return 30 + 30 + (512 - location.y) * 400 / 512; +} + +int CampaignLevel::getEndX() { + return getStartX() + width; +} + +int CampaignLevel::getEndY() { + return getStartY() + width; +} + +XYZ CampaignLevel::getCenter() { + XYZ center; + center.x = getStartX() + width / 2; + center.y = getStartY() + width / 2; + return center; +} + +int CampaignLevel::getWidth() { + return width; +} + +istream& CampaignLevel::operator<< (istream& is) { + is.ignore(256, ':'); + is.ignore(256, ':'); + is.ignore(256, ' '); + is >> mapname; + is.ignore(256, ':'); + is >> description; + for (size_t pos = description.find('_'); pos != string::npos; pos = description.find('_', pos)) { + description.replace(pos, 1, 1, ' '); + } + is.ignore(256, ':'); + is >> choosenext; + is.ignore(256, ':'); + int numnext, next; + is >> numnext; + for (int j = 0; j < numnext; j++) { + is.ignore(256, ':'); + is >> next; + nextlevel.push_back(next - 1); + } + is.ignore(256, ':'); + is >> location.x; + is.ignore(256, ':'); + is >> location.y; + return is; +} diff --git a/Source/Level/Campaign.h b/Source/Level/Campaign.h new file mode 100644 index 0000000..3cd4d51 --- /dev/null +++ b/Source/Level/Campaign.h @@ -0,0 +1,65 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include +#include + +#include "Math/Quaternions.h" + +extern bool campaign; + +extern int actuallevel; + +std::vector ListCampaigns(); +void LoadCampaign(); + +class CampaignLevel +{ +private: + int width; + struct Position { + int x, y; + }; +public: + std::string mapname; + std::string description; + int choosenext; + /* + 0 = Immediately load next level at the end of this one. + 1 = Go back to the world map. + 2 = Don't bring up the Fiery loading screen. Maybe other things, I've not investigated. + */ + //int numnext; // 0 on final level. As David said: he meant to add story branching, but he eventually hadn't. + std::vector nextlevel; + Position location; + CampaignLevel(); + int getStartX(); + int getStartY(); + int getEndX(); + int getEndY(); + XYZ getCenter(); + int getWidth(); + std::istream& operator<< (std::istream& is); + friend std::istream& operator>> (std::istream& is, CampaignLevel& cl) { + return cl << is; + } +}; + +extern std::vector campaignlevels; diff --git a/Source/Level/Dialog.cpp b/Source/Level/Dialog.cpp new file mode 100644 index 0000000..915fba1 --- /dev/null +++ b/Source/Level/Dialog.cpp @@ -0,0 +1,232 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" + +#include "Level/Dialog.h" +#include "Objects/Person.h" +#include "Utils/binio.h" +#include "Utils/Folders.h" +#include "Utils/Input.h" + +extern int hostile; + +int Dialog::indialogue; +int Dialog::whichdialogue; +bool Dialog::directing; +float Dialog::dialoguetime; +std::vector Dialog::dialogs; + +void Dialog::loadDialogs(FILE* tfile) +{ + int numdialogues; + funpackf(tfile, "Bi", &numdialogues); + for (int k = 0; k < numdialogues; k++) { + dialogs.push_back(Dialog(tfile)); + } +} + +Dialog::Dialog(FILE* tfile) : gonethrough(0) +{ + int numdialogscenes; + funpackf(tfile, "Bi", &numdialogscenes); + funpackf(tfile, "Bi", &type); + for (int l = 0; l < 10; l++) { + funpackf(tfile, "Bf Bf Bf", &participantlocation[l].x, &participantlocation[l].y, &participantlocation[l].z); + funpackf(tfile, "Bf", &participantyaw[l]); + } + for (int l = 0; l < numdialogscenes; l++) { + scenes.push_back(DialogScene(tfile)); + } +} + +std::string funpackf_string(FILE* tfile, int maxlength) +{ + int templength; + funpackf(tfile, "Bi", &templength); + if ((templength > maxlength) || (templength <= 0)) { + templength = maxlength; + } + int m; + char* text = new char[maxlength]; + for (m = 0; m < templength; m++) { + funpackf(tfile, "Bb", &text[m]); + if (text[m] == '\0') + break; + } + text[m] = 0; + std::string result(text); + delete[] text; + return result; +} + +void fpackf_string(FILE* tfile, std::string text) +{ + fpackf(tfile, "Bi", text.size()); + for (int m = 0; m < text.size(); m++) { + fpackf(tfile, "Bb", text[m]); + if (text[m] == '\0') + break; + } +} + +DialogScene::DialogScene(FILE* tfile) +{ + funpackf(tfile, "Bi", &location); + funpackf(tfile, "Bf", &color[0]); + funpackf(tfile, "Bf", &color[1]); + funpackf(tfile, "Bf", &color[2]); + funpackf(tfile, "Bi", &sound); + + text = funpackf_string(tfile, 128); + name = funpackf_string(tfile, 64); + + funpackf(tfile, "Bf Bf Bf", &camera.x, &camera.y, &camera.z); + funpackf(tfile, "Bi", &participantfocus); + funpackf(tfile, "Bi", &participantaction); + + for (int m = 0; m < 10; m++) + funpackf(tfile, "Bf Bf Bf", &participantfacing[m].x, &participantfacing[m].y, &participantfacing[m].z); + + funpackf(tfile, "Bf Bf", &camerayaw, &camerapitch); +} + +/* Load dialog from txt file, used by console */ +Dialog::Dialog(int type, std::string filename) : type(type) +{ + ifstream ipstream(Folders::getResourcePath(filename)); + ipstream.ignore(256, ':'); + int numscenes; + ipstream >> numscenes; + for (int i = 0; i < numscenes; i++) { + scenes.push_back(DialogScene(ipstream)); + for (unsigned j = 0; j < Person::players.size(); j++) { + scenes.back().participantfacing[j] = Person::players[j]->facing; + } + } + ipstream.close(); +} + +DialogScene::DialogScene(ifstream &ipstream) +{ + ipstream.ignore(256, ':'); + ipstream.ignore(256, ':'); + ipstream.ignore(256, ' '); + ipstream >> location; + ipstream.ignore(256, ':'); + ipstream >> color[0]; + ipstream >> color[1]; + ipstream >> color[2]; + ipstream.ignore(256, ':'); + getline(ipstream, name); + ipstream.ignore(256, ':'); + ipstream.ignore(256, ' '); + getline(ipstream, text); + for (int j = 0; j < 128; j++) { + if (text[j] == '\\') + text[j] = '\n'; + } + ipstream.ignore(256, ':'); + ipstream >> sound; +} + +void Dialog::tick(int id) +{ + unsigned playerId = type % 10; + bool special = (type > 9); + + if ((!hostile || (type > 40) && (type < 50)) && + (playerId < Person::players.size()) && + (playerId > 0) && + ((gonethrough == 0) || !special) && + (special || Input::isKeyPressed(Game::attackkey))) { + if ((distsq(&Person::players[0]->coords, &Person::players[playerId]->coords) < 6) || + (Person::players[playerId]->howactive >= typedead1) || + (type > 40) && (type < 50)) { + whichdialogue = id; + play(); + dialoguetime = 0; + gonethrough++; + } + } +} + +void Dialog::play() +{ + for (int i = 0; i < scenes.size(); i++) { + int playerId = scenes[i].participantfocus; + Person::players[playerId]->coords = participantlocation[playerId]; + Person::players[playerId]->yaw = participantyaw[playerId]; + Person::players[playerId]->targetyaw = participantyaw[playerId]; + Person::players[playerId]->velocity = 0; + Person::players[playerId]->animTarget = Person::players[playerId]->getIdle(); + Person::players[playerId]->frameTarget = 0; + } + + Dialog::directing = false; + Dialog::indialogue = 0; + + if (scenes[indialogue].sound != 0) { + Game::playdialoguescenesound(); + } +} + +void Dialog::saveDialogs(FILE* tfile) +{ + fpackf(tfile, "Bi", dialogs.size()); + + for (int k = 0; k < dialogs.size(); k++) { + dialogs[k].save(tfile); + } +} + +void Dialog::save(FILE* tfile) +{ + fpackf(tfile, "Bi", scenes.size()); + fpackf(tfile, "Bi", type); + for (int l = 0; l < 10; l++) { + fpackf(tfile, "Bf Bf Bf", participantlocation[l].x, participantlocation[l].y, participantlocation[l].z); + fpackf(tfile, "Bf", participantyaw[l]); + } + for (int l = 0; l < scenes.size(); l++) { + scenes[l].save(tfile); + } +} + +void DialogScene::save(FILE* tfile) +{ + fpackf(tfile, "Bi", location); + fpackf(tfile, "Bf", color[0]); + fpackf(tfile, "Bf", color[1]); + fpackf(tfile, "Bf", color[2]); + fpackf(tfile, "Bi", sound); + + fpackf_string(tfile, text); + fpackf_string(tfile, name); + + fpackf(tfile, "Bf Bf Bf", camera.x, camera.y, camera.z); + fpackf(tfile, "Bi", participantfocus); + fpackf(tfile, "Bi", participantaction); + + for (int m = 0; m < 10; m++) + fpackf(tfile, "Bf Bf Bf", participantfacing[m].x, participantfacing[m].y, participantfacing[m].z); + + fpackf(tfile, "Bf Bf", camerayaw, camerapitch); +} diff --git a/Source/Level/Dialog.h b/Source/Level/Dialog.h new file mode 100644 index 0000000..50691c5 --- /dev/null +++ b/Source/Level/Dialog.h @@ -0,0 +1,78 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _DIALOG_H_ +#define _DIALOG_H_ + +#include "stdio.h" +#include + +#include "Math/Quaternions.h" + +class DialogScene +{ +public: + DialogScene(FILE* tfile); + DialogScene(ifstream &ipstream); + void save(FILE* tfile); + + int location; + float color[3]; + int sound; + std::string text; + std::string name; + XYZ camera; + float camerayaw; + float camerapitch; + int participantfocus; + int participantaction; + XYZ participantfacing[10]; +}; + +class Dialog +{ +public: + Dialog(FILE* tfile); + Dialog(int type, std::string filename); + void tick(int id); + void play(); + void save(FILE* tfile); + + int type; + int gonethrough; + std::vector scenes; + XYZ participantlocation[10]; + float participantyaw[10]; + + static void loadDialogs(FILE*); + static void saveDialogs(FILE*); + + static bool inDialog() { return (indialogue != -1); } + static Dialog& currentDialog() { return dialogs[whichdialogue]; } + static DialogScene& currentScene() { return currentDialog().scenes[indialogue]; } + + static int indialogue; + static int whichdialogue; + static bool directing; + static float dialoguetime; + static std::vector dialogs; +}; + +#endif /*_DIALOG_H_*/ diff --git a/Source/Level/Hotspot.cpp b/Source/Level/Hotspot.cpp new file mode 100644 index 0000000..1f7956e --- /dev/null +++ b/Source/Level/Hotspot.cpp @@ -0,0 +1,38 @@ +/* +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Level/Hotspot.h" + +std::vector Hotspot::hotspots; +int Hotspot::current = 0; +int Hotspot::killhotspot = 0; + +Hotspot::Hotspot() : + position(), + type(0), + size(0) +{ +} + +Hotspot::Hotspot(XYZ p, int t, float s) : + position(p), + type(t), + size(s) +{ +} diff --git a/Source/Level/Hotspot.h b/Source/Level/Hotspot.h new file mode 100644 index 0000000..a8ef2ec --- /dev/null +++ b/Source/Level/Hotspot.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _HOTSPOT_H_ +#define _HOTSPOT_H_ + +#include "Math/Quaternions.h" +#include + +class Hotspot +{ +public: + static std::vector hotspots; + static int current; + static int killhotspot; + + Hotspot(); + Hotspot(XYZ position, int type, float size); + + XYZ position; + int type; + float size; + char text[256] = {0}; +}; + +#endif diff --git a/Source/Lights.cpp b/Source/Lights.cpp deleted file mode 100644 index cd20a66..0000000 --- a/Source/Lights.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -/**> HEADER FILES <**/ -#include "Lights.h" - -void SetUpLight(Light* whichsource, int whichlight) -{ - static float qattenuation[] = {0.0002f}; - - //Initialize lights - if (whichlight == 0) { - GLfloat LightAmbient[] = { whichsource->ambient[0], whichsource->ambient[1], whichsource->ambient[2], 1.0f}; - GLfloat LightDiffuse[] = { whichsource->color[0], whichsource->color[1], whichsource->color[2], 1.0f }; - GLfloat LightPosition[] = { whichsource->location.x, whichsource->location.y, whichsource->location.z, 0.0f }; - - glLightfv(GL_LIGHT0, GL_POSITION, LightPosition); - glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient); - glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse); - glEnable(GL_LIGHT0); - } else { - GLenum lightselect = GL_LIGHT1; - switch (whichlight) { - case 2: - lightselect = GL_LIGHT2; - break; - case 3: - lightselect = GL_LIGHT3; - break; - case 4: - lightselect = GL_LIGHT4; - break; - case 5: - lightselect = GL_LIGHT5; - break; - case 6: - lightselect = GL_LIGHT6; - break; - case 7: - lightselect = GL_LIGHT7; - break; - } - - GLfloat LightAmbient[] = { 0, 0, 0, 1.0f}; - GLfloat LightDiffuse[] = { whichsource->color[0], whichsource->color[1], whichsource->color[2], 1.0f }; - GLfloat LightPosition[] = { whichsource->location.x, whichsource->location.y, whichsource->location.z, 1.0f }; - - glLightfv(lightselect, GL_QUADRATIC_ATTENUATION, qattenuation); - glLightfv(lightselect, GL_POSITION, LightPosition); - glLightfv(lightselect, GL_AMBIENT, LightAmbient); - glLightfv(lightselect, GL_DIFFUSE, LightDiffuse); - glEnable(lightselect); - - } -} diff --git a/Source/Lights.h b/Source/Lights.h deleted file mode 100644 index e846fb6..0000000 --- a/Source/Lights.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _LIGHTS_H_ -#define _LIGHTS_H_ - - -/**> HEADER FILES <**/ -#include "gamegl.h" -#include "Quaternions.h" - -class Light -{ -public: - GLint type; - GLfloat color[3]; - GLfloat ambient[3]; - int attach; - XYZ location; - inline void setColors(GLfloat cr, GLfloat cg, GLfloat cb, - GLfloat ar, GLfloat ag, GLfloat ab) { - color[0] = cr; - color[1] = cg; - color[2] = cb; - ambient[0] = ar; - ambient[1] = ag; - ambient[2] = ab; - } -}; - -void SetUpLight(Light* whichsource, int whichlight); - -#endif diff --git a/Source/MacCompatibility.h b/Source/MacCompatibility.h index 5d6f955..26b0b60 100644 --- a/Source/MacCompatibility.h +++ b/Source/MacCompatibility.h @@ -58,7 +58,7 @@ typedef signed char SInt8; typedef unsigned int UInt32; -#include "Random.h" +#include "Math/Random.h" typedef struct AbsoluteTime { unsigned long hi; diff --git a/Source/Math/Frustum.cpp b/Source/Math/Frustum.cpp new file mode 100644 index 0000000..164074b --- /dev/null +++ b/Source/Math/Frustum.cpp @@ -0,0 +1,178 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Math/Frustum.h" +#include + +#include "Graphic/gamegl.h" + + +void FRUSTUM:: +GetFrustum() +{ + static float projmatrix[16]; + static float mvmatrix[16]; + static float clip[16]; + + glGetFloatv(GL_PROJECTION_MATRIX, projmatrix); + glGetFloatv(GL_MODELVIEW_MATRIX, mvmatrix); + + // Combine the matrices + clip[0] = mvmatrix[0] * projmatrix[0] + mvmatrix[1] * projmatrix[4] + mvmatrix[2] * projmatrix[8] + mvmatrix[3] * projmatrix[12]; + clip[1] = mvmatrix[0] * projmatrix[1] + mvmatrix[1] * projmatrix[5] + mvmatrix[2] * projmatrix[9] + mvmatrix[3] * projmatrix[13]; + clip[2] = mvmatrix[0] * projmatrix[2] + mvmatrix[1] * projmatrix[6] + mvmatrix[2] * projmatrix[10] + mvmatrix[3] * projmatrix[14]; + clip[3] = mvmatrix[0] * projmatrix[3] + mvmatrix[1] * projmatrix[7] + mvmatrix[2] * projmatrix[11] + mvmatrix[3] * projmatrix[15]; + + clip[4] = mvmatrix[4] * projmatrix[0] + mvmatrix[5] * projmatrix[4] + mvmatrix[6] * projmatrix[8] + mvmatrix[7] * projmatrix[12]; + clip[5] = mvmatrix[4] * projmatrix[1] + mvmatrix[5] * projmatrix[5] + mvmatrix[6] * projmatrix[9] + mvmatrix[7] * projmatrix[13]; + clip[6] = mvmatrix[4] * projmatrix[2] + mvmatrix[5] * projmatrix[6] + mvmatrix[6] * projmatrix[10] + mvmatrix[7] * projmatrix[14]; + clip[7] = mvmatrix[4] * projmatrix[3] + mvmatrix[5] * projmatrix[7] + mvmatrix[6] * projmatrix[11] + mvmatrix[7] * projmatrix[15]; + + clip[8] = mvmatrix[8] * projmatrix[0] + mvmatrix[9] * projmatrix[4] + mvmatrix[10] * projmatrix[8] + mvmatrix[11] * projmatrix[12]; + clip[9] = mvmatrix[8] * projmatrix[1] + mvmatrix[9] * projmatrix[5] + mvmatrix[10] * projmatrix[9] + mvmatrix[11] * projmatrix[13]; + clip[10] = mvmatrix[8] * projmatrix[2] + mvmatrix[9] * projmatrix[6] + mvmatrix[10] * projmatrix[10] + mvmatrix[11] * projmatrix[14]; + clip[11] = mvmatrix[8] * projmatrix[3] + mvmatrix[9] * projmatrix[7] + mvmatrix[10] * projmatrix[11] + mvmatrix[11] * projmatrix[15]; + + clip[12] = mvmatrix[12] * projmatrix[0] + mvmatrix[13] * projmatrix[4] + mvmatrix[14] * projmatrix[8] + mvmatrix[15] * projmatrix[12]; + clip[13] = mvmatrix[12] * projmatrix[1] + mvmatrix[13] * projmatrix[5] + mvmatrix[14] * projmatrix[9] + mvmatrix[15] * projmatrix[13]; + clip[14] = mvmatrix[12] * projmatrix[2] + mvmatrix[13] * projmatrix[6] + mvmatrix[14] * projmatrix[10] + mvmatrix[15] * projmatrix[14]; + clip[15] = mvmatrix[12] * projmatrix[3] + mvmatrix[13] * projmatrix[7] + mvmatrix[14] * projmatrix[11] + mvmatrix[15] * projmatrix[15]; + + // Right plane + frustum[0][0] = clip[3] - clip[0]; + frustum[0][1] = clip[7] - clip[4]; + frustum[0][2] = clip[11] - clip[8]; + frustum[0][3] = clip[15] - clip[12]; + + // Left plane + frustum[1][0] = clip[3] + clip[0]; + frustum[1][1] = clip[7] + clip[4]; + frustum[1][2] = clip[11] + clip[8]; + frustum[1][3] = clip[15] + clip[12]; + + // Bottom plane + frustum[2][0] = clip[3] + clip[1]; + frustum[2][1] = clip[7] + clip[5]; + frustum[2][2] = clip[11] + clip[9]; + frustum[2][3] = clip[15] + clip[13]; + + // Top plane + frustum[3][0] = clip[3] - clip[1]; + frustum[3][1] = clip[7] - clip[5]; + frustum[3][2] = clip[11] - clip[9]; + frustum[3][3] = clip[15] - clip[13]; + + // Far plane + frustum[4][0] = clip[3] - clip[2]; + frustum[4][1] = clip[7] - clip[6]; + frustum[4][2] = clip[11] - clip[10]; + frustum[4][3] = clip[15] - clip[14]; + + // Near plane + frustum[5][0] = clip[3] + clip[2]; + frustum[5][1] = clip[7] + clip[6]; + frustum[5][2] = clip[11] + clip[10]; + frustum[5][3] = clip[15] + clip[14]; +} + +int FRUSTUM:: +CubeInFrustum(float x, float y, float z, float size) +{ + static int c, c2; + + for (int i = 0; i < 6; i++) { + c = 0; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y - size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y - size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y + size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y + size) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y - size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y - size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y + size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y + size) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (c == 0) + return 0; + if (c == 8) + c2++; + } + if (c2 >= 6) + return 2; + else + return 1; +} + +int FRUSTUM:: +CubeInFrustum(float x, float y, float z, float size, float height) +{ + static int c, c2; + + for (int i = 0; i < 6; i++) { + c = 0; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y - height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y - height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y + height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y + height) + frustum[i][2] * (z - size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y - height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y - height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x - size) + frustum[i][1] * (y + height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (frustum[i][0] * (x + size) + frustum[i][1] * (y + height) + frustum[i][2] * (z + size) + frustum[i][3] > 0) + c++; + if (c == 0) + return 0; + if (c == 8) + c2++; + } + if (c2 >= 6) + return 2; + else + return 1; +} + +int FRUSTUM:: +SphereInFrustum(float x, float y, float z, float radius) +{ + static int c2; + + for (int i = 0; i < 6; i++) { + if (frustum[i][0] * x + frustum[i][1] * y + frustum[i][2] * z + frustum[i][3] > -1 * radius) + c2++; + else + return 0; + } + if (c2 >= 6) + return 2; + else + return 1; +} diff --git a/Source/Math/Frustum.h b/Source/Math/Frustum.h new file mode 100644 index 0000000..918a047 --- /dev/null +++ b/Source/Math/Frustum.h @@ -0,0 +1,34 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef FRUSTUM_H +#define FRUSTUM_H + +class FRUSTUM +{ +public: + float frustum[6][4]; + void GetFrustum(); + int CubeInFrustum(float, float, float, float); + int CubeInFrustum(float, float, float, float, float); + int SphereInFrustum(float, float, float, float); +}; + +#endif diff --git a/Source/Math/PhysicsMath.h b/Source/Math/PhysicsMath.h new file mode 100644 index 0000000..7dc9461 --- /dev/null +++ b/Source/Math/PhysicsMath.h @@ -0,0 +1,765 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _PHYSICSMATH_H_ +#define _PHYSICSMATH_H_ + +//#include + +#include "MacCompatibility.h" + +//------------------------------------------------------------------------// +// Misc. Constants +//------------------------------------------------------------------------// + +float const pi = 3.14159265f; +float const g = -32.174f; // acceleration due to gravity, ft/s^2 +float const rho = 0.0023769f; // desity of air at sea level, slugs/ft^3 +float const tol = 0.0000000001f; // float type tolerance + + +//------------------------------------------------------------------------// +// Misc. Functions +//------------------------------------------------------------------------// +inline float DegreesToRadians(float deg); +inline float RadiansToDegrees(float rad); + +inline float DegreesToRadians(float deg) +{ + return deg * pi / 180.0f; +} + +inline float RadiansToDegrees(float rad) +{ + return rad * 180.0f / pi; +} + +//------------------------------------------------------------------------// +// Vector Class and vector functions +//------------------------------------------------------------------------// +class Vector +{ +public: + float x; + float y; + float z; + + Vector(void); + Vector(float xi, float yi, float zi); + + float Magnitude(void); + void Normalize(void); + void Reverse(void); + + Vector& operator+=(Vector u); // vector addition + Vector& operator-=(Vector u); // vector subtraction + Vector& operator*=(float s); // scalar multiply + Vector& operator/=(float s); // scalar divide + + Vector operator-(void); + +}; + +inline Vector operator+(Vector u, Vector v); +inline Vector operator-(Vector u, Vector v); +inline Vector operator^(Vector u, Vector v); +inline float operator*(Vector u, Vector v); +inline Vector operator*(float s, Vector u); +inline Vector operator*(Vector u, float s); +inline Vector operator/(Vector u, float s); +inline float TripleScalarProduct(Vector u, Vector v, Vector w); +/* +float fast_sqrt2 (register float arg); +float fast_sqrt2 (register float arg) +{ +// Can replace with slower return std::sqrt(arg); +register float result; + +if (arg == 0.0) return 0.0; + +asm { +frsqrte result,arg // Calculate Square root +} + +// Newton Rhapson iterations. +result = result + 0.5 * result * (1.0 - arg * result * result); +result = result + 0.5 * result * (1.0 - arg * result * result); + +return result * arg; +} +*/ +inline Vector::Vector(void) +{ + x = 0; + y = 0; + z = 0; +} + +inline Vector::Vector(float xi, float yi, float zi) +{ + x = xi; + y = yi; + z = zi; +} + +inline float Vector::Magnitude(void) +{ + return (float) sqrt(x * x + y * y + z * z); +} + +inline void Vector::Normalize(void) +{ + float m = (float) sqrt(x * x + y * y + z * z); + if (m <= tol) + m = 1; + x /= m; + y /= m; + z /= m; + + if (fabs(x) < tol) + x = 0.0f; + if (fabs(y) < tol) + y = 0.0f; + if (fabs(z) < tol) + z = 0.0f; +} + +inline void Vector::Reverse(void) +{ + x = -x; + y = -y; + z = -z; +} + +inline Vector& Vector::operator+=(Vector u) +{ + x += u.x; + y += u.y; + z += u.z; + return *this; +} + +inline Vector& Vector::operator-=(Vector u) +{ + x -= u.x; + y -= u.y; + z -= u.z; + return *this; +} + +inline Vector& Vector::operator*=(float s) +{ + x *= s; + y *= s; + z *= s; + return *this; +} + +inline Vector& Vector::operator/=(float s) +{ + x /= s; + y /= s; + z /= s; + return *this; +} + +inline Vector Vector::operator-(void) +{ + return Vector(-x, -y, -z); +} + + +inline Vector operator+(Vector u, Vector v) +{ + return Vector(u.x + v.x, u.y + v.y, u.z + v.z); +} + +inline Vector operator-(Vector u, Vector v) +{ + return Vector(u.x - v.x, u.y - v.y, u.z - v.z); +} + +// Vector cross product (u cross v) +inline Vector operator^(Vector u, Vector v) +{ + return Vector( u.y * v.z - u.z * v.y, + -u.x * v.z + u.z * v.x, + u.x * v.y - u.y * v.x ); +} + +// Vector dot product +inline float operator*(Vector u, Vector v) +{ + return (u.x * v.x + u.y * v.y + u.z * v.z); +} + +inline Vector operator*(float s, Vector u) +{ + return Vector(u.x * s, u.y * s, u.z * s); +} + +inline Vector operator*(Vector u, float s) +{ + return Vector(u.x * s, u.y * s, u.z * s); +} + +inline Vector operator/(Vector u, float s) +{ + return Vector(u.x / s, u.y / s, u.z / s); +} + +// triple scalar product (u dot (v cross w)) +inline float TripleScalarProduct(Vector u, Vector v, Vector w) +{ + return float( (u.x * (v.y * w.z - v.z * w.y)) + + (u.y * (-v.x * w.z + v.z * w.x)) + + (u.z * (v.x * w.y - v.y * w.x)) ); + //return u*(v^w); + +} + + + +//------------------------------------------------------------------------// +// Matrix Class and matrix functions +//------------------------------------------------------------------------// + +class Matrix3x3 +{ +public: + // elements eij: i -> row, j -> column + float e11, e12, e13, e21, e22, e23, e31, e32, e33; + + Matrix3x3(void); + Matrix3x3( float r1c1, float r1c2, float r1c3, + float r2c1, float r2c2, float r2c3, + float r3c1, float r3c2, float r3c3 ); + + float det(void); + Matrix3x3 Transpose(void); + Matrix3x3 Inverse(void); + + Matrix3x3& operator+=(Matrix3x3 m); + Matrix3x3& operator-=(Matrix3x3 m); + Matrix3x3& operator*=(float s); + Matrix3x3& operator/=(float s); +}; + +inline Matrix3x3 operator+(Matrix3x3 m1, Matrix3x3 m2); +inline Matrix3x3 operator-(Matrix3x3 m1, Matrix3x3 m2); +inline Matrix3x3 operator/(Matrix3x3 m, float s); +inline Matrix3x3 operator*(Matrix3x3 m1, Matrix3x3 m2); +inline Matrix3x3 operator*(Matrix3x3 m, float s); +inline Matrix3x3 operator*(float s, Matrix3x3 m); +inline Vector operator*(Matrix3x3 m, Vector u); +inline Vector operator*(Vector u, Matrix3x3 m); + + + + + +inline Matrix3x3::Matrix3x3(void) +{ + e11 = 0; + e12 = 0; + e13 = 0; + e21 = 0; + e22 = 0; + e23 = 0; + e31 = 0; + e32 = 0; + e33 = 0; +} + +inline Matrix3x3::Matrix3x3( float r1c1, float r1c2, float r1c3, + float r2c1, float r2c2, float r2c3, + float r3c1, float r3c2, float r3c3 ) +{ + e11 = r1c1; + e12 = r1c2; + e13 = r1c3; + e21 = r2c1; + e22 = r2c2; + e23 = r2c3; + e31 = r3c1; + e32 = r3c2; + e33 = r3c3; +} + +inline float Matrix3x3::det(void) +{ + return e11 * e22 * e33 - + e11 * e32 * e23 + + e21 * e32 * e13 - + e21 * e12 * e33 + + e31 * e12 * e23 - + e31 * e22 * e13; +} + +inline Matrix3x3 Matrix3x3::Transpose(void) +{ + return Matrix3x3(e11, e21, e31, e12, e22, e32, e13, e23, e33); +} + +inline Matrix3x3 Matrix3x3::Inverse(void) +{ + float d = e11 * e22 * e33 - + e11 * e32 * e23 + + e21 * e32 * e13 - + e21 * e12 * e33 + + e31 * e12 * e23 - + e31 * e22 * e13; + + if (d == 0) + d = 1; + + return Matrix3x3( (e22 * e33 - e23 * e32) / d, + -(e12 * e33 - e13 * e32) / d, + (e12 * e23 - e13 * e22) / d, + -(e21 * e33 - e23 * e31) / d, + (e11 * e33 - e13 * e31) / d, + -(e11 * e23 - e13 * e21) / d, + (e21 * e32 - e22 * e31) / d, + -(e11 * e32 - e12 * e31) / d, + (e11 * e22 - e12 * e21) / d ); +} + +inline Matrix3x3& Matrix3x3::operator+=(Matrix3x3 m) +{ + e11 += m.e11; + e12 += m.e12; + e13 += m.e13; + e21 += m.e21; + e22 += m.e22; + e23 += m.e23; + e31 += m.e31; + e32 += m.e32; + e33 += m.e33; + return *this; +} + +inline Matrix3x3& Matrix3x3::operator-=(Matrix3x3 m) +{ + e11 -= m.e11; + e12 -= m.e12; + e13 -= m.e13; + e21 -= m.e21; + e22 -= m.e22; + e23 -= m.e23; + e31 -= m.e31; + e32 -= m.e32; + e33 -= m.e33; + return *this; +} + +inline Matrix3x3& Matrix3x3::operator*=(float s) +{ + e11 *= s; + e12 *= s; + e13 *= s; + e21 *= s; + e22 *= s; + e23 *= s; + e31 *= s; + e32 *= s; + e33 *= s; + return *this; +} + +inline Matrix3x3& Matrix3x3::operator/=(float s) +{ + e11 /= s; + e12 /= s; + e13 /= s; + e21 /= s; + e22 /= s; + e23 /= s; + e31 /= s; + e32 /= s; + e33 /= s; + return *this; +} + +inline Matrix3x3 operator+(Matrix3x3 m1, Matrix3x3 m2) +{ + return Matrix3x3( m1.e11 + m2.e11, + m1.e12 + m2.e12, + m1.e13 + m2.e13, + m1.e21 + m2.e21, + m1.e22 + m2.e22, + m1.e23 + m2.e23, + m1.e31 + m2.e31, + m1.e32 + m2.e32, + m1.e33 + m2.e33); +} + +inline Matrix3x3 operator-(Matrix3x3 m1, Matrix3x3 m2) +{ + return Matrix3x3( m1.e11 - m2.e11, + m1.e12 - m2.e12, + m1.e13 - m2.e13, + m1.e21 - m2.e21, + m1.e22 - m2.e22, + m1.e23 - m2.e23, + m1.e31 - m2.e31, + m1.e32 - m2.e32, + m1.e33 - m2.e33); +} + +inline Matrix3x3 operator/(Matrix3x3 m, float s) +{ + return Matrix3x3( m.e11 / s, + m.e12 / s, + m.e13 / s, + m.e21 / s, + m.e22 / s, + m.e23 / s, + m.e31 / s, + m.e32 / s, + m.e33 / s); +} + +inline Matrix3x3 operator*(Matrix3x3 m1, Matrix3x3 m2) +{ + return Matrix3x3( m1.e11 * m2.e11 + m1.e12 * m2.e21 + m1.e13 * m2.e31, + m1.e11 * m2.e12 + m1.e12 * m2.e22 + m1.e13 * m2.e32, + m1.e11 * m2.e13 + m1.e12 * m2.e23 + m1.e13 * m2.e33, + m1.e21 * m2.e11 + m1.e22 * m2.e21 + m1.e23 * m2.e31, + m1.e21 * m2.e12 + m1.e22 * m2.e22 + m1.e23 * m2.e32, + m1.e21 * m2.e13 + m1.e22 * m2.e23 + m1.e23 * m2.e33, + m1.e31 * m2.e11 + m1.e32 * m2.e21 + m1.e33 * m2.e31, + m1.e31 * m2.e12 + m1.e32 * m2.e22 + m1.e33 * m2.e32, + m1.e31 * m2.e13 + m1.e32 * m2.e23 + m1.e33 * m2.e33 ); +} + +inline Matrix3x3 operator*(Matrix3x3 m, float s) +{ + return Matrix3x3( m.e11 * s, + m.e12 * s, + m.e13 * s, + m.e21 * s, + m.e22 * s, + m.e23 * s, + m.e31 * s, + m.e32 * s, + m.e33 * s); +} + +inline Matrix3x3 operator*(float s, Matrix3x3 m) +{ + return Matrix3x3( m.e11 * s, + m.e12 * s, + m.e13 * s, + m.e21 * s, + m.e22 * s, + m.e23 * s, + m.e31 * s, + m.e32 * s, + m.e33 * s); +} + +inline Vector operator*(Matrix3x3 m, Vector u) +{ + return Vector( m.e11 * u.x + m.e12 * u.y + m.e13 * u.z, + m.e21 * u.x + m.e22 * u.y + m.e23 * u.z, + m.e31 * u.x + m.e32 * u.y + m.e33 * u.z); +} + +inline Vector operator*(Vector u, Matrix3x3 m) +{ + return Vector( u.x * m.e11 + u.y * m.e21 + u.z * m.e31, + u.x * m.e12 + u.y * m.e22 + u.z * m.e32, + u.x * m.e13 + u.y * m.e23 + u.z * m.e33); +} + +//------------------------------------------------------------------------// +// Quaternion Class and Quaternion functions +//------------------------------------------------------------------------// + +class Quaternion +{ +public: + float n; // number (scalar) part + Vector v; // vector part: v.x, v.y, v.z + + Quaternion(void); + Quaternion(float e0, float e1, float e2, float e3); + + float Magnitude(void); + Vector GetVector(void); + float GetScalar(void); + Quaternion operator+=(Quaternion q); + Quaternion operator-=(Quaternion q); + Quaternion operator*=(float s); + Quaternion operator/=(float s); + Quaternion operator~(void) const { + return Quaternion(n, -v.x, -v.y, -v.z); + } +}; + +inline Quaternion operator+(Quaternion q1, Quaternion q2); +inline Quaternion operator-(Quaternion q1, Quaternion q2); +inline Quaternion operator*(Quaternion q1, Quaternion q2); +inline Quaternion operator*(Quaternion q, float s); +inline Quaternion operator*(float s, Quaternion q); +inline Quaternion operator*(Quaternion q, Vector v); +inline Quaternion operator*(Vector v, Quaternion q); +inline Quaternion operator/(Quaternion q, float s); +inline float QGetAngle(Quaternion q); +inline Vector QGetAxis(Quaternion q); +inline Quaternion QRotate(Quaternion q1, Quaternion q2); +inline Vector QVRotate(Quaternion q, Vector v); +inline Quaternion MakeQFromEulerAngles(float x, float y, float z); +inline Vector MakeEulerAnglesFromQ(Quaternion q); + + +inline Quaternion::Quaternion(void) +{ + n = 0; + v.x = 0; + v.y = 0; + v.z = 0; +} + +inline Quaternion::Quaternion(float e0, float e1, float e2, float e3) +{ + n = e0; + v.x = e1; + v.y = e2; + v.z = e3; +} + +inline float Quaternion::Magnitude(void) +{ + return (float) sqrt(n * n + v.x * v.x + v.y * v.y + v.z * v.z); +} + +inline Vector Quaternion::GetVector(void) +{ + return Vector(v.x, v.y, v.z); +} + +inline float Quaternion::GetScalar(void) +{ + return n; +} + +inline Quaternion Quaternion::operator+=(Quaternion q) +{ + n += q.n; + v.x += q.v.x; + v.y += q.v.y; + v.z += q.v.z; + return *this; +} + +inline Quaternion Quaternion::operator-=(Quaternion q) +{ + n -= q.n; + v.x -= q.v.x; + v.y -= q.v.y; + v.z -= q.v.z; + return *this; +} + +inline Quaternion Quaternion::operator*=(float s) +{ + n *= s; + v.x *= s; + v.y *= s; + v.z *= s; + return *this; +} + +inline Quaternion Quaternion::operator/=(float s) +{ + n /= s; + v.x /= s; + v.y /= s; + v.z /= s; + return *this; +} + +/*inline Quaternion Quaternion::operator~() +{ +return Quaternion(n, -v.x, -v.y, -v.z); +}*/ + +inline Quaternion operator+(Quaternion q1, Quaternion q2) +{ + return Quaternion( q1.n + q2.n, + q1.v.x + q2.v.x, + q1.v.y + q2.v.y, + q1.v.z + q2.v.z); +} + +inline Quaternion operator-(Quaternion q1, Quaternion q2) +{ + return Quaternion( q1.n - q2.n, + q1.v.x - q2.v.x, + q1.v.y - q2.v.y, + q1.v.z - q2.v.z); +} + +inline Quaternion operator*(Quaternion q1, Quaternion q2) +{ + return Quaternion( q1.n * q2.n - q1.v.x * q2.v.x - q1.v.y * q2.v.y - q1.v.z * q2.v.z, + q1.n * q2.v.x + q1.v.x * q2.n + q1.v.y * q2.v.z - q1.v.z * q2.v.y, + q1.n * q2.v.y + q1.v.y * q2.n + q1.v.z * q2.v.x - q1.v.x * q2.v.z, + q1.n * q2.v.z + q1.v.z * q2.n + q1.v.x * q2.v.y - q1.v.y * q2.v.x); +} + +inline Quaternion operator*(Quaternion q, float s) +{ + return Quaternion(q.n * s, q.v.x * s, q.v.y * s, q.v.z * s); +} + +inline Quaternion operator*(float s, Quaternion q) +{ + return Quaternion(q.n * s, q.v.x * s, q.v.y * s, q.v.z * s); +} + +inline Quaternion operator*(Quaternion q, Vector v) +{ + return Quaternion( -(q.v.x * v.x + q.v.y * v.y + q.v.z * v.z), + q.n * v.x + q.v.y * v.z - q.v.z * v.y, + q.n * v.y + q.v.z * v.x - q.v.x * v.z, + q.n * v.z + q.v.x * v.y - q.v.y * v.x); +} + +inline Quaternion operator*(Vector v, Quaternion q) +{ + return Quaternion( -(q.v.x * v.x + q.v.y * v.y + q.v.z * v.z), + q.n * v.x + q.v.z * v.y - q.v.y * v.z, + q.n * v.y + q.v.x * v.z - q.v.z * v.x, + q.n * v.z + q.v.y * v.x - q.v.x * v.y); +} + +inline Quaternion operator/(Quaternion q, float s) +{ + return Quaternion(q.n / s, q.v.x / s, q.v.y / s, q.v.z / s); +} + +inline float QGetAngle(Quaternion q) +{ + return (float) (2 * acosf(q.n)); +} + +inline Vector QGetAxis(Quaternion q) +{ + Vector v; + float m; + + v = q.GetVector(); + m = v.Magnitude(); + + if (m <= tol) + return Vector(); + else + return v / m; +} + +inline Quaternion QRotate(Quaternion q1, Quaternion q2) +{ + return q1 * q2 * (~q1); +} + +inline Vector QVRotate(Quaternion q, Vector v) +{ + Quaternion t; + + + t = q * v * (~q); + + return t.GetVector(); +} + +inline Quaternion MakeQFromEulerAngles(float x, float y, float z) +{ + Quaternion q; + double roll = DegreesToRadians(x); + double pitch = DegreesToRadians(y); + double yaw = DegreesToRadians(z); + + double cyaw, cpitch, croll, syaw, spitch, sroll; + double cyawcpitch, syawspitch, cyawspitch, syawcpitch; + + cyaw = cos(0.5f * yaw); + cpitch = cos(0.5f * pitch); + croll = cos(0.5f * roll); + syaw = sin(0.5f * yaw); + spitch = sin(0.5f * pitch); + sroll = sin(0.5f * roll); + + cyawcpitch = cyaw * cpitch; + syawspitch = syaw * spitch; + cyawspitch = cyaw * spitch; + syawcpitch = syaw * cpitch; + + q.n = (float) (cyawcpitch * croll + syawspitch * sroll); + q.v.x = (float) (cyawcpitch * sroll - syawspitch * croll); + q.v.y = (float) (cyawspitch * croll + syawcpitch * sroll); + q.v.z = (float) (syawcpitch * croll - cyawspitch * sroll); + + return q; +} + +inline Vector MakeEulerAnglesFromQ(Quaternion q) +{ + double r11, r21, r31, r32, r33; + double q00, q11, q22, q33; + double tmp; + Vector u; + + q00 = q.n * q.n; + q11 = q.v.x * q.v.x; + q22 = q.v.y * q.v.y; + q33 = q.v.z * q.v.z; + + r11 = q00 + q11 - q22 - q33; + r21 = 2 * (q.v.x * q.v.y + q.n * q.v.z); + r31 = 2 * (q.v.x * q.v.z - q.n * q.v.y); + r32 = 2 * (q.v.y * q.v.z + q.n * q.v.x); + r33 = q00 - q11 - q22 + q33; + + tmp = fabs(r31); + if (tmp > 0.999999) { + double r12 = 2 * (q.v.x * q.v.y - q.n * q.v.z); + double r13 = 2 * (q.v.x * q.v.z + q.n * q.v.y); + + u.x = RadiansToDegrees(0.0f); //roll + u.y = RadiansToDegrees((float) (-(pi / 2) * r31 / tmp)); // pitch + u.z = RadiansToDegrees((float) atan2(-r12, -r31 * r13)); // yaw + return u; + } + + u.x = RadiansToDegrees((float) atan2(r32, r33)); // roll + u.y = RadiansToDegrees((float) asinf(-r31)); // pitch + u.z = RadiansToDegrees((float) atan2(r21, r11)); // yaw + return u; + + +} + + + + + +#endif diff --git a/Source/Math/Quaternions.cpp b/Source/Math/Quaternions.cpp new file mode 100644 index 0000000..7e68b97 --- /dev/null +++ b/Source/Math/Quaternions.cpp @@ -0,0 +1,534 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Math/Quaternions.h" + +// Functions +quaternion Quat_Mult(quaternion q1, quaternion q2) +{ + quaternion QResult; + float a, b, c, d, e, f, g, h; + a = (q1.w + q1.x) * (q2.w + q2.x); + b = (q1.z - q1.y) * (q2.y - q2.z); + c = (q1.w - q1.x) * (q2.y + q2.z); + d = (q1.y + q1.z) * (q2.w - q2.x); + e = (q1.x + q1.z) * (q2.x + q2.y); + f = (q1.x - q1.z) * (q2.x - q2.y); + g = (q1.w + q1.y) * (q2.w - q2.z); + h = (q1.w - q1.y) * (q2.w + q2.z); + QResult.w = b + (-e - f + g + h) / 2; + QResult.x = a - (e + f + g + h) / 2; + QResult.y = c + (e - f + g - h) / 2; + QResult.z = d + (e - f - g + h) / 2; + return QResult; +} + + + +quaternion To_Quat(Matrix_t m) +{ + // From Jason Shankel, (C) 2000. + static quaternion Quat; + + static double Tr = m[0][0] + m[1][1] + m[2][2] + 1.0, fourD; + static double q[4]; + + static int i, j, k; + if (Tr >= 1.0) { + fourD = 2.0 * fast_sqrt(Tr); + q[3] = fourD / 4.0; + q[0] = (m[2][1] - m[1][2]) / fourD; + q[1] = (m[0][2] - m[2][0]) / fourD; + q[2] = (m[1][0] - m[0][1]) / fourD; + } else { + if (m[0][0] > m[1][1]) { + i = 0; + } else { + i = 1; + } + if (m[2][2] > m[i][i]) { + i = 2; + } + j = (i + 1) % 3; + k = (j + 1) % 3; + fourD = 2.0 * fast_sqrt(m[i][i] - m[j][j] - m[k][k] + 1.0); + q[i] = fourD / 4.0; + q[j] = (m[j][i] + m[i][j]) / fourD; + q[k] = (m[k][i] + m[i][k]) / fourD; + q[3] = (m[j][k] - m[k][j]) / fourD; + } + + Quat.x = q[0]; + Quat.y = q[1]; + Quat.z = q[2]; + Quat.w = q[3]; + return Quat; +} +void Quat_2_Matrix(quaternion Quat, Matrix_t m) +{ + // From the GLVelocity site (http://glvelocity.gamedev.net) + float fW = Quat.w; + float fX = Quat.x; + float fY = Quat.y; + float fZ = Quat.z; + float fXX = fX * fX; + float fYY = fY * fY; + float fZZ = fZ * fZ; + m[0][0] = 1.0f - 2.0f * (fYY + fZZ); + m[1][0] = 2.0f * (fX * fY + fW * fZ); + m[2][0] = 2.0f * (fX * fZ - fW * fY); + m[3][0] = 0.0f; + m[0][1] = 2.0f * (fX * fY - fW * fZ); + m[1][1] = 1.0f - 2.0f * (fXX + fZZ); + m[2][1] = 2.0f * (fY * fZ + fW * fX); + m[3][1] = 0.0f; + m[0][2] = 2.0f * (fX * fZ + fW * fY); + m[1][2] = 2.0f * (fX * fZ - fW * fX); + m[2][2] = 1.0f - 2.0f * (fXX + fYY); + m[3][2] = 0.0f; + m[0][3] = 0.0f; + m[1][3] = 0.0f; + m[2][3] = 0.0f; + m[3][3] = 1.0f; +} +quaternion To_Quat(angle_axis Ang_Ax) +{ + // From the Quaternion Powers article on gamedev.net + static quaternion Quat; + + Quat.x = Ang_Ax.x * sin(Ang_Ax.angle / 2); + Quat.y = Ang_Ax.y * sin(Ang_Ax.angle / 2); + Quat.z = Ang_Ax.z * sin(Ang_Ax.angle / 2); + Quat.w = cos(Ang_Ax.angle / 2); + return Quat; +} +angle_axis Quat_2_AA(quaternion Quat) +{ + static angle_axis Ang_Ax; + static float scale, tw; + tw = (float)acosf(Quat.w) * 2; + scale = (float)sin(tw / 2.0); + Ang_Ax.x = Quat.x / scale; + Ang_Ax.y = Quat.y / scale; + Ang_Ax.z = Quat.z / scale; + + Ang_Ax.angle = 2.0 * acosf(Quat.w) / (float)PI * 180; + return Ang_Ax; +} + +quaternion To_Quat(int In_Degrees, euler Euler) +{ + // From the gamasutra quaternion article + static quaternion Quat; + static float cr, cp, cy, sr, sp, sy, cpcy, spsy; + //If we are in Degree mode, convert to Radians + if (In_Degrees) { + Euler.x = Euler.x * (float)PI / 180; + Euler.y = Euler.y * (float)PI / 180; + Euler.z = Euler.z * (float)PI / 180; + } + //Calculate trig identities + //Formerly roll, pitch, yaw + cr = float(cos(Euler.x / 2)); + cp = float(cos(Euler.y / 2)); + cy = float(cos(Euler.z / 2)); + sr = float(sin(Euler.x / 2)); + sp = float(sin(Euler.y / 2)); + sy = float(sin(Euler.z / 2)); + + cpcy = cp * cy; + spsy = sp * sy; + Quat.w = cr * cpcy + sr * spsy; + Quat.x = sr * cpcy - cr * spsy; + Quat.y = cr * sp * cy + sr * cp * sy; + Quat.z = cr * cp * sy - sr * sp * cy; + + return Quat; +} + +quaternion QNormalize(quaternion Quat) +{ + static float norm; + norm = Quat.x * Quat.x + + Quat.y * Quat.y + + Quat.z * Quat.z + + Quat.w * Quat.w; + Quat.x = float(Quat.x / norm); + Quat.y = float(Quat.y / norm); + Quat.z = float(Quat.z / norm); + Quat.w = float(Quat.w / norm); + return Quat; +} + +XYZ Quat2Vector(quaternion Quat) +{ + QNormalize(Quat); + + float fW = Quat.w; + float fX = Quat.x; + float fY = Quat.y; + float fZ = Quat.z; + + XYZ tempvec; + + tempvec.x = 2.0f * (fX * fZ - fW * fY); + tempvec.y = 2.0f * (fY * fZ + fW * fX); + tempvec.z = 1.0f - 2.0f * (fX * fX + fY * fY); + + return tempvec; +} + +bool PointInTriangle(Vector *p, Vector normal, float p11, float p12, float p13, float p21, float p22, float p23, float p31, float p32, float p33) +{ + static float u0, u1, u2; + static float v0, v1, v2; + static float a, b; + static float max; + static int i, j; + static bool bInter; + static float pointv[3]; + static float p1v[3]; + static float p2v[3]; + static float p3v[3]; + static float normalv[3]; + + bInter = 0; + + pointv[0] = p->x; + pointv[1] = p->y; + pointv[2] = p->z; + + + p1v[0] = p11; + p1v[1] = p12; + p1v[2] = p13; + + p2v[0] = p21; + p2v[1] = p22; + p2v[2] = p23; + + p3v[0] = p31; + p3v[1] = p32; + p3v[2] = p33; + + normalv[0] = normal.x; + normalv[1] = normal.y; + normalv[2] = normal.z; + +#define ABS(X) (((X)<0.f)?-(X):(X) ) +#define MAX(A, B) (((A)<(B))?(B):(A)) + max = MAX(MAX(ABS(normalv[0]), ABS(normalv[1])), ABS(normalv[2])); +#undef MAX + if (max == ABS(normalv[0])) { + i = 1; // y, z + j = 2; + } + if (max == ABS(normalv[1])) { + i = 0; // x, z + j = 2; + } + if (max == ABS(normalv[2])) { + i = 0; // x, y + j = 1; + } +#undef ABS + + u0 = pointv[i] - p1v[i]; + v0 = pointv[j] - p1v[j]; + u1 = p2v[i] - p1v[i]; + v1 = p2v[j] - p1v[j]; + u2 = p3v[i] - p1v[i]; + v2 = p3v[j] - p1v[j]; + + if (u1 > -1.0e-05f && u1 < 1.0e-05f) { // == 0.0f) + b = u0 / u2; + if (0.0f <= b && b <= 1.0f) { + a = (v0 - b * v2) / v1; + if ((a >= 0.0f) && (( a + b ) <= 1.0f)) + bInter = 1; + } + } else { + b = (v0 * u1 - u0 * v1) / (v2 * u1 - u2 * v1); + if (0.0f <= b && b <= 1.0f) { + a = (u0 - b * u2) / u1; + if ((a >= 0.0f) && (( a + b ) <= 1.0f )) + bInter = 1; + } + } + + return bInter; +} + +bool LineFacet(Vector p1, Vector p2, Vector pa, Vector pb, Vector pc, Vector *p) +{ + static float d; + static float denom, mu; + static Vector n; + + //Calculate the parameters for the plane + n.x = (pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y); + n.y = (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z); + n.z = (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x); + n.Normalize(); + d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; + + //Calculate the position on the line that intersects the plane + denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); + if (fabs(denom) < 0.0000001) // Line and plane don't intersect + return 0; + mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; + p->x = p1.x + mu * (p2.x - p1.x); + p->y = p1.y + mu * (p2.y - p1.y); + p->z = p1.z + mu * (p2.z - p1.z); + if (mu < 0 || mu > 1) // Intersection not along line segment + return 0; + + if (!PointInTriangle( p, n, pa.x, pa.y, pa.z, pb.x, pb.y, pb.z, pc.x, pc.y, pc.z)) { + return 0; + } + + return 1; +} + +bool PointInTriangle(XYZ *p, XYZ normal, XYZ *p1, XYZ *p2, XYZ *p3) +{ + static float u0, u1, u2; + static float v0, v1, v2; + static float a, b; + static float max; + static int i, j; + static bool bInter = 0; + static float pointv[3]; + static float p1v[3]; + static float p2v[3]; + static float p3v[3]; + static float normalv[3]; + + bInter = 0; + + pointv[0] = p->x; + pointv[1] = p->y; + pointv[2] = p->z; + + + p1v[0] = p1->x; + p1v[1] = p1->y; + p1v[2] = p1->z; + + p2v[0] = p2->x; + p2v[1] = p2->y; + p2v[2] = p2->z; + + p3v[0] = p3->x; + p3v[1] = p3->y; + p3v[2] = p3->z; + + normalv[0] = normal.x; + normalv[1] = normal.y; + normalv[2] = normal.z; + +#define ABS(X) (((X)<0.f)?-(X):(X) ) +#define MAX(A, B) (((A)<(B))?(B):(A)) + max = MAX(MAX(ABS(normalv[0]), ABS(normalv[1])), ABS(normalv[2])); +#undef MAX + if (max == ABS(normalv[0])) { + i = 1; // y, z + j = 2; + } + if (max == ABS(normalv[1])) { + i = 0; // x, z + j = 2; + } + if (max == ABS(normalv[2])) { + i = 0; // x, y + j = 1; + } +#undef ABS + + u0 = pointv[i] - p1v[i]; + v0 = pointv[j] - p1v[j]; + u1 = p2v[i] - p1v[i]; + v1 = p2v[j] - p1v[j]; + u2 = p3v[i] - p1v[i]; + v2 = p3v[j] - p1v[j]; + + if (u1 > -1.0e-05f && u1 < 1.0e-05f) { // == 0.0f) + b = u0 / u2; + if (0.0f <= b && b <= 1.0f) { + a = (v0 - b * v2) / v1; + if ((a >= 0.0f) && (( a + b ) <= 1.0f)) + bInter = 1; + } + } else { + b = (v0 * u1 - u0 * v1) / (v2 * u1 - u2 * v1); + if (0.0f <= b && b <= 1.0f) { + a = (u0 - b * u2) / u1; + if ((a >= 0.0f) && (( a + b ) <= 1.0f )) + bInter = 1; + } + } + + return bInter; +} + +bool LineFacet(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p) +{ + static float d; + static float denom, mu; + static XYZ n; + + //Calculate the parameters for the plane + n.x = (pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y); + n.y = (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z); + n.z = (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x); + Normalise(&n); + d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; + + //Calculate the position on the line that intersects the plane + denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); + if (fabs(denom) < 0.0000001) // Line and plane don't intersect + return 0; + mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; + p->x = p1.x + mu * (p2.x - p1.x); + p->y = p1.y + mu * (p2.y - p1.y); + p->z = p1.z + mu * (p2.z - p1.z); + if (mu < 0 || mu > 1) // Intersection not along line segment + return 0; + + if (!PointInTriangle( p, n, &pa, &pb, &pc)) { + return 0; + } + + return 1; +} + +float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p) +{ + static float d; + static float denom, mu; + static XYZ n; + + //Calculate the parameters for the plane + n.x = (pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y); + n.y = (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z); + n.z = (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x); + Normalise(&n); + d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; + + //Calculate the position on the line that intersects the plane + denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); + if (fabs(denom) < 0.0000001) // Line and plane don't intersect + return 0; + mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; + p->x = p1.x + mu * (p2.x - p1.x); + p->y = p1.y + mu * (p2.y - p1.y); + p->z = p1.z + mu * (p2.z - p1.z); + if (mu < 0 || mu > 1) // Intersection not along line segment + return 0; + + if (!PointInTriangle( p, n, &pa, &pb, &pc)) { + return 0; + } + + return 1; +} + +float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ n, XYZ *p) +{ + static float d; + static float denom, mu; + + //Calculate the parameters for the plane + d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; + + //Calculate the position on the line that intersects the plane + denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); + if (fabs(denom) < 0.0000001) // Line and plane don't intersect + return 0; + mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; + p->x = p1.x + mu * (p2.x - p1.x); + p->y = p1.y + mu * (p2.y - p1.y); + p->z = p1.z + mu * (p2.z - p1.z); + if (mu < 0 || mu > 1) // Intersection not along line segment + return 0; + + if (!PointInTriangle( p, n, &pa, &pb, &pc)) { + return 0; + } + return 1; +} + +float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *p) +{ + static float d; + static float denom, mu; + static XYZ n; + + //Calculate the parameters for the plane + n.x = (pb->y - pa->y) * (pc->z - pa->z) - (pb->z - pa->z) * (pc->y - pa->y); + n.y = (pb->z - pa->z) * (pc->x - pa->x) - (pb->x - pa->x) * (pc->z - pa->z); + n.z = (pb->x - pa->x) * (pc->y - pa->y) - (pb->y - pa->y) * (pc->x - pa->x); + Normalise(&n); + d = - n.x * pa->x - n.y * pa->y - n.z * pa->z; + + + //Calculate the position on the line that intersects the plane + denom = n.x * (p2->x - p1->x) + n.y * (p2->y - p1->y) + n.z * (p2->z - p1->z); + if (fabs(denom) < 0.0000001) // Line and plane don't intersect + return 0; + mu = - (d + n.x * p1->x + n.y * p1->y + n.z * p1->z) / denom; + p->x = p1->x + mu * (p2->x - p1->x); + p->y = p1->y + mu * (p2->y - p1->y); + p->z = p1->z + mu * (p2->z - p1->z); + if (mu < 0 || mu > 1) // Intersection not along line segment + return 0; + + if (!PointInTriangle( p, n, pa, pb, pc)) { + return 0; + } + return 1; +} + +float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *n, XYZ *p) +{ + static float d; + static float denom, mu; + + //Calculate the parameters for the plane + d = - n->x * pa->x - n->y * pa->y - n->z * pa->z; + + //Calculate the position on the line that intersects the plane + denom = n->x * (p2->x - p1->x) + n->y * (p2->y - p1->y) + n->z * (p2->z - p1->z); + if (fabs(denom) < 0.0000001) // Line and plane don't intersect + return 0; + mu = - (d + n->x * p1->x + n->y * p1->y + n->z * p1->z) / denom; + p->x = p1->x + mu * (p2->x - p1->x); + p->y = p1->y + mu * (p2->y - p1->y); + p->z = p1->z + mu * (p2->z - p1->z); + if (mu < 0 || mu > 1) // Intersection not along line segment + return 0; + + if (!PointInTriangle( p, *n, pa, pb, pc)) { + return 0; + } + return 1; +} + + diff --git a/Source/Math/Quaternions.h b/Source/Math/Quaternions.h new file mode 100644 index 0000000..de07302 --- /dev/null +++ b/Source/Math/Quaternions.h @@ -0,0 +1,499 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + + +#ifndef _QUATERNIONS_H_ +#define _QUATERNIONS_H_ + +#include "math.h" +#include "PhysicsMath.h" +#include "Graphic/gamegl.h" + +/**> Quaternion Structures <**/ +#define PI 3.14159265355555897932384626 +#define RADIANS 0 +#define DEGREES 1 +#define deg2rad .0174532925 + +//using namespace std; +typedef float Matrix_t [4][4]; +struct euler { + float x, y, z; +}; +struct angle_axis { + float x, y, z, angle; +}; +struct quaternion { + float x, y, z, w; +}; + +class XYZ +{ +public: + float x; + float y; + float z; + XYZ() : x(0.0f), y(0.0f), z(0.0f) {} + inline XYZ operator+(XYZ add); + inline XYZ operator-(XYZ add); + inline XYZ operator*(float add); + inline XYZ operator*(XYZ add); + inline XYZ operator/(float add); + inline void operator+=(XYZ add); + inline void operator-=(XYZ add); + inline void operator*=(float add); + inline void operator*=(XYZ add); + inline void operator/=(float add); + inline void operator=(float add); + inline void vec(Vector add); + inline bool operator==(XYZ add); +}; + +/*********************> Quaternion Function definition <********/ +quaternion To_Quat(int Degree_Flag, euler Euler); +quaternion To_Quat(angle_axis Ang_Ax); +quaternion To_Quat(Matrix_t m); +angle_axis Quat_2_AA(quaternion Quat); +void Quat_2_Matrix(quaternion Quat, Matrix_t m); +quaternion Normalize(quaternion Quat); +quaternion Quat_Mult(quaternion q1, quaternion q2); +quaternion QNormalize(quaternion Quat); +XYZ Quat2Vector(quaternion Quat); + +inline void CrossProduct(XYZ *P, XYZ *Q, XYZ *V); +inline void CrossProduct(XYZ P, XYZ Q, XYZ *V); +inline void Normalise(XYZ *vectory); +inline float normaldotproduct(XYZ point1, XYZ point2); +inline float fast_sqrt (register float arg); +bool PointInTriangle(XYZ *p, XYZ normal, XYZ *p1, XYZ *p2, XYZ *p3); +bool LineFacet(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p); +float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p); +float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ n, XYZ *p); +float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *n, XYZ *p); +float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *p); +bool PointInTriangle(Vector *p, Vector normal, float p11, float p12, float p13, float p21, float p22, float p23, float p31, float p32, float p33); +bool LineFacet(Vector p1, Vector p2, Vector pa, Vector pb, Vector pc, Vector *p); +inline void ReflectVector(XYZ *vel, const XYZ *n); +inline void ReflectVector(XYZ *vel, const XYZ &n); +inline XYZ DoRotation(XYZ thePoint, float xang, float yang, float zang); +inline XYZ DoRotationRadian(XYZ thePoint, float xang, float yang, float zang); +inline float findDistance(XYZ *point1, XYZ *point2); +inline float findLength(XYZ *point1); +inline float findLengthfast(XYZ *point1); +inline float distsq(XYZ *point1, XYZ *point2); +inline float distsq(XYZ point1, XYZ point2); +inline float distsqflat(XYZ *point1, XYZ *point2); +inline float dotproduct(const XYZ *point1, const XYZ *point2); +bool sphere_line_intersection ( + float x1, float y1 , float z1, + float x2, float y2 , float z2, + float x3, float y3 , float z3, float r ); +bool sphere_line_intersection ( + XYZ *p1, XYZ *p2, XYZ *p3, float *r ); +inline bool DistancePointLine( XYZ *Point, XYZ *LineStart, XYZ *LineEnd, float *Distance, XYZ *Intersection ); + + +inline void Normalise(XYZ *vectory) +{ + static float d; + d = fast_sqrt(vectory->x * vectory->x + vectory->y * vectory->y + vectory->z * vectory->z); + if (d == 0) { + return; + } + vectory->x /= d; + vectory->y /= d; + vectory->z /= d; +} + +inline XYZ XYZ::operator+(XYZ add) +{ + static XYZ ne; + ne = add; + ne.x += x; + ne.y += y; + ne.z += z; + return ne; +} + +inline XYZ XYZ::operator-(XYZ add) +{ + static XYZ ne; + ne = add; + ne.x = x - ne.x; + ne.y = y - ne.y; + ne.z = z - ne.z; + return ne; +} + +inline XYZ XYZ::operator*(float add) +{ + static XYZ ne; + ne.x = x * add; + ne.y = y * add; + ne.z = z * add; + return ne; +} + +inline XYZ XYZ::operator*(XYZ add) +{ + static XYZ ne; + ne.x = x * add.x; + ne.y = y * add.y; + ne.z = z * add.z; + return ne; +} + +inline XYZ XYZ::operator/(float add) +{ + static XYZ ne; + ne.x = x / add; + ne.y = y / add; + ne.z = z / add; + return ne; +} + +inline void XYZ::operator+=(XYZ add) +{ + x += add.x; + y += add.y; + z += add.z; +} + +inline void XYZ::operator-=(XYZ add) +{ + x = x - add.x; + y = y - add.y; + z = z - add.z; +} + +inline void XYZ::operator*=(float add) +{ + x = x * add; + y = y * add; + z = z * add; +} + +inline void XYZ::operator*=(XYZ add) +{ + x = x * add.x; + y = y * add.y; + z = z * add.z; +} + +inline void XYZ::operator/=(float add) +{ + x = x / add; + y = y / add; + z = z / add; +} + +inline void XYZ::operator=(float add) +{ + x = add; + y = add; + z = add; +} + +inline void XYZ::vec(Vector add) +{ + x = add.x; + y = add.y; + z = add.z; +} + +inline bool XYZ::operator==(XYZ add) +{ + if (x == add.x && y == add.y && z == add.z) + return 1; + return 0; +} + +inline void CrossProduct(XYZ *P, XYZ *Q, XYZ *V) +{ + V->x = P->y * Q->z - P->z * Q->y; + V->y = P->z * Q->x - P->x * Q->z; + V->z = P->x * Q->y - P->y * Q->x; +} + +inline void CrossProduct(XYZ P, XYZ Q, XYZ *V) +{ + V->x = P.y * Q.z - P.z * Q.y; + V->y = P.z * Q.x - P.x * Q.z; + V->z = P.x * Q.y - P.y * Q.x; +} + +inline float fast_sqrt (register float arg) +{ + return sqrtf(arg); +} + +inline float normaldotproduct(XYZ point1, XYZ point2) +{ + static GLfloat returnvalue; + Normalise(&point1); + Normalise(&point2); + returnvalue = (point1.x * point2.x + point1.y * point2.y + point1.z * point2.z); + return returnvalue; +} + +inline void ReflectVector(XYZ *vel, const XYZ *n) +{ + ReflectVector(vel, *n); +} + +inline void ReflectVector(XYZ *vel, const XYZ &n) +{ + static XYZ vn; + static XYZ vt; + static float dotprod; + + dotprod = dotproduct(&n, vel); + vn.x = n.x * dotprod; + vn.y = n.y * dotprod; + vn.z = n.z * dotprod; + + vt.x = vel->x - vn.x; + vt.y = vel->y - vn.y; + vt.z = vel->z - vn.z; + + vel->x = vt.x - vn.x; + vel->y = vt.y - vn.y; + vel->z = vt.z - vn.z; +} + +inline float dotproduct(const XYZ *point1, const XYZ *point2) +{ + static GLfloat returnvalue; + returnvalue = (point1->x * point2->x + point1->y * point2->y + point1->z * point2->z); + return returnvalue; +} + +inline float findDistance(XYZ *point1, XYZ *point2) +{ + return(fast_sqrt((point1->x - point2->x) * (point1->x - point2->x) + (point1->y - point2->y) * (point1->y - point2->y) + (point1->z - point2->z) * (point1->z - point2->z))); +} + +inline float findLength(XYZ *point1) +{ + return(fast_sqrt((point1->x) * (point1->x) + (point1->y) * (point1->y) + (point1->z) * (point1->z))); +} + + +inline float findLengthfast(XYZ *point1) +{ + return((point1->x) * (point1->x) + (point1->y) * (point1->y) + (point1->z) * (point1->z)); +} + +inline float distsq(XYZ *point1, XYZ *point2) +{ + return((point1->x - point2->x) * (point1->x - point2->x) + (point1->y - point2->y) * (point1->y - point2->y) + (point1->z - point2->z) * (point1->z - point2->z)); +} + +inline float distsq(XYZ point1, XYZ point2) +{ + return((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y) + (point1.z - point2.z) * (point1.z - point2.z)); +} + +inline float distsqflat(XYZ *point1, XYZ *point2) +{ + return((point1->x - point2->x) * (point1->x - point2->x) + (point1->z - point2->z) * (point1->z - point2->z)); +} + +inline XYZ DoRotation(XYZ thePoint, float xang, float yang, float zang) +{ + static XYZ newpoint; + if (xang) { + xang *= 6.283185f; + xang /= 360; + } + if (yang) { + yang *= 6.283185f; + yang /= 360; + } + if (zang) { + zang *= 6.283185f; + zang /= 360; + } + + + if (yang) { + newpoint.z = thePoint.z * cosf(yang) - thePoint.x * sinf(yang); + newpoint.x = thePoint.z * sinf(yang) + thePoint.x * cosf(yang); + thePoint.z = newpoint.z; + thePoint.x = newpoint.x; + } + + if (zang) { + newpoint.x = thePoint.x * cosf(zang) - thePoint.y * sinf(zang); + newpoint.y = thePoint.y * cosf(zang) + thePoint.x * sinf(zang); + thePoint.x = newpoint.x; + thePoint.y = newpoint.y; + } + + if (xang) { + newpoint.y = thePoint.y * cosf(xang) - thePoint.z * sinf(xang); + newpoint.z = thePoint.y * sinf(xang) + thePoint.z * cosf(xang); + thePoint.z = newpoint.z; + thePoint.y = newpoint.y; + } + + return thePoint; +} + +inline float square( float f ) +{ + return (f * f) ; +} + +inline bool sphere_line_intersection ( + float x1, float y1 , float z1, + float x2, float y2 , float z2, + float x3, float y3 , float z3, float r ) +{ + + // x1,y1,z1 P1 coordinates (point of line) + // x2,y2,z2 P2 coordinates (point of line) + // x3,y3,z3, r P3 coordinates and radius (sphere) + // x,y,z intersection coordinates + // + // This function returns a pointer array which first index indicates + // the number of intersection point, followed by coordinate pairs. + + //~ static float x , y , z; + static float a, b, c, /*mu,*/ i ; + + if (x1 > x3 + r && x2 > x3 + r) return(0); + if (x1 < x3 - r && x2 < x3 - r) return(0); + if (y1 > y3 + r && y2 > y3 + r) return(0); + if (y1 < y3 - r && y2 < y3 - r) return(0); + if (z1 > z3 + r && z2 > z3 + r) return(0); + if (z1 < z3 - r && z2 < z3 - r) return(0); + a = square(x2 - x1) + square(y2 - y1) + square(z2 - z1); + b = 2 * ( (x2 - x1) * (x1 - x3) + + (y2 - y1) * (y1 - y3) + + (z2 - z1) * (z1 - z3) ) ; + c = square(x3) + square(y3) + + square(z3) + square(x1) + + square(y1) + square(z1) - + 2 * ( x3 * x1 + y3 * y1 + z3 * z1 ) - square(r) ; + i = b * b - 4 * a * c ; + + if ( i < 0.0 ) { + // no intersection + return(0); + } + return(1); +} + +inline bool sphere_line_intersection ( + XYZ *p1, XYZ *p2, XYZ *p3, float *r ) +{ + + // x1,p1->y,p1->z P1 coordinates (point of line) + // p2->x,p2->y,p2->z P2 coordinates (point of line) + // p3->x,p3->y,p3->z, r P3 coordinates and radius (sphere) + // x,y,z intersection coordinates + // + // This function returns a pointer array which first index indicates + // the number of intersection point, followed by coordinate pairs. + + //~ static float x , y , z; + static float a, b, c, /*mu,*/ i ; + + if (p1->x > p3->x + *r && p2->x > p3->x + *r) return(0); + if (p1->x < p3->x - *r && p2->x < p3->x - *r) return(0); + if (p1->y > p3->y + *r && p2->y > p3->y + *r) return(0); + if (p1->y < p3->y - *r && p2->y < p3->y - *r) return(0); + if (p1->z > p3->z + *r && p2->z > p3->z + *r) return(0); + if (p1->z < p3->z - *r && p2->z < p3->z - *r) return(0); + a = square(p2->x - p1->x) + square(p2->y - p1->y) + square(p2->z - p1->z); + b = 2 * ( (p2->x - p1->x) * (p1->x - p3->x) + + (p2->y - p1->y) * (p1->y - p3->y) + + (p2->z - p1->z) * (p1->z - p3->z) ) ; + c = square(p3->x) + square(p3->y) + + square(p3->z) + square(p1->x) + + square(p1->y) + square(p1->z) - + 2 * ( p3->x * p1->x + p3->y * p1->y + p3->z * p1->z ) - square(*r) ; + i = b * b - 4 * a * c ; + + if ( i < 0.0 ) { + // no intersection + return(0); + } + return(1); +} + +inline XYZ DoRotationRadian(XYZ thePoint, float xang, float yang, float zang) +{ + static XYZ newpoint; + static XYZ oldpoint; + + oldpoint = thePoint; + + if (yang != 0) { + newpoint.z = oldpoint.z * cosf(yang) - oldpoint.x * sinf(yang); + newpoint.x = oldpoint.z * sinf(yang) + oldpoint.x * cosf(yang); + oldpoint.z = newpoint.z; + oldpoint.x = newpoint.x; + } + + if (zang != 0) { + newpoint.x = oldpoint.x * cosf(zang) - oldpoint.y * sinf(zang); + newpoint.y = oldpoint.y * cosf(zang) + oldpoint.x * sinf(zang); + oldpoint.x = newpoint.x; + oldpoint.y = newpoint.y; + } + + if (xang != 0) { + newpoint.y = oldpoint.y * cosf(xang) - oldpoint.z * sinf(xang); + newpoint.z = oldpoint.y * sinf(xang) + oldpoint.z * cosf(xang); + oldpoint.z = newpoint.z; + oldpoint.y = newpoint.y; + } + + return oldpoint; + +} + +inline bool DistancePointLine( XYZ *Point, XYZ *LineStart, XYZ *LineEnd, float *Distance, XYZ *Intersection ) +{ + float LineMag; + float U; + + LineMag = findDistance( LineEnd, LineStart ); + + U = ( ( ( Point->x - LineStart->x ) * ( LineEnd->x - LineStart->x ) ) + + ( ( Point->y - LineStart->y ) * ( LineEnd->y - LineStart->y ) ) + + ( ( Point->z - LineStart->z ) * ( LineEnd->z - LineStart->z ) ) ) / + ( LineMag * LineMag ); + + if ( U < 0.0f || U > 1.0f ) + return 0; // closest point does not fall within the line segment + + Intersection->x = LineStart->x + U * ( LineEnd->x - LineStart->x ); + Intersection->y = LineStart->y + U * ( LineEnd->y - LineStart->y ); + Intersection->z = LineStart->z + U * ( LineEnd->z - LineStart->z ); + + *Distance = findDistance( Point, Intersection ); + + return 1; +} + +#endif diff --git a/Source/Math/Random.h b/Source/Math/Random.h new file mode 100644 index 0000000..096cedc --- /dev/null +++ b/Source/Math/Random.h @@ -0,0 +1,31 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _RANDOM_H_ +#define _RANDOM_H_ + +#include + +static inline short Random() +{ + return rand(); +} + +#endif diff --git a/Source/Menu.cpp b/Source/Menu.cpp deleted file mode 100644 index f1fb94f..0000000 --- a/Source/Menu.cpp +++ /dev/null @@ -1,974 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include -#include -#include -#include "gamegl.h" - -#include "Menu.h" -#include "Settings.h" -#include "Input.h" -#include "Campaign.h" - -// Should not be needed, Menu should call methods from other classes to launch maps and challenges and so on -#include "Awards.h" -#include "openal_wrapper.h" - -using namespace Game; - -extern float multiplier; -extern std::set> resolutions; -extern int mainmenu; -extern std::vector campaignlevels; -extern float musicvolume[4]; -extern float oldmusicvolume[4]; -extern bool stillloading; -extern bool visibleloading; -extern int whichchoice; -extern int leveltheme; - -extern void toggleFullscreen(); - -int entername = 0; - -std::vector Menu::items; - -MenuItem::MenuItem(MenuItemType _type, int _id, const string& _text, Texture _texture, - int _x, int _y, int _w, int _h, float _r, float _g, float _b, - float _linestartsize, float _lineendsize): - type(_type), - id(_id), - text(_text), - texture(_texture), - x(_x), - y(_y), - w(_w), - h(_h), - r(_r), - g(_g), - b(_b), - effectfade(0), - linestartsize(_linestartsize), - lineendsize(_lineendsize) -{ - if (type == MenuItem::BUTTON) { - if (w == -1) { - w = text.length() * 10; - } - if (h == -1) { - h = 20; - } - } -} - -void Menu::clearMenu() -{ - items.clear(); -} - -void Menu::addLabel(int id, const string& text, int x, int y, float r, float g, float b) -{ - items.emplace_back(MenuItem::LABEL, id, text, Texture(), x, y, -1, -1, r, g, b); -} -void Menu::addButton(int id, const string& text, int x, int y, float r, float g, float b) -{ - items.emplace_back(MenuItem::BUTTON, id, text, Texture(), x, y, -1, -1, r, g, b); -} -void Menu::addImage(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b) -{ - items.emplace_back(MenuItem::IMAGE, id, "", texture, x, y, w, h, r, g, b); -} -void Menu::addButtonImage(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b) -{ - items.emplace_back(MenuItem::IMAGEBUTTON, id, "", texture, x, y, w, h, r, g, b); -} -void Menu::addMapLine(int x, int y, int w, int h, float startsize, float endsize, float r, float g, float b) -{ - items.emplace_back(MenuItem::MAPLINE, -1, "", Texture(), x, y, w, h, r, g, b, startsize, endsize); -} -void Menu::addMapMarker(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b) -{ - items.emplace_back(MenuItem::MAPMARKER, id, "", texture, x, y, w, h, r, g, b); -} -void Menu::addMapLabel(int id, const string& text, int x, int y, float r, float g, float b) -{ - items.emplace_back(MenuItem::MAPLABEL, id, text, Texture(), x, y, -1, -1, r, g, b); -} - -void Menu::setText(int id, const string& text) -{ - for (vector::iterator it = items.begin(); it != items.end(); it++) - if (it->id == id) { - it->text = text; - it->w = it->text.length() * 10; - break; - } -} - -void Menu::setText(int id, const string& text, int x, int y, int w, int h) -{ - for (vector::iterator it = items.begin(); it != items.end(); it++) - if (it->id == id) { - it->text = text; - it->x = x; - it->y = y; - if (w == -1) - it->w = it->text.length() * 10; - if (h == -1) - it->h = 20; - break; - } -} - -int Menu::getSelected(int mousex, int mousey) -{ - for (vector::iterator it = items.begin(); it != items.end(); it++) - if (it->type == MenuItem::BUTTON || it->type == MenuItem::IMAGEBUTTON || it->type == MenuItem::MAPMARKER) { - int mx = mousex; - int my = mousey; - if (it->type == MenuItem::MAPMARKER) { - mx -= 1; - my += 2; - } - if (mx >= it->x && mx < it->x + it->w && my >= it->y && my < it->y + it->h) - return it->id; - } - return -1; -} - -void Menu::handleFadeEffect() -{ - for (vector::iterator it = items.begin(); it != items.end(); it++) { - if (it->id == Game::selected) { - it->effectfade += multiplier * 5; - if (it->effectfade > 1) - it->effectfade = 1; - } else { - it->effectfade -= multiplier * 5; - if (it->effectfade < 0) - it->effectfade = 0; - } - } -} - -void Menu::drawItems() -{ - handleFadeEffect(); - glEnable(GL_TEXTURE_2D); - glEnable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - for (vector::iterator it = items.begin(); it != items.end(); it++) { - switch (it->type) { - case MenuItem::IMAGE: - case MenuItem::IMAGEBUTTON: - case MenuItem::MAPMARKER: - glColor4f(it->r, it->g, it->b, 1); - glPushMatrix(); - if (it->type == MenuItem::MAPMARKER) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glTranslatef(2.5, -4.5, 0); //from old code - } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } - it->texture.bind(); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex3f(it->x, it->y, 0); - glTexCoord2f(1, 0); - glVertex3f(it->x + it->w, it->y, 0); - glTexCoord2f(1, 1); - glVertex3f(it->x + it->w, it->y + it->h, 0); - glTexCoord2f(0, 1); - glVertex3f(it->x, it->y + it->h, 0); - glEnd(); - if (it->type != MenuItem::IMAGE) { - //mouseover highlight - for (int i = 0; i < 10; i++) { - if (1 - ((float)i) / 10 - (1 - it->effectfade) > 0) { - glColor4f(it->r, it->g, it->b, (1 - ((float)i) / 10 - (1 - it->effectfade))*.25); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex3f(it->x - ((float)i) * 1 / 2, it->y - ((float)i) * 1 / 2, 0); - glTexCoord2f(1, 0); - glVertex3f(it->x + it->w + ((float)i) * 1 / 2, it->y - ((float)i) * 1 / 2, 0); - glTexCoord2f(1, 1); - glVertex3f(it->x + it->w + ((float)i) * 1 / 2, it->y + it->h + ((float)i) * 1 / 2, 0); - glTexCoord2f(0, 1); - glVertex3f(it->x - ((float)i) * 1 / 2, it->y + it->h + ((float)i) * 1 / 2, 0); - glEnd(); - } - } - } - glPopMatrix(); - break; - case MenuItem::LABEL: - case MenuItem::BUTTON: - glColor4f(it->r, it->g, it->b, 1); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Game::text->glPrint(it->x, it->y, it->text.c_str(), 0, 1, 640, 480); - if (it->type != MenuItem::LABEL) { - //mouseover highlight - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - for (int i = 0; i < 15; i++) { - if (1 - ((float)i) / 15 - (1 - it->effectfade) > 0) { - glColor4f(it->r, it->g, it->b, (1 - ((float)i) / 10 - (1 - it->effectfade))*.25); - Game::text->glPrint(it->x - ((float)i), it->y, it->text.c_str(), 0, 1 + ((float)i) / 70, 640, 480); - } - } - } - break; - case MenuItem::MAPLABEL: - Game::text->glPrintOutlined(0.9, 0, 0, it->x, it->y, it->text.c_str(), 0, 0.6, 640, 480); - break; - case MenuItem::MAPLINE: { - XYZ linestart; - linestart.x = it->x; - linestart.y = it->y; - linestart.z = 0; - XYZ lineend; - lineend.x = it->x + it->w; - lineend.y = it->y + it->h; - lineend.z = 0; - XYZ offset = lineend - linestart; - XYZ fac = offset; - Normalise(&fac); - offset = DoRotation(offset, 0, 0, 90); - Normalise(&offset); - - linestart += fac * 4 * it->linestartsize; - lineend -= fac * 4 * it->lineendsize; - - glDisable(GL_TEXTURE_2D); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(it->r, it->g, it->b, 1); - glPushMatrix(); - glTranslatef(2, -5, 0); //from old code - glBegin(GL_QUADS); - glVertex3f(linestart.x - offset.x * it->linestartsize, linestart.y - offset.y * it->linestartsize, 0.0f); - glVertex3f(linestart.x + offset.x * it->linestartsize, linestart.y + offset.y * it->linestartsize, 0.0f); - glVertex3f(lineend.x + offset.x * it->lineendsize, lineend.y + offset.y * it->lineendsize, 0.0f); - glVertex3f(lineend.x - offset.x * it->lineendsize, lineend.y - offset.y * it->lineendsize, 0.0f); - glEnd(); - glPopMatrix(); - glEnable(GL_TEXTURE_2D); - } - break; - default: - case MenuItem::NONE: - break; - } - } -} - -void Menu::updateSettingsMenu() -{ - std::string sbuf = std::string("Resolution: ") + to_string(newscreenwidth) + "*" + to_string(newscreenheight); - if (((float)newscreenwidth <= (float)newscreenheight * 1.61) && ((float)newscreenwidth >= (float)newscreenheight * 1.59)) { - sbuf += " (widescreen)"; - } - setText(0, sbuf); - setText(14, fullscreen ? "Fullscreen: On" : "Fullscreen: Off"); - if (newdetail == 0) setText(1, "Detail: Low"); - if (newdetail == 1) setText(1, "Detail: Medium"); - if (newdetail == 2) setText(1, "Detail: High"); - if (bloodtoggle == 0) setText(2, "Blood: Off"); - if (bloodtoggle == 1) setText(2, "Blood: On, low detail"); - if (bloodtoggle == 2) setText(2, "Blood: On, high detail (slower)"); - setText(4, ismotionblur ? "Blur Effects: Enabled (less compatible)" : "Blur Effects: Disabled (more compatible)"); - setText(5, decals ? "Decals: Enabled (slower)" : "Decals: Disabled"); - setText(6, musictoggle ? "Music: Enabled" : "Music: Disabled"); - setText(9, invertmouse ? "Invert mouse: Yes" : "Invert mouse: No"); - setText(10, std::string("Mouse Speed: ") + to_string(int(usermousesensitivity * 5))); - setText(11, std::string("Volume: ") + to_string(int(volume * 100)) + "%"); - setText(13, showdamagebar ? "Damage Bar: On" : "Damage Bar: Off"); - if ((newdetail == detail) && (newscreenheight == (int)screenheight) && (newscreenwidth == (int)screenwidth)) { - setText(8, "Back"); - } else { - setText(8, "Back (some changes take effect next time Lugaru is opened)"); - } -} - -void Menu::updateStereoConfigMenu() -{ - setText(0, std::string("Stereo mode: ") + StereoModeName(newstereomode)); - setText(1, std::string("Stereo separation: ") + to_string(stereoseparation)); - setText(2, std::string("Reverse stereo: ") + (stereoreverse ? "Yes" : "No")); -} - -void Menu::updateControlsMenu() -{ - setText(0, (string)"Forwards: " + (keyselect == 0 ? "_" : Input::keyToChar(forwardkey))); - setText(1, (string)"Back: " + (keyselect == 1 ? "_" : Input::keyToChar(backkey))); - setText(2, (string)"Left: " + (keyselect == 2 ? "_" : Input::keyToChar(leftkey))); - setText(3, (string)"Right: " + (keyselect == 3 ? "_" : Input::keyToChar(rightkey))); - setText(4, (string)"Crouch: " + (keyselect == 4 ? "_" : Input::keyToChar(crouchkey))); - setText(5, (string)"Jump: " + (keyselect == 5 ? "_" : Input::keyToChar(jumpkey))); - setText(6, (string)"Draw: " + (keyselect == 6 ? "_" : Input::keyToChar(drawkey))); - setText(7, (string)"Throw: " + (keyselect == 7 ? "_" : Input::keyToChar(throwkey))); - setText(8, (string)"Attack: " + (keyselect == 8 ? "_" : Input::keyToChar(attackkey))); - if (devtools) { - setText(9, (string)"Console: " + (keyselect == 9 ? "_" : Input::keyToChar(consolekey))); - } -} - -/* -Values of mainmenu : -1 Main menu -2 Menu pause (resume/end game) -3 Option menu -4 Controls configuration menu -5 Main game menu (choose level or challenge) -6 Deleting user menu -7 User managment menu (select/add) -8 Choose difficulty menu -9 Challenge level selection menu -10 End of the campaign congratulation (is that really a menu?) -11 Same that 9 ??? => unused -18 stereo configuration -*/ - -void Menu::Load() -{ - clearMenu(); - switch (mainmenu) { - case 1: - case 2: - addImage(0, Mainmenuitems[0], 150, 480 - 128, 256, 128); - addButtonImage(1, Mainmenuitems[mainmenu == 1 ? 1 : 5], 18, 480 - 152 - 32, 128, 32); - addButtonImage(2, Mainmenuitems[2], 18, 480 - 228 - 32, 112, 32); - addButtonImage(3, Mainmenuitems[mainmenu == 1 ? 3 : 6], 18, 480 - 306 - 32, mainmenu == 1 ? 68 : 132, 32); - break; - case 3: - addButton( 0, "", 10 + 20, 440); - addButton(14, "", 10 + 400, 440); - addButton( 1, "", 10 + 60, 405); - addButton( 2, "", 10 + 70, 370); - addButton( 4, "", 10 , 335); - addButton( 5, "", 10 + 60, 300); - addButton( 6, "", 10 + 70, 265); - addButton( 9, "", 10 , 230); - addButton(10, "", 20 , 195); - addButton(11, "", 10 + 60, 160); - addButton(13, "", 30 , 125); - addButton( 7, "-Configure Controls-", 10 + 15, 90); - addButton(12, "-Configure Stereo -", 10 + 15, 55); - addButton(8, "Back", 10, 10); - updateSettingsMenu(); - break; - case 4: - addButton(0, "", 10 , 400); - addButton(1, "", 10 + 40, 360); - addButton(2, "", 10 + 40, 320); - addButton(3, "", 10 + 30, 280); - addButton(4, "", 10 + 20, 240); - addButton(5, "", 10 + 40, 200); - addButton(6, "", 10 + 40, 160); - addButton(7, "", 10 + 30, 120); - addButton(8, "", 10 + 20, 80); - if (devtools) { - addButton(9, "", 10 + 10, 40); - } - addButton(devtools ? 10 : 9, "Back", 10, 10); - updateControlsMenu(); - break; - case 5: { - LoadCampaign(); - addLabel(-1, Account::active().getName(), 5, 400); - addButton(1, "Tutorial", 5, 300); - addButton(2, "Challenge", 5, 240); - addButton(3, "Delete User", 400, 10); - addButton(4, "Main Menu", 5, 10); - addButton(5, "Change User", 5, 180); - addButton(6, "Campaign : " + Account::active().getCurrentCampaign(), 200, 420); - - //show campaign map - //with (2,-5) offset from old code - addImage(-1, Mainmenuitems[7], 150 + 2, 60 - 5, 400, 400); - //show levels - int numlevels = Account::active().getCampaignChoicesMade(); - numlevels += numlevels > 0 ? campaignlevels[numlevels - 1].nextlevel.size() : 1; - for (int i = 0; i < numlevels; i++) { - XYZ midpoint = campaignlevels[i].getCenter(); - float itemsize = campaignlevels[i].getWidth(); - const bool active = (i >= Account::active().getCampaignChoicesMade()); - if (!active) { - itemsize /= 2; - } - - if (i >= 1) { - XYZ start = campaignlevels[i - 1].getCenter(); - addMapLine(start.x, start.y, midpoint.x - start.x, midpoint.y - start.y, 0.5, active ? 1 : 0.5, active ? 1 : 0.5, 0, 0); - } - addMapMarker(NB_CAMPAIGN_MENU_ITEM + i, Mapcircletexture, - midpoint.x - itemsize / 2, midpoint.y - itemsize / 2, itemsize, itemsize, active ? 1 : 0.5, 0, 0); - - if (active) { - addMapLabel(-2, campaignlevels[i].description, - campaignlevels[i].getStartX() + 10, - campaignlevels[i].getStartY() - 4); - } - } - } - break; - case 6: - addLabel(-1, "Are you sure you want to delete this user?", 10, 400); - addButton(1, "Yes", 10, 360); - addButton(2, "No", 10, 320); - break; - case 7: - if (Account::getNbAccounts() < 8) - addButton(0, "New User", 10, 400); - else - addLabel(0, "No More Users", 10, 400); - addLabel(-2, "", 20, 400); - addButton(Account::getNbAccounts() + 1, "Back", 10, 10); - for (int i = 0; i < Account::getNbAccounts(); i++) { - addButton(i + 1, Account::get(i).getName(), 10, 340 - 20 * (i + 1)); - } - break; - case 8: - addButton(0, "Easier", 10, 400); - addButton(1, "Difficult", 10, 360); - addButton(2, "Insane", 10, 320); - break; - case 9: - for (int i = 0; i < numchallengelevels; i++) { - string name = "Level "; - name += to_string(i + 1); - if (name.size() < 17) { - name.append((17 - name.size()), ' '); - } - name += to_string(int(Account::active().getHighScore(i))); - if (name.size() < 32) { - name.append((32 - name.size()), ' '); - } - int fasttime = (int)round(Account::active().getFastTime(i)); - name += to_string(int((fasttime - fasttime % 60) / 60)); - name += ":"; - if (fasttime % 60 < 10) - name += "0"; - name += to_string(fasttime % 60); - - addButton(i, name, 10, 400 - i * 25, i > Account::active().getProgress() ? 0.5 : 1, 0, 0); - } - - addButton(-1, " High Score Best Time", 10, 440); - addButton(numchallengelevels, "Back", 10, 10); - break; - case 10: { - addLabel(0, "Congratulations!", 220, 330); - addLabel(1, "You have avenged your family and", 140, 300); - addLabel(2, "restored peace to the island of Lugaru.", 110, 270); - addButton(3, "Back", 10, 10); - addLabel(4, string("Your score: ") + to_string((int)Account::active().getCampaignScore()), 190, 200); - addLabel(5, string("Highest score: ") + to_string((int)Account::active().getCampaignHighScore()), 190, 180); - } - break; - case 18: - addButton(0, "", 70, 400); - addButton(1, "", 10, 360); - addButton(2, "", 40, 320); - addButton(3, "Back", 10, 10); - updateStereoConfigMenu(); - break; - } -} - -void Menu::Tick() -{ - //escape key pressed - if (Input::isKeyPressed(SDL_SCANCODE_ESCAPE) && - (mainmenu >= 3) && (mainmenu != 8) && !((mainmenu == 7) && entername)) { - selected = -1; - //finished with settings menu - if (mainmenu == 3) { - SaveSettings(); - } - //effects - if (mainmenu >= 3 && mainmenu != 8) { - fireSound(); - flash(); - } - //go back - switch (mainmenu) { - case 3: - case 5: - mainmenu = gameon ? 2 : 1; - break; - case 4: - case 18: - mainmenu = 3; - break; - case 6: - case 7: - case 9: - case 10: - mainmenu = 5; - break; - } - } - - //menu buttons - selected = getSelected(mousecoordh * 640 / screenwidth, 480 - mousecoordv * 480 / screenheight); - - // some specific case where we do something even if the left mouse button is not pressed. - if ((mainmenu == 5) && (endgame == 2)) { - Account::active().endGame(); - endgame = 0; - } - if (mainmenu == 10) - endgame = 2; - if (mainmenu == 18 && Input::isKeyPressed(MOUSEBUTTON2) && selected == 1) { - stereoseparation -= 0.001; - updateStereoConfigMenu(); - } - - static int oldmainmenu = mainmenu; - - if (Input::MouseClicked() && (selected >= 0)) { // handling of the left mouse clic in menus - set>::iterator newscreenresolution; - switch (mainmenu) { - case 1: - case 2: - switch (selected) { - case 1: - if (gameon) { //resume - mainmenu = 0; - pause_sound(stream_menutheme); - resume_stream(leveltheme); - } else { //new game - fireSound(firestartsound); - flash(); - mainmenu = (Account::hasActive() ? 5 : 7); - selected = -1; - } - break; - case 2: //options - fireSound(); - flash(); - mainmenu = 3; - if (newdetail > 2) - newdetail = detail; - if (newdetail < 0) - newdetail = detail; - if (newscreenwidth > 3000) - newscreenwidth = screenwidth; - if (newscreenwidth < 0) - newscreenwidth = screenwidth; - if (newscreenheight > 3000) - newscreenheight = screenheight; - if (newscreenheight < 0) - newscreenheight = screenheight; - break; - case 3: - fireSound(); - flash(); - if (gameon) { //end game - gameon = 0; - mainmenu = 1; - } else { //quit - tryquit = 1; - pause_sound(stream_menutheme); - } - break; - } - break; - case 3: - fireSound(); - switch (selected) { - case 0: - newscreenresolution = resolutions.find(make_pair(newscreenwidth, newscreenheight)); - /* Next one (end() + 1 is also end() so the ++ is safe even if it was not found) */ - newscreenresolution++; - if (newscreenresolution == resolutions.end()) { - /* It was the last one (or not found), go back to the beginning */ - newscreenresolution = resolutions.begin(); - } - newscreenwidth = newscreenresolution->first; - newscreenheight = newscreenresolution->second; - break; - case 1: - newdetail++; - if (newdetail > 2) - newdetail = 0; - break; - case 2: - bloodtoggle++; - if (bloodtoggle > 2) - bloodtoggle = 0; - break; - case 4: - ismotionblur = !ismotionblur; - break; - case 5: - decals = !decals; - break; - case 6: - musictoggle = !musictoggle; - if (musictoggle) { - emit_stream_np(stream_menutheme); - } else { - pause_sound(leveltheme); - pause_sound(stream_fighttheme); - pause_sound(stream_menutheme); - - for (int i = 0; i < 4; i++) { - oldmusicvolume[i] = 0; - musicvolume[i] = 0; - } - } - break; - case 7: // controls - flash(); - mainmenu = 4; - selected = -1; - keyselect = -1; - break; - case 8: - flash(); - SaveSettings(); - mainmenu = gameon ? 2 : 1; - break; - case 9: - invertmouse = !invertmouse; - break; - case 10: - usermousesensitivity += .2; - if (usermousesensitivity > 2) - usermousesensitivity = .2; - break; - case 11: - volume += .1f; - if (volume > 1.0001f) - volume = 0; - OPENAL_SetSFXMasterVolume((int)(volume * 255)); - break; - case 12: - flash(); - newstereomode = stereomode; - mainmenu = 18; - keyselect = -1; - break; - case 13: - showdamagebar = !showdamagebar; - break; - case 14: - toggleFullscreen(); - break; - } - updateSettingsMenu(); - break; - case 4: - if (!waiting) { - fireSound(); - if (selected < (devtools ? 10 : 9) && keyselect == -1) - keyselect = selected; - if (keyselect != -1) - setKeySelected(); - if (selected == (devtools ? 10 : 9)) { - flash(); - mainmenu = 3; - } - } - updateControlsMenu(); - break; - case 5: - fireSound(); - flash(); - if ((selected - NB_CAMPAIGN_MENU_ITEM >= Account::active().getCampaignChoicesMade())) { - startbonustotal = 0; - - loading = 2; - loadtime = 0; - targetlevel = 7; - if (firstload) - TickOnceAfter(); - else - LoadStuff(); - whichchoice = selected - NB_CAMPAIGN_MENU_ITEM - Account::active().getCampaignChoicesMade(); - actuallevel = (Account::active().getCampaignChoicesMade() > 0 ? campaignlevels[Account::active().getCampaignChoicesMade() - 1].nextlevel[whichchoice] : 0); - visibleloading = 1; - stillloading = 1; - Loadlevel(campaignlevels[actuallevel].mapname.c_str()); - campaign = 1; - mainmenu = 0; - gameon = 1; - pause_sound(stream_menutheme); - } - switch (selected) { - case 1: - startbonustotal = 0; - - loading = 2; - loadtime = 0; - targetlevel = -1; - if (firstload) { - TickOnceAfter(); - } else - LoadStuff(); - Loadlevel(-1); - - mainmenu = 0; - gameon = 1; - pause_sound(stream_menutheme); - break; - case 2: - mainmenu = 9; - break; - case 3: - mainmenu = 6; - break; - case 4: - mainmenu = (gameon ? 2 : 1); - break; - case 5: - mainmenu = 7; - break; - case 6: - vector campaigns = ListCampaigns(); - vector::iterator c; - if ((c = find(campaigns.begin(), campaigns.end(), Account::active().getCurrentCampaign())) == campaigns.end()) { - if (!campaigns.empty()) - Account::active().setCurrentCampaign(campaigns.front()); - } else { - c++; - if (c == campaigns.end()) - c = campaigns.begin(); - Account::active().setCurrentCampaign(*c); - } - Load(); - break; - } - break; - case 6: - fireSound(); - if (selected == 1) { - flash(); - Account::destroyActive(); - mainmenu = 7; - } else if (selected == 2) { - flash(); - mainmenu = 5; - } - break; - case 7: - fireSound(); - if (selected == 0 && Account::getNbAccounts() < 8) { - entername = 1; - } else if (selected < Account::getNbAccounts() + 1) { - flash(); - mainmenu = 5; - Account::setActive(selected - 1); - } else if (selected == Account::getNbAccounts() + 1) { - flash(); - if (Account::hasActive()) { - mainmenu = 5; - } else { - mainmenu = 1; - } - displaytext[0].clear(); - displayselected = 0; - entername = 0; - } - break; - case 8: - fireSound(); - flash(); - if (selected <= 2) - Account::active().setDifficulty(selected); - mainmenu = 5; - break; - case 9: - if (selected < numchallengelevels && selected <= Account::active().getProgress()) { - fireSound(); - flash(); - - startbonustotal = 0; - - loading = 2; - loadtime = 0; - targetlevel = selected; - if (firstload) - TickOnceAfter(); - else - LoadStuff(); - Loadlevel(selected); - campaign = 0; - - mainmenu = 0; - gameon = 1; - pause_sound(stream_menutheme); - } - if (selected == numchallengelevels) { - fireSound(); - flash(); - mainmenu = 5; - } - break; - case 10: - if (selected == 3) { - fireSound(); - flash(); - mainmenu = 5; - } - break; - case 18: - if (selected == 1) - stereoseparation += 0.001; - else { - fireSound(); - if (selected == 0) { - newstereomode = (StereoMode)(newstereomode + 1); - while (!CanInitStereo(newstereomode)) { - printf("Failed to initialize mode %s (%i)\n", StereoModeName(newstereomode).c_str(), newstereomode); - newstereomode = (StereoMode)(newstereomode + 1); - if (newstereomode >= stereoCount) - newstereomode = stereoNone; - } - } else if (selected == 2) { - stereoreverse = !stereoreverse; - } else if (selected == 3) { - flash(); - mainmenu = 3; - - stereomode = newstereomode; - InitStereo(stereomode); - } - } - updateStereoConfigMenu(); - break; - } - } - - OPENAL_SetFrequency(channels[stream_menutheme]); - - if (entername) { - inputText(displaytext[0], &displayselected); - if (!waiting) { // the input as finished - if (!displaytext[0].empty()) { // with enter - Account::add(string(displaytext[0])); - - mainmenu = 8; - - flash(); - - fireSound(firestartsound); - - displaytext[0].clear(); - - displayselected = 0; - } - entername = 0; - Load(); - } - - displayblinkdelay -= multiplier; - if (displayblinkdelay <= 0) { - displayblinkdelay = .3; - displayblink = !displayblink; - } - } - - if (entername) { - setText(0, displaytext[0], 20, 400, -1, -1); - setText(-2, displayblink ? "_" : "", 20 + displayselected * 10, 400, -1, -1); - } - - if (oldmainmenu != mainmenu) - Load(); - oldmainmenu = mainmenu; - -} - -int setKeySelected_thread(void* data) -{ - using namespace Game; - int scancode = -1; - SDL_Event evenement; - while (scancode == -1) { - SDL_WaitEvent(&evenement); - switch (evenement.type) { - case SDL_KEYDOWN: - scancode = evenement.key.keysym.scancode; - break; - case SDL_MOUSEBUTTONDOWN: - scancode = SDL_NUM_SCANCODES + evenement.button.button; - break; - default: - break; - } - } - if (scancode != SDL_SCANCODE_ESCAPE) { - fireSound(); - switch (keyselect) { - case 0: - forwardkey = scancode; - break; - case 1: - backkey = scancode; - break; - case 2: - leftkey = scancode; - break; - case 3: - rightkey = scancode; - break; - case 4: - crouchkey = scancode; - break; - case 5: - jumpkey = scancode; - break; - case 6: - drawkey = scancode; - break; - case 7: - throwkey = scancode; - break; - case 8: - attackkey = scancode; - break; - case 9: - consolekey = scancode; - break; - default: - break; - } - } - keyselect = -1; - waiting = false; - Menu::Load(); - return 0; -} - -void Menu::setKeySelected() -{ - waiting = true; - printf("launch thread\n"); - SDL_Thread* thread = SDL_CreateThread(setKeySelected_thread, NULL, NULL); - if ( thread == NULL ) { - fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError()); - waiting = false; - return; - } -} diff --git a/Source/Menu.h b/Source/Menu.h deleted file mode 100644 index bf99bdc..0000000 --- a/Source/Menu.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _MENU_H_ -#define _MENU_H_ - -#include "Game.h" - -struct MenuItem { - enum MenuItemType {NONE, LABEL, BUTTON, IMAGE, IMAGEBUTTON, MAPMARKER, MAPLINE, MAPLABEL} type; - int id; - string text; - Texture texture; - int x, y, w, h; - float r, g, b; - float effectfade; - - float linestartsize; - float lineendsize; - - MenuItem(MenuItemType _type, int _id, const string& _text, Texture _texture, - int _x, int _y, int _w, int _h, float _r, float _g, float _b, - float _linestartsize = 1, float _lineendsize = 1); -}; - -class Menu -{ -public: - static void clearMenu(); - static void addLabel(int id, const string& text, int x, int y, float r = 1, float g = 0, float b = 0); - static void addButton(int id, const string& text, int x, int y, float r = 1, float g = 0, float b = 0); - static void addImage(int id, Texture texture, int x, int y, int w, int h, float r = 1, float g = 1, float b = 1); - static void addButtonImage(int id, Texture texture, int x, int y, int w, int h, float r = 1, float g = 1, float b = 1); - static void addMapLine(int x, int y, int w, int h, float startsize, float endsize, float r, float g, float b); - static void addMapMarker(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b); - static void addMapLabel(int id, const string& text, int x, int y, float r = 1, float g = 0, float b = 0); - static void setText(int id, const string& text); - static void setText(int id, const string& text, int x, int y, int w, int h); - static int getSelected(int mousex, int mousey); - static void drawItems(); - - static void Load(); - static void Tick(); - static void updateSettingsMenu(); - static void updateStereoConfigMenu(); - static void updateControlsMenu(); - static void setKeySelected(); - -private: - static void handleFadeEffect(); - - static std::vector items; -}; - -#endif diff --git a/Source/Menu/Menu.cpp b/Source/Menu/Menu.cpp new file mode 100644 index 0000000..3639740 --- /dev/null +++ b/Source/Menu/Menu.cpp @@ -0,0 +1,974 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include +#include +#include + +#include "Graphic/gamegl.h" +#include "Level/Campaign.h" +#include "Menu/Menu.h" +#include "User/Settings.h" +#include "Utils/Input.h" + +// Should not be needed, Menu should call methods from other classes to launch maps and challenges and so on +#include "Level/Awards.h" +#include "Audio/openal_wrapper.h" + +using namespace Game; + +extern float multiplier; +extern std::set> resolutions; +extern int mainmenu; +extern std::vector campaignlevels; +extern float musicvolume[4]; +extern float oldmusicvolume[4]; +extern bool stillloading; +extern bool visibleloading; +extern int whichchoice; +extern int leveltheme; + +extern void toggleFullscreen(); + +int entername = 0; + +std::vector Menu::items; + +MenuItem::MenuItem(MenuItemType _type, int _id, const string& _text, Texture _texture, + int _x, int _y, int _w, int _h, float _r, float _g, float _b, + float _linestartsize, float _lineendsize): + type(_type), + id(_id), + text(_text), + texture(_texture), + x(_x), + y(_y), + w(_w), + h(_h), + r(_r), + g(_g), + b(_b), + effectfade(0), + linestartsize(_linestartsize), + lineendsize(_lineendsize) +{ + if (type == MenuItem::BUTTON) { + if (w == -1) { + w = text.length() * 10; + } + if (h == -1) { + h = 20; + } + } +} + +void Menu::clearMenu() +{ + items.clear(); +} + +void Menu::addLabel(int id, const string& text, int x, int y, float r, float g, float b) +{ + items.emplace_back(MenuItem::LABEL, id, text, Texture(), x, y, -1, -1, r, g, b); +} +void Menu::addButton(int id, const string& text, int x, int y, float r, float g, float b) +{ + items.emplace_back(MenuItem::BUTTON, id, text, Texture(), x, y, -1, -1, r, g, b); +} +void Menu::addImage(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b) +{ + items.emplace_back(MenuItem::IMAGE, id, "", texture, x, y, w, h, r, g, b); +} +void Menu::addButtonImage(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b) +{ + items.emplace_back(MenuItem::IMAGEBUTTON, id, "", texture, x, y, w, h, r, g, b); +} +void Menu::addMapLine(int x, int y, int w, int h, float startsize, float endsize, float r, float g, float b) +{ + items.emplace_back(MenuItem::MAPLINE, -1, "", Texture(), x, y, w, h, r, g, b, startsize, endsize); +} +void Menu::addMapMarker(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b) +{ + items.emplace_back(MenuItem::MAPMARKER, id, "", texture, x, y, w, h, r, g, b); +} +void Menu::addMapLabel(int id, const string& text, int x, int y, float r, float g, float b) +{ + items.emplace_back(MenuItem::MAPLABEL, id, text, Texture(), x, y, -1, -1, r, g, b); +} + +void Menu::setText(int id, const string& text) +{ + for (vector::iterator it = items.begin(); it != items.end(); it++) + if (it->id == id) { + it->text = text; + it->w = it->text.length() * 10; + break; + } +} + +void Menu::setText(int id, const string& text, int x, int y, int w, int h) +{ + for (vector::iterator it = items.begin(); it != items.end(); it++) + if (it->id == id) { + it->text = text; + it->x = x; + it->y = y; + if (w == -1) + it->w = it->text.length() * 10; + if (h == -1) + it->h = 20; + break; + } +} + +int Menu::getSelected(int mousex, int mousey) +{ + for (vector::iterator it = items.begin(); it != items.end(); it++) + if (it->type == MenuItem::BUTTON || it->type == MenuItem::IMAGEBUTTON || it->type == MenuItem::MAPMARKER) { + int mx = mousex; + int my = mousey; + if (it->type == MenuItem::MAPMARKER) { + mx -= 1; + my += 2; + } + if (mx >= it->x && mx < it->x + it->w && my >= it->y && my < it->y + it->h) + return it->id; + } + return -1; +} + +void Menu::handleFadeEffect() +{ + for (vector::iterator it = items.begin(); it != items.end(); it++) { + if (it->id == Game::selected) { + it->effectfade += multiplier * 5; + if (it->effectfade > 1) + it->effectfade = 1; + } else { + it->effectfade -= multiplier * 5; + if (it->effectfade < 0) + it->effectfade = 0; + } + } +} + +void Menu::drawItems() +{ + handleFadeEffect(); + glEnable(GL_TEXTURE_2D); + glEnable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + for (vector::iterator it = items.begin(); it != items.end(); it++) { + switch (it->type) { + case MenuItem::IMAGE: + case MenuItem::IMAGEBUTTON: + case MenuItem::MAPMARKER: + glColor4f(it->r, it->g, it->b, 1); + glPushMatrix(); + if (it->type == MenuItem::MAPMARKER) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTranslatef(2.5, -4.5, 0); //from old code + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + it->texture.bind(); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex3f(it->x, it->y, 0); + glTexCoord2f(1, 0); + glVertex3f(it->x + it->w, it->y, 0); + glTexCoord2f(1, 1); + glVertex3f(it->x + it->w, it->y + it->h, 0); + glTexCoord2f(0, 1); + glVertex3f(it->x, it->y + it->h, 0); + glEnd(); + if (it->type != MenuItem::IMAGE) { + //mouseover highlight + for (int i = 0; i < 10; i++) { + if (1 - ((float)i) / 10 - (1 - it->effectfade) > 0) { + glColor4f(it->r, it->g, it->b, (1 - ((float)i) / 10 - (1 - it->effectfade))*.25); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex3f(it->x - ((float)i) * 1 / 2, it->y - ((float)i) * 1 / 2, 0); + glTexCoord2f(1, 0); + glVertex3f(it->x + it->w + ((float)i) * 1 / 2, it->y - ((float)i) * 1 / 2, 0); + glTexCoord2f(1, 1); + glVertex3f(it->x + it->w + ((float)i) * 1 / 2, it->y + it->h + ((float)i) * 1 / 2, 0); + glTexCoord2f(0, 1); + glVertex3f(it->x - ((float)i) * 1 / 2, it->y + it->h + ((float)i) * 1 / 2, 0); + glEnd(); + } + } + } + glPopMatrix(); + break; + case MenuItem::LABEL: + case MenuItem::BUTTON: + glColor4f(it->r, it->g, it->b, 1); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Game::text->glPrint(it->x, it->y, it->text.c_str(), 0, 1, 640, 480); + if (it->type != MenuItem::LABEL) { + //mouseover highlight + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + for (int i = 0; i < 15; i++) { + if (1 - ((float)i) / 15 - (1 - it->effectfade) > 0) { + glColor4f(it->r, it->g, it->b, (1 - ((float)i) / 10 - (1 - it->effectfade))*.25); + Game::text->glPrint(it->x - ((float)i), it->y, it->text.c_str(), 0, 1 + ((float)i) / 70, 640, 480); + } + } + } + break; + case MenuItem::MAPLABEL: + Game::text->glPrintOutlined(0.9, 0, 0, it->x, it->y, it->text.c_str(), 0, 0.6, 640, 480); + break; + case MenuItem::MAPLINE: { + XYZ linestart; + linestart.x = it->x; + linestart.y = it->y; + linestart.z = 0; + XYZ lineend; + lineend.x = it->x + it->w; + lineend.y = it->y + it->h; + lineend.z = 0; + XYZ offset = lineend - linestart; + XYZ fac = offset; + Normalise(&fac); + offset = DoRotation(offset, 0, 0, 90); + Normalise(&offset); + + linestart += fac * 4 * it->linestartsize; + lineend -= fac * 4 * it->lineendsize; + + glDisable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(it->r, it->g, it->b, 1); + glPushMatrix(); + glTranslatef(2, -5, 0); //from old code + glBegin(GL_QUADS); + glVertex3f(linestart.x - offset.x * it->linestartsize, linestart.y - offset.y * it->linestartsize, 0.0f); + glVertex3f(linestart.x + offset.x * it->linestartsize, linestart.y + offset.y * it->linestartsize, 0.0f); + glVertex3f(lineend.x + offset.x * it->lineendsize, lineend.y + offset.y * it->lineendsize, 0.0f); + glVertex3f(lineend.x - offset.x * it->lineendsize, lineend.y - offset.y * it->lineendsize, 0.0f); + glEnd(); + glPopMatrix(); + glEnable(GL_TEXTURE_2D); + } + break; + default: + case MenuItem::NONE: + break; + } + } +} + +void Menu::updateSettingsMenu() +{ + std::string sbuf = std::string("Resolution: ") + to_string(newscreenwidth) + "*" + to_string(newscreenheight); + if (((float)newscreenwidth <= (float)newscreenheight * 1.61) && ((float)newscreenwidth >= (float)newscreenheight * 1.59)) { + sbuf += " (widescreen)"; + } + setText(0, sbuf); + setText(14, fullscreen ? "Fullscreen: On" : "Fullscreen: Off"); + if (newdetail == 0) setText(1, "Detail: Low"); + if (newdetail == 1) setText(1, "Detail: Medium"); + if (newdetail == 2) setText(1, "Detail: High"); + if (bloodtoggle == 0) setText(2, "Blood: Off"); + if (bloodtoggle == 1) setText(2, "Blood: On, low detail"); + if (bloodtoggle == 2) setText(2, "Blood: On, high detail (slower)"); + setText(4, ismotionblur ? "Blur Effects: Enabled (less compatible)" : "Blur Effects: Disabled (more compatible)"); + setText(5, decals ? "Decals: Enabled (slower)" : "Decals: Disabled"); + setText(6, musictoggle ? "Music: Enabled" : "Music: Disabled"); + setText(9, invertmouse ? "Invert mouse: Yes" : "Invert mouse: No"); + setText(10, std::string("Mouse Speed: ") + to_string(int(usermousesensitivity * 5))); + setText(11, std::string("Volume: ") + to_string(int(volume * 100)) + "%"); + setText(13, showdamagebar ? "Damage Bar: On" : "Damage Bar: Off"); + if ((newdetail == detail) && (newscreenheight == (int)screenheight) && (newscreenwidth == (int)screenwidth)) { + setText(8, "Back"); + } else { + setText(8, "Back (some changes take effect next time Lugaru is opened)"); + } +} + +void Menu::updateStereoConfigMenu() +{ + setText(0, std::string("Stereo mode: ") + StereoModeName(newstereomode)); + setText(1, std::string("Stereo separation: ") + to_string(stereoseparation)); + setText(2, std::string("Reverse stereo: ") + (stereoreverse ? "Yes" : "No")); +} + +void Menu::updateControlsMenu() +{ + setText(0, (string)"Forwards: " + (keyselect == 0 ? "_" : Input::keyToChar(forwardkey))); + setText(1, (string)"Back: " + (keyselect == 1 ? "_" : Input::keyToChar(backkey))); + setText(2, (string)"Left: " + (keyselect == 2 ? "_" : Input::keyToChar(leftkey))); + setText(3, (string)"Right: " + (keyselect == 3 ? "_" : Input::keyToChar(rightkey))); + setText(4, (string)"Crouch: " + (keyselect == 4 ? "_" : Input::keyToChar(crouchkey))); + setText(5, (string)"Jump: " + (keyselect == 5 ? "_" : Input::keyToChar(jumpkey))); + setText(6, (string)"Draw: " + (keyselect == 6 ? "_" : Input::keyToChar(drawkey))); + setText(7, (string)"Throw: " + (keyselect == 7 ? "_" : Input::keyToChar(throwkey))); + setText(8, (string)"Attack: " + (keyselect == 8 ? "_" : Input::keyToChar(attackkey))); + if (devtools) { + setText(9, (string)"Console: " + (keyselect == 9 ? "_" : Input::keyToChar(consolekey))); + } +} + +/* +Values of mainmenu : +1 Main menu +2 Menu pause (resume/end game) +3 Option menu +4 Controls configuration menu +5 Main game menu (choose level or challenge) +6 Deleting user menu +7 User managment menu (select/add) +8 Choose difficulty menu +9 Challenge level selection menu +10 End of the campaign congratulation (is that really a menu?) +11 Same that 9 ??? => unused +18 stereo configuration +*/ + +void Menu::Load() +{ + clearMenu(); + switch (mainmenu) { + case 1: + case 2: + addImage(0, Mainmenuitems[0], 150, 480 - 128, 256, 128); + addButtonImage(1, Mainmenuitems[mainmenu == 1 ? 1 : 5], 18, 480 - 152 - 32, 128, 32); + addButtonImage(2, Mainmenuitems[2], 18, 480 - 228 - 32, 112, 32); + addButtonImage(3, Mainmenuitems[mainmenu == 1 ? 3 : 6], 18, 480 - 306 - 32, mainmenu == 1 ? 68 : 132, 32); + break; + case 3: + addButton( 0, "", 10 + 20, 440); + addButton(14, "", 10 + 400, 440); + addButton( 1, "", 10 + 60, 405); + addButton( 2, "", 10 + 70, 370); + addButton( 4, "", 10 , 335); + addButton( 5, "", 10 + 60, 300); + addButton( 6, "", 10 + 70, 265); + addButton( 9, "", 10 , 230); + addButton(10, "", 20 , 195); + addButton(11, "", 10 + 60, 160); + addButton(13, "", 30 , 125); + addButton( 7, "-Configure Controls-", 10 + 15, 90); + addButton(12, "-Configure Stereo -", 10 + 15, 55); + addButton(8, "Back", 10, 10); + updateSettingsMenu(); + break; + case 4: + addButton(0, "", 10 , 400); + addButton(1, "", 10 + 40, 360); + addButton(2, "", 10 + 40, 320); + addButton(3, "", 10 + 30, 280); + addButton(4, "", 10 + 20, 240); + addButton(5, "", 10 + 40, 200); + addButton(6, "", 10 + 40, 160); + addButton(7, "", 10 + 30, 120); + addButton(8, "", 10 + 20, 80); + if (devtools) { + addButton(9, "", 10 + 10, 40); + } + addButton(devtools ? 10 : 9, "Back", 10, 10); + updateControlsMenu(); + break; + case 5: { + LoadCampaign(); + addLabel(-1, Account::active().getName(), 5, 400); + addButton(1, "Tutorial", 5, 300); + addButton(2, "Challenge", 5, 240); + addButton(3, "Delete User", 400, 10); + addButton(4, "Main Menu", 5, 10); + addButton(5, "Change User", 5, 180); + addButton(6, "Campaign : " + Account::active().getCurrentCampaign(), 200, 420); + + //show campaign map + //with (2,-5) offset from old code + addImage(-1, Mainmenuitems[7], 150 + 2, 60 - 5, 400, 400); + //show levels + int numlevels = Account::active().getCampaignChoicesMade(); + numlevels += numlevels > 0 ? campaignlevels[numlevels - 1].nextlevel.size() : 1; + for (int i = 0; i < numlevels; i++) { + XYZ midpoint = campaignlevels[i].getCenter(); + float itemsize = campaignlevels[i].getWidth(); + const bool active = (i >= Account::active().getCampaignChoicesMade()); + if (!active) { + itemsize /= 2; + } + + if (i >= 1) { + XYZ start = campaignlevels[i - 1].getCenter(); + addMapLine(start.x, start.y, midpoint.x - start.x, midpoint.y - start.y, 0.5, active ? 1 : 0.5, active ? 1 : 0.5, 0, 0); + } + addMapMarker(NB_CAMPAIGN_MENU_ITEM + i, Mapcircletexture, + midpoint.x - itemsize / 2, midpoint.y - itemsize / 2, itemsize, itemsize, active ? 1 : 0.5, 0, 0); + + if (active) { + addMapLabel(-2, campaignlevels[i].description, + campaignlevels[i].getStartX() + 10, + campaignlevels[i].getStartY() - 4); + } + } + } + break; + case 6: + addLabel(-1, "Are you sure you want to delete this user?", 10, 400); + addButton(1, "Yes", 10, 360); + addButton(2, "No", 10, 320); + break; + case 7: + if (Account::getNbAccounts() < 8) + addButton(0, "New User", 10, 400); + else + addLabel(0, "No More Users", 10, 400); + addLabel(-2, "", 20, 400); + addButton(Account::getNbAccounts() + 1, "Back", 10, 10); + for (int i = 0; i < Account::getNbAccounts(); i++) { + addButton(i + 1, Account::get(i).getName(), 10, 340 - 20 * (i + 1)); + } + break; + case 8: + addButton(0, "Easier", 10, 400); + addButton(1, "Difficult", 10, 360); + addButton(2, "Insane", 10, 320); + break; + case 9: + for (int i = 0; i < numchallengelevels; i++) { + string name = "Level "; + name += to_string(i + 1); + if (name.size() < 17) { + name.append((17 - name.size()), ' '); + } + name += to_string(int(Account::active().getHighScore(i))); + if (name.size() < 32) { + name.append((32 - name.size()), ' '); + } + int fasttime = (int)round(Account::active().getFastTime(i)); + name += to_string(int((fasttime - fasttime % 60) / 60)); + name += ":"; + if (fasttime % 60 < 10) + name += "0"; + name += to_string(fasttime % 60); + + addButton(i, name, 10, 400 - i * 25, i > Account::active().getProgress() ? 0.5 : 1, 0, 0); + } + + addButton(-1, " High Score Best Time", 10, 440); + addButton(numchallengelevels, "Back", 10, 10); + break; + case 10: { + addLabel(0, "Congratulations!", 220, 330); + addLabel(1, "You have avenged your family and", 140, 300); + addLabel(2, "restored peace to the island of Lugaru.", 110, 270); + addButton(3, "Back", 10, 10); + addLabel(4, string("Your score: ") + to_string((int)Account::active().getCampaignScore()), 190, 200); + addLabel(5, string("Highest score: ") + to_string((int)Account::active().getCampaignHighScore()), 190, 180); + } + break; + case 18: + addButton(0, "", 70, 400); + addButton(1, "", 10, 360); + addButton(2, "", 40, 320); + addButton(3, "Back", 10, 10); + updateStereoConfigMenu(); + break; + } +} + +void Menu::Tick() +{ + //escape key pressed + if (Input::isKeyPressed(SDL_SCANCODE_ESCAPE) && + (mainmenu >= 3) && (mainmenu != 8) && !((mainmenu == 7) && entername)) { + selected = -1; + //finished with settings menu + if (mainmenu == 3) { + SaveSettings(); + } + //effects + if (mainmenu >= 3 && mainmenu != 8) { + fireSound(); + flash(); + } + //go back + switch (mainmenu) { + case 3: + case 5: + mainmenu = gameon ? 2 : 1; + break; + case 4: + case 18: + mainmenu = 3; + break; + case 6: + case 7: + case 9: + case 10: + mainmenu = 5; + break; + } + } + + //menu buttons + selected = getSelected(mousecoordh * 640 / screenwidth, 480 - mousecoordv * 480 / screenheight); + + // some specific case where we do something even if the left mouse button is not pressed. + if ((mainmenu == 5) && (endgame == 2)) { + Account::active().endGame(); + endgame = 0; + } + if (mainmenu == 10) + endgame = 2; + if (mainmenu == 18 && Input::isKeyPressed(MOUSEBUTTON2) && selected == 1) { + stereoseparation -= 0.001; + updateStereoConfigMenu(); + } + + static int oldmainmenu = mainmenu; + + if (Input::MouseClicked() && (selected >= 0)) { // handling of the left mouse clic in menus + set>::iterator newscreenresolution; + switch (mainmenu) { + case 1: + case 2: + switch (selected) { + case 1: + if (gameon) { //resume + mainmenu = 0; + pause_sound(stream_menutheme); + resume_stream(leveltheme); + } else { //new game + fireSound(firestartsound); + flash(); + mainmenu = (Account::hasActive() ? 5 : 7); + selected = -1; + } + break; + case 2: //options + fireSound(); + flash(); + mainmenu = 3; + if (newdetail > 2) + newdetail = detail; + if (newdetail < 0) + newdetail = detail; + if (newscreenwidth > 3000) + newscreenwidth = screenwidth; + if (newscreenwidth < 0) + newscreenwidth = screenwidth; + if (newscreenheight > 3000) + newscreenheight = screenheight; + if (newscreenheight < 0) + newscreenheight = screenheight; + break; + case 3: + fireSound(); + flash(); + if (gameon) { //end game + gameon = 0; + mainmenu = 1; + } else { //quit + tryquit = 1; + pause_sound(stream_menutheme); + } + break; + } + break; + case 3: + fireSound(); + switch (selected) { + case 0: + newscreenresolution = resolutions.find(make_pair(newscreenwidth, newscreenheight)); + /* Next one (end() + 1 is also end() so the ++ is safe even if it was not found) */ + newscreenresolution++; + if (newscreenresolution == resolutions.end()) { + /* It was the last one (or not found), go back to the beginning */ + newscreenresolution = resolutions.begin(); + } + newscreenwidth = newscreenresolution->first; + newscreenheight = newscreenresolution->second; + break; + case 1: + newdetail++; + if (newdetail > 2) + newdetail = 0; + break; + case 2: + bloodtoggle++; + if (bloodtoggle > 2) + bloodtoggle = 0; + break; + case 4: + ismotionblur = !ismotionblur; + break; + case 5: + decals = !decals; + break; + case 6: + musictoggle = !musictoggle; + if (musictoggle) { + emit_stream_np(stream_menutheme); + } else { + pause_sound(leveltheme); + pause_sound(stream_fighttheme); + pause_sound(stream_menutheme); + + for (int i = 0; i < 4; i++) { + oldmusicvolume[i] = 0; + musicvolume[i] = 0; + } + } + break; + case 7: // controls + flash(); + mainmenu = 4; + selected = -1; + keyselect = -1; + break; + case 8: + flash(); + SaveSettings(); + mainmenu = gameon ? 2 : 1; + break; + case 9: + invertmouse = !invertmouse; + break; + case 10: + usermousesensitivity += .2; + if (usermousesensitivity > 2) + usermousesensitivity = .2; + break; + case 11: + volume += .1f; + if (volume > 1.0001f) + volume = 0; + OPENAL_SetSFXMasterVolume((int)(volume * 255)); + break; + case 12: + flash(); + newstereomode = stereomode; + mainmenu = 18; + keyselect = -1; + break; + case 13: + showdamagebar = !showdamagebar; + break; + case 14: + toggleFullscreen(); + break; + } + updateSettingsMenu(); + break; + case 4: + if (!waiting) { + fireSound(); + if (selected < (devtools ? 10 : 9) && keyselect == -1) + keyselect = selected; + if (keyselect != -1) + setKeySelected(); + if (selected == (devtools ? 10 : 9)) { + flash(); + mainmenu = 3; + } + } + updateControlsMenu(); + break; + case 5: + fireSound(); + flash(); + if ((selected - NB_CAMPAIGN_MENU_ITEM >= Account::active().getCampaignChoicesMade())) { + startbonustotal = 0; + + loading = 2; + loadtime = 0; + targetlevel = 7; + if (firstload) + TickOnceAfter(); + else + LoadStuff(); + whichchoice = selected - NB_CAMPAIGN_MENU_ITEM - Account::active().getCampaignChoicesMade(); + actuallevel = (Account::active().getCampaignChoicesMade() > 0 ? campaignlevels[Account::active().getCampaignChoicesMade() - 1].nextlevel[whichchoice] : 0); + visibleloading = 1; + stillloading = 1; + Loadlevel(campaignlevels[actuallevel].mapname.c_str()); + campaign = 1; + mainmenu = 0; + gameon = 1; + pause_sound(stream_menutheme); + } + switch (selected) { + case 1: + startbonustotal = 0; + + loading = 2; + loadtime = 0; + targetlevel = -1; + if (firstload) { + TickOnceAfter(); + } else + LoadStuff(); + Loadlevel(-1); + + mainmenu = 0; + gameon = 1; + pause_sound(stream_menutheme); + break; + case 2: + mainmenu = 9; + break; + case 3: + mainmenu = 6; + break; + case 4: + mainmenu = (gameon ? 2 : 1); + break; + case 5: + mainmenu = 7; + break; + case 6: + vector campaigns = ListCampaigns(); + vector::iterator c; + if ((c = find(campaigns.begin(), campaigns.end(), Account::active().getCurrentCampaign())) == campaigns.end()) { + if (!campaigns.empty()) + Account::active().setCurrentCampaign(campaigns.front()); + } else { + c++; + if (c == campaigns.end()) + c = campaigns.begin(); + Account::active().setCurrentCampaign(*c); + } + Load(); + break; + } + break; + case 6: + fireSound(); + if (selected == 1) { + flash(); + Account::destroyActive(); + mainmenu = 7; + } else if (selected == 2) { + flash(); + mainmenu = 5; + } + break; + case 7: + fireSound(); + if (selected == 0 && Account::getNbAccounts() < 8) { + entername = 1; + } else if (selected < Account::getNbAccounts() + 1) { + flash(); + mainmenu = 5; + Account::setActive(selected - 1); + } else if (selected == Account::getNbAccounts() + 1) { + flash(); + if (Account::hasActive()) { + mainmenu = 5; + } else { + mainmenu = 1; + } + displaytext[0].clear(); + displayselected = 0; + entername = 0; + } + break; + case 8: + fireSound(); + flash(); + if (selected <= 2) + Account::active().setDifficulty(selected); + mainmenu = 5; + break; + case 9: + if (selected < numchallengelevels && selected <= Account::active().getProgress()) { + fireSound(); + flash(); + + startbonustotal = 0; + + loading = 2; + loadtime = 0; + targetlevel = selected; + if (firstload) + TickOnceAfter(); + else + LoadStuff(); + Loadlevel(selected); + campaign = 0; + + mainmenu = 0; + gameon = 1; + pause_sound(stream_menutheme); + } + if (selected == numchallengelevels) { + fireSound(); + flash(); + mainmenu = 5; + } + break; + case 10: + if (selected == 3) { + fireSound(); + flash(); + mainmenu = 5; + } + break; + case 18: + if (selected == 1) + stereoseparation += 0.001; + else { + fireSound(); + if (selected == 0) { + newstereomode = (StereoMode)(newstereomode + 1); + while (!CanInitStereo(newstereomode)) { + printf("Failed to initialize mode %s (%i)\n", StereoModeName(newstereomode).c_str(), newstereomode); + newstereomode = (StereoMode)(newstereomode + 1); + if (newstereomode >= stereoCount) + newstereomode = stereoNone; + } + } else if (selected == 2) { + stereoreverse = !stereoreverse; + } else if (selected == 3) { + flash(); + mainmenu = 3; + + stereomode = newstereomode; + InitStereo(stereomode); + } + } + updateStereoConfigMenu(); + break; + } + } + + OPENAL_SetFrequency(channels[stream_menutheme]); + + if (entername) { + inputText(displaytext[0], &displayselected); + if (!waiting) { // the input as finished + if (!displaytext[0].empty()) { // with enter + Account::add(string(displaytext[0])); + + mainmenu = 8; + + flash(); + + fireSound(firestartsound); + + displaytext[0].clear(); + + displayselected = 0; + } + entername = 0; + Load(); + } + + displayblinkdelay -= multiplier; + if (displayblinkdelay <= 0) { + displayblinkdelay = .3; + displayblink = !displayblink; + } + } + + if (entername) { + setText(0, displaytext[0], 20, 400, -1, -1); + setText(-2, displayblink ? "_" : "", 20 + displayselected * 10, 400, -1, -1); + } + + if (oldmainmenu != mainmenu) + Load(); + oldmainmenu = mainmenu; + +} + +int setKeySelected_thread(void* data) +{ + using namespace Game; + int scancode = -1; + SDL_Event evenement; + while (scancode == -1) { + SDL_WaitEvent(&evenement); + switch (evenement.type) { + case SDL_KEYDOWN: + scancode = evenement.key.keysym.scancode; + break; + case SDL_MOUSEBUTTONDOWN: + scancode = SDL_NUM_SCANCODES + evenement.button.button; + break; + default: + break; + } + } + if (scancode != SDL_SCANCODE_ESCAPE) { + fireSound(); + switch (keyselect) { + case 0: + forwardkey = scancode; + break; + case 1: + backkey = scancode; + break; + case 2: + leftkey = scancode; + break; + case 3: + rightkey = scancode; + break; + case 4: + crouchkey = scancode; + break; + case 5: + jumpkey = scancode; + break; + case 6: + drawkey = scancode; + break; + case 7: + throwkey = scancode; + break; + case 8: + attackkey = scancode; + break; + case 9: + consolekey = scancode; + break; + default: + break; + } + } + keyselect = -1; + waiting = false; + Menu::Load(); + return 0; +} + +void Menu::setKeySelected() +{ + waiting = true; + printf("launch thread\n"); + SDL_Thread* thread = SDL_CreateThread(setKeySelected_thread, NULL, NULL); + if ( thread == NULL ) { + fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError()); + waiting = false; + return; + } +} diff --git a/Source/Menu/Menu.h b/Source/Menu/Menu.h new file mode 100644 index 0000000..bf99bdc --- /dev/null +++ b/Source/Menu/Menu.h @@ -0,0 +1,72 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _MENU_H_ +#define _MENU_H_ + +#include "Game.h" + +struct MenuItem { + enum MenuItemType {NONE, LABEL, BUTTON, IMAGE, IMAGEBUTTON, MAPMARKER, MAPLINE, MAPLABEL} type; + int id; + string text; + Texture texture; + int x, y, w, h; + float r, g, b; + float effectfade; + + float linestartsize; + float lineendsize; + + MenuItem(MenuItemType _type, int _id, const string& _text, Texture _texture, + int _x, int _y, int _w, int _h, float _r, float _g, float _b, + float _linestartsize = 1, float _lineendsize = 1); +}; + +class Menu +{ +public: + static void clearMenu(); + static void addLabel(int id, const string& text, int x, int y, float r = 1, float g = 0, float b = 0); + static void addButton(int id, const string& text, int x, int y, float r = 1, float g = 0, float b = 0); + static void addImage(int id, Texture texture, int x, int y, int w, int h, float r = 1, float g = 1, float b = 1); + static void addButtonImage(int id, Texture texture, int x, int y, int w, int h, float r = 1, float g = 1, float b = 1); + static void addMapLine(int x, int y, int w, int h, float startsize, float endsize, float r, float g, float b); + static void addMapMarker(int id, Texture texture, int x, int y, int w, int h, float r, float g, float b); + static void addMapLabel(int id, const string& text, int x, int y, float r = 1, float g = 0, float b = 0); + static void setText(int id, const string& text); + static void setText(int id, const string& text, int x, int y, int w, int h); + static int getSelected(int mousex, int mousey); + static void drawItems(); + + static void Load(); + static void Tick(); + static void updateSettingsMenu(); + static void updateStereoConfigMenu(); + static void updateControlsMenu(); + static void setKeySelected(); + +private: + static void handleFadeEffect(); + + static std::vector items; +}; + +#endif diff --git a/Source/Models.cpp b/Source/Models.cpp deleted file mode 100644 index 2c30168..0000000 --- a/Source/Models.cpp +++ /dev/null @@ -1,1487 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Game.h" -#include "Models.h" -#include "Utils/Folders.h" - -extern float multiplier; -extern float viewdistance; -extern XYZ viewer; -extern float fadestart; -extern float texdetail; -extern bool decals; - -extern bool visibleloading; - -int Model::LineCheck(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate) -{ - static int j; - static float distance; - static float olddistance; - static int intersecting; - static int firstintersecting; - static XYZ point; - - *p1 = *p1 - *move; - *p2 = *p2 - *move; - if (*rotate) - *p1 = DoRotation(*p1, 0, -*rotate, 0); - if (*rotate) - *p2 = DoRotation(*p2, 0, -*rotate, 0); - if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) - return -1; - firstintersecting = -1; - - for (j = 0; j < TriangleNum; j++) { - intersecting = LineFacetd(p1, p2, &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], &facenormals[j], &point); - distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); - if ((distance < olddistance || firstintersecting == -1) && intersecting) { - olddistance = distance; - firstintersecting = j; - *p = point; - } - } - - if (*rotate) - *p = DoRotation(*p, 0, *rotate, 0); - *p = *p + *move; - return firstintersecting; -} - -int Model::LineCheckPossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate) -{ - static int j; - static float distance; - static float olddistance; - static int intersecting; - static int firstintersecting; - static XYZ point; - - *p1 = *p1 - *move; - *p2 = *p2 - *move; - if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) - return -1; - firstintersecting = -1; - if (*rotate) - *p1 = DoRotation(*p1, 0, -*rotate, 0); - if (*rotate) - *p2 = DoRotation(*p2, 0, -*rotate, 0); - - if (numpossible > 0 && numpossible < TriangleNum) - for (j = 0; j < numpossible; j++) { - if (possible[j] >= 0 && possible[j] < TriangleNum) { - intersecting = LineFacetd(p1, p2, &vertex[Triangles[possible[j]].vertex[0]], &vertex[Triangles[possible[j]].vertex[1]], &vertex[Triangles[possible[j]].vertex[2]], &facenormals[possible[j]], &point); - distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); - if ((distance < olddistance || firstintersecting == -1) && intersecting) { - olddistance = distance; - firstintersecting = possible[j]; - *p = point; - } - } - } - - if (*rotate) - *p = DoRotation(*p, 0, *rotate, 0); - *p = *p + *move; - return firstintersecting; -} - -int Model::LineCheckSlidePossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate) -{ - static int j; - static float distance; - static float olddistance; - static int intersecting; - static int firstintersecting; - static XYZ point; - - *p1 = *p1 - *move; - *p2 = *p2 - *move; - if (!sphere_line_intersection(p1, p2, &boundingspherecenter, &boundingsphereradius)) - return -1; - firstintersecting = -1; - if (*rotate) - *p1 = DoRotation(*p1, 0, -*rotate, 0); - if (*rotate) - *p2 = DoRotation(*p2, 0, -*rotate, 0); - - if (numpossible) - for (j = 0; j < numpossible; j++) { - if (possible[j] >= 0 && possible[j] < TriangleNum) { - intersecting = LineFacetd(p1, p2, &vertex[Triangles[possible[j]].vertex[0]], &vertex[Triangles[possible[j]].vertex[1]], &vertex[Triangles[possible[j]].vertex[2]], &facenormals[possible[j]], &point); - distance = (point.x - p1->x) * (point.x - p1->x) + (point.y - p1->y) * (point.y - p1->y) + (point.z - p1->z) * (point.z - p1->z); - if ((distance < olddistance || firstintersecting == -1) && intersecting) { - olddistance = distance; - firstintersecting = possible[j]; - } - } - } - - if (firstintersecting > 0) { - distance = abs((facenormals[firstintersecting].x * p2->x) + (facenormals[firstintersecting].y * p2->y) + (facenormals[firstintersecting].z * p2->z) - ((facenormals[firstintersecting].x * vertex[Triangles[firstintersecting].vertex[0]].x) + (facenormals[firstintersecting].y * vertex[Triangles[firstintersecting].vertex[0]].y) + (facenormals[firstintersecting].z * vertex[Triangles[firstintersecting].vertex[0]].z))); - *p2 -= facenormals[firstintersecting] * distance; - } - - if (*rotate) - *p2 = DoRotation(*p2, 0, *rotate, 0); - *p2 = *p2 + *move; - return firstintersecting; -} - -int Model::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate) -{ - static int i, j; - static float distance; - static float olddistance; - static int intersecting; - static int firstintersecting; - static XYZ point; - static XYZ oldp1; - - firstintersecting = -1; - - oldp1 = *p1; - *p1 = *p1 - *move; - if (*rotate) - *p1 = DoRotation(*p1, 0, -*rotate, 0); - if (distsq(p1, &boundingspherecenter) > radius * radius + boundingsphereradius * boundingsphereradius) - return -1; - - for (i = 0; i < 4; i++) { - for (j = 0; j < TriangleNum; j++) { - intersecting = 0; - distance = abs((facenormals[j].x * p1->x) + (facenormals[j].y * p1->y) + (facenormals[j].z * p1->z) - ((facenormals[j].x * vertex[Triangles[j].vertex[0]].x) + (facenormals[j].y * vertex[Triangles[j].vertex[0]].y) + (facenormals[j].z * vertex[Triangles[j].vertex[0]].z))); - if (distance < radius) { - point = *p1 - facenormals[j] * distance; - if (PointInTriangle( &point, facenormals[j], &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]])) - intersecting = 1; - if (!intersecting) - intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], p1, &radius); - if (!intersecting) - intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], p1, &radius); - if (!intersecting) - intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[2]], p1, &radius); - if (intersecting) { - *p1 += facenormals[j] * (distance - radius); - } - } - if ((distance < olddistance || firstintersecting == -1) && intersecting) { - olddistance = distance; - firstintersecting = j; - *p = point; - } - } - } - if (*rotate) - *p = DoRotation(*p, 0, *rotate, 0); - *p = *p + *move; - if (*rotate) - *p1 = DoRotation(*p1, 0, *rotate, 0); - *p1 += *move; - return firstintersecting; -} - -int Model::SphereCheckPossible(XYZ *p1, float radius, XYZ *move, float *rotate) -{ - static int j; - static float distance; - static float olddistance; - static int intersecting; - static int firstintersecting; - static XYZ point; - static XYZ oldp1; - - firstintersecting = -1; - - oldp1 = *p1; - *p1 = *p1 - *move; - - numpossible = 0; - - if (*rotate) - *p1 = DoRotation(*p1, 0, -*rotate, 0); - if (distsq(p1, &boundingspherecenter) > radius * radius + boundingsphereradius * boundingsphereradius) { - *p1 = oldp1; - return -1; - } - - for (j = 0; j < TriangleNum; j++) { - intersecting = 0; - distance = abs((facenormals[j].x * p1->x) + (facenormals[j].y * p1->y) + (facenormals[j].z * p1->z) - ((facenormals[j].x * vertex[Triangles[j].vertex[0]].x) + (facenormals[j].y * vertex[Triangles[j].vertex[0]].y) + (facenormals[j].z * vertex[Triangles[j].vertex[0]].z))); - if (distance < radius) { - point = *p1 - facenormals[j] * distance; - if (PointInTriangle( &point, facenormals[j], &vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]])) - intersecting = 1; - if (!intersecting) - intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[1]], p1, &radius); - if (!intersecting) - intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[1]], &vertex[Triangles[j].vertex[2]], p1, &radius); - if (!intersecting) - intersecting = sphere_line_intersection(&vertex[Triangles[j].vertex[0]], &vertex[Triangles[j].vertex[2]], p1, &radius); - if (intersecting) { - possible[numpossible] = j; - numpossible++; - } - } - if ((distance < olddistance || firstintersecting == -1) && intersecting) { - olddistance = distance; - firstintersecting = j; - } - } - if (*rotate) - *p1 = DoRotation(*p1, 0, *rotate, 0); - *p1 += *move; - return firstintersecting; -} - - -void Model::UpdateVertexArray() -{ - if (type != normaltype && type != decalstype) - return; - static int i; - static int j; - if (!flat) - for (i = 0; i < TriangleNum; i++) { - j = i * 24; - vArray[j + 0] = Triangles[i].gx[0]; - vArray[j + 1] = Triangles[i].gy[0]; - vArray[j + 2] = normals[Triangles[i].vertex[0]].x; - vArray[j + 3] = normals[Triangles[i].vertex[0]].y; - vArray[j + 4] = normals[Triangles[i].vertex[0]].z; - vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; - vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; - vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; - - vArray[j + 8] = Triangles[i].gx[1]; - vArray[j + 9] = Triangles[i].gy[1]; - vArray[j + 10] = normals[Triangles[i].vertex[1]].x; - vArray[j + 11] = normals[Triangles[i].vertex[1]].y; - vArray[j + 12] = normals[Triangles[i].vertex[1]].z; - vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; - vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; - vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; - - vArray[j + 16] = Triangles[i].gx[2]; - vArray[j + 17] = Triangles[i].gy[2]; - vArray[j + 18] = normals[Triangles[i].vertex[2]].x; - vArray[j + 19] = normals[Triangles[i].vertex[2]].y; - vArray[j + 20] = normals[Triangles[i].vertex[2]].z; - vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; - vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; - vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; - } - if (flat) - for (i = 0; i < TriangleNum; i++) { - j = i * 24; - vArray[j + 0] = Triangles[i].gx[0]; - vArray[j + 1] = Triangles[i].gy[0]; - vArray[j + 2] = facenormals[i].x * -1; - vArray[j + 3] = facenormals[i].y * -1; - vArray[j + 4] = facenormals[i].z * -1; - vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; - vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; - vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; - - vArray[j + 8] = Triangles[i].gx[1]; - vArray[j + 9] = Triangles[i].gy[1]; - vArray[j + 10] = facenormals[i].x * -1; - vArray[j + 11] = facenormals[i].y * -1; - vArray[j + 12] = facenormals[i].z * -1; - vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; - vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; - vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; - - vArray[j + 16] = Triangles[i].gx[2]; - vArray[j + 17] = Triangles[i].gy[2]; - vArray[j + 18] = facenormals[i].x * -1; - vArray[j + 19] = facenormals[i].y * -1; - vArray[j + 20] = facenormals[i].z * -1; - vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; - vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; - vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; - - } -} - -void Model::UpdateVertexArrayNoTex() -{ - if (type != normaltype && type != decalstype) - return; - static int i; - static int j; - if (!flat) - for (i = 0; i < TriangleNum; i++) { - j = i * 24; - vArray[j + 2] = normals[Triangles[i].vertex[0]].x; - vArray[j + 3] = normals[Triangles[i].vertex[0]].y; - vArray[j + 4] = normals[Triangles[i].vertex[0]].z; - vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; - vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; - vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; - - vArray[j + 10] = normals[Triangles[i].vertex[1]].x; - vArray[j + 11] = normals[Triangles[i].vertex[1]].y; - vArray[j + 12] = normals[Triangles[i].vertex[1]].z; - vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; - vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; - vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; - - vArray[j + 18] = normals[Triangles[i].vertex[2]].x; - vArray[j + 19] = normals[Triangles[i].vertex[2]].y; - vArray[j + 20] = normals[Triangles[i].vertex[2]].z; - vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; - vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; - vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; - } - if (flat) - for (i = 0; i < TriangleNum; i++) { - j = i * 24; - vArray[j + 2] = facenormals[i].x * -1; - vArray[j + 3] = facenormals[i].y * -1; - vArray[j + 4] = facenormals[i].z * -1; - vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; - vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; - vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; - - vArray[j + 10] = facenormals[i].x * -1; - vArray[j + 11] = facenormals[i].y * -1; - vArray[j + 12] = facenormals[i].z * -1; - vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; - vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; - vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; - - vArray[j + 18] = facenormals[i].x * -1; - vArray[j + 19] = facenormals[i].y * -1; - vArray[j + 20] = facenormals[i].z * -1; - vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; - vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; - vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; - } -} - -void Model::UpdateVertexArrayNoTexNoNorm() -{ - if (type != normaltype && type != decalstype) - return; - static int i; - static int j; - for (i = 0; i < TriangleNum; i++) { - j = i * 24; - vArray[j + 5] = vertex[Triangles[i].vertex[0]].x; - vArray[j + 6] = vertex[Triangles[i].vertex[0]].y; - vArray[j + 7] = vertex[Triangles[i].vertex[0]].z; - - vArray[j + 13] = vertex[Triangles[i].vertex[1]].x; - vArray[j + 14] = vertex[Triangles[i].vertex[1]].y; - vArray[j + 15] = vertex[Triangles[i].vertex[1]].z; - - vArray[j + 21] = vertex[Triangles[i].vertex[2]].x; - vArray[j + 22] = vertex[Triangles[i].vertex[2]].y; - vArray[j + 23] = vertex[Triangles[i].vertex[2]].z; - } -} - -bool Model::loadnotex(const std::string& filename ) -{ - FILE *tfile; - long i; - - type = notextype; - color = 0; - - tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); - - // read model settings - - fseek(tfile, 0, SEEK_SET); - funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); - - // read the model data - deallocate(); - - numpossible = 0; - - owner = (int*)malloc(sizeof(int) * vertexNum); - possible = (int*)malloc(sizeof(int) * TriangleNum); - vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); - Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); - vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); - - for (i = 0; i < vertexNum; i++) { - funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); - } - - for (i = 0; i < TriangleNum; i++) { - short vertex[ 6]; - funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); - Triangles[i].vertex[ 0] = vertex[ 0]; - Triangles[i].vertex[ 1] = vertex[ 2]; - Triangles[i].vertex[ 2] = vertex[ 4]; - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); - } - - fclose(tfile); - - UpdateVertexArray(); - - for (i = 0; i < vertexNum; i++) { - owner[i] = -1; - } - - static int j; - boundingsphereradius = 0; - for (i = 0; i < vertexNum; i++) { - for (j = 0; j < vertexNum; j++) { - if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { - boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; - boundingspherecenter = (vertex[i] + vertex[j]) / 2; - } - } - } - boundingsphereradius = fast_sqrt(boundingsphereradius); - - return true; -} - - -bool Model::load(const std::string& filename, bool texture ) -{ - FILE *tfile; - long i; - - LOGFUNC; - - LOG(std::string("Loading model...") + filename); - - if (visibleloading) - Game::LoadingScreen(); - - type = normaltype; - color = 0; - - tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); - - // read model settings - - fseek(tfile, 0, SEEK_SET); - funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); - - // read the model data - deallocate(); - - numpossible = 0; - - owner = (int*)malloc(sizeof(int) * vertexNum); - possible = (int*)malloc(sizeof(int) * TriangleNum); - vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); - normals = (XYZ*)malloc(sizeof(XYZ) * vertexNum); - facenormals = (XYZ*)malloc(sizeof(XYZ) * TriangleNum); - Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); - vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); - - for (i = 0; i < vertexNum; i++) { - funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); - } - - for (i = 0; i < TriangleNum; i++) { - short vertex[ 6]; - funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); - Triangles[i].vertex[ 0] = vertex[ 0]; - Triangles[i].vertex[ 1] = vertex[ 2]; - Triangles[i].vertex[ 2] = vertex[ 4]; - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); - } - - modelTexture.xsz = 0; - - fclose(tfile); - - UpdateVertexArray(); - - for (i = 0; i < vertexNum; i++) { - owner[i] = -1; - } - - static int j; - boundingsphereradius = 0; - for (i = 0; i < vertexNum; i++) { - for (j = 0; j < vertexNum; j++) { - if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { - boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; - boundingspherecenter = (vertex[i] + vertex[j]) / 2; - } - } - } - boundingsphereradius = fast_sqrt(boundingsphereradius); - - return true; -} - -bool Model::loaddecal(const std::string& filename, bool texture ) -{ - FILE *tfile; - long i, j; - - LOGFUNC; - - LOG(std::string("Loading decal...") + Folders::getResourcePath(filename)); - - type = decalstype; - numdecals = 0; - color = 0; - - tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); - - // read model settings - - fseek(tfile, 0, SEEK_SET); - funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); - - // read the model data - - deallocate(); - - numpossible = 0; - - owner = (int*)malloc(sizeof(int) * vertexNum); - possible = (int*)malloc(sizeof(int) * TriangleNum); - vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); - normals = (XYZ*)malloc(sizeof(XYZ) * vertexNum); - facenormals = (XYZ*)malloc(sizeof(XYZ) * TriangleNum); - Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); - vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); - - - for (i = 0; i < vertexNum; i++) { - funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); - } - - for (i = 0; i < TriangleNum; i++) { - short vertex[ 6]; - funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); - Triangles[i].vertex[ 0] = vertex[ 0]; - Triangles[i].vertex[ 1] = vertex[ 2]; - Triangles[i].vertex[ 2] = vertex[ 4]; - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); - } - - - modelTexture.xsz = 0; - - fclose(tfile); - - UpdateVertexArray(); - - for (i = 0; i < vertexNum; i++) { - owner[i] = -1; - } - - boundingsphereradius = 0; - for (i = 0; i < vertexNum; i++) { - for (j = 0; j < vertexNum; j++) { - if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { - boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; - boundingspherecenter = (vertex[i] + vertex[j]) / 2; - } - } - } - boundingsphereradius = fast_sqrt(boundingsphereradius); - - //allow decals - if (!decaltexcoords) { - decaltexcoords = (float***)malloc(sizeof(float**)*max_model_decals); - for (i = 0; i < max_model_decals; i++) { - decaltexcoords[i] = (float**)malloc(sizeof(float*) * 3); - for (j = 0; j < 3; j++) { - decaltexcoords[i][j] = (float*)malloc(sizeof(float) * 2); - } - } - decalvertex = (XYZ**)malloc(sizeof(XYZ*)*max_model_decals); - for (i = 0; i < max_model_decals; i++) { - decalvertex[i] = (XYZ*)malloc(sizeof(XYZ) * 3); - } - - decaltype = (int*)malloc(sizeof(int) * max_model_decals); - decalopacity = (float*)malloc(sizeof(float) * max_model_decals); - decalrotation = (float*)malloc(sizeof(float) * max_model_decals); - decalalivetime = (float*)malloc(sizeof(float) * max_model_decals); - decalposition = (XYZ*)malloc(sizeof(XYZ) * max_model_decals); - } - - return true; -} - -bool Model::loadraw(const std::string& filename) -{ - FILE *tfile; - long i; - - LOGFUNC; - - LOG(std::string("Loading raw...") + filename); - - type = rawtype; - color = 0; - - tfile = Folders::openMandatoryFile( Folders::getResourcePath(filename), "rb" ); - - // read model settings - - fseek(tfile, 0, SEEK_SET); - funpackf(tfile, "Bs Bs", &vertexNum, &TriangleNum); - - // read the model data - deallocate(); - - numpossible = 0; - - owner = (int*)malloc(sizeof(int) * vertexNum); - possible = (int*)malloc(sizeof(int) * TriangleNum); - vertex = (XYZ*)malloc(sizeof(XYZ) * vertexNum); - Triangles = (TexturedTriangle*)malloc(sizeof(TexturedTriangle) * TriangleNum); - vArray = (GLfloat*)malloc(sizeof(GLfloat) * TriangleNum * 24); - - - for (i = 0; i < vertexNum; i++) { - funpackf(tfile, "Bf Bf Bf", &vertex[i].x, &vertex[i].y, &vertex[i].z); - } - - for (i = 0; i < TriangleNum; i++) { - short vertex[ 6]; - funpackf(tfile, "Bs Bs Bs Bs Bs Bs", &vertex[ 0], &vertex[ 1], &vertex[ 2], &vertex[ 3], &vertex[ 4], &vertex[ 5]); - Triangles[i].vertex[ 0] = vertex[ 0]; - Triangles[i].vertex[ 1] = vertex[ 2]; - Triangles[i].vertex[ 2] = vertex[ 4]; - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gx[0], &Triangles[i].gx[1], &Triangles[i].gx[2]); - funpackf(tfile, "Bf Bf Bf", &Triangles[i].gy[0], &Triangles[i].gy[1], &Triangles[i].gy[2]); - } - - - fclose(tfile); - - for (i = 0; i < vertexNum; i++) { - owner[i] = -1; - } - - return true; -} - - -void Model::UniformTexCoords() -{ - static int i; - for (i = 0; i < TriangleNum; i++) { - Triangles[i].gy[0] = vertex[Triangles[i].vertex[0]].y; - Triangles[i].gy[1] = vertex[Triangles[i].vertex[1]].y; - Triangles[i].gy[2] = vertex[Triangles[i].vertex[2]].y; - Triangles[i].gx[0] = vertex[Triangles[i].vertex[0]].x; - Triangles[i].gx[1] = vertex[Triangles[i].vertex[1]].x; - Triangles[i].gx[2] = vertex[Triangles[i].vertex[2]].x; - } - UpdateVertexArray(); -} - - -void Model::FlipTexCoords() -{ - static int i; - for (i = 0; i < TriangleNum; i++) { - Triangles[i].gy[0] = -Triangles[i].gy[0]; - Triangles[i].gy[1] = -Triangles[i].gy[1]; - Triangles[i].gy[2] = -Triangles[i].gy[2]; - } - UpdateVertexArray(); -} - -void Model::ScaleTexCoords(float howmuch) -{ - static int i; - for (i = 0; i < TriangleNum; i++) { - Triangles[i].gx[0] *= howmuch; - Triangles[i].gx[1] *= howmuch; - Triangles[i].gx[2] *= howmuch; - Triangles[i].gy[0] *= howmuch; - Triangles[i].gy[1] *= howmuch; - Triangles[i].gy[2] *= howmuch; - } - UpdateVertexArray(); -} - -void Model::Scale(float xscale, float yscale, float zscale) -{ - static int i; - for (i = 0; i < vertexNum; i++) { - vertex[i].x *= xscale; - vertex[i].y *= yscale; - vertex[i].z *= zscale; - } - UpdateVertexArray(); - - static int j; - - boundingsphereradius = 0; - for (i = 0; i < vertexNum; i++) { - for (j = 0; j < vertexNum; j++) { - if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { - boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; - boundingspherecenter = (vertex[i] + vertex[j]) / 2; - } - } - } - boundingsphereradius = fast_sqrt(boundingsphereradius); -} - -void Model::ScaleNormals(float xscale, float yscale, float zscale) -{ - if (type != normaltype && type != decalstype) - return; - static int i; - for (i = 0; i < vertexNum; i++) { - normals[i].x *= xscale; - normals[i].y *= yscale; - normals[i].z *= zscale; - } - for (i = 0; i < TriangleNum; i++) { - facenormals[i].x *= xscale; - facenormals[i].y *= yscale; - facenormals[i].z *= zscale; - } - UpdateVertexArray(); -} - -void Model::Translate(float xtrans, float ytrans, float ztrans) -{ - static int i; - for (i = 0; i < vertexNum; i++) { - vertex[i].x += xtrans; - vertex[i].y += ytrans; - vertex[i].z += ztrans; - } - UpdateVertexArray(); - - static int j; - boundingsphereradius = 0; - for (i = 0; i < vertexNum; i++) { - for (j = 0; j < vertexNum; j++) { - if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { - boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; - boundingspherecenter = (vertex[i] + vertex[j]) / 2; - } - } - } - boundingsphereradius = fast_sqrt(boundingsphereradius); -} - -void Model::Rotate(float xang, float yang, float zang) -{ - static int i; - for (i = 0; i < vertexNum; i++) { - vertex[i] = DoRotation(vertex[i], xang, yang, zang); - } - UpdateVertexArray(); - - static int j; - boundingsphereradius = 0; - for (i = 0; i < vertexNum; i++) { - for (j = 0; j < vertexNum; j++) { - if (j != i && distsq(&vertex[j], &vertex[i]) / 2 > boundingsphereradius) { - boundingsphereradius = distsq(&vertex[j], &vertex[i]) / 2; - boundingspherecenter = (vertex[i] + vertex[j]) / 2; - } - } - } - boundingsphereradius = fast_sqrt(boundingsphereradius); -} - - -void Model::CalculateNormals(bool facenormalise) -{ - if (visibleloading) - Game::LoadingScreen(); - static int i; - if (type != normaltype && type != decalstype) - return; - - for (i = 0; i < vertexNum; i++) { - normals[i].x = 0; - normals[i].y = 0; - normals[i].z = 0; - } - - for (i = 0; i < TriangleNum; i++) { - CrossProduct(vertex[Triangles[i].vertex[1]] - vertex[Triangles[i].vertex[0]], vertex[Triangles[i].vertex[2]] - vertex[Triangles[i].vertex[0]], &facenormals[i]); - - normals[Triangles[i].vertex[0]].x += facenormals[i].x; - normals[Triangles[i].vertex[0]].y += facenormals[i].y; - normals[Triangles[i].vertex[0]].z += facenormals[i].z; - - normals[Triangles[i].vertex[1]].x += facenormals[i].x; - normals[Triangles[i].vertex[1]].y += facenormals[i].y; - normals[Triangles[i].vertex[1]].z += facenormals[i].z; - - normals[Triangles[i].vertex[2]].x += facenormals[i].x; - normals[Triangles[i].vertex[2]].y += facenormals[i].y; - normals[Triangles[i].vertex[2]].z += facenormals[i].z; - if (facenormalise) - Normalise(&facenormals[i]); - } - for (i = 0; i < vertexNum; i++) { - Normalise(&normals[i]); - normals[i] *= -1; - } - UpdateVertexArrayNoTex(); -} - -void Model::drawimmediate() -{ - textureptr.bind(); - glBegin(GL_TRIANGLES); - for (int i = 0; i < TriangleNum; i++) { - glTexCoord2f(Triangles[i].gx[0], Triangles[i].gy[0]); - if (color) - glColor3f(normals[Triangles[i].vertex[0]].x, normals[Triangles[i].vertex[0]].y, normals[Triangles[i].vertex[0]].z); - if (!color && !flat) - glNormal3f(normals[Triangles[i].vertex[0]].x, normals[Triangles[i].vertex[0]].y, normals[Triangles[i].vertex[0]].z); - if (!color && flat) - glNormal3f(facenormals[i].x, facenormals[i].y, facenormals[i].y); - glVertex3f(vertex[Triangles[i].vertex[0]].x, vertex[Triangles[i].vertex[0]].y, vertex[Triangles[i].vertex[0]].z); - - glTexCoord2f(Triangles[i].gx[1], Triangles[i].gy[1]); - if (color) - glColor3f(normals[Triangles[i].vertex[1]].x, normals[Triangles[i].vertex[1]].y, normals[Triangles[i].vertex[1]].z); - if (!color && !flat) - glNormal3f(normals[Triangles[i].vertex[1]].x, normals[Triangles[i].vertex[1]].y, normals[Triangles[i].vertex[1]].z); - if (!color && flat) - glNormal3f(facenormals[i].x, facenormals[i].y, facenormals[i].y); - glVertex3f(vertex[Triangles[i].vertex[1]].x, vertex[Triangles[i].vertex[1]].y, vertex[Triangles[i].vertex[1]].z); - - glTexCoord2f(Triangles[i].gx[2], Triangles[i].gy[2]); - if (color) - glColor3f(normals[Triangles[i].vertex[2]].x, normals[Triangles[i].vertex[2]].y, normals[Triangles[i].vertex[2]].z); - if (!color && !flat) - glNormal3f(normals[Triangles[i].vertex[2]].x, normals[Triangles[i].vertex[2]].y, normals[Triangles[i].vertex[2]].z); - if (!color && flat) - glNormal3f(facenormals[i].x, facenormals[i].y, facenormals[i].y); - glVertex3f(vertex[Triangles[i].vertex[2]].x, vertex[Triangles[i].vertex[2]].y, vertex[Triangles[i].vertex[2]].z); - } - glEnd(); -} - -void Model::draw() -{ - if (type != normaltype && type != decalstype) - return; - - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - if (!color) - glInterleavedArrays( GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); - if (color) - glInterleavedArrays( GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); - textureptr.bind(); - - glDrawArrays(GL_TRIANGLES, 0, TriangleNum * 3); - - if (!color) - glDisableClientState(GL_NORMAL_ARRAY); - if (color) - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -//TODO: phase out in favor of Texture -void Model::drawdifftex(GLuint texture) -{ - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if (!color) - glInterleavedArrays( GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); - if (color) - glInterleavedArrays( GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); - - glBindTexture(GL_TEXTURE_2D, (unsigned long)texture); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); - - -#ifndef WIN32 - glLockArraysEXT( 0, TriangleNum * 3); -#endif - glDrawArrays(GL_TRIANGLES, 0, TriangleNum * 3); -#ifndef WIN32 - glUnlockArraysEXT(); -#endif - - - if (!color) - glDisableClientState(GL_NORMAL_ARRAY); - if (color) - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -void Model::drawdifftex(Texture texture) -{ - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if (!color) - glInterleavedArrays( GL_T2F_N3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); - if (color) - glInterleavedArrays( GL_T2F_C3F_V3F, 8 * sizeof(GLfloat), &vArray[0]); - - texture.bind(); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); - - -#ifndef WIN32 - glLockArraysEXT( 0, TriangleNum * 3); -#endif - glDrawArrays(GL_TRIANGLES, 0, TriangleNum * 3); -#ifndef WIN32 - glUnlockArraysEXT(); -#endif - - - if (!color) - glDisableClientState(GL_NORMAL_ARRAY); - if (color) - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -void Model::drawdecals(Texture shadowtexture, Texture bloodtexture, Texture bloodtexture2, Texture breaktexture) -{ - if (decals) { - if (type != decalstype) - return; - static int i; - static int lasttype; - static bool blend; - - blend = 1; - - lasttype = -1; - glEnable(GL_BLEND); - glDisable(GL_LIGHTING); - glDisable(GL_CULL_FACE); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(0); - if (numdecals > max_model_decals) - numdecals = max_model_decals; - for (i = 0; i < numdecals; i++) { - if (decaltype[i] == blooddecalfast && decalalivetime[i] < 2) - decalalivetime[i] = 2; - - if (decaltype[i] == shadowdecal && decaltype[i] != lasttype) { - shadowtexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - } - if (decaltype[i] == breakdecal && decaltype[i] != lasttype) { - breaktexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - } - if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalslow) && decaltype[i] != lasttype) { - bloodtexture.bind(); - if (blend) { - blend = 0; - glAlphaFunc(GL_GREATER, 0.15); - glBlendFunc(GL_ONE, GL_ZERO); - } - } - if ((decaltype[i] == blooddecalfast) && decaltype[i] != lasttype) { - bloodtexture2.bind(); - if (blend) { - blend = 0; - glAlphaFunc(GL_GREATER, 0.15); - glBlendFunc(GL_ONE, GL_ZERO); - } - } - if (decaltype[i] == shadowdecal) { - glColor4f(1, 1, 1, decalopacity[i]); - } - if (decaltype[i] == breakdecal) { - glColor4f(1, 1, 1, decalopacity[i]); - if (decalalivetime[i] > 58) - glColor4f(1, 1, 1, decalopacity[i] * (60 - decalalivetime[i]) / 2); - } - if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow)) { - glColor4f(1, 1, 1, decalopacity[i]); - if (decalalivetime[i] < 4) - glColor4f(1, 1, 1, decalopacity[i]*decalalivetime[i]*.25); - if (decalalivetime[i] > 58) - glColor4f(1, 1, 1, decalopacity[i] * (60 - decalalivetime[i]) / 2); - } - lasttype = decaltype[i]; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glBegin(GL_TRIANGLES); - for (int j = 0; j < 3; j++) { - glTexCoord2f(decaltexcoords[i][j][0], decaltexcoords[i][j][1]); - glVertex3f(decalvertex[i][j].x, decalvertex[i][j].y, decalvertex[i][j].z); - } - glEnd(); - glPopMatrix(); - } - for (i = numdecals - 1; i >= 0; i--) { - decalalivetime[i] += multiplier; - if (decaltype[i] == blooddecalslow) - decalalivetime[i] -= multiplier * 2 / 3; - if (decaltype[i] == blooddecalfast) - decalalivetime[i] += multiplier * 4; - if (decaltype[i] == shadowdecal) - DeleteDecal(i); - if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow) && decalalivetime[i] >= 60) - DeleteDecal(i); - } - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } -} - -void Model::DeleteDecal(int which) -{ - if (decals) { - if (type != decalstype) - return; - decaltype[which] = decaltype[numdecals - 1]; - decalposition[which] = decalposition[numdecals - 1]; - for (int i = 0; i < 3; i++) { - decalvertex[which][i] = decalvertex[numdecals - 1][i]; - decaltexcoords[which][i][0] = decaltexcoords[numdecals - 1][i][0]; - decaltexcoords[which][i][1] = decaltexcoords[numdecals - 1][i][1]; - } - decalrotation[which] = decalrotation[numdecals - 1]; - decalalivetime[which] = decalalivetime[numdecals - 1]; - decalopacity[which] = decalopacity[numdecals - 1]; - numdecals--; - } -} - -void Model::MakeDecal(int atype, XYZ *where, float *size, float *opacity, float *rotation) -{ - if (decals) { - if (type != decalstype) - return; - - static float placex, placez; - static XYZ rot; - static float distance; - static int i, j; - - if (*opacity > 0) - if (distsq(where, &boundingspherecenter) < (boundingsphereradius + *size) * (boundingsphereradius + *size)) - for (i = 0; i < TriangleNum; i++) { - if (facenormals[i].y < -.1 && (vertex[Triangles[i].vertex[0]].y < where->y || vertex[Triangles[i].vertex[1]].y < where->y || vertex[Triangles[i].vertex[2]].y < where->y)) { - decalposition[numdecals] = *where; - decaltype[numdecals] = atype; - decalrotation[numdecals] = *rotation; - decalalivetime[numdecals] = 0; - distance = abs(((facenormals[i].x * where->x) + (facenormals[i].y * where->y) + (facenormals[i].z * where->z) - ((facenormals[i].x * vertex[Triangles[i].vertex[0]].x) + (facenormals[i].y * vertex[Triangles[i].vertex[0]].y) + (facenormals[i].z * vertex[Triangles[i].vertex[0]].z))) / facenormals[i].y); - decalopacity[numdecals] = *opacity - distance / 10; - - if (decalopacity[numdecals > 0]) { - placex = vertex[Triangles[i].vertex[0]].x; - placez = vertex[Triangles[i].vertex[0]].z; - - decaltexcoords[numdecals][0][0] = (placex - where->x) / (*size) / 2 + .5; - decaltexcoords[numdecals][0][1] = (placez - where->z) / (*size) / 2 + .5; - - decalvertex[numdecals][0].x = placex; - decalvertex[numdecals][0].z = placez; - decalvertex[numdecals][0].y = vertex[Triangles[i].vertex[0]].y; - - - placex = vertex[Triangles[i].vertex[1]].x; - placez = vertex[Triangles[i].vertex[1]].z; - - decaltexcoords[numdecals][1][0] = (placex - where->x) / (*size) / 2 + .5; - decaltexcoords[numdecals][1][1] = (placez - where->z) / (*size) / 2 + .5; - - decalvertex[numdecals][1].x = placex; - decalvertex[numdecals][1].z = placez; - decalvertex[numdecals][1].y = vertex[Triangles[i].vertex[1]].y; - - - placex = vertex[Triangles[i].vertex[2]].x; - placez = vertex[Triangles[i].vertex[2]].z; - - decaltexcoords[numdecals][2][0] = (placex - where->x) / (*size) / 2 + .5; - decaltexcoords[numdecals][2][1] = (placez - where->z) / (*size) / 2 + .5; - - decalvertex[numdecals][2].x = placex; - decalvertex[numdecals][2].z = placez; - decalvertex[numdecals][2].y = vertex[Triangles[i].vertex[2]].y; - - if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) - if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) - if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) - if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { - if (decalrotation[numdecals]) { - for (j = 0; j < 3; j++) { - rot.y = 0; - rot.x = decaltexcoords[numdecals][j][0] - .5; - rot.z = decaltexcoords[numdecals][j][1] - .5; - rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); - decaltexcoords[numdecals][j][0] = rot.x + .5; - decaltexcoords[numdecals][j][1] = rot.z + .5; - } - } - if (numdecals < max_model_decals - 1) - numdecals++; - } - } - } - } - } -} - -void Model::MakeDecal(int atype, XYZ where, float size, float opacity, float rotation) -{ - if (decals) { - if (type != decalstype) - return; - - static float placex, placez; - static XYZ rot; - static float distance; - static int i, j; - - if (opacity > 0) - if (distsq(&where, &boundingspherecenter) < (boundingsphereradius + size) * (boundingsphereradius + size)) - for (i = 0; i < TriangleNum; i++) { - distance = abs(((facenormals[i].x * where.x) + (facenormals[i].y * where.y) + (facenormals[i].z * where.z) - ((facenormals[i].x * vertex[Triangles[i].vertex[0]].x) + (facenormals[i].y * vertex[Triangles[i].vertex[0]].y) + (facenormals[i].z * vertex[Triangles[i].vertex[0]].z)))); - if (distance < .02 && abs(facenormals[i].y) > abs(facenormals[i].x) && abs(facenormals[i].y) > abs(facenormals[i].z)) { - decalposition[numdecals] = where; - decaltype[numdecals] = atype; - decalrotation[numdecals] = rotation; - decalalivetime[numdecals] = 0; - decalopacity[numdecals] = opacity - distance / 10; - - if (decalopacity[numdecals > 0]) { - placex = vertex[Triangles[i].vertex[0]].x; - placez = vertex[Triangles[i].vertex[0]].z; - - decaltexcoords[numdecals][0][0] = (placex - where.x) / (size) / 2 + .5; - decaltexcoords[numdecals][0][1] = (placez - where.z) / (size) / 2 + .5; - - decalvertex[numdecals][0].x = placex; - decalvertex[numdecals][0].z = placez; - decalvertex[numdecals][0].y = vertex[Triangles[i].vertex[0]].y; - - - placex = vertex[Triangles[i].vertex[1]].x; - placez = vertex[Triangles[i].vertex[1]].z; - - decaltexcoords[numdecals][1][0] = (placex - where.x) / (size) / 2 + .5; - decaltexcoords[numdecals][1][1] = (placez - where.z) / (size) / 2 + .5; - - decalvertex[numdecals][1].x = placex; - decalvertex[numdecals][1].z = placez; - decalvertex[numdecals][1].y = vertex[Triangles[i].vertex[1]].y; - - - placex = vertex[Triangles[i].vertex[2]].x; - placez = vertex[Triangles[i].vertex[2]].z; - - decaltexcoords[numdecals][2][0] = (placex - where.x) / (size) / 2 + .5; - decaltexcoords[numdecals][2][1] = (placez - where.z) / (size) / 2 + .5; - - decalvertex[numdecals][2].x = placex; - decalvertex[numdecals][2].z = placez; - decalvertex[numdecals][2].y = vertex[Triangles[i].vertex[2]].y; - - if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) - if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) - if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) - if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { - if (decalrotation[numdecals]) { - for (j = 0; j < 3; j++) { - rot.y = 0; - rot.x = decaltexcoords[numdecals][j][0] - .5; - rot.z = decaltexcoords[numdecals][j][1] - .5; - rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); - decaltexcoords[numdecals][j][0] = rot.x + .5; - decaltexcoords[numdecals][j][1] = rot.z + .5; - } - } - if (numdecals < max_model_decals - 1) - numdecals++; - } - } - } else if (distance < .02 && abs(facenormals[i].x) > abs(facenormals[i].y) && abs(facenormals[i].x) > abs(facenormals[i].z)) { - decalposition[numdecals] = where; - decaltype[numdecals] = atype; - decalrotation[numdecals] = rotation; - decalalivetime[numdecals] = 0; - decalopacity[numdecals] = opacity - distance / 10; - - if (decalopacity[numdecals > 0]) { - placex = vertex[Triangles[i].vertex[0]].y; - placez = vertex[Triangles[i].vertex[0]].z; - - decaltexcoords[numdecals][0][0] = (placex - where.y) / (size) / 2 + .5; - decaltexcoords[numdecals][0][1] = (placez - where.z) / (size) / 2 + .5; - - decalvertex[numdecals][0].x = vertex[Triangles[i].vertex[0]].x; - decalvertex[numdecals][0].z = placez; - decalvertex[numdecals][0].y = placex; - - - placex = vertex[Triangles[i].vertex[1]].y; - placez = vertex[Triangles[i].vertex[1]].z; - - decaltexcoords[numdecals][1][0] = (placex - where.y) / (size) / 2 + .5; - decaltexcoords[numdecals][1][1] = (placez - where.z) / (size) / 2 + .5; - - decalvertex[numdecals][1].x = vertex[Triangles[i].vertex[1]].x; - decalvertex[numdecals][1].z = placez; - decalvertex[numdecals][1].y = placex; - - - placex = vertex[Triangles[i].vertex[2]].y; - placez = vertex[Triangles[i].vertex[2]].z; - - decaltexcoords[numdecals][2][0] = (placex - where.y) / (size) / 2 + .5; - decaltexcoords[numdecals][2][1] = (placez - where.z) / (size) / 2 + .5; - - decalvertex[numdecals][2].x = vertex[Triangles[i].vertex[2]].x; - decalvertex[numdecals][2].z = placez; - decalvertex[numdecals][2].y = placex; - - if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) - if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) - if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) - if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { - if (decalrotation[numdecals]) { - for (j = 0; j < 3; j++) { - rot.y = 0; - rot.x = decaltexcoords[numdecals][j][0] - .5; - rot.z = decaltexcoords[numdecals][j][1] - .5; - rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); - decaltexcoords[numdecals][j][0] = rot.x + .5; - decaltexcoords[numdecals][j][1] = rot.z + .5; - } - } - if (numdecals < max_model_decals - 1) - numdecals++; - } - } - } else if (distance < .02 && abs(facenormals[i].z) > abs(facenormals[i].y) && abs(facenormals[i].z) > abs(facenormals[i].x)) { - decalposition[numdecals] = where; - decaltype[numdecals] = atype; - decalrotation[numdecals] = rotation; - decalalivetime[numdecals] = 0; - decalopacity[numdecals] = opacity - distance / 10; - - if (decalopacity[numdecals > 0]) { - placex = vertex[Triangles[i].vertex[0]].x; - placez = vertex[Triangles[i].vertex[0]].y; - - decaltexcoords[numdecals][0][0] = (placex - where.x) / (size) / 2 + .5; - decaltexcoords[numdecals][0][1] = (placez - where.y) / (size) / 2 + .5; - - decalvertex[numdecals][0].x = placex; - decalvertex[numdecals][0].z = vertex[Triangles[i].vertex[0]].z; - decalvertex[numdecals][0].y = placez; - - - placex = vertex[Triangles[i].vertex[1]].x; - placez = vertex[Triangles[i].vertex[1]].y; - - decaltexcoords[numdecals][1][0] = (placex - where.x) / (size) / 2 + .5; - decaltexcoords[numdecals][1][1] = (placez - where.y) / (size) / 2 + .5; - - decalvertex[numdecals][1].x = placex; - decalvertex[numdecals][1].z = vertex[Triangles[i].vertex[1]].z; - decalvertex[numdecals][1].y = placez; - - - placex = vertex[Triangles[i].vertex[2]].x; - placez = vertex[Triangles[i].vertex[2]].y; - - decaltexcoords[numdecals][2][0] = (placex - where.x) / (size) / 2 + .5; - decaltexcoords[numdecals][2][1] = (placez - where.y) / (size) / 2 + .5; - - decalvertex[numdecals][2].x = placex; - decalvertex[numdecals][2].z = vertex[Triangles[i].vertex[2]].z; - decalvertex[numdecals][2].y = placez; - - if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) - if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) - if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) - if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) { - if (decalrotation[numdecals]) { - for (j = 0; j < 3; j++) { - rot.y = 0; - rot.x = decaltexcoords[numdecals][j][0] - .5; - rot.z = decaltexcoords[numdecals][j][1] - .5; - rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); - decaltexcoords[numdecals][j][0] = rot.x + .5; - decaltexcoords[numdecals][j][1] = rot.z + .5; - } - } - if (numdecals < max_model_decals - 1) - numdecals++; - } - } - } - } - } -} - -Model::~Model() -{ - deallocate(); - textureptr.destroy(); -} - -void Model::deallocate() -{ - int i = 0, j = 0; - - if (owner) - free(owner); - owner = 0; - - if (possible) - free(possible); - possible = 0; - - if (vertex) - free(vertex); - vertex = 0; - - if (normals) - free(normals); - normals = 0; - - if (facenormals) - free(facenormals); - facenormals = 0; - - if (Triangles) - free(Triangles); - Triangles = 0; - - if (vArray) - free(vArray); - vArray = 0; - - - //allow decals - if (decaltexcoords) { - for (i = 0; i < max_model_decals; i++) { - for (j = 0; j < 3; j++) { - free(decaltexcoords[i][j]); - } - free(decaltexcoords[i]); - } - free(decaltexcoords); - } - decaltexcoords = 0; - - - if (decalvertex) { - for (i = 0; i < max_model_decals; i++) { - free(decalvertex[i]); - } - free(decalvertex); - } - decalvertex = 0; - - - free(decaltype); - decaltype = 0; - - free(decalopacity); - decalopacity = 0; - - free(decalrotation); - decalrotation = 0; - - free(decalalivetime); - decalalivetime = 0; - - free(decalposition); - decalposition = 0; - -}; - -Model::Model() -{ - vertexNum = 0, TriangleNum = 0; - hastexture = 0; - - type = 0, oldtype = 0; - - possible = 0; - owner = 0; - vertex = 0; - normals = 0; - facenormals = 0; - Triangles = 0; - vArray = 0; - - memset(&modelTexture, 0, sizeof(modelTexture)); - numpossible = 0; - color = 0; - - boundingspherecenter = 0; - boundingsphereradius = 0; - - decaltexcoords = 0; - decalvertex = 0; - decaltype = 0; - decalopacity = 0; - decalrotation = 0; - decalalivetime = 0; - decalposition = 0; - - numdecals = 0; - - flat = 0; - - type = nothing; -} - diff --git a/Source/Models.h b/Source/Models.h deleted file mode 100644 index 3874e23..0000000 --- a/Source/Models.h +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _MODELS_H_ -#define _MODELS_H_ - -/**> Model Loading <**/ -// -// Model Maximums -// -#include "gamegl.h" -#include -#include -#include -#include - -#include "Terrain.h" -#include "binio.h" -#include "Quaternions.h" -#include "Texture.h" - -// -// Textures List -// -typedef struct { - long xsz, ysz; - GLubyte *txt; -} ModelTexture; - -// -// Model Structures -// - -class TexturedTriangle -{ -public: - short vertex[3]; - float gx[3], gy[3]; -}; - -#define max_model_decals 300 - -#define nothing 0 -#define normaltype 4 -#define notextype 1 -#define rawtype 2 -#define decalstype 3 - -class Model -{ -public: - short vertexNum, TriangleNum; - bool hastexture; - - int type, oldtype; - - int* possible; - int* owner; - XYZ* vertex; - XYZ* normals; - XYZ* facenormals; - TexturedTriangle* Triangles; - GLfloat* vArray; - - /*int possible[max_model_vertex]; - int owner[max_textured_triangle]; - XYZ vertex[max_model_vertex]; - XYZ normals[max_model_vertex]; - XYZ facenormals[max_textured_triangle]; - TexturedTriangle Triangles[max_textured_triangle]; - GLfloat vArray[max_textured_triangle*24];*/ - - Texture textureptr; - ModelTexture modelTexture; - int numpossible; - bool color; - - XYZ boundingspherecenter; - float boundingsphereradius; - - float*** decaltexcoords; - XYZ** decalvertex; - int* decaltype; - float* decalopacity; - float* decalrotation; - float* decalalivetime; - XYZ* decalposition; - - /*float decaltexcoords[max_model_decals][3][2]; - XYZ decalvertex[max_model_decals][3]; - int decaltype[max_model_decals]; - float decalopacity[max_model_decals]; - float decalrotation[max_model_decals]; - float decalalivetime[max_model_decals]; - XYZ decalposition[max_model_decals];*/ - - int numdecals; - - bool flat; - - void DeleteDecal(int which); - void MakeDecal(int atype, XYZ *where, float *size, float *opacity, float *rotation); - void MakeDecal(int atype, XYZ where, float size, float opacity, float rotation); - void drawdecals(Texture shadowtexture, Texture bloodtexture, Texture bloodtexture2, Texture breaktexture); - int SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate); - int SphereCheckPossible(XYZ *p1, float radius, XYZ *move, float *rotate); - int LineCheck(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate); - int LineCheckPossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate); - int LineCheckSlidePossible(XYZ *p1, XYZ *p2, XYZ *p, XYZ *move, float *rotate); - void UpdateVertexArray(); - void UpdateVertexArrayNoTex(); - void UpdateVertexArrayNoTexNoNorm(); - bool loadnotex(const std::string& filename); - bool loadraw(const std::string& filename); - bool load(const std::string& filename, bool texture); - bool loaddecal(const std::string& filename, bool texture); - void Scale(float xscale, float yscale, float zscale); - void FlipTexCoords(); - void UniformTexCoords(); - void ScaleTexCoords(float howmuch); - void ScaleNormals(float xscale, float yscale, float zscale); - void Translate(float xtrans, float ytrans, float ztrans); - void CalculateNormals(bool facenormalise); - void draw(); - void drawdifftex(GLuint texture); - void drawdifftex(Texture texture); - void drawimmediate(); - void Rotate(float xang, float yang, float zang); - ~Model(); - void deallocate(); - Model(); -}; - -#endif diff --git a/Source/Objects.cpp b/Source/Objects.cpp deleted file mode 100644 index dd4f4a7..0000000 --- a/Source/Objects.cpp +++ /dev/null @@ -1,841 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Objects.h" -extern XYZ viewer; -extern float viewdistance; -extern float fadestart; -extern int environment; -extern float texscale; -extern Light light; -extern float multiplier; -extern float gravity; -extern FRUSTUM frustum; -extern Terrain terrain; -extern bool foliage; -extern int detail; -extern float blurness; -extern float windvar; -extern float playerdist; -extern bool skyboxtexture; - -//Functions - -bool Objects::checkcollide(XYZ startpoint, XYZ endpoint, int which) -{ - static XYZ colpoint, colviewer, coltarget; - static int i; - - startpoint.y += .1; - endpoint.y += .1; - startpoint.y -= .1; - endpoint.y -= .1; - - for (i = 0; i < numobjects; i++) { - if (type[i] != treeleavestype && type[i] != treetrunktype && type[i] != bushtype && type[i] != firetype && i != which) { - colviewer = startpoint; - coltarget = endpoint; - if (model[i].LineCheck(&colviewer, &coltarget, &colpoint, &position[i], &yaw[i]) != -1) - return 1; - } - } - - return 0; -} - -void Objects::SphereCheckPossible(XYZ *p1, float radius) -{ - static int i, j; - static int whichpatchx; - static int whichpatchz; - - whichpatchx = p1->x / (terrain.size / subdivision * terrain.scale); - whichpatchz = p1->z / (terrain.size / subdivision * terrain.scale); - - if (whichpatchx >= 0 && whichpatchz >= 0 && whichpatchx < subdivision && whichpatchz < subdivision) - if (terrain.patchobjectnum[whichpatchx][whichpatchz] > 0 && terrain.patchobjectnum[whichpatchx][whichpatchz] < 500) - for (j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { - i = terrain.patchobjects[whichpatchx][whichpatchz][j]; - possible[i] = 0; - if (model[i].SphereCheckPossible(p1, radius, &position[i], &yaw[i]) != -1) { - possible[i] = 1; - } - } -} - -void Objects::Draw() -{ - static float distance; - static int i; - static XYZ moved, terrainlight; - bool hidden; - - for (i = 0; i < numobjects; i++) { - if (type[i] != firetype) { - moved = DoRotation(model[i].boundingspherecenter, 0, yaw[i], 0); - if (type[i] == tunneltype || frustum.SphereInFrustum(position[i].x + moved.x, position[i].y + moved.y, position[i].z + moved.z, model[i].boundingsphereradius)) { - distance = distsq(&viewer, &position[i]); - distance *= 1.2; - hidden = !(distsqflat(&viewer, &position[i]) > playerdist + 3 || (type[i] != bushtype && type[i] != treeleavestype)); - if (!hidden) { - - if (detail == 2 && distance > viewdistance * viewdistance / 4 && environment == desertenvironment) - glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, blurness ); - else - glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); - distance = (viewdistance * viewdistance - (distance - (viewdistance * viewdistance * fadestart)) * (1 / (1 - fadestart))) / viewdistance / viewdistance; - if (distance > 1) - distance = 1; - if (distance > 0) { - - if (occluded[i] < 6) { - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - if (!model[i].color) - glEnable(GL_LIGHTING); - else - glDisable(GL_LIGHTING); - glDepthMask(1); - glTranslatef(position[i].x, position[i].y, position[i].z); - if (type[i] == bushtype) { - messedwith[i] -= multiplier; - if (rotxvel[i] || rotx[i]) { - if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); - if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); - if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotx[i] < 0) rotxvel[i] += multiplier * 4; - if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; - if (fabs(rotx[i]) < multiplier * 4) - rotx[i] = 0; - if (fabs(rotxvel[i]) < multiplier * 4) - rotxvel[i] = 0; - - rotx[i] += rotxvel[i] * multiplier * 4; - } - if (rotyvel[i] || roty[i]) { - if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); - if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); - if (roty[i] > 0) rotyvel[i] -= multiplier * 4; - if (roty[i] < 0) rotyvel[i] += multiplier * 4; - if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; - if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; - if (fabs(roty[i]) < multiplier * 4) - roty[i] = 0; - if (fabs(rotyvel[i]) < multiplier * 4) - rotyvel[i] = 0; - - roty[i] += rotyvel[i] * multiplier * 4; - } - if (roty[i]) { - glRotatef(roty[i], 1, 0, 0); - } - if (rotx[i]) { - glRotatef(-rotx[i], 0, 0, 1); - } - if (rotx[i] > 10) - rotx[i] = 10; - if (rotx[i] < -10) - rotx[i] = -10; - if (roty[i] > 10) - roty[i] = 10; - if (roty[i] < -10) - roty[i] = -10; - } - if (type[i] == treetrunktype || type[i] == treeleavestype) { - if (type[i] == treetrunktype || environment == desertenvironment) { - messedwith[i] -= multiplier; - if (rotxvel[i] || rotx[i]) { - if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); - if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); - if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotx[i] < 0) rotxvel[i] += multiplier * 4; - if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; - if (fabs(rotx[i]) < multiplier * 4) - rotx[i] = 0; - if (fabs(rotxvel[i]) < multiplier * 4) - rotxvel[i] = 0; - - rotx[i] += rotxvel[i] * multiplier * 4; - } - if (rotyvel[i] || roty[i]) { - if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); - if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); - if (roty[i] > 0) rotyvel[i] -= multiplier * 4; - if (roty[i] < 0) rotyvel[i] += multiplier * 4; - if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; - if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; - if (fabs(roty[i]) < multiplier * 4) - roty[i] = 0; - if (fabs(rotyvel[i]) < multiplier * 4) - rotyvel[i] = 0; - - roty[i] += rotyvel[i] * multiplier * 4; - } - if (roty[i]) { - glRotatef(roty[i] / 6, 1, 0, 0); - } - if (rotx[i]) { - glRotatef(-rotx[i] / 6, 0, 0, 1); - } - if (rotx[i] > 10) - rotx[i] = 10; - if (rotx[i] < -10) - rotx[i] = -10; - if (roty[i] > 10) - roty[i] = 10; - if (roty[i] < -10) - roty[i] = -10; - } else { - messedwith[i] -= multiplier; - if (rotxvel[i] || rotx[i]) { - if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); - if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); - if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotx[i] < 0) rotxvel[i] += multiplier * 4; - if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; - if (fabs(rotx[i]) < multiplier * 4) - rotx[i] = 0; - if (fabs(rotxvel[i]) < multiplier * 4) - rotxvel[i] = 0; - - rotx[i] += rotxvel[i] * multiplier * 4; - } - if (rotyvel[i] || roty[i]) { - if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); - if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); - if (roty[i] > 0) rotyvel[i] -= multiplier * 4; - if (roty[i] < 0) rotyvel[i] += multiplier * 4; - if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; - if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; - if (fabs(roty[i]) < multiplier * 4) - roty[i] = 0; - if (fabs(rotyvel[i]) < multiplier * 4) - rotyvel[i] = 0; - - roty[i] += rotyvel[i] * multiplier * 4; - } - if (roty[i]) { - glRotatef(roty[i] / 4, 1, 0, 0); - } - if (rotx[i]) { - glRotatef(-rotx[i] / 4, 0, 0, 1); - } - if (rotx[i] > 10) - rotx[i] = 10; - if (rotx[i] < -10) - rotx[i] = -10; - if (roty[i] > 10) - roty[i] = 10; - if (roty[i] < -10) - roty[i] = -10; - } - - } - if (/*detail==2&&*/environment == snowyenvironment) { - if (type[i] == treeleavestype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == treetrunktype) { - glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == bushtype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - } - if (/*detail==2&&*/environment == grassyenvironment) { - if (type[i] == treeleavestype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == treetrunktype) { - glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == bushtype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - } - if (/*detail==2&&*/environment == desertenvironment) { - if (type[i] == bushtype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - } - glRotatef(yaw[i], 0, 1, 0); - if (distance > 1) - distance = 1; - glColor4f((1 - shadowed[i]) / 2 + .5, (1 - shadowed[i]) / 2 + .5, (1 - shadowed[i]) / 2 + .5, distance); - if (distance >= 1) { - glDisable(GL_BLEND); - glAlphaFunc(GL_GREATER, 0.5); - } - if (distance < 1) { - glEnable(GL_BLEND); - glAlphaFunc(GL_GREATER, 0.1); - } - if (type[i] != treetrunktype && type[i] != treeleavestype && type[i] != bushtype && type[i] != rocktype) { - glEnable(GL_CULL_FACE); - glAlphaFunc(GL_GREATER, 0.0001); - model[i].drawdifftex(boxtextureptr); - model[i].drawdecals(terrain.shadowtexture, terrain.bloodtexture, terrain.bloodtexture2, terrain.breaktexture); - } - if (type[i] == rocktype) { - glEnable(GL_CULL_FACE); - glAlphaFunc(GL_GREATER, 0.0001); - glColor4f((1 - shadowed[i]) / 2 + light.ambient[0], (1 - shadowed[i]) / 2 + light.ambient[1], (1 - shadowed[i]) / 2 + light.ambient[2], distance); - model[i].drawdifftex(rocktextureptr); - model[i].drawdecals(terrain.shadowtexture, terrain.bloodtexture, terrain.bloodtexture2, terrain.breaktexture); - } - if (type[i] == treeleavestype) { - glDisable(GL_CULL_FACE); - glDisable(GL_LIGHTING); - terrainlight = terrain.getLighting(position[i].x, position[i].z); - if (!hidden) { - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance); - if (distance < 1) - glAlphaFunc(GL_GREATER, 0.2); - } - if (hidden) { - glDepthMask(0); - glEnable(GL_BLEND); - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance / 3); - glAlphaFunc(GL_GREATER, 0); - } - model[i].drawdifftex(treetextureptr); - } - if (type[i] == bushtype) { - glDisable(GL_CULL_FACE); - glDisable(GL_LIGHTING); - terrainlight = terrain.getLighting(position[i].x, position[i].z); - if (!hidden) { - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance); - if (distance < 1) - glAlphaFunc(GL_GREATER, 0.2); - } - if (hidden) { - glDepthMask(0); - glEnable(GL_BLEND); - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance / 3); - glAlphaFunc(GL_GREATER, 0); - } - model[i].drawdifftex(bushtextureptr); - } - if (type[i] == treetrunktype) { - glEnable(GL_CULL_FACE); - terrainlight = terrain.getLighting(position[i].x, position[i].z); - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance); - model[i].drawdifftex(treetextureptr); - } - glPopMatrix(); - } - } - } - } - } - } - - glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); - for (i = 0; i < numobjects; i++) { - if (type[i] == treeleavestype || type[i] == bushtype) { - moved = DoRotation(model[i].boundingspherecenter, 0, yaw[i], 0); - if (frustum.SphereInFrustum(position[i].x + moved.x, position[i].y + moved.y, position[i].z + moved.z, model[i].boundingsphereradius)) { - hidden = distsqflat(&viewer, &position[i]) <= playerdist + 3; - if (hidden) { - distance = 1; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glEnable(GL_LIGHTING); - glDepthMask(1); - glTranslatef(position[i].x, position[i].y, position[i].z); - if (type[i] == bushtype) { - messedwith[i] -= multiplier; - if (rotxvel[i] || rotx[i]) { - if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); - if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); - if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotx[i] < 0) rotxvel[i] += multiplier * 4; - if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; - if (fabs(rotx[i]) < multiplier * 4) - rotx[i] = 0; - if (fabs(rotxvel[i]) < multiplier * 4) - rotxvel[i] = 0; - - rotx[i] += rotxvel[i] * multiplier * 4; - } - if (rotyvel[i] || roty[i]) { - if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); - if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); - if (roty[i] > 0) rotyvel[i] -= multiplier * 4; - if (roty[i] < 0) rotyvel[i] += multiplier * 4; - if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; - if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; - if (fabs(roty[i]) < multiplier * 4) - roty[i] = 0; - if (fabs(rotyvel[i]) < multiplier * 4) - rotyvel[i] = 0; - - roty[i] += rotyvel[i] * multiplier * 4; - } - if (roty[i]) { - glRotatef(roty[i], 1, 0, 0); - } - if (rotx[i]) { - glRotatef(-rotx[i], 0, 0, 1); - } - if (rotx[i] > 10) - rotx[i] = 10; - if (rotx[i] < -10) - rotx[i] = -10; - if (roty[i] > 10) - roty[i] = 10; - if (roty[i] < -10) - roty[i] = -10; - } - if (type[i] == treetrunktype || type[i] == treeleavestype) { - messedwith[i] -= multiplier; - if (rotxvel[i] || rotx[i]) { - if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); - if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); - if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotx[i] < 0) rotxvel[i] += multiplier * 4; - if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; - if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; - if (fabs(rotx[i]) < multiplier * 4) - rotx[i] = 0; - if (fabs(rotxvel[i]) < multiplier * 4) - rotxvel[i] = 0; - - rotx[i] += rotxvel[i] * multiplier * 4; - } - if (rotyvel[i] || roty[i]) { - if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); - if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); - if (roty[i] > 0) rotyvel[i] -= multiplier * 4; - if (roty[i] < 0) rotyvel[i] += multiplier * 4; - if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; - if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; - if (fabs(roty[i]) < multiplier * 4) - roty[i] = 0; - if (fabs(rotyvel[i]) < multiplier * 4) - rotyvel[i] = 0; - - roty[i] += rotyvel[i] * multiplier * 4; - } - if (roty[i]) { - glRotatef(roty[i] / 2, 1, 0, 0); - } - if (rotx[i]) { - glRotatef(-rotx[i] / 2, 0, 0, 1); - } - if (rotx[i] > 10) - rotx[i] = 10; - if (rotx[i] < -10) - rotx[i] = -10; - if (roty[i] > 10) - roty[i] = 10; - if (roty[i] < -10) - roty[i] = -10; - } - if (environment == snowyenvironment) { - if (type[i] == treeleavestype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == treetrunktype) { - glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == bushtype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - } - if (environment == grassyenvironment) { - if (type[i] == treeleavestype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == treetrunktype) { - glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - if (type[i] == bushtype) { - glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); - } - } - glRotatef(yaw[i], 0, 1, 0); - glColor4f(1, 1, 1, distance); - if (type[i] == treeleavestype) { - glDisable(GL_CULL_FACE); - glDisable(GL_LIGHTING); - terrainlight = terrain.getLighting(position[i].x, position[i].z); - glDepthMask(0); - glEnable(GL_BLEND); - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, .3); - glAlphaFunc(GL_GREATER, 0); - glDisable(GL_ALPHA_TEST); - model[i].drawdifftex(treetextureptr); - } - if (type[i] == bushtype) { - glDisable(GL_CULL_FACE); - glDisable(GL_LIGHTING); - terrainlight = terrain.getLighting(position[i].x, position[i].z); - glDepthMask(0); - glEnable(GL_BLEND); - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, .3); - glAlphaFunc(GL_GREATER, 0); - glDisable(GL_ALPHA_TEST); - model[i].drawdifftex(bushtextureptr); - } - glPopMatrix(); - } - } - } - } - if (environment == desertenvironment) - glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); - glEnable(GL_ALPHA_TEST); - SetUpLight(&light, 0); -} - -void Objects::DeleteObject(int which) -{ - type[numobjects - 1] = 0; - yaw[numobjects - 1] = 0; - position[numobjects - 1] = 0; - scale[numobjects - 1] = 0; - friction[numobjects - 1] = 0; - - numobjects--; -} - -void Objects::MakeObject(int atype, XYZ where, float ayaw, float ascale) -{ - if ((atype != treeleavestype && atype != bushtype) || foliage == 1) { - scale[numobjects] = ascale; - if (atype == treeleavestype) - scale[numobjects] += fabs((float)(Random() % 100) / 900) * ascale; - - onfire[numobjects] = 0; - flamedelay[numobjects] = 0; - type[numobjects] = atype; - - if (atype == firetype) - onfire[numobjects] = 1; - - position[numobjects] = where; - if (atype == bushtype) - position[numobjects].y = terrain.getHeight(position[numobjects].x, position[numobjects].z) - .3; - yaw[numobjects] = ayaw; - - rotxvel[numobjects] = 0; - rotyvel[numobjects] = 0; - rotx[numobjects] = 0; - roty[numobjects] = 0; - - if (atype == boxtype) model[numobjects].loaddecal("Models/Box.solid", 0); - if (atype == cooltype) model[numobjects].loaddecal("Models/Cool.solid", 0); - if (atype == walltype) model[numobjects].loaddecal("Models/Wall.solid", 0); - if (atype == tunneltype) model[numobjects].loaddecal("Models/Tunnel.solid", 0); - if (atype == chimneytype) model[numobjects].loaddecal("Models/Chimney.solid", 0); - if (atype == spiketype) model[numobjects].load("Models/Spike.solid", 0); - if (atype == weirdtype) model[numobjects].loaddecal("Models/Weird.solid", 0); - if (atype == rocktype) model[numobjects].loaddecal("Models/Rock.solid", 0); - if (atype == treetrunktype) model[numobjects].load("Models/TreeTrunk.solid", 0); - if (atype == treeleavestype) model[numobjects].load("Models/Leaves.solid", 0); - if (atype == bushtype) model[numobjects].load("Models/Bush.solid", 0); - - if (atype == boxtype) friction[numobjects] = 1.5; - if (atype == cooltype) friction[numobjects] = 1.5; - if (atype == walltype) friction[numobjects] = 1.5; - if (atype == platformtype) friction[numobjects] = 1.5; - if (atype == tunneltype) friction[numobjects] = 1.5; - if (atype == chimneytype) friction[numobjects] = 1.5; - if (atype == rocktype) friction[numobjects] = .5; - if (atype == rocktype && ascale>.5) friction[numobjects] = 1.5; - if (atype == weirdtype) friction[numobjects] = 1.5; - if (atype == spiketype) friction[numobjects] = .4; - if (atype == treetrunktype) friction[numobjects] = .4; - if (atype == treeleavestype) friction[numobjects] = 0; - - if (atype == platformtype) { - model[numobjects].loaddecal("Models/Platform.solid", 0); - model[numobjects].Rotate(90, 0, 0); - } - - if (type[numobjects] == boxtype || type[numobjects] == cooltype || type[numobjects] == spiketype || type[numobjects] == weirdtype || type[numobjects] == walltype || type[numobjects] == chimneytype || type[numobjects] == tunneltype || type[numobjects] == platformtype) { - model[numobjects].ScaleTexCoords(scale[numobjects] * 1.5); - } - if (type[numobjects] == rocktype) { - model[numobjects].ScaleTexCoords(scale[numobjects] * 3); - } - model[numobjects].flat = 1; - if (atype == treetrunktype || atype == treeleavestype || atype == rocktype) { - model[numobjects].flat = 0; - } - model[numobjects].Scale(.3 * scale[numobjects], .3 * scale[numobjects], .3 * scale[numobjects]); - model[numobjects].Rotate(90, 1, 1); - if (type[numobjects] == rocktype) { - model[numobjects].Rotate(ayaw * 5, 1, 1); - } - model[numobjects].CalculateNormals(1); - model[numobjects].ScaleNormals(-1, -1, -1); - - if (atype == treetrunktype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { - if (detail == 2) - terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 2, .4, 0); - } - - if (atype == bushtype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { - if (detail == 2) - terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 1, .4, 0); - } - - if (atype != treeleavestype && atype != bushtype && atype != firetype) - terrain.AddObject(where + DoRotation(model[numobjects].boundingspherecenter, 0, ayaw, 0), model[numobjects].boundingsphereradius, numobjects); - - numobjects++; - } -} - -void Objects::MakeObject(int atype, XYZ where, float ayaw, float apitch, float ascale) -{ - if ((atype != treeleavestype && atype != bushtype) || foliage == 1) { - scale[numobjects] = ascale; - if (atype == treeleavestype) - scale[numobjects] += fabs((float)(Random() % 100) / 900) * ascale; - - onfire[numobjects] = 0; - flamedelay[numobjects] = 0; - type[numobjects] = atype; - - if (atype == firetype) - onfire[numobjects] = 1; - - position[numobjects] = where; - if (atype == bushtype) - position[numobjects].y = terrain.getHeight(position[numobjects].x, position[numobjects].z) - .3; - yaw[numobjects] = ayaw; - pitch[numobjects] = apitch; - - rotxvel[numobjects] = 0; - rotyvel[numobjects] = 0; - rotx[numobjects] = 0; - roty[numobjects] = 0; - - if (atype == boxtype) model[numobjects].loaddecal("Models/Box.solid", 0); - if (atype == cooltype) model[numobjects].loaddecal("Models/Cool.solid", 0); - if (atype == walltype) model[numobjects].loaddecal("Models/Wall.solid", 0); - if (atype == tunneltype) model[numobjects].loaddecal("Models/Tunnel.solid", 0); - if (atype == chimneytype) model[numobjects].loaddecal("Models/Chimney.solid", 0); - if (atype == spiketype) model[numobjects].load("Models/Spike.solid", 0); - if (atype == weirdtype) model[numobjects].loaddecal("Models/Weird.solid", 0); - if (atype == rocktype) model[numobjects].loaddecal("Models/Rock.solid", 0); - if (atype == treetrunktype) model[numobjects].load("Models/TreeTrunk.solid", 0); - if (atype == treeleavestype) model[numobjects].load("Models/Leaves.solid", 0); - if (atype == bushtype) model[numobjects].load("Models/Bush.solid", 0); - - if (atype == boxtype) friction[numobjects] = 1.5; - if (atype == cooltype) friction[numobjects] = 1.5; - if (atype == walltype) friction[numobjects] = 1.5; - if (atype == platformtype) friction[numobjects] = 1.5; - if (atype == tunneltype) friction[numobjects] = 1.5; - if (atype == chimneytype) friction[numobjects] = 1.5; - if (atype == rocktype) friction[numobjects] = .5; - if (atype == rocktype && ascale>.5) friction[numobjects] = 1.5; - if (atype == weirdtype) friction[numobjects] = 1.5; - if (atype == spiketype) friction[numobjects] = .4; - if (atype == treetrunktype) friction[numobjects] = .4; - if (atype == treeleavestype) friction[numobjects] = 0; - - if (friction[numobjects] == 1.5 && fabs(apitch) > 5) - friction[numobjects] = .5; - - if (atype == platformtype) { - model[numobjects].loaddecal("Models/Platform.solid", 0); - model[numobjects].Rotate(90, 0, 0); - } - - if (type[numobjects] == boxtype || type[numobjects] == cooltype || type[numobjects] == spiketype || type[numobjects] == weirdtype || type[numobjects] == walltype || type[numobjects] == chimneytype || type[numobjects] == tunneltype || type[numobjects] == platformtype) { - model[numobjects].ScaleTexCoords(scale[numobjects] * 1.5); - } - if (type[numobjects] == rocktype) { - model[numobjects].ScaleTexCoords(scale[numobjects] * 3); - } - model[numobjects].flat = 1; - if (atype == treetrunktype || atype == treeleavestype || atype == rocktype) { - model[numobjects].flat = 0; - } - model[numobjects].Scale(.3 * scale[numobjects], .3 * scale[numobjects], .3 * scale[numobjects]); - model[numobjects].Rotate(90, 1, 1); - model[numobjects].Rotate(apitch, 0, 0); - if (type[numobjects] == rocktype) { - model[numobjects].Rotate(ayaw * 5, 0, 0); - } - model[numobjects].CalculateNormals(1); - model[numobjects].ScaleNormals(-1, -1, -1); - - if (atype == treetrunktype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { - if (detail == 2) - terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 2, .4, 0); - } - - if (atype == bushtype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { - if (detail == 2) - terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 1, .4, 0); - } - - if (atype != treeleavestype && atype != bushtype && atype != firetype) - terrain.AddObject(where + DoRotation(model[numobjects].boundingspherecenter, 0, ayaw, 0), model[numobjects].boundingsphereradius, numobjects); - - numobjects++; - } -} - -void Objects::DoStuff() -{ - XYZ spawnpoint; - for (int i = 0; i < numobjects; i++) { - if (type[i] == firetype) - onfire[i] = 1; - if (onfire[i]) { - if (type[i] == bushtype) - flamedelay[i] -= multiplier * 3; - if (type[i] == firetype) - flamedelay[i] -= multiplier * 3; - if (type[i] == treeleavestype) - flamedelay[i] -= multiplier * 4; - while (flamedelay[i] < 0 && onfire[i]) { - flamedelay[i] += .006; - if (type[i] == bushtype || type[i] == firetype) { - spawnpoint.x = ((float)(Random() % 100)) / 30 * scale[i]; - spawnpoint.y = ((float)(Random() % 100) + 60) / 30 * scale[i]; - spawnpoint.z = 0; - spawnpoint = DoRotation(spawnpoint, 0, Random() % 360, 0); - spawnpoint += position[i]; - Sprite::MakeSprite(flamesprite, spawnpoint, spawnpoint * 0, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 5 * scale[i], 1); - } - if (type[i] == treeleavestype) { - spawnpoint.x = ((float)(Random() % 100)) / 80 * scale[i]; - spawnpoint.y = ((float)(Random() % 100) + 80) / 12 * scale[i]; - spawnpoint.z = 0; - spawnpoint = DoRotation(spawnpoint, 0, Random() % 360, 0); - spawnpoint += position[i]; - Sprite::MakeSprite(flamesprite, spawnpoint, spawnpoint * 0, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 6, 1); - } - } - - } - } -} - -void Objects::DoShadows() -{ - int i, j, k, l; - static XYZ testpoint, testpoint2, terrainpoint, lightloc, col; - lightloc = light.location; - if (!skyboxtexture) - lightloc = 0; - lightloc.y += 10; - Normalise(&lightloc); - int patchx, patchz; - - if (numobjects > 0) - for (i = 0; i < numobjects; i++) { - if (type[i] != treeleavestype && type[i] != treetrunktype && type[i] != bushtype && type[i] != firetype) { - for (j = 0; j < model[i].vertexNum; j++) { - terrainpoint = position[i] + DoRotation(model[i].vertex[j] + model[i].normals[j] * .1, 0, yaw[i], 0); - //terrainpoint.y+=model[i].boundingsphereradius; - shadowed[i] = 0; - patchx = terrainpoint.x / (terrain.size / subdivision * terrain.scale); - patchz = terrainpoint.z / (terrain.size / subdivision * terrain.scale); - if (patchx >= 0 && patchz >= 0 && patchx < subdivision && patchz < subdivision) - if (terrain.patchobjectnum[patchx][patchz]) - for (k = 0; k < terrain.patchobjectnum[patchx][patchz]; k++) { - l = terrain.patchobjects[patchx][patchz][k]; - if (type[l] != treetrunktype/*&&l!=i*/) { - testpoint = terrainpoint; - testpoint2 = terrainpoint + lightloc * 50 * (1 - shadowed[i]); - if (model[l].LineCheck(&testpoint, &testpoint2, &col, &position[l], &yaw[l]) != -1) { - shadowed[i] = 1 - (findDistance(&terrainpoint, &col) / 50); - } - } - } - if (shadowed[i] > 0) { - col = model[i].normals[j] - DoRotation(lightloc * shadowed[i], 0, -yaw[i], 0); - Normalise(&col); - for (k = 0; k < model[i].TriangleNum; k++) { - if (model[i].Triangles[k].vertex[0] == j) { - l = k * 24; - model[i].vArray[l + 2] = col.x; - model[i].vArray[l + 3] = col.y; - model[i].vArray[l + 4] = col.z; - } - if (model[i].Triangles[k].vertex[1] == j) { - l = k * 24; - model[i].vArray[l + 10] = col.x; - model[i].vArray[l + 11] = col.y; - model[i].vArray[l + 12] = col.z; - } - if (model[i].Triangles[k].vertex[2] == j) { - l = k * 24; - model[i].vArray[l + 18] = col.x; - model[i].vArray[l + 19] = col.y; - model[i].vArray[l + 20] = col.z; - } - } - } - } - } - shadowed[i] = 0; - } -} - -Objects::Objects() -{ - center = 0; - radius = 0; - numobjects = 0; - - memset(position, 0, sizeof(position)); - memset(type, 0, sizeof(type)); - memset(yaw, 0, sizeof(yaw)); - memset(pitch, 0, sizeof(pitch)); - memset(rotx, 0, sizeof(rotx)); - memset(rotxvel, 0, sizeof(rotxvel)); - memset(roty, 0, sizeof(roty)); - memset(rotyvel, 0, sizeof(rotyvel)); - memset(possible, 0, sizeof(possible)); - memset(model, 0, sizeof(model)); - memset(displaymodel, 0, sizeof(displaymodel)); - memset(friction, 0, sizeof(friction)); - memset(scale, 0, sizeof(scale)); - memset(messedwith, 0, sizeof(messedwith)); - memset(checked, 0, sizeof(checked)); - memset(shadowed, 0, sizeof(shadowed)); - memset(occluded, 0, sizeof(occluded)); - memset(onfire, 0, sizeof(onfire)); - memset(flamedelay, 0, sizeof(flamedelay)); -} - -Objects::~Objects() -{ - boxtextureptr.destroy(); - treetextureptr.destroy(); - bushtextureptr.destroy(); - rocktextureptr.destroy(); -}; - diff --git a/Source/Objects.h b/Source/Objects.h deleted file mode 100644 index 8b5f592..0000000 --- a/Source/Objects.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _OBJECTS_H_ -#define _OBJECTS_H_ - -#include "Quaternions.h" -#include "gamegl.h" -#include "ImageIO.h" -#include "Quaternions.h" -#include "Frustum.h" -#include "Lights.h" -#include "Models.h" -#include "Terrain.h" -#include "Sprite.h" -#include "Texture.h" -#include -// -// Model Structures -// - -#define max_objects 300 - -#define boxtype 0 -#define weirdtype 1 -#define spiketype 2 -#define treetrunktype 3 -#define treeleavestype 4 -#define bushtype 5 -#define rocktype 6 -#define walltype 7 -#define chimneytype 8 -#define platformtype 9 -#define tunneltype 11 -#define cooltype 12 -#define firetype 13 - - -class Objects -{ -public: - XYZ center; - float radius; - XYZ position[max_objects]; - int type[max_objects]; - float yaw[max_objects]; - float pitch[max_objects]; - float rotx[max_objects]; - float rotxvel[max_objects]; - float roty[max_objects]; - float rotyvel[max_objects]; - int numobjects; - bool possible[max_objects]; - Model model[max_objects]; - Model displaymodel[max_objects]; - float friction[max_objects]; - float scale[max_objects]; - float messedwith[max_objects]; - float checked[max_objects]; - Texture boxtextureptr; - Texture treetextureptr; - Texture bushtextureptr; - Texture rocktextureptr; - float shadowed[max_objects]; - float occluded[max_objects]; - bool checkcollide(XYZ startpoint, XYZ endpoint, int which); - bool onfire[max_objects]; - float flamedelay[max_objects]; - - void SphereCheckPossible(XYZ *p1, float radius); - void DeleteObject(int which); - void MakeObject(int atype, XYZ where, float ayaw, float ascale); - void MakeObject(int atype, XYZ where, float ayaw, float apitch, float ascale); - void Draw(); - void DoShadows(); - void DoStuff(); - - Objects(); - ~Objects(); -}; - -#endif - diff --git a/Source/Objects/Objects.cpp b/Source/Objects/Objects.cpp new file mode 100644 index 0000000..20e117c --- /dev/null +++ b/Source/Objects/Objects.cpp @@ -0,0 +1,842 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Objects/Objects.h" + +extern XYZ viewer; +extern float viewdistance; +extern float fadestart; +extern int environment; +extern float texscale; +extern Light light; +extern float multiplier; +extern float gravity; +extern FRUSTUM frustum; +extern Terrain terrain; +extern bool foliage; +extern int detail; +extern float blurness; +extern float windvar; +extern float playerdist; +extern bool skyboxtexture; + +//Functions + +bool Objects::checkcollide(XYZ startpoint, XYZ endpoint, int which) +{ + static XYZ colpoint, colviewer, coltarget; + static int i; + + startpoint.y += .1; + endpoint.y += .1; + startpoint.y -= .1; + endpoint.y -= .1; + + for (i = 0; i < numobjects; i++) { + if (type[i] != treeleavestype && type[i] != treetrunktype && type[i] != bushtype && type[i] != firetype && i != which) { + colviewer = startpoint; + coltarget = endpoint; + if (model[i].LineCheck(&colviewer, &coltarget, &colpoint, &position[i], &yaw[i]) != -1) + return 1; + } + } + + return 0; +} + +void Objects::SphereCheckPossible(XYZ *p1, float radius) +{ + static int i, j; + static int whichpatchx; + static int whichpatchz; + + whichpatchx = p1->x / (terrain.size / subdivision * terrain.scale); + whichpatchz = p1->z / (terrain.size / subdivision * terrain.scale); + + if (whichpatchx >= 0 && whichpatchz >= 0 && whichpatchx < subdivision && whichpatchz < subdivision) + if (terrain.patchobjectnum[whichpatchx][whichpatchz] > 0 && terrain.patchobjectnum[whichpatchx][whichpatchz] < 500) + for (j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { + i = terrain.patchobjects[whichpatchx][whichpatchz][j]; + possible[i] = 0; + if (model[i].SphereCheckPossible(p1, radius, &position[i], &yaw[i]) != -1) { + possible[i] = 1; + } + } +} + +void Objects::Draw() +{ + static float distance; + static int i; + static XYZ moved, terrainlight; + bool hidden; + + for (i = 0; i < numobjects; i++) { + if (type[i] != firetype) { + moved = DoRotation(model[i].boundingspherecenter, 0, yaw[i], 0); + if (type[i] == tunneltype || frustum.SphereInFrustum(position[i].x + moved.x, position[i].y + moved.y, position[i].z + moved.z, model[i].boundingsphereradius)) { + distance = distsq(&viewer, &position[i]); + distance *= 1.2; + hidden = !(distsqflat(&viewer, &position[i]) > playerdist + 3 || (type[i] != bushtype && type[i] != treeleavestype)); + if (!hidden) { + + if (detail == 2 && distance > viewdistance * viewdistance / 4 && environment == desertenvironment) + glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, blurness ); + else + glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); + distance = (viewdistance * viewdistance - (distance - (viewdistance * viewdistance * fadestart)) * (1 / (1 - fadestart))) / viewdistance / viewdistance; + if (distance > 1) + distance = 1; + if (distance > 0) { + + if (occluded[i] < 6) { + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (!model[i].color) + glEnable(GL_LIGHTING); + else + glDisable(GL_LIGHTING); + glDepthMask(1); + glTranslatef(position[i].x, position[i].y, position[i].z); + if (type[i] == bushtype) { + messedwith[i] -= multiplier; + if (rotxvel[i] || rotx[i]) { + if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); + if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); + if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotx[i] < 0) rotxvel[i] += multiplier * 4; + if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; + if (fabs(rotx[i]) < multiplier * 4) + rotx[i] = 0; + if (fabs(rotxvel[i]) < multiplier * 4) + rotxvel[i] = 0; + + rotx[i] += rotxvel[i] * multiplier * 4; + } + if (rotyvel[i] || roty[i]) { + if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); + if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); + if (roty[i] > 0) rotyvel[i] -= multiplier * 4; + if (roty[i] < 0) rotyvel[i] += multiplier * 4; + if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; + if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; + if (fabs(roty[i]) < multiplier * 4) + roty[i] = 0; + if (fabs(rotyvel[i]) < multiplier * 4) + rotyvel[i] = 0; + + roty[i] += rotyvel[i] * multiplier * 4; + } + if (roty[i]) { + glRotatef(roty[i], 1, 0, 0); + } + if (rotx[i]) { + glRotatef(-rotx[i], 0, 0, 1); + } + if (rotx[i] > 10) + rotx[i] = 10; + if (rotx[i] < -10) + rotx[i] = -10; + if (roty[i] > 10) + roty[i] = 10; + if (roty[i] < -10) + roty[i] = -10; + } + if (type[i] == treetrunktype || type[i] == treeleavestype) { + if (type[i] == treetrunktype || environment == desertenvironment) { + messedwith[i] -= multiplier; + if (rotxvel[i] || rotx[i]) { + if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); + if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); + if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotx[i] < 0) rotxvel[i] += multiplier * 4; + if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; + if (fabs(rotx[i]) < multiplier * 4) + rotx[i] = 0; + if (fabs(rotxvel[i]) < multiplier * 4) + rotxvel[i] = 0; + + rotx[i] += rotxvel[i] * multiplier * 4; + } + if (rotyvel[i] || roty[i]) { + if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); + if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); + if (roty[i] > 0) rotyvel[i] -= multiplier * 4; + if (roty[i] < 0) rotyvel[i] += multiplier * 4; + if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; + if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; + if (fabs(roty[i]) < multiplier * 4) + roty[i] = 0; + if (fabs(rotyvel[i]) < multiplier * 4) + rotyvel[i] = 0; + + roty[i] += rotyvel[i] * multiplier * 4; + } + if (roty[i]) { + glRotatef(roty[i] / 6, 1, 0, 0); + } + if (rotx[i]) { + glRotatef(-rotx[i] / 6, 0, 0, 1); + } + if (rotx[i] > 10) + rotx[i] = 10; + if (rotx[i] < -10) + rotx[i] = -10; + if (roty[i] > 10) + roty[i] = 10; + if (roty[i] < -10) + roty[i] = -10; + } else { + messedwith[i] -= multiplier; + if (rotxvel[i] || rotx[i]) { + if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); + if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); + if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotx[i] < 0) rotxvel[i] += multiplier * 4; + if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; + if (fabs(rotx[i]) < multiplier * 4) + rotx[i] = 0; + if (fabs(rotxvel[i]) < multiplier * 4) + rotxvel[i] = 0; + + rotx[i] += rotxvel[i] * multiplier * 4; + } + if (rotyvel[i] || roty[i]) { + if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); + if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); + if (roty[i] > 0) rotyvel[i] -= multiplier * 4; + if (roty[i] < 0) rotyvel[i] += multiplier * 4; + if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; + if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; + if (fabs(roty[i]) < multiplier * 4) + roty[i] = 0; + if (fabs(rotyvel[i]) < multiplier * 4) + rotyvel[i] = 0; + + roty[i] += rotyvel[i] * multiplier * 4; + } + if (roty[i]) { + glRotatef(roty[i] / 4, 1, 0, 0); + } + if (rotx[i]) { + glRotatef(-rotx[i] / 4, 0, 0, 1); + } + if (rotx[i] > 10) + rotx[i] = 10; + if (rotx[i] < -10) + rotx[i] = -10; + if (roty[i] > 10) + roty[i] = 10; + if (roty[i] < -10) + roty[i] = -10; + } + + } + if (/*detail==2&&*/environment == snowyenvironment) { + if (type[i] == treeleavestype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == treetrunktype) { + glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == bushtype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + } + if (/*detail==2&&*/environment == grassyenvironment) { + if (type[i] == treeleavestype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == treetrunktype) { + glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == bushtype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + } + if (/*detail==2&&*/environment == desertenvironment) { + if (type[i] == bushtype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + } + glRotatef(yaw[i], 0, 1, 0); + if (distance > 1) + distance = 1; + glColor4f((1 - shadowed[i]) / 2 + .5, (1 - shadowed[i]) / 2 + .5, (1 - shadowed[i]) / 2 + .5, distance); + if (distance >= 1) { + glDisable(GL_BLEND); + glAlphaFunc(GL_GREATER, 0.5); + } + if (distance < 1) { + glEnable(GL_BLEND); + glAlphaFunc(GL_GREATER, 0.1); + } + if (type[i] != treetrunktype && type[i] != treeleavestype && type[i] != bushtype && type[i] != rocktype) { + glEnable(GL_CULL_FACE); + glAlphaFunc(GL_GREATER, 0.0001); + model[i].drawdifftex(boxtextureptr); + model[i].drawdecals(terrain.shadowtexture, terrain.bloodtexture, terrain.bloodtexture2, terrain.breaktexture); + } + if (type[i] == rocktype) { + glEnable(GL_CULL_FACE); + glAlphaFunc(GL_GREATER, 0.0001); + glColor4f((1 - shadowed[i]) / 2 + light.ambient[0], (1 - shadowed[i]) / 2 + light.ambient[1], (1 - shadowed[i]) / 2 + light.ambient[2], distance); + model[i].drawdifftex(rocktextureptr); + model[i].drawdecals(terrain.shadowtexture, terrain.bloodtexture, terrain.bloodtexture2, terrain.breaktexture); + } + if (type[i] == treeleavestype) { + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + terrainlight = terrain.getLighting(position[i].x, position[i].z); + if (!hidden) { + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance); + if (distance < 1) + glAlphaFunc(GL_GREATER, 0.2); + } + if (hidden) { + glDepthMask(0); + glEnable(GL_BLEND); + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance / 3); + glAlphaFunc(GL_GREATER, 0); + } + model[i].drawdifftex(treetextureptr); + } + if (type[i] == bushtype) { + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + terrainlight = terrain.getLighting(position[i].x, position[i].z); + if (!hidden) { + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance); + if (distance < 1) + glAlphaFunc(GL_GREATER, 0.2); + } + if (hidden) { + glDepthMask(0); + glEnable(GL_BLEND); + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance / 3); + glAlphaFunc(GL_GREATER, 0); + } + model[i].drawdifftex(bushtextureptr); + } + if (type[i] == treetrunktype) { + glEnable(GL_CULL_FACE); + terrainlight = terrain.getLighting(position[i].x, position[i].z); + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, distance); + model[i].drawdifftex(treetextureptr); + } + glPopMatrix(); + } + } + } + } + } + } + + glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); + for (i = 0; i < numobjects; i++) { + if (type[i] == treeleavestype || type[i] == bushtype) { + moved = DoRotation(model[i].boundingspherecenter, 0, yaw[i], 0); + if (frustum.SphereInFrustum(position[i].x + moved.x, position[i].y + moved.y, position[i].z + moved.z, model[i].boundingsphereradius)) { + hidden = distsqflat(&viewer, &position[i]) <= playerdist + 3; + if (hidden) { + distance = 1; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glEnable(GL_LIGHTING); + glDepthMask(1); + glTranslatef(position[i].x, position[i].y, position[i].z); + if (type[i] == bushtype) { + messedwith[i] -= multiplier; + if (rotxvel[i] || rotx[i]) { + if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); + if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); + if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotx[i] < 0) rotxvel[i] += multiplier * 4; + if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; + if (fabs(rotx[i]) < multiplier * 4) + rotx[i] = 0; + if (fabs(rotxvel[i]) < multiplier * 4) + rotxvel[i] = 0; + + rotx[i] += rotxvel[i] * multiplier * 4; + } + if (rotyvel[i] || roty[i]) { + if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); + if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); + if (roty[i] > 0) rotyvel[i] -= multiplier * 4; + if (roty[i] < 0) rotyvel[i] += multiplier * 4; + if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; + if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; + if (fabs(roty[i]) < multiplier * 4) + roty[i] = 0; + if (fabs(rotyvel[i]) < multiplier * 4) + rotyvel[i] = 0; + + roty[i] += rotyvel[i] * multiplier * 4; + } + if (roty[i]) { + glRotatef(roty[i], 1, 0, 0); + } + if (rotx[i]) { + glRotatef(-rotx[i], 0, 0, 1); + } + if (rotx[i] > 10) + rotx[i] = 10; + if (rotx[i] < -10) + rotx[i] = -10; + if (roty[i] > 10) + roty[i] = 10; + if (roty[i] < -10) + roty[i] = -10; + } + if (type[i] == treetrunktype || type[i] == treeleavestype) { + messedwith[i] -= multiplier; + if (rotxvel[i] || rotx[i]) { + if (rotx[i] > 0) rotxvel[i] -= multiplier * 8 * fabs(rotx[i]); + if (rotx[i] < 0) rotxvel[i] += multiplier * 8 * fabs(rotx[i]); + if (rotx[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotx[i] < 0) rotxvel[i] += multiplier * 4; + if (rotxvel[i] > 0) rotxvel[i] -= multiplier * 4; + if (rotxvel[i] < 0) rotxvel[i] += multiplier * 4; + if (fabs(rotx[i]) < multiplier * 4) + rotx[i] = 0; + if (fabs(rotxvel[i]) < multiplier * 4) + rotxvel[i] = 0; + + rotx[i] += rotxvel[i] * multiplier * 4; + } + if (rotyvel[i] || roty[i]) { + if (roty[i] > 0) rotyvel[i] -= multiplier * 8 * fabs(roty[i]); + if (roty[i] < 0) rotyvel[i] += multiplier * 8 * fabs(roty[i]); + if (roty[i] > 0) rotyvel[i] -= multiplier * 4; + if (roty[i] < 0) rotyvel[i] += multiplier * 4; + if (rotyvel[i] > 0) rotyvel[i] -= multiplier * 4; + if (rotyvel[i] < 0) rotyvel[i] += multiplier * 4; + if (fabs(roty[i]) < multiplier * 4) + roty[i] = 0; + if (fabs(rotyvel[i]) < multiplier * 4) + rotyvel[i] = 0; + + roty[i] += rotyvel[i] * multiplier * 4; + } + if (roty[i]) { + glRotatef(roty[i] / 2, 1, 0, 0); + } + if (rotx[i]) { + glRotatef(-rotx[i] / 2, 0, 0, 1); + } + if (rotx[i] > 10) + rotx[i] = 10; + if (rotx[i] < -10) + rotx[i] = -10; + if (roty[i] > 10) + roty[i] = 10; + if (roty[i] < -10) + roty[i] = -10; + } + if (environment == snowyenvironment) { + if (type[i] == treeleavestype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == treetrunktype) { + glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == bushtype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + } + if (environment == grassyenvironment) { + if (type[i] == treeleavestype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 1.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == treetrunktype) { + glRotatef((sin(windvar + position[i].x * .3) + .5)*.5 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + if (type[i] == bushtype) { + glRotatef((sin(windvar + position[i].x * .3) + .5) * 4 * .5 * (sin(windvar * 2 + position[i].x * .3) + 1) / 2, 1, 0, 0); + } + } + glRotatef(yaw[i], 0, 1, 0); + glColor4f(1, 1, 1, distance); + if (type[i] == treeleavestype) { + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + terrainlight = terrain.getLighting(position[i].x, position[i].z); + glDepthMask(0); + glEnable(GL_BLEND); + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, .3); + glAlphaFunc(GL_GREATER, 0); + glDisable(GL_ALPHA_TEST); + model[i].drawdifftex(treetextureptr); + } + if (type[i] == bushtype) { + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + terrainlight = terrain.getLighting(position[i].x, position[i].z); + glDepthMask(0); + glEnable(GL_BLEND); + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, .3); + glAlphaFunc(GL_GREATER, 0); + glDisable(GL_ALPHA_TEST); + model[i].drawdifftex(bushtextureptr); + } + glPopMatrix(); + } + } + } + } + if (environment == desertenvironment) + glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); + glEnable(GL_ALPHA_TEST); + SetUpLight(&light, 0); +} + +void Objects::DeleteObject(int which) +{ + type[numobjects - 1] = 0; + yaw[numobjects - 1] = 0; + position[numobjects - 1] = 0; + scale[numobjects - 1] = 0; + friction[numobjects - 1] = 0; + + numobjects--; +} + +void Objects::MakeObject(int atype, XYZ where, float ayaw, float ascale) +{ + if ((atype != treeleavestype && atype != bushtype) || foliage == 1) { + scale[numobjects] = ascale; + if (atype == treeleavestype) + scale[numobjects] += fabs((float)(Random() % 100) / 900) * ascale; + + onfire[numobjects] = 0; + flamedelay[numobjects] = 0; + type[numobjects] = atype; + + if (atype == firetype) + onfire[numobjects] = 1; + + position[numobjects] = where; + if (atype == bushtype) + position[numobjects].y = terrain.getHeight(position[numobjects].x, position[numobjects].z) - .3; + yaw[numobjects] = ayaw; + + rotxvel[numobjects] = 0; + rotyvel[numobjects] = 0; + rotx[numobjects] = 0; + roty[numobjects] = 0; + + if (atype == boxtype) model[numobjects].loaddecal("Models/Box.solid", 0); + if (atype == cooltype) model[numobjects].loaddecal("Models/Cool.solid", 0); + if (atype == walltype) model[numobjects].loaddecal("Models/Wall.solid", 0); + if (atype == tunneltype) model[numobjects].loaddecal("Models/Tunnel.solid", 0); + if (atype == chimneytype) model[numobjects].loaddecal("Models/Chimney.solid", 0); + if (atype == spiketype) model[numobjects].load("Models/Spike.solid", 0); + if (atype == weirdtype) model[numobjects].loaddecal("Models/Weird.solid", 0); + if (atype == rocktype) model[numobjects].loaddecal("Models/Rock.solid", 0); + if (atype == treetrunktype) model[numobjects].load("Models/TreeTrunk.solid", 0); + if (atype == treeleavestype) model[numobjects].load("Models/Leaves.solid", 0); + if (atype == bushtype) model[numobjects].load("Models/Bush.solid", 0); + + if (atype == boxtype) friction[numobjects] = 1.5; + if (atype == cooltype) friction[numobjects] = 1.5; + if (atype == walltype) friction[numobjects] = 1.5; + if (atype == platformtype) friction[numobjects] = 1.5; + if (atype == tunneltype) friction[numobjects] = 1.5; + if (atype == chimneytype) friction[numobjects] = 1.5; + if (atype == rocktype) friction[numobjects] = .5; + if (atype == rocktype && ascale>.5) friction[numobjects] = 1.5; + if (atype == weirdtype) friction[numobjects] = 1.5; + if (atype == spiketype) friction[numobjects] = .4; + if (atype == treetrunktype) friction[numobjects] = .4; + if (atype == treeleavestype) friction[numobjects] = 0; + + if (atype == platformtype) { + model[numobjects].loaddecal("Models/Platform.solid", 0); + model[numobjects].Rotate(90, 0, 0); + } + + if (type[numobjects] == boxtype || type[numobjects] == cooltype || type[numobjects] == spiketype || type[numobjects] == weirdtype || type[numobjects] == walltype || type[numobjects] == chimneytype || type[numobjects] == tunneltype || type[numobjects] == platformtype) { + model[numobjects].ScaleTexCoords(scale[numobjects] * 1.5); + } + if (type[numobjects] == rocktype) { + model[numobjects].ScaleTexCoords(scale[numobjects] * 3); + } + model[numobjects].flat = 1; + if (atype == treetrunktype || atype == treeleavestype || atype == rocktype) { + model[numobjects].flat = 0; + } + model[numobjects].Scale(.3 * scale[numobjects], .3 * scale[numobjects], .3 * scale[numobjects]); + model[numobjects].Rotate(90, 1, 1); + if (type[numobjects] == rocktype) { + model[numobjects].Rotate(ayaw * 5, 1, 1); + } + model[numobjects].CalculateNormals(1); + model[numobjects].ScaleNormals(-1, -1, -1); + + if (atype == treetrunktype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { + if (detail == 2) + terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 2, .4, 0); + } + + if (atype == bushtype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { + if (detail == 2) + terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 1, .4, 0); + } + + if (atype != treeleavestype && atype != bushtype && atype != firetype) + terrain.AddObject(where + DoRotation(model[numobjects].boundingspherecenter, 0, ayaw, 0), model[numobjects].boundingsphereradius, numobjects); + + numobjects++; + } +} + +void Objects::MakeObject(int atype, XYZ where, float ayaw, float apitch, float ascale) +{ + if ((atype != treeleavestype && atype != bushtype) || foliage == 1) { + scale[numobjects] = ascale; + if (atype == treeleavestype) + scale[numobjects] += fabs((float)(Random() % 100) / 900) * ascale; + + onfire[numobjects] = 0; + flamedelay[numobjects] = 0; + type[numobjects] = atype; + + if (atype == firetype) + onfire[numobjects] = 1; + + position[numobjects] = where; + if (atype == bushtype) + position[numobjects].y = terrain.getHeight(position[numobjects].x, position[numobjects].z) - .3; + yaw[numobjects] = ayaw; + pitch[numobjects] = apitch; + + rotxvel[numobjects] = 0; + rotyvel[numobjects] = 0; + rotx[numobjects] = 0; + roty[numobjects] = 0; + + if (atype == boxtype) model[numobjects].loaddecal("Models/Box.solid", 0); + if (atype == cooltype) model[numobjects].loaddecal("Models/Cool.solid", 0); + if (atype == walltype) model[numobjects].loaddecal("Models/Wall.solid", 0); + if (atype == tunneltype) model[numobjects].loaddecal("Models/Tunnel.solid", 0); + if (atype == chimneytype) model[numobjects].loaddecal("Models/Chimney.solid", 0); + if (atype == spiketype) model[numobjects].load("Models/Spike.solid", 0); + if (atype == weirdtype) model[numobjects].loaddecal("Models/Weird.solid", 0); + if (atype == rocktype) model[numobjects].loaddecal("Models/Rock.solid", 0); + if (atype == treetrunktype) model[numobjects].load("Models/TreeTrunk.solid", 0); + if (atype == treeleavestype) model[numobjects].load("Models/Leaves.solid", 0); + if (atype == bushtype) model[numobjects].load("Models/Bush.solid", 0); + + if (atype == boxtype) friction[numobjects] = 1.5; + if (atype == cooltype) friction[numobjects] = 1.5; + if (atype == walltype) friction[numobjects] = 1.5; + if (atype == platformtype) friction[numobjects] = 1.5; + if (atype == tunneltype) friction[numobjects] = 1.5; + if (atype == chimneytype) friction[numobjects] = 1.5; + if (atype == rocktype) friction[numobjects] = .5; + if (atype == rocktype && ascale>.5) friction[numobjects] = 1.5; + if (atype == weirdtype) friction[numobjects] = 1.5; + if (atype == spiketype) friction[numobjects] = .4; + if (atype == treetrunktype) friction[numobjects] = .4; + if (atype == treeleavestype) friction[numobjects] = 0; + + if (friction[numobjects] == 1.5 && fabs(apitch) > 5) + friction[numobjects] = .5; + + if (atype == platformtype) { + model[numobjects].loaddecal("Models/Platform.solid", 0); + model[numobjects].Rotate(90, 0, 0); + } + + if (type[numobjects] == boxtype || type[numobjects] == cooltype || type[numobjects] == spiketype || type[numobjects] == weirdtype || type[numobjects] == walltype || type[numobjects] == chimneytype || type[numobjects] == tunneltype || type[numobjects] == platformtype) { + model[numobjects].ScaleTexCoords(scale[numobjects] * 1.5); + } + if (type[numobjects] == rocktype) { + model[numobjects].ScaleTexCoords(scale[numobjects] * 3); + } + model[numobjects].flat = 1; + if (atype == treetrunktype || atype == treeleavestype || atype == rocktype) { + model[numobjects].flat = 0; + } + model[numobjects].Scale(.3 * scale[numobjects], .3 * scale[numobjects], .3 * scale[numobjects]); + model[numobjects].Rotate(90, 1, 1); + model[numobjects].Rotate(apitch, 0, 0); + if (type[numobjects] == rocktype) { + model[numobjects].Rotate(ayaw * 5, 0, 0); + } + model[numobjects].CalculateNormals(1); + model[numobjects].ScaleNormals(-1, -1, -1); + + if (atype == treetrunktype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { + if (detail == 2) + terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 2, .4, 0); + } + + if (atype == bushtype && position[numobjects].y < terrain.getHeight(position[numobjects].x, position[numobjects].z) + 1) { + if (detail == 2) + terrain.MakeDecal(shadowdecalpermanent, position[numobjects], 1, .4, 0); + } + + if (atype != treeleavestype && atype != bushtype && atype != firetype) + terrain.AddObject(where + DoRotation(model[numobjects].boundingspherecenter, 0, ayaw, 0), model[numobjects].boundingsphereradius, numobjects); + + numobjects++; + } +} + +void Objects::DoStuff() +{ + XYZ spawnpoint; + for (int i = 0; i < numobjects; i++) { + if (type[i] == firetype) + onfire[i] = 1; + if (onfire[i]) { + if (type[i] == bushtype) + flamedelay[i] -= multiplier * 3; + if (type[i] == firetype) + flamedelay[i] -= multiplier * 3; + if (type[i] == treeleavestype) + flamedelay[i] -= multiplier * 4; + while (flamedelay[i] < 0 && onfire[i]) { + flamedelay[i] += .006; + if (type[i] == bushtype || type[i] == firetype) { + spawnpoint.x = ((float)(Random() % 100)) / 30 * scale[i]; + spawnpoint.y = ((float)(Random() % 100) + 60) / 30 * scale[i]; + spawnpoint.z = 0; + spawnpoint = DoRotation(spawnpoint, 0, Random() % 360, 0); + spawnpoint += position[i]; + Sprite::MakeSprite(flamesprite, spawnpoint, spawnpoint * 0, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 5 * scale[i], 1); + } + if (type[i] == treeleavestype) { + spawnpoint.x = ((float)(Random() % 100)) / 80 * scale[i]; + spawnpoint.y = ((float)(Random() % 100) + 80) / 12 * scale[i]; + spawnpoint.z = 0; + spawnpoint = DoRotation(spawnpoint, 0, Random() % 360, 0); + spawnpoint += position[i]; + Sprite::MakeSprite(flamesprite, spawnpoint, spawnpoint * 0, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 6, 1); + } + } + + } + } +} + +void Objects::DoShadows() +{ + int i, j, k, l; + static XYZ testpoint, testpoint2, terrainpoint, lightloc, col; + lightloc = light.location; + if (!skyboxtexture) + lightloc = 0; + lightloc.y += 10; + Normalise(&lightloc); + int patchx, patchz; + + if (numobjects > 0) + for (i = 0; i < numobjects; i++) { + if (type[i] != treeleavestype && type[i] != treetrunktype && type[i] != bushtype && type[i] != firetype) { + for (j = 0; j < model[i].vertexNum; j++) { + terrainpoint = position[i] + DoRotation(model[i].vertex[j] + model[i].normals[j] * .1, 0, yaw[i], 0); + //terrainpoint.y+=model[i].boundingsphereradius; + shadowed[i] = 0; + patchx = terrainpoint.x / (terrain.size / subdivision * terrain.scale); + patchz = terrainpoint.z / (terrain.size / subdivision * terrain.scale); + if (patchx >= 0 && patchz >= 0 && patchx < subdivision && patchz < subdivision) + if (terrain.patchobjectnum[patchx][patchz]) + for (k = 0; k < terrain.patchobjectnum[patchx][patchz]; k++) { + l = terrain.patchobjects[patchx][patchz][k]; + if (type[l] != treetrunktype/*&&l!=i*/) { + testpoint = terrainpoint; + testpoint2 = terrainpoint + lightloc * 50 * (1 - shadowed[i]); + if (model[l].LineCheck(&testpoint, &testpoint2, &col, &position[l], &yaw[l]) != -1) { + shadowed[i] = 1 - (findDistance(&terrainpoint, &col) / 50); + } + } + } + if (shadowed[i] > 0) { + col = model[i].normals[j] - DoRotation(lightloc * shadowed[i], 0, -yaw[i], 0); + Normalise(&col); + for (k = 0; k < model[i].TriangleNum; k++) { + if (model[i].Triangles[k].vertex[0] == j) { + l = k * 24; + model[i].vArray[l + 2] = col.x; + model[i].vArray[l + 3] = col.y; + model[i].vArray[l + 4] = col.z; + } + if (model[i].Triangles[k].vertex[1] == j) { + l = k * 24; + model[i].vArray[l + 10] = col.x; + model[i].vArray[l + 11] = col.y; + model[i].vArray[l + 12] = col.z; + } + if (model[i].Triangles[k].vertex[2] == j) { + l = k * 24; + model[i].vArray[l + 18] = col.x; + model[i].vArray[l + 19] = col.y; + model[i].vArray[l + 20] = col.z; + } + } + } + } + } + shadowed[i] = 0; + } +} + +Objects::Objects() +{ + center = 0; + radius = 0; + numobjects = 0; + + memset(position, 0, sizeof(position)); + memset(type, 0, sizeof(type)); + memset(yaw, 0, sizeof(yaw)); + memset(pitch, 0, sizeof(pitch)); + memset(rotx, 0, sizeof(rotx)); + memset(rotxvel, 0, sizeof(rotxvel)); + memset(roty, 0, sizeof(roty)); + memset(rotyvel, 0, sizeof(rotyvel)); + memset(possible, 0, sizeof(possible)); + memset(model, 0, sizeof(model)); + memset(displaymodel, 0, sizeof(displaymodel)); + memset(friction, 0, sizeof(friction)); + memset(scale, 0, sizeof(scale)); + memset(messedwith, 0, sizeof(messedwith)); + memset(checked, 0, sizeof(checked)); + memset(shadowed, 0, sizeof(shadowed)); + memset(occluded, 0, sizeof(occluded)); + memset(onfire, 0, sizeof(onfire)); + memset(flamedelay, 0, sizeof(flamedelay)); +} + +Objects::~Objects() +{ + boxtextureptr.destroy(); + treetextureptr.destroy(); + bushtextureptr.destroy(); + rocktextureptr.destroy(); +}; + diff --git a/Source/Objects/Objects.h b/Source/Objects/Objects.h new file mode 100644 index 0000000..83f17c2 --- /dev/null +++ b/Source/Objects/Objects.h @@ -0,0 +1,101 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _OBJECTS_H_ +#define _OBJECTS_H_ + +#include "Environment/Lights.h" +#include "Environment/Terrain.h" +#include "Graphic/gamegl.h" +#include "Graphic/Models.h" +#include "Graphic/Sprite.h" +#include "Graphic/Texture.h" +#include "Math/Frustum.h" +#include "Math/Quaternions.h" +#include "Math/Quaternions.h" +#include "Utils/ImageIO.h" + +#include +// +// Model Structures +// + +#define max_objects 300 + +#define boxtype 0 +#define weirdtype 1 +#define spiketype 2 +#define treetrunktype 3 +#define treeleavestype 4 +#define bushtype 5 +#define rocktype 6 +#define walltype 7 +#define chimneytype 8 +#define platformtype 9 +#define tunneltype 11 +#define cooltype 12 +#define firetype 13 + + +class Objects +{ +public: + XYZ center; + float radius; + XYZ position[max_objects]; + int type[max_objects]; + float yaw[max_objects]; + float pitch[max_objects]; + float rotx[max_objects]; + float rotxvel[max_objects]; + float roty[max_objects]; + float rotyvel[max_objects]; + int numobjects; + bool possible[max_objects]; + Model model[max_objects]; + Model displaymodel[max_objects]; + float friction[max_objects]; + float scale[max_objects]; + float messedwith[max_objects]; + float checked[max_objects]; + Texture boxtextureptr; + Texture treetextureptr; + Texture bushtextureptr; + Texture rocktextureptr; + float shadowed[max_objects]; + float occluded[max_objects]; + bool checkcollide(XYZ startpoint, XYZ endpoint, int which); + bool onfire[max_objects]; + float flamedelay[max_objects]; + + void SphereCheckPossible(XYZ *p1, float radius); + void DeleteObject(int which); + void MakeObject(int atype, XYZ where, float ayaw, float ascale); + void MakeObject(int atype, XYZ where, float ayaw, float apitch, float ascale); + void Draw(); + void DoShadows(); + void DoStuff(); + + Objects(); + ~Objects(); +}; + +#endif + diff --git a/Source/Objects/Person.cpp b/Source/Objects/Person.cpp new file mode 100644 index 0000000..c1eb88d --- /dev/null +++ b/Source/Objects/Person.cpp @@ -0,0 +1,6893 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +/**> HEADER FILES <**/ +#include "Animation/Animation.h" +#include "Audio/openal_wrapper.h" +#include "Audio/Sounds.h" +#include "Level/Awards.h" +#include "Level/Dialog.h" +#include "Objects/Person.h" +#include "Utils/Folders.h" + +#include "Game.h" + +extern float multiplier; +extern Terrain terrain; +extern float gravity; +extern int environment; +extern int detail; +extern FRUSTUM frustum; +extern XYZ viewer; +extern float realmultiplier; +extern int slomo; +extern float slomodelay; +extern bool cellophane; +extern float texdetail; +extern float realtexdetail; +extern GLubyte bloodText[512 * 512 * 3]; +extern GLubyte wolfbloodText[512 * 512 * 3]; +extern int bloodtoggle; +extern Objects objects; +extern bool autoslomo; +extern float camerashake; +extern float woozy; +extern float viewdistance; +extern float blackout; +extern int difficulty; +extern bool decals; +extern float fadestart; +extern bool freeze; +extern bool winfreeze; +extern bool showpoints; +extern bool immediate; +extern int tutoriallevel; +extern float smoketex; +extern int tutorialstage; +extern bool reversaltrain; +extern bool canattack; +extern bool cananger; +extern float damagedealt; +extern int hostile; +extern float hostiletime; + +extern bool gamestarted; + +std::vector> Person::players(1, std::shared_ptr(new Person())); + +Person::Person() : + whichpatchx(0), + whichpatchz(0), + animCurrent(bounceidleanim), + animTarget(bounceidleanim), + frameCurrent(0), + frameTarget(1), + oldanimCurrent(0), + oldanimTarget(0), + oldframeCurrent(0), + oldframeTarget(0), + howactive(typeactive), + parriedrecently(0), + superruntoggle(false), + lastattack(0), lastattack2(0), lastattack3(0), + currentoffset(), targetoffset(), offset(), + target(0), + transspeed(0), + + realoldcoords(), + oldcoords(), + coords(), + velocity(), + + proportionhead(), + proportionlegs(), + proportionarms(), + proportionbody(), + + unconscioustime(0), + + immobile(false), + + velspeed(0), + targetyaw(0), + targetrot(0), + rot(0), + oldrot(0), + lookyaw(0), + lookpitch(0), + yaw(0), + pitch(0), + lowyaw(0), + tilt(0), + targettilt(0), + tilt2(0), + targettilt2(0), + rabbitkickenabled(false), + + bloodloss(0), + bleeddelay(0), + skiddelay(0), + skiddingdelay(0), + deathbleeding(0), + tempdeltav(0), + + damagetolerance(200), + damage(0), + permanentdamage(0), + superpermanentdamage(0), + lastcollide(0), + dead(0), + + jumppower(5), + onground(false), + + wentforweapon(0), + + calcrot(false), + + facing(), + + bleeding(0), + bleedx(0), bleedy(0), + direction(0), + texupdatedelay(0), + + headyaw(0), headpitch(0), + targetheadyaw(0), targetheadpitch(0), + + onterrain(false), + pause(false), + + grabdelay(0), + + victim(nullptr), + hasvictim(false), + + updatedelay(0), + normalsupdatedelay(0), + + jumpstart(false), + forwardkeydown(false), + forwardstogglekeydown(false), + rightkeydown(false), + leftkeydown(false), + backkeydown(false), + jumpkeydown(false), + jumptogglekeydown(false), + crouchkeydown(false), + crouchtogglekeydown(false), + drawkeydown(false), + drawtogglekeydown(false), + throwkeydown(false), + throwtogglekeydown(false), + attackkeydown(false), + feint(false), + lastfeint(false), + headless(false), + + crouchkeydowntime(0), + jumpkeydowntime(0), + freefall(false), + + turnspeed(0), + + aitype(passivetype), + aiupdatedelay(0), + losupdatedelay(0), + ally(0), + collide(0), + collided(-10), + avoidcollided(0), + loaded(false), + whichdirection(false), + whichdirectiondelay(0), + avoidsomething(false), + avoidwhere(), + blooddimamount(0), + + staggerdelay(0), + blinkdelay(0), + twitchdelay(0), + twitchdelay2(0), + twitchdelay3(0), + lefthandmorphness(0), + righthandmorphness(0), + headmorphness(0), + chestmorphness(0), + tailmorphness(0), + targetlefthandmorphness(0), + targetrighthandmorphness(0), + targetheadmorphness(1), + targetchestmorphness(0), + targettailmorphness(0), + lefthandmorphstart(0), lefthandmorphend(0), + righthandmorphstart(0), righthandmorphend(0), + headmorphstart(0), headmorphend(0), + chestmorphstart(0), chestmorphend(0), + tailmorphstart(0), tailmorphend(0), + + weaponmissdelay(0), + highreversaldelay(0), + lowreversaldelay(0), + + creature(rabbittype), + + id(0), + + skeleton(), + + speed(0), + scale(-1), + power(0), + speedmult(0), + + protectionhead(0), + protectionhigh(0), + protectionlow(0), + armorhead(0), + armorhigh(0), + armorlow(0), + metalhead(false), + metalhigh(false), + metallow(false), + + numclothes(0), + + landhard(false), + bled(false), + spurt(false), + onfire(false), + onfiredelay(0), + burnt(0), + + flamedelay(0), + + playerdetail(0), + + num_weapons(0), + weaponactive(-1), + weaponstuck(-1), + weaponstuckwhere(0), + + numwaypoints(0), + pausetime(0), + + headtarget(), + interestdelay(0), + + finalfinaltarget(), + finaltarget(), + finalpathfindpoint(0), + targetpathfindpoint(0), + lastpathfindpoint(0), + lastpathfindpoint2(0), + lastpathfindpoint3(0), + lastpathfindpoint4(0), + + waypoint(0), + + lastseen(), + lastseentime(0), + lastchecktime(0), + stunned(0), + surprised(0), + runninghowlong(0), + occluded(0), + lastoccluded(0), + laststanding(0), + escapednum(0), + + speechdelay(0), + neckspurtdelay(0), + neckspurtparticledelay(0), + neckspurtamount(0), + + whichskin(0), + rabbitkickragdoll(false), + + tempanimation(), + + jumpclimb(false) +{ +} + +/* Read a person in tfile. Throws an error if it’s not valid */ +Person::Person(FILE *tfile, int mapvers, unsigned i) : Person() +{ + id = i; + funpackf(tfile, "Bi Bi Bf Bf Bf Bi", &whichskin, &creature, &coords.x, &coords.y, &coords.z, &num_weapons); + if (mapvers >= 5) { + funpackf(tfile, "Bi", &howactive); + } else { + howactive = typeactive; + } + if (mapvers >= 3) { + funpackf(tfile, "Bf", &scale); + } else { + scale = -1; + } + if (mapvers >= 11) { + funpackf(tfile, "Bb", &immobile); + } else { + immobile = 0; + } + if (mapvers >= 12) { + funpackf(tfile, "Bf", &yaw); + } else { + yaw = 0; + } + targetyaw = yaw; + if (num_weapons < 0 || num_weapons > 5) { + throw InvalidPersonException(); + } + if (num_weapons > 0 && num_weapons < 5) { + for (int j = 0; j < num_weapons; j++) { + weaponids[j] = weapons.size(); + int type; + funpackf(tfile, "Bi", &type); + weapons.push_back(Weapon(type, id)); + } + } + funpackf(tfile, "Bi", &numwaypoints); + for (int j = 0; j < numwaypoints; j++) { + funpackf(tfile, "Bf", &waypoints[j].x); + funpackf(tfile, "Bf", &waypoints[j].y); + funpackf(tfile, "Bf", &waypoints[j].z); + if (mapvers >= 5) { + funpackf(tfile, "Bi", &waypointtype[j]); + } else { + waypointtype[j] = wpkeepwalking; + } + } + + funpackf(tfile, "Bi", &waypoint); + if (waypoint > (numwaypoints - 1)) { + waypoint = 0; + } + + funpackf(tfile, "Bf Bf Bf", &armorhead, &armorhigh, &armorlow); + funpackf(tfile, "Bf Bf Bf", &protectionhead, &protectionhigh, &protectionlow); + funpackf(tfile, "Bf Bf Bf", &metalhead, &metalhigh, &metallow); + funpackf(tfile, "Bf Bf", &power, &speedmult); + + float headprop, legprop, armprop, bodyprop; + + if (mapvers >= 4) { + funpackf(tfile, "Bf Bf Bf Bf", &headprop, &bodyprop, &armprop, &legprop); + } else { + headprop = 1; + bodyprop = 1; + armprop = 1; + legprop = 1; + } + + if (creature == wolftype) { + proportionhead = 1.1 * headprop; + proportionbody = 1.1 * bodyprop; + proportionarms = 1.1 * armprop; + proportionlegs = 1.1 * legprop; + } else if (creature == rabbittype) { + proportionhead = 1.2 * headprop; + proportionbody = 1.05 * bodyprop; + proportionarms = 1.00 * armprop; + proportionlegs = 1.1 * legprop; + proportionlegs.y = 1.05 * legprop; + } + + funpackf(tfile, "Bi", &numclothes); + for (int k = 0; k < numclothes; k++) { + int templength; + funpackf(tfile, "Bi", &templength); + for (int l = 0; l < templength; l++) + funpackf(tfile, "Bb", &clothes[k][l]); + clothes[k][templength] = '\0'; + funpackf(tfile, "Bf Bf Bf", &clothestintr[k], &clothestintg[k], &clothestintb[k]); + } + + loaded = true; + + if (scale < 0) { + if (creature == wolftype) { + scale = .23; + damagetolerance = 300; + } else { + scale = .2; + } + } + + oldcoords = coords; + realoldcoords = coords; +} + +void Person::skeletonLoad(bool clothes) +{ + skeleton.id = id; + if (creature != wolftype) { + skeleton.Load( + "Skeleton/BasicFigure", + "Skeleton/BasicFigureLow", + "Skeleton/RabbitBelt", + "Models/Body.solid", + "Models/Body2.solid", + "Models/Body3.solid", + "Models/Body4.solid", + "Models/Body5.solid", + "Models/Body6.solid", + "Models/Body7.solid", + "Models/BodyLow.solid", + "Models/Belt.solid", + clothes + ); + } else { + skeleton.Load( + "Skeleton/BasicFigureWolf", + "Skeleton/BasicFigureWolfLow", + "Skeleton/RabbitBelt", + "Models/Wolf.solid", + "Models/Wolf2.solid", + "Models/Wolf3.solid", + "Models/Wolf4.solid", + "Models/Wolf5.solid", + "Models/Wolf6.solid", + "Models/Wolf7.solid", + "Models/WolfLow.solid", + "Models/Belt.solid", + clothes + ); + } + + skeleton.drawmodel.textureptr.load(creatureskin[creature][whichskin], 1, &skeleton.skinText[0], &skeleton.skinsize); +} + +/* EFFECT + * + * USES: + * GameTick/doPlayerCollisions + */ +void Person::CheckKick() +{ + if (!(hasvictim + && (animTarget == rabbitkickanim + && victim + && victim != this->shared_from_this() + && frameCurrent >= 2 + && animCurrent == rabbitkickanim) + && distsq(&coords, &victim->coords) < 1.2 + && !victim->skeleton.free)) + return; + + if (Animation::animations[victim->animTarget].height != lowheight) { + float damagemult = (creature == wolftype ? 2.5 : 1.) * power * power; + XYZ relative = velocity; + relative.y = 0; + Normalise(&relative); + + victim->spurt = 1; + DoBlood(.2, 250); + if (tutoriallevel != 1) + emit_sound_at(heavyimpactsound, victim->coords); + victim->RagDoll(0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * 120 * damagemult; + } + victim->Puff(neck); + victim->DoDamage(100 * damagemult / victim->protectionhigh); + if (id == 0) + camerashake += .4; + + target = 0; + frameCurrent = 3; + animTarget = backflipanim; + frameTarget = 4; + velocity = facing * -10; + velocity.y = 5; + skeleton.free = 0; + if (id == 0) + resume_stream(whooshsound); + + award_bonus(id, cannon); + } else if (victim->isCrouch()) { + animTarget = rabbitkickreversedanim; + animCurrent = rabbitkickreversedanim; + victim->animCurrent = rabbitkickreversalanim; + victim->animTarget = rabbitkickreversalanim; + targettilt2 = 0; + frameCurrent = 0; + frameTarget = 1; + target = 0; + velocity = 0; + victim->oldcoords = victim->coords; + coords = victim->coords; + victim->targetyaw = targetyaw; + victim->victim = this->shared_from_this(); + } +} + +/* EFFECT + * + * USES: + * GameTick/doPlayerCollisions - spread fire between players + * GameTick/doDevKeys - press f to ignite + * Person::DoStuff - spread fire from lit campfires and bushes + */ +void Person::CatchFire() +{ + XYZ flatfacing, flatvelocity; + int howmany; + for (int i = 0; i < 10; i++) { + howmany = abs(Random() % (skeleton.joints.size())); + if (skeleton.free) { + flatvelocity = skeleton.joints[howmany].velocity; + flatfacing = skeleton.joints[howmany].position * scale + coords; + } else { + flatvelocity = velocity; + flatfacing = DoRotation(DoRotation(DoRotation(skeleton.joints[howmany].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; + } + Sprite::MakeSprite(flamesprite, flatfacing, flatvelocity, 1, 1, 1, 2, 1); + } + + onfiredelay = 0.5; + + emit_sound_at(firestartsound, coords); + + emit_stream_at(stream_firesound, coords); + + flamedelay = 0; + + onfire = 1; +} + +/* FUNCTION + * idle animation for this creature (depending on status) + */ +int Person::getIdle() +{ + if (Dialog::inDialog() && (howactive == typeactive) && (creature == rabbittype)) + return talkidleanim; + if (hasvictim && (victim != this->shared_from_this())/*||(id==0&&attackkeydown)*/) + if (/*(id==0&&attackkeydown)||*/(!victim->dead && victim->aitype != passivetype && + victim->aitype != searchtype && aitype != passivetype && aitype != searchtype && + victim->id < Person::players.size())) { + if ((aitype == playercontrolled && stunned <= 0 && weaponactive == -1) || pause) { + if (creature == rabbittype) + return fightidleanim; + if (creature == wolftype) + return wolfidle; + } + if (aitype == playercontrolled && stunned <= 0 && weaponactive != -1) { + if (weapons[weaponids[weaponactive]].getType() == knife) + return knifefightidleanim; + if (weapons[weaponids[weaponactive]].getType() == sword && victim->weaponactive != -1) + return swordfightidlebothanim; + if (weapons[weaponids[weaponactive]].getType() == sword) + return swordfightidleanim; + if (weapons[weaponids[weaponactive]].getType() == staff) + return swordfightidleanim; + } + if (aitype != playercontrolled && stunned <= 0 && creature != wolftype && !pause) + return fightsidestep; + } + if ((damage > permanentdamage || damage > damagetolerance * .8 || deathbleeding > 0) && creature != wolftype) + return hurtidleanim; + if (howactive == typesitting) return sitanim; + if (howactive == typesittingwall) return sitwallanim; + if (howactive == typesleeping) return sleepanim; + if (howactive == typedead1) return dead1anim; + if (howactive == typedead2) return dead2anim; + if (howactive == typedead3) return dead3anim; + if (howactive == typedead4) return dead4anim; + if (creature == rabbittype) return bounceidleanim; + if (creature == wolftype) return wolfidle; + return 0; +} + +/* FUNCTION + * crouch animation for this creature + */ +int Person::getCrouch() +{ + if (creature == rabbittype) + return crouchanim; + if (creature == wolftype) + return wolfcrouchanim; + return 0; +} + +/* FUNCTION + * running animation for this creature (can be upright or all fours) + */ +int Person::getRun() +{ + if (creature == rabbittype && (!superruntoggle || weaponactive != -1)) + return runanim; + if (creature == wolftype && (!superruntoggle)) + return wolfrunanim; + + if (creature == rabbittype && (superruntoggle && weaponactive == -1)) + return rabbitrunninganim; + if (creature == wolftype && (superruntoggle)) + return wolfrunninganim; + return 0; +} + +/* FUNCTION + */ +int Person::getStop() +{ + if (creature == rabbittype) + return stopanim; + if (creature == wolftype) + return wolfstopanim; + return 0; +} + +/* FUNCTION + */ +int Person::getLanding() +{ + if (creature == rabbittype) + return landanim; + if (creature == wolftype) + return wolflandanim; + return 0; +} + +/* FUNCTION + */ +int Person::getLandhard() +{ + if (creature == rabbittype) + return landhardanim; + if (creature == wolftype) + return wolflandhardanim; + return 0; +} + +/* EFFECT + * + * USES: + * Person::DoAnimations + */ +static void +SolidHitBonus(int playerid) +{ + if (bonustime < 1.5 && bonus >= solidhit && bonus <= megacombo) + award_bonus(playerid, bonus == megacombo ? bonus : bonus + 1); + else + award_bonus(playerid, solidhit); +} + +/* EFFECT + * spawns blood effects + */ +void Person::DoBlood(float howmuch, int which) +{ + // FIXME: should abstract out inputs + static int bleedxint, bleedyint; + static XYZ bloodvel; + if (bloodtoggle && tutoriallevel != 1) { + if (bleeding <= 0 && spurt) { + spurt = 0; + for (int i = 0; i < 3; i++) { + // emit blood particles + bloodvel = 0; + if (skeleton.free) { + bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); + bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); + Sprite::MakeSprite(bloodflamesprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .3, 1); + } else { + bloodvel.z = 10; + bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); + Sprite::MakeSprite(bloodflamesprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .3, 1); + } + } + if (Random() % 2 == 0) // 50% chance + for (int i = 0; i < 3; i++) { + if (Random() % 2 != 0) { + // emit teeth particles + bloodvel = 0; + if (skeleton.free) { + bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); + bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + } else { + bloodvel.z = 10; + bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; + } + bloodvel *= .2; + if (skeleton.free) { + Sprite::MakeSprite(splintersprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); + } else { + Sprite::MakeSprite(splintersprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); + } + Sprite::setLastSpriteSpecial(3); // sets it to teeth + } + } + } + if (decals) { + // FIXME: manipulating attributes + bleeding = howmuch + (float)abs(Random() % 100) / 200 - .25; + bleedxint = 0; + bleedyint = 0; + if (creature == rabbittype) + while (bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { + bleedxint = abs(Random() % 512); + bleedyint = abs(Random() % 512); + } + if (creature == wolftype) + while (wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { + bleedxint = abs(Random() % 512); + bleedyint = abs(Random() % 512); + } + bleedy = bleedxint; + bleedx = bleedyint; + bleedy /= realtexdetail; + bleedx /= realtexdetail; + direction = abs(Random() % 2) * 2 - 1; + } + + } + if (bleeding > 2) + bleeding = 2; +} + +/* EFFECT + * spawns big blood effects and ??? + * modifies character's skin texture + */ +void Person::DoBloodBig(float howmuch, int which) +{ + static int bleedxint, bleedyint, i, j; + static XYZ bloodvel; + if (howmuch && id == 0) + blooddimamount = 1; + + if (tutoriallevel != 1 || id == 0) + if (aitype != playercontrolled && howmuch > 0) { + // play pain sounds + int whichsound = -1; + + if (creature == wolftype) { + int i = abs(Random() % 2); + if (i == 0) + whichsound = snarlsound; + if (i == 1) + whichsound = snarl2sound; + } + if (creature == rabbittype) { + int i = abs(Random() % 2); + if (i == 0) + whichsound = rabbitpainsound; + if (i == 1 && howmuch >= 2) + whichsound = rabbitpain1sound; + } + + if (whichsound != -1) { + emit_sound_at(whichsound, coords); + addEnvSound(coords); + } + } + + if (id == 0 && howmuch > 0) { + Game::flash(.5, 0); + } + + if (bloodtoggle && decals && tutoriallevel != 1) { + if (bleeding <= 0 && spurt) { + spurt = 0; + for (int i = 0; i < 3; i++) { + // emit blood particles + // FIXME: copypaste from above + bloodvel = 0; + if (skeleton.free) { + bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); + bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); + Sprite::MakeSprite(bloodflamesprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .3, 1); + } else { + bloodvel.z = 10; + bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); + Sprite::MakeSprite(bloodflamesprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .3, 1); + } + } + } + + // weird texture manipulation code follows. + // looks like this is painting blood onto the character's skin texture + // FIXME: surely there's a better way + + int offsetx = 0, offsety = 0; + if (which == 225) { + offsety = Random() % 40; + offsetx = abs(Random() % 60); + } + if (which == 190 || which == 185) { + offsety = Random() % 40; + offsetx = abs(Random() % 100) - 20; + } + if (which == 175) { + offsety = Random() % 10; + offsetx = Random() % 10; + } + if (which == 170) { + offsety = Random() % 20; + offsetx = Random() % 20; + } + if (which == 220 || which == 215) { + offsetx = 20; + } + + + int startx = 512; + int starty = 512; + int endx = 0; + int endy = 0; + GLubyte color; + if (creature == rabbittype) + for (i = 0; i < 512; i++) { + for (j = 0; j < 512; j++) { + if (bloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && bloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { + if (i < startx) startx = i; + if (j < starty) starty = j; + if (i > endx) endx = i; + if (j > endy) endy = j; + } + } + } + if (creature == wolftype) + for (i = 0; i < 512; i++) { + for (j = 0; j < 512; j++) { + if (wolfbloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && wolfbloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { + if (i < startx) startx = i; + if (j < starty) starty = j; + if (i > endx) endx = i; + if (j > endy) endy = j; + } + } + } + + startx += offsetx; + endx += offsetx; + starty += offsety; + endy += offsety; + + if (startx < 0) startx = 0; + if (starty < 0) starty = 0; + if (endx > 512 - 1) endx = 512 - 1; + if (endy > 512 - 1) endy = 512 - 1; + if (endx < startx) endx = startx; + if (endy < starty) endy = starty; + + startx /= realtexdetail; + starty /= realtexdetail; + endx /= realtexdetail; + endy /= realtexdetail; + + int texdetailint = realtexdetail; + int where; + if (creature == rabbittype) + for (i = startx; i < endx; i++) { + for (j = starty; j < endy; j++) { + if (bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { + color = Random() % 85 + 170; + where = i * skeleton.skinsize * 3 + j * 3; + if (skeleton.skinText[where + 0] > color / 2) + skeleton.skinText[where + 0] = color / 2; + skeleton.skinText[where + 1] = 0; + skeleton.skinText[where + 2] = 0; + } + } + } + if (creature == wolftype) + for (i = startx; i < endx; i++) { + for (j = starty; j < endy; j++) { + if (wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { + color = Random() % 85 + 170; + where = i * skeleton.skinsize * 3 + j * 3; + if (skeleton.skinText[where + 0] > color / 2) + skeleton.skinText[where + 0] = color / 2; + skeleton.skinText[where + 1] = 0; + skeleton.skinText[where + 2] = 0; + } + } + } + skeleton.drawmodel.textureptr.bind(); + DoMipmaps(); + + bleedxint = 0; + bleedyint = 0; + if (creature == rabbittype) + while (bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { + bleedxint = abs(Random() % 512); + bleedyint = abs(Random() % 512); + } + if (creature == wolftype) + while (wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { + bleedxint = abs(Random() % 512); + bleedyint = abs(Random() % 512); + } + bleedy = bleedxint + offsetx; + bleedx = bleedyint + offsety; + bleedy /= realtexdetail; + bleedx /= realtexdetail; + if (bleedx < 0) + bleedx = 0; + if (bleedy < 0) + bleedy = 0; + if (bleedx > skeleton.skinsize - 1) + bleedx = skeleton.skinsize - 1; + if (bleedy > skeleton.skinsize - 1) + bleedy = skeleton.skinsize - 1; + direction = abs(Random() % 2) * 2 - 1; + + } + bleeding = howmuch + (float)abs(Random() % 100) / 200 - .25; + deathbleeding += bleeding; + bloodloss += bleeding * 3; + + if (tutoriallevel != 1 && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) { + if (abs(Random() % 2) == 0) { + aitype = gethelptype; + lastseentime = 12; + } else + aitype = attacktypecutoff; + ally = 0; + } + if (bleeding > 2) + bleeding = 2; +} + +/* EFFECT + * similar to DoBloodBig + */ +bool Person::DoBloodBigWhere(float howmuch, int which, XYZ where) +{ + static int i, j; + static XYZ bloodvel; + static XYZ startpoint, endpoint, colpoint, movepoint; + static float rotationpoint; + static int whichtri; + static XYZ p1, p2, p3, p0; + XYZ bary; + XYZ gxx, gyy; + float coordsx, coordsy; + float total; + + if (bloodtoggle && decals && tutoriallevel != 1) { + where -= coords; + if (!skeleton.free) + where = DoRotation(where, 0, -yaw, 0); + //where=scale; + startpoint = where; + startpoint.y += 100; + endpoint = where; + endpoint.y -= 100; + movepoint = 0; + rotationpoint = 0; + // ray testing for a tri in the character model + whichtri = skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &colpoint, &movepoint, &rotationpoint); + if (whichtri != -1) { + // low level geometry math + p0 = colpoint; + p1 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[whichtri].vertex[0]]; + p2 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[whichtri].vertex[1]]; + p3 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[whichtri].vertex[2]]; + + bary.x = distsq(&p0, &p1); + bary.y = distsq(&p0, &p2); + bary.z = distsq(&p0, &p3); + + total = bary.x + bary.y + bary.z; + bary.x /= total; + bary.y /= total; + bary.z /= total; + + bary.x = 1 - bary.x; + bary.y = 1 - bary.y; + bary.z = 1 - bary.z; + + total = bary.x + bary.y + bary.z; + bary.x /= total; + bary.y /= total; + bary.z /= total; + + + gxx.x = skeleton.drawmodel.Triangles[whichtri].gx[0]; + gxx.y = skeleton.drawmodel.Triangles[whichtri].gx[1]; + gxx.z = skeleton.drawmodel.Triangles[whichtri].gx[2]; + gyy.x = skeleton.drawmodel.Triangles[whichtri].gy[0]; + gyy.y = skeleton.drawmodel.Triangles[whichtri].gy[1]; + gyy.z = skeleton.drawmodel.Triangles[whichtri].gy[2]; + coordsx = skeleton.drawmodel.Triangles[whichtri].gx[0] * bary.x + skeleton.drawmodel.Triangles[whichtri].gx[1] * bary.y + skeleton.drawmodel.Triangles[whichtri].gx[2] * bary.z; + coordsy = skeleton.drawmodel.Triangles[whichtri].gy[0] * bary.x + skeleton.drawmodel.Triangles[whichtri].gy[1] * bary.y + skeleton.drawmodel.Triangles[whichtri].gy[2] * bary.z; + + if (bleeding <= 0 && spurt) { + spurt = 0; + for (int i = 0; i < 3; i++) { + // emit blood particles + // FIXME: more copypaste code + bloodvel = 0; + if (skeleton.free) { + bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); + bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); + Sprite::MakeSprite(bloodflamesprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .3, 1); + } else { + bloodvel.z = 10; + bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); + Sprite::MakeSprite(bloodflamesprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .3, 1); + } + } + } + + // texture manipulation follows + + int offsetx = 0, offsety = 0; + offsetx = (1 + coordsy) * 512 - 291; + offsety = coordsx * 512 - 437; + + int startx = 512; + int starty = 512; + int endx = 0; + int endy = 0; + GLubyte color; + if (creature == rabbittype) + for (i = 0; i < 512; i++) { + for (j = 0; j < 512; j++) { + if (bloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && bloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { + if (i < startx) startx = i; + if (j < starty) starty = j; + if (i > endx) endx = i; + if (j > endy) endy = j; + } + } + } + if (creature == wolftype) + for (i = 0; i < 512; i++) { + for (j = 0; j < 512; j++) { + if (wolfbloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && wolfbloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { + if (i < startx) startx = i; + if (j < starty) starty = j; + if (i > endx) endx = i; + if (j > endy) endy = j; + } + } + } + startx += offsetx; + endx += offsetx; + starty += offsety; + endy += offsety; + + if (startx < 0) startx = 0; + if (starty < 0) starty = 0; + if (endx > 512 - 1) endx = 512 - 1; + if (endy > 512 - 1) endy = 512 - 1; + if (endx < startx) endx = startx; + if (endy < starty) endy = starty; + + startx /= realtexdetail; + starty /= realtexdetail; + endx /= realtexdetail; + endy /= realtexdetail; + + int texdetailint = realtexdetail; + int where; + if (creature == rabbittype) + for (i = startx; i < endx; i++) { + for (j = starty; j < endy; j++) { + if (bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { + color = Random() % 85 + 170; + where = i * skeleton.skinsize * 3 + j * 3; + if (skeleton.skinText[where + 0] > color / 2) + skeleton.skinText[where + 0] = color / 2; + skeleton.skinText[where + 1] = 0; + skeleton.skinText[where + 2] = 0; + } else if (bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= 160 + 4 && bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= 160 - 4) { + color = Random() % 85 + 170; + where = i * skeleton.skinsize * 3 + j * 3; + if (skeleton.skinText[where + 0] > color / 2) + skeleton.skinText[where + 0] = color / 2; + skeleton.skinText[where + 1] = 0; + skeleton.skinText[where + 2] = 0; + } + } + } + if (creature == wolftype) + for (i = startx; i < endx; i++) { + for (j = starty; j < endy; j++) { + if (wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { + color = Random() % 85 + 170; + where = i * skeleton.skinsize * 3 + j * 3; + if (skeleton.skinText[where + 0] > color / 2) + skeleton.skinText[where + 0] = color / 2; + skeleton.skinText[where + 1] = 0; + skeleton.skinText[where + 2] = 0; + } else if (wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= 160 + 4 && wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= 160 - 4) { + color = Random() % 85 + 170; + where = i * skeleton.skinsize * 3 + j * 3; + if (skeleton.skinText[where + 0] > color / 2) + skeleton.skinText[where + 0] = color / 2; + skeleton.skinText[where + 1] = 0; + skeleton.skinText[where + 2] = 0; + } + } + } + skeleton.drawmodel.textureptr.bind(); + DoMipmaps(); + + bleedy = (1 + coordsy) * 512; + bleedx = coordsx * 512; + bleedy /= realtexdetail; + bleedx /= realtexdetail; + if (bleedx < 0) + bleedx = 0; + if (bleedy < 0) + bleedy = 0; + if (bleedx > skeleton.skinsize - 1) + bleedx = skeleton.skinsize - 1; + if (bleedy > skeleton.skinsize - 1) + bleedy = skeleton.skinsize - 1; + direction = abs(Random() % 2) * 2 - 1; + } + if (whichtri == -1) + return 0; + } + bleeding = howmuch + (float)abs(Random() % 100) / 200 - .25; + deathbleeding += bleeding; + bloodloss += bleeding * 3; + + if (tutoriallevel != 1 && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) { + if (abs(Random() % 2) == 0) { + aitype = gethelptype; + lastseentime = 12; + } else + aitype = attacktypecutoff; + ally = 0; + } + if (bleeding > 2) + bleeding = 2; + return 1; +} + + + +/* EFFECT + * guessing this performs a reversal + */ +void Person::Reverse() +{ + if (!((victim->aitype == playercontrolled + || hostiletime > 1 + || staggerdelay <= 0) + && victim->animTarget != jumpupanim + && victim->animTarget != jumpdownanim + && (tutoriallevel != 1 || cananger) + && hostile)) + return; + + if (normaldotproduct (victim->facing, victim->coords - coords) > 0 + && (victim->id != 0 || difficulty >= 2) + && (creature != wolftype || victim->creature == wolftype)) + return; + + if (animTarget == sweepanim) { + animTarget = sweepreversedanim; + animCurrent = sweepreversedanim; + victim->animCurrent = sweepreversalanim; + victim->animTarget = sweepreversalanim; + } + if (animTarget == spinkickanim) { + animTarget = spinkickreversedanim; + animCurrent = spinkickreversedanim; + victim->animCurrent = spinkickreversalanim; + victim->animTarget = spinkickreversalanim; + } + if (animTarget == upunchanim || animTarget == rabbittacklinganim) { + if (animTarget == rabbittacklinganim) { + frameCurrent = 6; + frameTarget = 7; + victim->frameCurrent = 6; + victim->frameTarget = 7; + } + animTarget = upunchreversedanim; + animCurrent = upunchreversedanim; + victim->animCurrent = upunchreversalanim; + victim->animTarget = upunchreversalanim; + } + if (animTarget == staffhitanim && distsq(&victim->coords, &coords) < 2 && ((victim->id == 0 && victim->crouchkeydown) || Random() % 4 == 0)) { + if (victim->weaponactive != -1) { + victim->throwtogglekeydown = 1; + XYZ tempVelocity = victim->velocity * .2; + if (tempVelocity.x == 0) + tempVelocity.x = .1; + weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); + victim->num_weapons--; + if (victim->num_weapons) { + victim->weaponids[0] = victim->weaponids[victim->num_weapons]; + if (victim->weaponstuck == victim->num_weapons) + victim->weaponstuck = 0; + } + + victim->weaponactive = -1; + for (unsigned j = 0; j < Person::players.size(); j++) { + Person::players[j]->wentforweapon = 0; + } + } + + animTarget = staffhitreversedanim; + animCurrent = staffhitreversedanim; + victim->animCurrent = staffhitreversalanim; + victim->animTarget = staffhitreversalanim; + } + if (animTarget == staffspinhitanim && distsq(&victim->coords, &coords) < 2 && ((victim->id == 0 && victim->crouchkeydown) || Random() % 2 == 0)) { + if (victim->weaponactive != -1) { + victim->throwtogglekeydown = 1; + XYZ tempVelocity = victim->velocity * .2; + if (tempVelocity.x == 0) + tempVelocity.x = .1; + weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); + victim->num_weapons--; + if (victim->num_weapons) { + victim->weaponids[0] = victim->weaponids[victim->num_weapons]; + if (victim->weaponstuck == victim->num_weapons) + victim->weaponstuck = 0; + } + + victim->weaponactive = -1; + for (unsigned j = 0; j < Person::players.size(); j++) { + Person::players[j]->wentforweapon = 0; + } + } + animTarget = staffspinhitreversedanim; + animCurrent = staffspinhitreversedanim; + victim->animCurrent = staffspinhitreversalanim; + victim->animTarget = staffspinhitreversalanim; + } + if (animTarget == swordslashanim && distsq(&victim->coords, &coords) < 2 && ((victim->id == 0 && victim->crouchkeydown) || Random() % 4 == 0)) { + if (victim->weaponactive != -1) { + victim->throwtogglekeydown = 1; + XYZ tempVelocity = victim->velocity * .2; + if (tempVelocity.x == 0) + tempVelocity.x = .1; + weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); + victim->num_weapons--; + if (victim->num_weapons) { + victim->weaponids[0] = victim->weaponids[victim->num_weapons]; + if (victim->weaponstuck == victim->num_weapons) + victim->weaponstuck = 0; + } + + victim->weaponactive = -1; + for (unsigned j = 0; j < Person::players.size(); j++) { + Person::players[j]->wentforweapon = 0; + } + } + animTarget = swordslashreversedanim; + animCurrent = swordslashreversedanim; + victim->animCurrent = swordslashreversalanim; + victim->animTarget = swordslashreversalanim; + } + if (animTarget == knifeslashstartanim && distsq(&victim->coords, &coords) < 2 && (victim->id == 0 || Random() % 4 == 0)) { + if (victim->weaponactive != -1) { + victim->throwtogglekeydown = 1; + XYZ tempVelocity = victim->velocity * .2; + if (tempVelocity.x == 0) + tempVelocity.x = .1; + weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); + victim->num_weapons--; + if (victim->num_weapons) { + victim->weaponids[0] = victim->weaponids[victim->num_weapons]; + if (victim->weaponstuck == victim->num_weapons) + victim->weaponstuck = 0; + } + + victim->weaponactive = -1; + for (unsigned j = 0; j < Person::players.size(); j++) { + Person::players[j]->wentforweapon = 0; + } + } + animTarget = knifeslashreversedanim; + animCurrent = knifeslashreversedanim; + victim->animCurrent = knifeslashreversalanim; + victim->animTarget = knifeslashreversalanim; + } + if (animTarget != knifeslashstartanim && animTarget != staffhitanim && animTarget != staffspinhitanim && animTarget != winduppunchanim && animTarget != wolfslapanim && animTarget != swordslashanim) { + victim->targettilt2 = targettilt2; + victim->frameCurrent = frameCurrent; + victim->frameTarget = frameTarget; + victim->target = target; + victim->velocity = 0; + victim->oldcoords = victim->coords; + victim->coords = coords; + victim->targetyaw = targetyaw; + victim->yaw = targetyaw; + victim->victim = this->shared_from_this(); + } + if (animTarget == winduppunchanim) { + animTarget = winduppunchblockedanim; + victim->animTarget = blockhighleftanim; + victim->frameTarget = 1; + victim->target = .5; + victim->victim = this->shared_from_this(); + victim->targetyaw = targetyaw + 180; + } + if (animTarget == wolfslapanim) { + animTarget = winduppunchblockedanim; + victim->animTarget = blockhighleftanim; + victim->frameTarget = 1; + victim->target = .5; + victim->victim = this->shared_from_this(); + victim->targetyaw = targetyaw + 180; + } + if ((animTarget == swordslashanim || animTarget == staffhitanim || animTarget == staffspinhitanim) && victim->weaponactive != -1) { + animTarget = swordslashparriedanim; + parriedrecently = .4; + victim->parriedrecently = 0; + victim->animTarget = swordslashparryanim; + victim->frameTarget = 1; + victim->target = .5; + victim->victim = this->shared_from_this(); + victim->targetyaw = targetyaw + 180; + + if (abs(Random() % 20) == 0 || weapons[victim->weaponids[victim->weaponactive]].getType() == knife) { + if (victim->weaponactive != -1) { + if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { + if (weapons[victim->weaponids[0]].getType() == staff) + weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + if (weapons[weaponids[0]].getType() == staff) + weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + emit_sound_at(swordstaffsound, victim->coords); + } else { + emit_sound_at(metalhitsound, victim->coords); + } + } + XYZ aim; + victim->Puff(righthand); + victim->target = 0; + victim->frameTarget = 0; + victim->animTarget = staggerbackhighanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + aim = DoRotation(facing, 0, 90, 0) * 21; + aim.y += 7; + weapons[victim->weaponids[0]].drop(aim * -.2, aim); + victim->num_weapons--; + if (victim->num_weapons) { + victim->weaponids[0] = victim->weaponids[num_weapons]; + if (victim->weaponstuck == victim->num_weapons) + victim->weaponstuck = 0; + } + victim->weaponactive = -1; + for (unsigned i = 0; i < Person::players.size(); i++) { + Person::players[i]->wentforweapon = 0; + } + } + + if (abs(Random() % 20) == 0) { + if (weaponactive != -1) { + if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { + if (weapons[victim->weaponids[0]].getType() == staff) + weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + if (weapons[weaponids[0]].getType() == staff) + weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + + emit_sound_at(swordstaffsound, coords); + } else { + emit_sound_at(metalhitsound, coords); + } + } + + XYZ aim; + Puff(righthand); + target = 0; + frameTarget = 0; + animTarget = staggerbackhighanim; + targetyaw = targetyaw + 180; + target = 0; + aim = DoRotation(facing, 0, 90, 0) * 21; + aim.y += 7; + weapons[victim->weaponids[0]].drop(aim * -.2, aim); + num_weapons--; + if (num_weapons) { + weaponids[0] = weaponids[num_weapons]; + if (weaponstuck == num_weapons) + weaponstuck = 0; + } + weaponactive = -1; + for (unsigned i = 0; i < Person::players.size(); i++) { + Person::players[i]->wentforweapon = 0; + } + + + } + } + if (hasvictim) + if (animTarget == knifeslashstartanim || animTarget == swordslashanim || animTarget == staffhitanim || animTarget == staffspinhitanim) { + if ((animTarget != staffhitanim && animTarget != staffspinhitanim) || distsq(&coords, &victim->coords) > .2) { + victim->animTarget = dodgebackanim; + victim->frameTarget = 0; + victim->target = 0; + + XYZ rotatetarget; + rotatetarget = coords - victim->coords; + Normalise(&rotatetarget); + victim->targetyaw = -asin(0 - rotatetarget.x); + victim->targetyaw *= 360 / 6.28; + if (rotatetarget.z < 0) + victim->targetyaw = 180 - victim->targetyaw; + + victim->targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; //*-70; + + victim->lastattack3 = victim->lastattack2; + victim->lastattack2 = victim->lastattack; + victim->lastattack = victim->animTarget; + } else { + victim->animTarget = sweepanim; + victim->frameTarget = 0; + victim->target = 0; + + XYZ rotatetarget; + rotatetarget = coords - victim->coords; + Normalise(&rotatetarget); + victim->targetyaw = -asin(0 - rotatetarget.x); + victim->targetyaw *= 360 / 6.28; + if (rotatetarget.z < 0) + victim->targetyaw = 180 - victim->targetyaw; + + victim->targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; //*-70; + + victim->lastattack3 = victim->lastattack2; + victim->lastattack2 = victim->lastattack; + victim->lastattack = victim->animTarget; + } + } + + velocity = 0; + victim->velocity = 0; + + if (aitype != playercontrolled) { + feint = 0; + if (escapednum < 2) { + int chances = ((difficulty == 2) ? 3 : ((difficulty == 1) ? 5 : 10)); + if ((Random() % chances) == 0) { + feint = 1; + } + } + } + + if (victim->id == 0 && Animation::animations[victim->animTarget].attack == reversal) + numreversals++; +} + +/* EFFECT + * get hurt + */ +void Person::DoDamage(float howmuch) +{ + // subtract health (temporary?) + if (tutoriallevel != 1) + damage += howmuch / power; + // stats? + if (id != 0) + damagedealt += howmuch / power; + if (id == 0) + damagetaken += howmuch / power; + + // reset bonuses + if (id == 0 && (bonus == solidhit || bonus == twoxcombo || bonus == threexcombo || bonus == fourxcombo || bonus == megacombo)) + bonus = 0; + // subtract health + if (tutoriallevel != 1) + permanentdamage += howmuch / 2 / power; + if (tutoriallevel != 1) + superpermanentdamage += howmuch / 4 / power; + // visual effects + if (permanentdamage > damagetolerance / 2 && permanentdamage - howmuch < damagetolerance / 2 && Random() % 2) + DoBlood(1, 255); + if ((permanentdamage > damagetolerance * .8 && Random() % 2 && !deathbleeding) || spurt) + DoBlood(1, 255); + spurt = 0; + if (id == 0) + camerashake += howmuch / 100; + if (id == 0 && ((howmuch > 50 && damage > damagetolerance / 2))) + blackout = damage / damagetolerance; + if (blackout > 1) + blackout = 1; + + // cancel attack? + if (aitype == passivetype && damage < damagetolerance && ((tutoriallevel != 1 || cananger) && hostile)) + aitype = attacktypecutoff; + if (tutoriallevel != 1 && aitype != playercontrolled && damage < damagetolerance && damage > damagetolerance * 2 / 3 && creature == rabbittype) { + if (abs(Random() % 2) == 0) { + aitype = gethelptype; + lastseentime = 12; + } else + aitype = attacktypecutoff; + ally = 0; + } + + if (howmuch > damagetolerance * 50 && skeleton.free != 2) { + XYZ flatvelocity2; + XYZ flatfacing2; + for (int i = 0; i < skeleton.joints.size(); i++) { + if (skeleton.free) { + flatvelocity2 = skeleton.joints[i].velocity; + flatfacing2 = skeleton.joints[i].position * scale + coords; + } else { + flatvelocity2 = velocity; + flatfacing2 = DoRotation(DoRotation(DoRotation(skeleton.joints[i].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; + } + flatvelocity2.x += (float)(abs(Random() % 100) - 50) / 10; + flatvelocity2.y += (float)(abs(Random() % 100) - 50) / 10; + flatvelocity2.z += (float)(abs(Random() % 100) - 50) / 10; + Sprite::MakeSprite(bloodflamesprite, flatfacing2, flatvelocity2, 1, 1, 1, 3, 1); + Sprite::MakeSprite(bloodsprite, flatfacing2, flatvelocity2, 1, 1, 1, .4, 1); + Sprite::MakeSprite(cloudsprite, flatfacing2, flatvelocity2 * 0, .6, 0, 0, 1, .5); + } + + emit_sound_at(splattersound, coords); + + skeleton.free = 2; + DoDamage(10000); + RagDoll(0); + if (!dead && creature == wolftype) { + award_bonus(0, Wolfbonus); + } + dead = 2; + coords = 20; + } + + // play sounds + if (tutoriallevel != 1 || id == 0) + if (speechdelay <= 0 && !dead && aitype != playercontrolled) { + int whichsound = -1; + + if (creature == wolftype) { + int i = abs(Random() % 2); + if (i == 0) + whichsound = snarlsound; + if (i == 1) + whichsound = snarl2sound; + } + if (creature == rabbittype) { + int i = abs(Random() % 2); + if (i == 0) + whichsound = rabbitpainsound; + if (i == 1 && damage > damagetolerance) + whichsound = rabbitpain1sound; + } + + if (whichsound != -1) { + emit_sound_at(whichsound, coords); + addEnvSound(coords); + } + } + speechdelay = .3; +} + +/* EFFECT + * calculate/animate head facing direction? + */ +void Person::DoHead() +{ + static XYZ rotatearound; + static XYZ facing; + static float lookspeed = 500; + + if (!freeze && !winfreeze) { + + //head facing + targetheadyaw = (float)((int)((0 - yaw - targetheadyaw + 180) * 100) % 36000) / 100; + targetheadpitch = (float)((int)(targetheadpitch * 100) % 36000) / 100; + + while (targetheadyaw > 180)targetheadyaw -= 360; + while (targetheadyaw < -180)targetheadyaw += 360; + + if (targetheadyaw > 160) + targetheadpitch = targetheadpitch * -1; + if (targetheadyaw < -160) + targetheadpitch = targetheadpitch * -1; + if (targetheadyaw > 160) + targetheadyaw = targetheadyaw - 180; + if (targetheadyaw < -160) + targetheadyaw = targetheadyaw + 180; + + if (targetheadpitch > 120) + targetheadpitch = 120; + if (targetheadpitch < -120) + targetheadpitch = -120; + if (targetheadyaw > 120) + targetheadyaw = 120; + if (targetheadyaw < -120) + targetheadyaw = -120; + + if (!isIdle()) + targetheadpitch = 0; + if (isIdle()) { + if (targetheadyaw > 80) + targetheadyaw = 80; + if (targetheadyaw < -80) + targetheadyaw = -80; + if (targetheadpitch > 50) + targetheadpitch = 50; + if (targetheadpitch < -50) + targetheadpitch = -50; + } + + if (abs(headyaw - targetheadyaw) < multiplier * lookspeed) + headyaw = targetheadyaw; + else if (headyaw > targetheadyaw) { + headyaw -= multiplier * lookspeed; + } else if (headyaw < targetheadyaw) { + headyaw += multiplier * lookspeed; + } + + if (abs(headpitch - targetheadpitch) < multiplier * lookspeed / 2) + headpitch = targetheadpitch; + else if (headpitch > targetheadpitch) { + headpitch -= multiplier * lookspeed / 2; + } else if (headpitch < targetheadpitch) { + headpitch += multiplier * lookspeed / 2; + } + + rotatearound = jointPos(neck); + jointPos(head) = rotatearound + DoRotation(jointPos(head) - rotatearound, headpitch, 0, 0); + + facing = 0; + facing.z = -1; + if (animTarget != bounceidleanim && animTarget != fightidleanim && animTarget != wolfidle && animTarget != knifefightidleanim && animTarget != drawrightanim && animTarget != drawleftanim && animTarget != walkanim) { + facing = DoRotation(facing, headpitch * .4, 0, 0); + facing = DoRotation(facing, 0, headyaw * .4, 0); + } + + if (animTarget == bounceidleanim || animTarget == fightidleanim || animTarget == wolfidle || animTarget == knifefightidleanim || animTarget == drawrightanim || animTarget == drawleftanim) { + facing = DoRotation(facing, headpitch * .8, 0, 0); + facing = DoRotation(facing, 0, headyaw * .8, 0); + } + + if (animTarget == walkanim) { + facing = DoRotation(facing, headpitch * .6, 0, 0); + facing = DoRotation(facing, 0, headyaw * .6, 0); + } + + skeleton.specialforward[0] = facing; + //skeleton.specialforward[0]=DoRotation(facing,0,yaw,0); + for (int i = 0; i < skeleton.muscles.size(); i++) { + if (skeleton.muscles[i].visible && (skeleton.muscles[i].parent1->label == head || skeleton.muscles[i].parent2->label == head)) { + skeleton.FindRotationMuscle(i, animTarget); + } + } + } +} + +/* EFFECT + * ragdolls character? + */ +void Person::RagDoll(bool checkcollision) +{ + static XYZ change; + static int l, i, j; + static float speed; + if (!skeleton.free) { + if (id == 0) + numfalls++; + if (id == 0 && isFlip()) + numflipfail++; + + escapednum = 0; + + facing = 0; + facing.z = 1; + facing = DoRotation(facing, 0, yaw, 0); + + skeleton.freetime = 0; + + skeleton.longdead = 0; + + skeleton.free = 1; + skeleton.broken = 0; + skeleton.spinny = 1; + freefall = 1; + skeleton.freefall = 1; + + if (!isnormal(velocity.x)) velocity.x = 0; + if (!isnormal(velocity.y)) velocity.y = 0; + if (!isnormal(velocity.z)) velocity.z = 0; + if (!isnormal(yaw)) yaw = 0; + if (!isnormal(coords.x)) coords = 0; + if (!isnormal(tilt)) tilt = 0; + if (!isnormal(tilt2)) tilt2 = 0; + + for (int i = 0; i < skeleton.joints.size(); i++) { + skeleton.joints[i].delay = 0; + skeleton.joints[i].locked = 0; + skeleton.joints[i].position = DoRotation(DoRotation(DoRotation(skeleton.joints[i].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0); + if (!isnormal(skeleton.joints[i].position.x)) skeleton.joints[i].position = DoRotation(skeleton.joints[i].position, 0, yaw, 0); + if (!isnormal(skeleton.joints[i].position.x)) skeleton.joints[i].position = coords; + skeleton.joints[i].position.y += .1; + skeleton.joints[i].oldposition = skeleton.joints[i].position; + skeleton.joints[i].realoldposition = skeleton.joints[i].position * scale + coords; + } + + for (int i = 0; i < skeleton.joints.size(); i++) { + skeleton.joints[i].velocity = 0; + skeleton.joints[i].velchange = 0; + } + skeleton.DoConstraints(&coords, &scale); + if (Animation::animations[animCurrent].height == lowheight || Animation::animations[animTarget].height == lowheight) { + skeleton.DoConstraints(&coords, &scale); + skeleton.DoConstraints(&coords, &scale); + skeleton.DoConstraints(&coords, &scale); + skeleton.DoConstraints(&coords, &scale); + } + + speed = targetFrame().speed * 2; + if (currentFrame().speed > targetFrame().speed) { + speed = currentFrame().speed * 2; + } + if (transspeed) + speed = transspeed * 2; + + speed *= speedmult; + + for (int i = 0; i < skeleton.joints.size(); i++) { + if ((Animation::animations[animCurrent].attack != reversed || animCurrent == swordslashreversedanim) && animCurrent != rabbitkickanim && !isLanding() && !wasLanding() && Animation::animations[animCurrent].height == Animation::animations[animTarget].height) + skeleton.joints[i].velocity = velocity / scale + facing * 5 + DoRotation(DoRotation(DoRotation((targetFrame().joints[i].position - currentFrame().joints[i].position) * speed, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0); + else + skeleton.joints[i].velocity = velocity / scale + facing * 5; + change.x = (float)(Random() % 100) / 100; + change.y = (float)(Random() % 100) / 100; + change.z = (float)(Random() % 100) / 100; + skeleton.joints[i].velocity += change; + skeleton.joints[abs(Random() % skeleton.joints.size())].velocity -= change; + + change.x = (float)(Random() % 100) / 100; + change.y = (float)(Random() % 100) / 100; + change.z = (float)(Random() % 100) / 100; + skeleton.joints[i].velchange += change; + skeleton.joints[abs(Random() % skeleton.joints.size())].velchange -= change; + } + + if (checkcollision) { + XYZ average; + XYZ lowpoint; + XYZ colpoint; + int howmany; + average = 0; + howmany = 0; + for (j = 0; j < skeleton.joints.size(); j++) { + average += skeleton.joints[j].position; + howmany++; + } + average /= howmany; + coords += average * scale; + for (j = 0; j < skeleton.joints.size(); j++) { + skeleton.joints[j].position -= average; + } + + whichpatchx = coords.x / (terrain.size / subdivision * terrain.scale); + whichpatchz = coords.z / (terrain.size / subdivision * terrain.scale); + if (terrain.patchobjectnum[whichpatchx][whichpatchz]) + for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { + i = terrain.patchobjects[whichpatchx][whichpatchz][l]; + lowpoint = coords; + lowpoint.y += 1; + if (SphereCheck(&lowpoint, 3, &colpoint, &objects.position[i], &objects.yaw[i], &objects.model[i]) != -1) { + coords.x = lowpoint.x; + coords.z = lowpoint.z; + } + } + } + + yaw = 0; + updatedelay = 0; + + velocity = 0; + for (int i = 0; i < skeleton.joints.size(); i++) { + velocity += skeleton.joints[i].velocity * scale; + } + velocity /= skeleton.joints.size(); + + // drop weapon + if (Random() % 2 == 0) { + if (weaponactive != -1 && animTarget != rabbitkickanim && num_weapons > 0) { + weapons[weaponids[0]].drop(jointVel(righthand) * scale * -.3, jointVel(righthand) * scale); + weapons[weaponids[0]].velocity.x += .01; + num_weapons--; + if (num_weapons) { + weaponids[0] = weaponids[num_weapons]; + if (weaponstuck == num_weapons) + weaponstuck = 0; + } + weaponactive = -1; + for (unsigned i = 0; i < Person::players.size(); i++) { + Person::players[i]->wentforweapon = 0; + } + } + } + + animTarget = bounceidleanim; + animCurrent = bounceidleanim; + frameTarget = 0; + frameCurrent = 0; + } +} + + + +/* EFFECT + */ +void Person::FootLand(bodypart whichfoot, float opacity) +{ + if ((whichfoot != leftfoot) && (whichfoot != rightfoot)) { + cerr << "FootLand called on wrong bodypart" << endl; + return; + } + static XYZ terrainlight; + static XYZ footvel, footpoint; + if (opacity >= 1 || skiddelay <= 0) { + if (opacity > 1) { + footvel = 0; + footpoint = DoRotation(jointPos(whichfoot), 0, yaw, 0) * scale + coords; + if (distsq(&footpoint, &viewer)) + Sprite::MakeSprite(cloudsprite, footpoint, footvel, 1, 1, 1, .5, .2 * opacity); + } else if (onterrain && terrain.getOpacity(coords.x, coords.z) < .2) { + footvel = velocity / 5; + if (footvel.y < .8) + footvel.y = .8; + footpoint = DoRotation(jointPos(whichfoot), 0, yaw, 0) * scale + coords; + footpoint.y = terrain.getHeight(footpoint.x, footpoint.z); + terrainlight = terrain.getLighting(footpoint.x, footpoint.z); + if (distsq(&footpoint, &viewer) < viewdistance * viewdistance / 4) { + if (environment == snowyenvironment) { + Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7 * opacity); + if (detail == 2) { + terrain.MakeDecal(footprintdecal, footpoint, .2, 1 * opacity, yaw); + } + } else if (environment == grassyenvironment) { + Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5 * opacity); + } else if (environment == desertenvironment) { + Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7 * opacity); + if (detail == 2) { + terrain.MakeDecal(footprintdecal, footpoint, .2, .25 * opacity, yaw); + } + } + } + } else if (isLanding() || (animTarget == jumpupanim) || isLandhard()) { + footvel = velocity / 5; + if (footvel.y < .8) + footvel.y = .8; + footpoint = DoRotation(jointPos(whichfoot), 0, yaw, 0) * scale + coords; + if (distsq(&footpoint, &viewer) < viewdistance * viewdistance / 4) { + Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, 1, 1, 1, .5, .2 * opacity); + } + } + } +} + +/* EFFECT + * make a puff effect at a body part (dust effect?) + */ +void Person::Puff(int whichlabel) +{ + static XYZ footvel, footpoint; + + footvel = 0; + footpoint = DoRotation(jointPos(whichlabel), 0, yaw, 0) * scale + coords; + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .9, .3); +} + +/* EFFECT + * I think I added this in an attempt to clean up code + */ +void Person::setAnimation(int animation) +{ + animTarget = animation; + frameTarget = 0; + target = 0; +} + +/* EFFECT + * MONSTER + * TODO: ??? + */ +void Person::DoAnimations() +{ + if (!skeleton.free) { + static float oldtarget; + + if (isIdle() && animCurrent != getIdle()) + normalsupdatedelay = 0; + + if (animTarget == tempanim || animCurrent == tempanim) { + Animation::animations[tempanim] = tempanimation; + } + if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { + float gLoc[3]; + float vel[3]; + gLoc[0] = coords.x; + gLoc[1] = coords.y; + gLoc[2] = coords.z; + vel[0] = velocity.x; + vel[1] = velocity.y; + vel[2] = velocity.z; + + if (id == 0) { + OPENAL_3D_SetAttributes(channels[whooshsound], gLoc, vel); + OPENAL_SetVolume(channels[whooshsound], 64 * findLength(&velocity) / 5); + } + if (((velocity.y < -15) || (crouchkeydown && velocity.y < -8)) && abs(velocity.y) * 4 > fast_sqrt(velocity.x * velocity.x * velocity.z * velocity.z)) + landhard = 1; + if (!crouchkeydown && velocity.y >= -15) + landhard = 0; + } + if ((animCurrent == jumpupanim || animTarget == jumpdownanim)/*&&velocity.y<40*/ && !isFlip() && (!isLanding() && !isLandhard()) && ((crouchkeydown && !crouchtogglekeydown))) { + XYZ targfacing; + targfacing = 0; + targfacing.z = 1; + + targfacing = DoRotation(targfacing, 0, targetyaw, 0); + + if (normaldotproduct(targfacing, velocity) >= -.3) + animTarget = flipanim; + else + animTarget = backflipanim; + crouchtogglekeydown = 1; + frameTarget = 0; + target = 0; + + if (id == 0) + numflipped++; + } + + if (Animation::animations[animTarget].attack != reversed) + feint = 0; + if (!crouchkeydown || (isLanding() || isLandhard()) || (wasLanding() || wasLandhard())) { + crouchtogglekeydown = 0; + if (aitype == playercontrolled) + feint = 0; + } else { + if (!crouchtogglekeydown && Animation::animations[animTarget].attack == reversed && aitype == playercontrolled && (escapednum < 2 || reversaltrain)) + feint = 1; + if (!isFlip()) + crouchtogglekeydown = 1; + } + + + if (Animation::animations[animTarget].attack || animCurrent == getupfrombackanim || animCurrent == getupfromfrontanim) { + if (detail) + normalsupdatedelay = 0; + } + + if (target >= 1) { + if (animTarget == rollanim && frameTarget == 3 && onfire) { + onfire = 0; + emit_sound_at(fireendsound, coords); + pause_sound(stream_firesound); + deathbleeding = 0; + } + + if (animTarget == rabbittacklinganim && frameTarget == 1) { + if (victim->aitype == attacktypecutoff && victim->stunned <= 0 && victim->surprised <= 0 && victim->id != 0) + Reverse(); + if (animTarget == rabbittacklinganim && frameTarget == 1 && !victim->isCrouch() && victim->animTarget != backhandspringanim) { + if (normaldotproduct(victim->facing, facing) > 0) + victim->animTarget = rabbittackledbackanim; + else + victim->animTarget = rabbittackledfrontanim; + victim->frameTarget = 2; + victim->target = 0; + victim->yaw = yaw; + victim->targetyaw = yaw; + if (victim->aitype == gethelptype) + victim->DoDamage(victim->damagetolerance - victim->damage); + //victim->DoDamage(30); + if (creature == wolftype) { + DoBloodBig(0, 255); + emit_sound_at(clawslicesound, victim->coords); + victim->spurt = 1; + victim->DoBloodBig(1 / victim->armorhead, 210); + } + award_bonus(id, TackleBonus, + victim->aitype == gethelptype ? 50 : 0); + } + } + + if (!drawtogglekeydown && drawkeydown && (weaponactive == -1 || num_weapons == 1) && (targetFrame().label || (animTarget != animCurrent && animCurrent == rollanim)) && num_weapons > 0 && creature != wolftype) { + if (weapons[weaponids[0]].getType() == knife) { + if (weaponactive == -1) + weaponactive = 0; + else if (weaponactive == 0) + weaponactive = -1; + + if (weaponactive == -1) { + emit_sound_at(knifesheathesound, coords); + } + if (weaponactive != -1) { + emit_sound_at(knifedrawsound, coords, 128); + } + } + drawtogglekeydown = 1; + } + //Footstep sounds + if (tutoriallevel != 1 || id == 0) + if ((targetFrame().label && (targetFrame().label < 5 || targetFrame().label == 8))) { + int whichsound; + if (onterrain) { + if (terrain.getOpacity(coords.x, coords.z) < .2) { + if (targetFrame().label == 1) + whichsound = footstepsound; + else + whichsound = footstepsound2; + if (targetFrame().label == 1) + FootLand(leftfoot, 1); + if (targetFrame().label == 2) + FootLand(rightfoot, 1); + if (targetFrame().label == 3 && isRun()) { + FootLand(rightfoot, 1); + FootLand(leftfoot, 1); + } + + } + if (terrain.getOpacity(coords.x, coords.z) >= .2) { + if (targetFrame().label == 1) + whichsound = footstepsound3; + else + whichsound = footstepsound4; + } + } + if (!onterrain) { + if (targetFrame().label == 1) + whichsound = footstepsound3; + else + whichsound = footstepsound4; + } + if (targetFrame().label == 4 && (weaponactive == -1 || (animTarget != knifeslashstartanim && animTarget != knifethrowanim && animTarget != crouchstabanim && animTarget != swordgroundstabanim && animTarget != knifefollowanim))) { + if (Animation::animations[animTarget].attack != neutral) { + unsigned r = abs(Random() % 3); + if (r == 0) + whichsound = lowwhooshsound; + if (r == 1) + whichsound = midwhooshsound; + if (r == 2) + whichsound = highwhooshsound; + } + if (Animation::animations[animTarget].attack == neutral) + whichsound = movewhooshsound; + } else if (targetFrame().label == 4) + whichsound = knifeswishsound; + if (targetFrame().label == 8 && tutoriallevel != 1) + whichsound = landsound2; + + emit_sound_at(whichsound, coords, 256.); + + if (id == 0) + if (whichsound == footstepsound || whichsound == footstepsound2 || whichsound == footstepsound3 || whichsound == footstepsound4) { + if (animTarget == wolfrunninganim || animTarget == rabbitrunninganim) { + addEnvSound(coords, 15); + } else { + addEnvSound(coords, 6); + } + } + + if (targetFrame().label == 3) { + whichsound--; + emit_sound_at(whichsound, coords, 128.); + } + } + + //Combat sounds + if (tutoriallevel != 1 || id == 0) + if (speechdelay <= 0) + if (animTarget != crouchstabanim && animTarget != swordgroundstabanim && animTarget != staffgroundsmashanim) + if ((targetFrame().label && (targetFrame().label < 5 || targetFrame().label == 8))) { + int whichsound = -1; + if (targetFrame().label == 4 && aitype != playercontrolled) { + if (Animation::animations[animTarget].attack != neutral) { + unsigned r = abs(Random() % 4); + if (creature == rabbittype) { + if (r == 0) whichsound = rabbitattacksound; + if (r == 1) whichsound = rabbitattack2sound; + if (r == 2) whichsound = rabbitattack3sound; + if (r == 3) whichsound = rabbitattack4sound; + } + if (creature == wolftype) { + if (r == 0) whichsound = barksound; + if (r == 1) whichsound = bark2sound; + if (r == 2) whichsound = bark3sound; + if (r == 3) whichsound = barkgrowlsound; + } + speechdelay = .3; + } + } + + if (whichsound != -1) { + emit_sound_at(whichsound, coords); + } + } + + + + if ((!wasLanding() && !wasLandhard()) && animCurrent != getIdle() && (isLanding() || isLandhard())) { + FootLand(leftfoot, 1); + FootLand(rightfoot, 1); + } + + transspeed = 0; + currentoffset = targetoffset; + frameTarget = frameCurrent; + animCurrent = animTarget; + frameTarget++; + + if (animTarget == removeknifeanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + for (unsigned i = 0; i < weapons.size(); i++) { + if (weapons[i].owner == -1) + if (distsqflat(&coords, &weapons[i].position) < 4 && weaponactive == -1) { + if (distsq(&coords, &weapons[i].position) >= 1) { + if (weapons[i].getType() != staff) { + emit_sound_at(knifedrawsound, coords, 128.); + } + + takeWeapon(i); + } + } + } + } + + if (animTarget == crouchremoveknifeanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + for (unsigned i = 0; i < weapons.size(); i++) { + bool willwork = true; + if (weapons[i].owner != -1) + if (Person::players[weapons[i].owner]->weaponstuck != -1) + if (Person::players[weapons[i].owner]->weaponids[Person::players[weapons[i].owner]->weaponstuck] == int(i)) + if (Person::players[weapons[i].owner]->num_weapons > 1) + willwork = 0; + if ((weapons[i].owner == -1) || (hasvictim && (weapons[i].owner == int(victim->id)) && victim->skeleton.free)) + if (willwork && distsqflat(&coords, &weapons[i].position) < 3 && weaponactive == -1) { + if (distsq(&coords, &weapons[i].position) < 1 || hasvictim) { + bool fleshstuck = false; + if (weapons[i].owner != -1) + if (victim->weaponstuck != -1) { + if (victim->weaponids[victim->weaponstuck] == int(i)) { + fleshstuck = true; + } + } + if (fleshstuck) { + emit_sound_at(fleshstabremovesound, coords, 128.); + } else { + if (weapons[i].getType() != staff) { + emit_sound_at(knifedrawsound, coords, 128.); + } + } + if (weapons[i].owner != -1) { + victim = Person::players[weapons[i].owner]; + if (victim->num_weapons == 1) + victim->num_weapons = 0; + else + victim->num_weapons = 1; + + //victim->weaponactive=-1; + victim->skeleton.longdead = 0; + victim->skeleton.free = 1; + victim->skeleton.broken = 0; + + for (int j = 0; j < victim->skeleton.joints.size(); j++) { + victim->skeleton.joints[j].velchange = 0; + victim->skeleton.joints[j].locked = 0; + } + + XYZ relative; + relative = 0; + relative.y = 10; + Normalise(&relative); + XYZ footvel, footpoint; + footvel = 0; + footpoint = weapons[i].position; + if (victim->weaponstuck != -1) { + if (victim->weaponids[victim->weaponstuck] == int(i)) { + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .8, .3); + weapons[i].bloody = 2; + weapons[i].blooddrip = 5; + victim->weaponstuck = -1; + } + } + if (victim->num_weapons > 0) { + if (victim->weaponstuck != 0 && victim->weaponstuck != -1) + victim->weaponstuck = 0; + if (victim->weaponids[0] == int(i)) + victim->weaponids[0] = victim->weaponids[victim->num_weapons]; + } + + victim->jointVel(abdomen) += relative * 6; + victim->jointVel(neck) += relative * 6; + victim->jointVel(rightshoulder) += relative * 6; + victim->jointVel(leftshoulder) += relative * 6; + } + takeWeapon(i); + } + } + } + } + + if (animCurrent == drawleftanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (weaponactive == -1) + weaponactive = 0; + else if (weaponactive == 0) { + weaponactive = -1; + if (num_weapons == 2) { + int buffer; + buffer = weaponids[0]; + weaponids[0] = weaponids[1]; + weaponids[1] = buffer; + } + } + if (weaponactive == -1) { + emit_sound_at(knifesheathesound, coords, 128.); + } + if (weaponactive != -1) { + emit_sound_at(knifedrawsound, coords, 128.); + } + } + + + if ((animCurrent == walljumprightkickanim && animTarget == walljumprightkickanim) || (animCurrent == walljumpleftkickanim && animTarget == walljumpleftkickanim)) { + XYZ rotatetarget = DoRotation(skeleton.forward, 0, yaw, 0); + Normalise(&rotatetarget); + targetyaw = -asin(0 - rotatetarget.x); + targetyaw *= 360 / 6.28; + if (rotatetarget.z < 0) + targetyaw = 180 - targetyaw; + + if (animTarget == walljumprightkickanim) + targetyaw += 40; + if (animTarget == walljumpleftkickanim) + targetyaw -= 40; + } + + bool dojumpattack; + dojumpattack = 0; + if ((animTarget == rabbitrunninganim || animTarget == wolfrunninganim) && frameTarget == 3 && (jumpkeydown || attackkeydown || id != 0)) + dojumpattack = 1; + if (hasvictim) + if (distsq(&victim->coords, &/*Person::players[i]->*/coords) < 5 && victim->aitype == gethelptype && (attackkeydown) && !victim->skeleton.free && victim->isRun() && victim->runninghowlong >= 1) + dojumpattack = 1; + if (!hostile) + dojumpattack = 0; + if (dojumpattack) { + if ((animTarget == rabbitrunninganim || animTarget == wolfrunninganim) && id == 0) { + animTarget = rabbittackleanim; + frameTarget = 0; + emit_sound_at(jumpsound, coords); + } + + float closestdist; + closestdist = 0; + int closestid; + closestid = -1; + XYZ targetloc; + targetloc = velocity; + Normalise(&targetloc); + targetloc += coords; + for (unsigned i = 0; i < Person::players.size(); i++) { + if (i != id) + if (distsq(&targetloc, &Person::players[i]->coords) < closestdist || closestdist == 0) { + closestdist = distsq(&targetloc, &Person::players[i]->coords); + closestid = i; + } + } + if (closestid != -1) + if (closestdist < 5 && !Person::players[closestid]->dead && Animation::animations[Person::players[closestid]->animTarget].height != lowheight && Person::players[closestid]->animTarget != backhandspringanim) { + hasvictim = 1; + victim = Person::players[closestid]; + coords = victim->coords; + animCurrent = rabbittacklinganim; + animTarget = rabbittacklinganim; + frameCurrent = 0; + frameTarget = 1; + XYZ rotatetarget; + if (coords.z != victim->coords.z || coords.x != victim->coords.x) { + rotatetarget = coords - victim->coords; + Normalise(&rotatetarget); + targetyaw = -asin(0 - rotatetarget.x); + targetyaw *= 360 / 6.28; + if (rotatetarget.z < 0) + targetyaw = 180 - targetyaw; + } + if (animTarget != rabbitrunninganim) { + emit_sound_at(jumpsound, coords, 128.); + } + } + } + + //Move impacts + float damagemult = 1 * power; + if (creature == wolftype) + damagemult = 2.5 * power; + if (hasvictim) { + damagemult /= victim->damagetolerance / 200; + } + if ((Animation::animations[animTarget].attack == normalattack || animTarget == walljumprightkickanim || animTarget == walljumpleftkickanim) && (!feint) && (victim->skeleton.free != 2 || animTarget == killanim || animTarget == dropkickanim || animTarget == crouchstabanim || animTarget == swordgroundstabanim || animTarget == staffgroundsmashanim)) { + if (animTarget == spinkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && 3 && Animation::animations[victim->animTarget].height != lowheight) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2 || creature == wolftype) { + victim->spurt = 1; + DoBlood(.2, 250); + if (creature == wolftype) + DoBloodBig(0, 250); + } + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 128.); + } + if (creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhead, 175); + } + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, -90, 0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->jointVel(head) += relative * damagemult * 200; + victim->Puff(head); + victim->DoDamage(damagemult * 100 / victim->protectionhead); + + SolidHitBonus(id); + } + } + + if (animTarget == wolfslapanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && 3 && Animation::animations[victim->animTarget].height != lowheight) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2 || creature == wolftype) { + victim->spurt = 1; + if (creature == wolftype) + DoBloodBig(0, 235); + } + emit_sound_at(whooshhitsound, victim->coords); + if (creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2, 175); + } + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + relative.y -= 1; + Normalise(&relative); + relative = DoRotation(relative, 0, 90, 0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 20; + } + victim->jointVel(head) += relative * damagemult * 100; + victim->Puff(head); + victim->DoDamage(damagemult * 50 / victim->protectionhead); + } + } + + if (animTarget == walljumprightkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) { + escapednum = 0; + if (id == 0) + camerashake += .4; + victim->spurt = 1; + DoBlood(.2, 250); + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 160.); + } + if (creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhead, 175); + } + victim->RagDoll(0); + XYZ relative; + relative = facing; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, -90, 0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->jointVel(head) += relative * damagemult * 200; + victim->Puff(head); + victim->DoDamage(damagemult * 150 / victim->protectionhead); + + if (victim->damage > victim->damagetolerance) + award_bonus(id, style); + else + SolidHitBonus(id); + } + } + + if (animTarget == walljumpleftkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) { + escapednum = 0; + if (id == 0) + camerashake += .4; + victim->spurt = 1; + DoBlood(.2, 250); + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 160.); + } + if (creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhead, 175); + } + victim->RagDoll(0); + XYZ relative; + relative = facing; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, 90, 0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->jointVel(head) += relative * damagemult * 200; + victim->Puff(head); + victim->DoDamage(damagemult * 150 / victim->protectionhead); + + if (victim->damage > victim->damagetolerance) + award_bonus(id, style); + else + SolidHitBonus(id); + } + } + + if (animTarget == blockhighleftstrikeanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 235); + } + emit_sound_at(whooshhitsound, victim->coords); + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 30; + } + victim->jointVel(head) += relative * damagemult * 100; + victim->Puff(head); + victim->DoDamage(damagemult * 50 / victim->protectionhead); + } + } + + if (animTarget == killanim && Animation::animations[animTarget].frames[frameCurrent].label == 8) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && victim->dead) { + escapednum = 0; + if (id == 0) + camerashake += .2; + emit_sound_at(whooshhitsound, victim->coords, 128.); + + victim->skeleton.longdead = 0; + victim->skeleton.free = 1; + victim->skeleton.broken = 0; + victim->skeleton.spinny = 1; + + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velchange = 0; + victim->skeleton.joints[i].delay = 0; + victim->skeleton.joints[i].locked = 0; + //victim->skeleton.joints[i].velocity=0; + } + + XYZ relative; + relative = 0; + relative.y = 1; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity.y = relative.y * 10; + victim->skeleton.joints[i].position.y += relative.y * .3; + victim->skeleton.joints[i].oldposition.y += relative.y * .3; + victim->skeleton.joints[i].realoldposition.y += relative.y * .3; + } + victim->Puff(abdomen); + victim->jointVel(abdomen).y = relative.y * 400; + } + } + + if (animTarget == killanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 9 && victim->dead) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, coords, 128.); + } + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 90; + } + victim->Puff(abdomen); + if (victim->dead != 2 && victim->permanentdamage > victim->damagetolerance - 250 && autoslomo) { + slomo = 1; + slomodelay = .2; + } + victim->DoDamage(damagemult * 500 / victim->protectionhigh); + victim->jointVel(abdomen) += relative * damagemult * 300; + } + } + + if (animTarget == dropkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 9 && victim->skeleton.free) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (tutoriallevel != 1) { + emit_sound_at(thudsound, coords); + } + + victim->skeleton.longdead = 0; + victim->skeleton.free = 1; + victim->skeleton.broken = 0; + victim->skeleton.spinny = 1; + + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velchange = 0; + //victim->skeleton.joints[i].delay=0; + victim->skeleton.joints[i].locked = 0; + } + XYZ relative; + relative = victim->coords - coords; + Normalise(&relative); + relative.y += .3; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 20; + } + if (!victim->dead) + SolidHitBonus(id); + + victim->Puff(abdomen); + victim->DoDamage(damagemult * 20 / victim->protectionhigh); + victim->jointVel(abdomen) += relative * damagemult * 200; + staggerdelay = .5; + if (!victim->dead) + staggerdelay = 1.2; + + + } + } + + if ((animTarget == crouchstabanim || animTarget == swordgroundstabanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + + if (hasvictim) + if (!victim->skeleton.free) + hasvictim = 0; + + if (!hasvictim) { + terrain.MakeDecal(blooddecalfast, (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2), .08, .6, Random() % 360); + emit_sound_at(knifesheathesound, coords, 128.); + } + + if (victim && hasvictim) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) { + + XYZ where, startpoint, endpoint, movepoint, colpoint; + float rotationpoint; + int whichtri; + if (weapons[weaponids[weaponactive]].getType() == knife) { + where = (weapons[weaponids[weaponactive]].tippoint * .6 + weapons[weaponids[weaponactive]].position * .4); + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + //where=scale; + startpoint = where; + startpoint.y += 100; + endpoint = where; + endpoint.y -= 100; + } + if (weapons[weaponids[weaponactive]].getType() == sword) { + where = weapons[weaponids[weaponactive]].position; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + startpoint = where; + where = weapons[weaponids[weaponactive]].tippoint; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + endpoint = where; + } + if (weapons[weaponids[weaponactive]].getType() == staff) { + where = weapons[weaponids[weaponactive]].position; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + startpoint = where; + where = weapons[weaponids[weaponactive]].tippoint; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + endpoint = where; + } + movepoint = 0; + rotationpoint = 0; + whichtri = victim->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &colpoint, &movepoint, &rotationpoint); + + if (whichtri != -1) { + if (victim->dead != 2) { + victim->DoDamage(abs((victim->damagetolerance - victim->permanentdamage) * 2)); + if (!victim->dead) + award_bonus(id, FinishedBonus); + } + if (bloodtoggle) + weapons[weaponids[weaponactive]].bloody = 2; + + victim->skeleton.longdead = 0; + victim->skeleton.free = 1; + victim->skeleton.broken = 0; + + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velchange = 0; + victim->skeleton.joints[i].locked = 0; + //victim->skeleton.joints[i].velocity=0; + } + emit_sound_at(fleshstabsound, coords, 128); + + } + if (whichtri != -1 || weapons[weaponids[weaponactive]].bloody) { + weapons[weaponids[weaponactive]].blooddrip += 5; + weapons[weaponids[weaponactive]].blooddripdelay = 0; + } + if (whichtri == -1) { + hasvictim = 0; + emit_sound_at(knifesheathesound, coords, 128.); + } + } + } + } + + if ((animTarget == crouchstabanim || animTarget == swordgroundstabanim) && Animation::animations[animTarget].frames[frameCurrent].label == 6) { + if (!hasvictim) { + emit_sound_at(knifedrawsound, coords, 128); + } + + if (victim && hasvictim) { + XYZ footvel, footpoint; + + emit_sound_at(fleshstabremovesound, coords, 128.); + + footvel = 0; + footpoint = (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2); + + if (weapons[weaponids[weaponactive]].getType() == sword) { + XYZ where, startpoint, endpoint, movepoint; + float rotationpoint; + int whichtri; + + where = weapons[weaponids[weaponactive]].position; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + startpoint = where; + where = weapons[weaponids[weaponactive]].tippoint; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + endpoint = where; + + movepoint = 0; + rotationpoint = 0; + whichtri = victim->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &footpoint, &movepoint, &rotationpoint); + footpoint += victim->coords; + + if (whichtri == -1) { + footpoint = (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2); + } + } + if (weapons[weaponids[weaponactive]].getType() == staff) { + XYZ where, startpoint, endpoint, movepoint; + float rotationpoint; + int whichtri; + + where = weapons[weaponids[weaponactive]].position; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + startpoint = where; + where = weapons[weaponids[weaponactive]].tippoint; + where -= victim->coords; + if (!victim->skeleton.free) + where = DoRotation(where, 0, -victim->yaw, 0); + endpoint = where; + + movepoint = 0; + rotationpoint = 0; + whichtri = victim->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &footpoint, &movepoint, &rotationpoint); + footpoint += victim->coords; + + if (whichtri == -1) { + footpoint = (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2); + } + } + hasvictim = victim->DoBloodBigWhere(2, 220, footpoint); + if (hasvictim) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) { + victim->skeleton.longdead = 0; + victim->skeleton.free = 1; + victim->skeleton.broken = 0; + + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velchange = 0; + victim->skeleton.joints[i].locked = 0; + //victim->skeleton.joints[i].velocity=0; + } + + XYZ relative; + relative = 0; + relative.y = 10; + Normalise(&relative); + //victim->Puff(abdomen); + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .8, .3); + + if (victim->bloodloss < victim->damagetolerance) { + victim->bloodloss += 1000; + victim->bled = 0; + } + + victim->jointVel(abdomen) += relative * damagemult * 20; + } + } + } + if (!hasvictim && onterrain) { + weapons[weaponids[weaponactive]].bloody = 0; + weapons[weaponids[weaponactive]].blooddrip = 0; + } + } + + if (animTarget == upunchanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 235); + } + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 128); + } + + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity = relative * 30; + } + victim->jointVel(head) += relative * damagemult * 150; + + victim->frameTarget = 0; + victim->animTarget = staggerbackhardanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + victim->stunned = 1; + + victim->Puff(head); + victim->Puff(abdomen); + victim->DoDamage(damagemult * 60 / victim->protectionhigh); + + SolidHitBonus(id); + } + } + + + if (animTarget == winduppunchanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 2) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (victim->damage <= victim->damagetolerance - 60 && normaldotproduct(victim->facing, victim->coords - coords) < (scale * 5) * (scale * 5) * 0 && Animation::animations[victim->animTarget].height != lowheight) { + if (tutoriallevel != 1) { + emit_sound_at(thudsound, victim->coords); + } + } else if (victim->damage <= victim->damagetolerance - 60 && normaldotproduct(victim->facing, victim->coords - coords) < (scale * 5) * (scale * 5) * 0 && Animation::animations[victim->animTarget].height == lowheight) { + if (tutoriallevel != 1) { + emit_sound_at(whooshhitsound, victim->coords); + } + } else { + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords); + } + } + + if (victim->damage > victim->damagetolerance - 60 || normaldotproduct(victim->facing, victim->coords - coords) > 0 || Animation::animations[victim->animTarget].height == lowheight) + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + relative.y = .3; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity = relative * 5; + } + victim->jointVel(abdomen) += relative * damagemult * 400; + + victim->frameTarget = 0; + victim->animTarget = staggerbackhardanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + victim->stunned = 1; + + victim->Puff(abdomen); + victim->DoDamage(damagemult * 60 / victim->protectionhigh); + + SolidHitBonus(id); + } + } + + if (animTarget == blockhighleftanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 4) { + if (victim->id == 0) + camerashake += .4; + emit_sound_at(landsound2, victim->coords); + + Puff(righthand); + } + } + + if (animTarget == swordslashparryanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 4) { + if (victim->id == 0) + camerashake += .4; + + if (weaponactive != -1) { + if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { + if (weapons[victim->weaponids[0]].getType() == staff) + weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + if (weapons[weaponids[0]].getType() == staff) + weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + + emit_sound_at(swordstaffsound, victim->coords); + } else { + emit_sound_at(metalhitsound, victim->coords); + } + } + + //Puff(righthand); + } + } + + if (animTarget == knifethrowanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (weaponactive != -1) { + escapednum = 0; + XYZ aim; + aim = victim->coords + DoRotation(victim->jointPos(abdomen), 0, victim->yaw, 0) * victim->scale + victim->velocity * findDistance(&victim->coords, &coords) / 50 - (coords + DoRotation(jointPos(righthand), 0, yaw, 0) * scale); + Normalise(&aim); + weapons[weaponids[0]].thrown(aim * 50); + num_weapons--; + if (num_weapons) { + weaponids[0] = weaponids[num_weapons]; + } + weaponactive = -1; + } + } + + if (animTarget == knifeslashstartanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (hasvictim) + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 4.5 &&/*Animation::animations[victim->animTarget].height!=lowheight&&*/victim->animTarget != dodgebackanim && victim->animTarget != rollanim) { + escapednum = 0; + if (tutoriallevel != 1) + victim->DoBloodBig(1.5 / victim->armorhigh, 225); + + award_bonus(id, Slicebonus); + if (tutoriallevel != 1) { + emit_sound_at(knifeslicesound, victim->coords); + } + //victim->jointVel(abdomen)+=relative*damagemult*200; + if (Animation::animations[victim->animTarget].attack && (victim->aitype != playercontrolled || victim->animTarget == knifeslashstartanim) && (victim->creature == rabbittype || victim->deathbleeding <= 0)) { + if (victim->id != 0 || difficulty == 2) { + victim->frameTarget = 0; + victim->animTarget = staggerbackhardanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + } + } + victim->lowreversaldelay = 0; + victim->highreversaldelay = 0; + if (aitype != playercontrolled) + weaponmissdelay = .6; + + if (tutoriallevel != 1) + if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) + weapons[weaponids[weaponactive]].bloody = 1; + if (tutoriallevel != 1) + weapons[weaponids[weaponactive]].blooddrip += 3; + + XYZ footvel, footpoint; + footvel = 0; + if (skeleton.free) { + footpoint = (victim->jointPos(abdomen) + victim->jointPos(neck)) / 2 * victim->scale + victim->coords; + } else { + footpoint = DoRotation((victim->jointPos(abdomen) + victim->jointPos(neck)) / 2, 0, victim->yaw, 0) * victim->scale + victim->coords; + } + if (tutoriallevel != 1) { + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .6, .3); + footvel = DoRotation(facing, 0, 90, 0) * .8; + //footvel.y-=.3; + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .2, 1); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .2, 1); + } + if (tutoriallevel == 1) { + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .6, .3); + } + victim->DoDamage(damagemult * 0); + } + } + if (animTarget == swordslashanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim) { + if (victim->weaponactive == -1 || normaldotproduct(victim->facing, victim->coords - coords) > 0 || (Random() % 2 == 0)) { + award_bonus(id, Slashbonus); + escapednum = 0; + if (tutoriallevel != 1) { + if (normaldotproduct(victim->facing, victim->coords - coords) < 0) + victim->DoBloodBig(2 / victim->armorhigh, 190); + else + victim->DoBloodBig(2 / victim->armorhigh, 185); + victim->deathbleeding = 1; + emit_sound_at(swordslicesound, victim->coords); + } + //victim->jointVel(abdomen)+=relative*damagemult*200; + if (tutoriallevel != 1) { + victim->frameTarget = 0; + victim->animTarget = staggerbackhardanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + } + + if (tutoriallevel != 1) { + if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) + weapons[weaponids[weaponactive]].bloody = 1; + weapons[weaponids[weaponactive]].blooddrip += 3; + + float bloodlossamount; + bloodlossamount = 200 + abs((float)(Random() % 40)) - 20; + victim->bloodloss += bloodlossamount / victim->armorhigh; + //victim->bloodloss+=100*(6.5-distsq(&coords,&victim->coords)); + victim->DoDamage(damagemult * 0); + + XYZ footvel, footpoint; + footvel = 0; + if (skeleton.free) { + footpoint = (victim->jointPos(abdomen) + victim->jointPos(neck)) / 2 * victim->scale + victim->coords; + } else { + footpoint = DoRotation((victim->jointPos(abdomen) + victim->jointPos(neck)) / 2, 0, victim->yaw, 0) * victim->scale + victim->coords; + } + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); + footvel = DoRotation(facing, 0, 90, 0) * .8; + footvel.y -= .3; + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); + } + } else { + if (victim->weaponactive != -1) { + if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { + if (weapons[victim->weaponids[0]].getType() == staff) + weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + if (weapons[weaponids[0]].getType() == staff) + weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; + + emit_sound_at(swordstaffsound, victim->coords); + } else { + emit_sound_at(metalhitsound, victim->coords); + } + } + + + XYZ aim; + victim->Puff(righthand); + victim->target = 0; + victim->frameTarget = 0; + victim->animTarget = staggerbackhighanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + aim = DoRotation(facing, 0, 90, 0) * 21; + aim.y += 7; + weapons[victim->weaponids[0]].drop(aim * -.2, aim); + victim->num_weapons--; + if (victim->num_weapons) { + victim->weaponids[0] = victim->weaponids[num_weapons]; + if (victim->weaponstuck == victim->num_weapons) + victim->weaponstuck = 0; + } + victim->weaponactive = -1; + for (unsigned i = 0; i < Person::players.size(); i++) { + Person::players[i]->wentforweapon = 0; + } + + } + } + } + + if (animTarget == staffhitanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim && victim->animTarget != sweepanim) { + if (tutoriallevel != 1) { + weapons[weaponids[0]].damage += .4 + float(abs(Random() % 100) - 50) / 250; + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2 || creature == wolftype) { + victim->spurt = 1; + } + emit_sound_at(staffheadsound, victim->coords); + } + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, 90, 0); + relative.y -= 1; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 60; + } + victim->jointVel(head) += relative * damagemult * 230; + victim->jointVel(neck) += relative * damagemult * 230; + victim->Puff(head); + if (tutoriallevel != 1) { + victim->DoDamage(damagemult * 120 / victim->protectionhigh); + + award_bonus(id, solidhit, 30); + } + } + } + + if (animTarget == staffspinhitanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim && victim->animTarget != sweepanim) { + if (tutoriallevel != 1) { + weapons[weaponids[0]].damage += .6 + float(abs(Random() % 100) - 50) / 250; + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2 || creature == wolftype) { + victim->spurt = 1; + } + emit_sound_at(staffheadsound, victim->coords); + } + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, -90, 0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->jointVel(head) += relative * damagemult * 220; + victim->jointVel(neck) += relative * damagemult * 220; + victim->Puff(head); + if (tutoriallevel != 1) { + victim->DoDamage(damagemult * 350 / victim->protectionhead); + + award_bonus(id, solidhit, 60); + } + } + } + + if (animTarget == staffgroundsmashanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5) { + escapednum = 0; + if (tutoriallevel != 1) { + if (!victim->dead) + weapons[weaponids[0]].damage += .4 + float(abs(Random() % 100) - 50) / 500; + if (id == 0) + camerashake += .4; + if (Random() % 2 || creature == wolftype) { + victim->spurt = 1; + } + emit_sound_at(staffbodysound, victim->coords); + } + victim->skeleton.longdead = 0; + victim->skeleton.free = 1; + victim->skeleton.broken = 0; + + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velchange = 0; + victim->skeleton.joints[i].locked = 0; + //victim->skeleton.joints[i].velocity=0; + } + + victim->RagDoll(0); + XYZ relative; + relative = 0; + relative.y = -1; + Normalise(&relative); + if (!victim->dead) { + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity = relative * damagemult * 40; + } + victim->jointVel(abdomen) += relative * damagemult * 40; + } + if (victim->dead) { + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity = relative * damagemult * abs(Random() % 20); + } + } + victim->Puff(abdomen); + if (tutoriallevel != 1) { + victim->DoDamage(damagemult * 100 / victim->protectionhigh); + + if (!victim->dead) { + award_bonus(id, solidhit, 40); + } + } + } + } + + if (animTarget == lowkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != highheight) { + escapednum = 0; + if (id == 0) + camerashake += .4; + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + + SolidHitBonus(id); + + if (Animation::animations[victim->animTarget].height == lowheight) { + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 250); + } + victim->RagDoll(0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->jointVel(head) += relative * damagemult * 200; + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 128.); + } + victim->Puff(head); + victim->DoDamage(damagemult * 100 / victim->protectionhead); + if (victim->howactive == typesleeping) + victim->DoDamage(damagemult * 150 / victim->protectionhead); + if (creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhead, 175); + } + } else { + if (victim->damage >= victim->damagetolerance) + victim->RagDoll(0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 10; + } + victim->jointVel(abdomen) += relative * damagemult * 200; + victim->frameTarget = 0; + victim->animTarget = staggerbackhighanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + if (tutoriallevel != 1) { + emit_sound_at(landsound2, victim->coords, 128.); + } + victim->Puff(abdomen); + victim->DoDamage(damagemult * 30 / victim->protectionhigh); + if (creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhigh, 170); + } + } + + } + } + + if (animTarget == sweepanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if ((victim->animTarget != jumpupanim) && + (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) && + (victim != this->shared_from_this())) { + escapednum = 0; + if (id == 0) + camerashake += .2; + if (tutoriallevel != 1) { + emit_sound_at(landsound2, victim->coords, 128.); + } + XYZ relative; + relative = victim->coords - coords; + relative.y = 0; + Normalise(&relative); + + if (Animation::animations[victim->animTarget].height == middleheight || Animation::animations[victim->animCurrent].height == middleheight || victim->damage >= victim->damagetolerance - 40) { + victim->RagDoll(0); + + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 15; + } + relative = DoRotation(relative, 0, -90, 0); + relative.y += .1; + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + if (victim->skeleton.joints[i].label == leftfoot || victim->skeleton.joints[i].label == rightfoot || victim->skeleton.joints[i].label == leftankle || victim->skeleton.joints[i].label == rightankle) + victim->skeleton.joints[i].velocity = relative * 80; + } + victim->Puff(rightankle); + victim->Puff(leftankle); + victim->DoDamage(damagemult * 40 / victim->protectionlow); + } else { + if (victim->damage >= victim->damagetolerance) + victim->RagDoll(0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 10; + } + relative = DoRotation(relative, 0, -90, 0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + if (victim->skeleton.joints[i].label == leftfoot || victim->skeleton.joints[i].label == rightfoot || victim->skeleton.joints[i].label == leftankle || victim->skeleton.joints[i].label == rightankle) + victim->skeleton.joints[i].velocity += relative * damagemult * 80; + } + victim->jointVel(abdomen) += relative * damagemult * 200; + victim->frameTarget = 0; + victim->animTarget = staggerbackhighanim; + victim->targetyaw = targetyaw + 180; + victim->target = 0; + if (tutoriallevel != 1) { + emit_sound_at(landsound2, victim->coords, 128.); + } + victim->Puff(abdomen); + victim->DoDamage(damagemult * 30 / victim->protectionlow); + } + + SolidHitBonus(id); + + } + } + } + if (Animation::animations[animTarget].attack == reversal && (!victim->feint || (victim->lastattack == victim->lastattack2 && victim->lastattack2 == victim->lastattack3 && Random() % 2) || animTarget == knifefollowanim)) { + if (animTarget == spinkickreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 230); + } + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 128.); + } + if (creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhigh, 170); + } + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - oldcoords; + relative.y = 0; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->jointVel(abdomen) += relative * damagemult * 200; + victim->Puff(abdomen); + victim->DoDamage(damagemult * 150 / victim->protectionhigh); + + award_bonus(id, Reversal); + } + + if ((animTarget == swordslashreversalanim || animTarget == knifeslashreversalanim || animTarget == staffhitreversalanim || animTarget == staffspinhitreversalanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (victim->weaponactive != -1 && victim->num_weapons > 0) { + if (weapons[victim->weaponids[victim->weaponactive]].owner == int(victim->id)) { + takeWeapon(victim->weaponids[victim->weaponactive]); + victim->num_weapons--; + if (victim->num_weapons > 0) { + victim->weaponids[victim->weaponactive] = victim->weaponids[victim->num_weapons]; + } + victim->weaponactive = -1; + } + } + } + + if (animTarget == staffhitreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 230); + } + emit_sound_at(whooshhitsound, victim->coords, 128.); + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - oldcoords; + relative.y = 0; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 30; + } + victim->jointVel(abdomen) += relative * damagemult * 200; + victim->Puff(head); + victim->DoDamage(damagemult * 70 / victim->protectionhigh); + } + + if (animTarget == staffspinhitreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 230); + } + + award_bonus(id, staffreversebonus); + + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 128.); + } + victim->RagDoll(0); + award_bonus(id, staffreversebonus); // Huh, again? + + XYZ relative; + relative = victim->coords - oldcoords; + relative.y = 0; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 30; + } + victim->jointVel(abdomen) += relative * damagemult * 200; + victim->Puff(head); + victim->DoDamage(damagemult * 70 / victim->protectionhigh); + } + + if (animTarget == upunchreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + escapednum = 0; + victim->RagDoll(1); + XYZ relative; + relative = facing; + relative.y = 0; + Normalise(&relative); + relative.y -= .1; + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 70; + } + victim->jointVel(lefthand) *= .1; + victim->jointVel(leftwrist) *= .2; + victim->jointVel(leftelbow) *= .5; + victim->jointVel(leftshoulder) *= .7; + victim->jointVel(righthand) *= .1; + victim->jointVel(rightwrist) *= .2; + victim->jointVel(rightelbow) *= .5; + victim->jointVel(rightshoulder) *= .7; + + victim->Puff(abdomen); + victim->DoDamage(damagemult * 90 / victim->protectionhigh); + + award_bonus(id, Reversal); + + bool doslice; + doslice = 0; + if (weaponactive != -1 || creature == wolftype) + doslice = 1; + if (creature == rabbittype && weaponactive != -1) + if (weapons[weaponids[0]].getType() == staff) + doslice = 0; + if (doslice) { + if (weaponactive != -1) { + victim->DoBloodBig(2 / victim->armorhigh, 225); + emit_sound_at(knifeslicesound, victim->coords); + if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) + weapons[weaponids[weaponactive]].bloody = 1; + weapons[weaponids[weaponactive]].blooddrip += 3; + } + if (weaponactive == -1 && creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhigh, 175); + } + } + } + + + + if (animTarget == swordslashreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + escapednum = 0; + victim->RagDoll(1); + XYZ relative; + relative = facing; + relative.y = 0; + Normalise(&relative); + relative.y -= .1; + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 70; + } + victim->jointVel(lefthand) *= .1 - 1; + victim->jointVel(leftwrist) *= .2 - 1; + victim->jointVel(leftelbow) *= .5 - 1; + victim->jointVel(leftshoulder) *= .7 - 1; + victim->jointVel(righthand) *= .1 - 1; + victim->jointVel(rightwrist) *= .2 - 1; + victim->jointVel(rightelbow) *= .5 - 1; + victim->jointVel(rightshoulder) *= .7 - 1; + + award_bonus(id, swordreversebonus); + } + + if (hasvictim && animTarget == knifeslashreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 230); + } + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 128.); + } + victim->RagDoll(0); + XYZ relative; + relative = victim->coords - oldcoords; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, -90, 0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->jointVel(abdomen) += relative * damagemult * 200; + victim->Puff(abdomen); + victim->DoDamage(damagemult * 30 / victim->protectionhigh); + + award_bonus(id, Reversal); + } + + if (hasvictim && animTarget == sneakattackanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + escapednum = 0; + victim->RagDoll(0); + victim->skeleton.spinny = 0; + XYZ relative; + relative = facing * -1; + relative.y = -3; + Normalise(&relative); + if (victim->id == 0) + relative /= 30; + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 40; + } + victim->damage = victim->damagetolerance; + victim->permanentdamage = victim->damagetolerance - 1; + bool doslice; + doslice = 0; + if (weaponactive != -1 || creature == wolftype) + doslice = 1; + if (creature == rabbittype && weaponactive != -1) + if (weapons[weaponids[0]].getType() == staff) + doslice = 0; + if (doslice) { + if (weaponactive != -1) { + victim->DoBloodBig(200, 225); + emit_sound_at(knifeslicesound, victim->coords); + if (bloodtoggle) + weapons[weaponids[weaponactive]].bloody = 2; + weapons[weaponids[weaponactive]].blooddrip += 5; + } + + if (creature == wolftype && weaponactive == -1) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2, 175); + } + } + award_bonus(id, spinecrusher); + } + + if (hasvictim && (animTarget == knifefollowanim || animTarget == knifesneakattackanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (weaponactive != -1 && victim->bloodloss < victim->damagetolerance) { + escapednum = 0; + if (animTarget == knifefollowanim) + victim->DoBloodBig(200, 210); + if (animTarget == knifesneakattackanim) { + XYZ footvel, footpoint; + footvel = 0; + footpoint = weapons[weaponids[0]].tippoint; + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); + footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); + victim->DoBloodBig(200, 195); + award_bonus(id, tracheotomy); + } + if (animTarget == knifefollowanim) { + award_bonus(id, Stabbonus); + XYZ footvel, footpoint; + footvel = 0; + footpoint = weapons[weaponids[0]].tippoint; + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); + footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position) * -1; + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .2, 1); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .2, 1); + + } + victim->bloodloss += 10000; + victim->velocity = 0; + emit_sound_at(fleshstabsound, victim->coords); + if (bloodtoggle) + weapons[weaponids[weaponactive]].bloody = 2; + weapons[weaponids[weaponactive]].blooddrip += 5; + } + } + + if (hasvictim && (animTarget == knifefollowanim || animTarget == knifesneakattackanim) && Animation::animations[animTarget].frames[frameCurrent].label == 6) { + escapednum = 0; + victim->velocity = 0; + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity = 0; + } + if (animTarget == knifefollowanim) { + victim->RagDoll(0); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity = 0; + } + } + if (weaponactive != -1 && Animation::animations[victim->animTarget].attack != reversal) { + emit_sound_at(fleshstabremovesound, victim->coords); + if (bloodtoggle) + weapons[weaponids[weaponactive]].bloody = 2; + weapons[weaponids[weaponactive]].blooddrip += 5; + + XYZ footvel, footpoint; + footvel = 0; + footpoint = weapons[weaponids[0]].tippoint; + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); + footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position) * -1; + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); + } + } + + if (hasvictim && (animTarget == swordsneakattackanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { + if (weaponactive != -1 && victim->bloodloss < victim->damagetolerance) { + award_bonus(id, backstab); + + escapednum = 0; + + XYZ footvel, footpoint; + footvel = 0; + footpoint = (weapons[weaponids[0]].tippoint + weapons[weaponids[0]].position) / 2; + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); + footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodflamesprite, footpoint, DoRotation(footvel * 5, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .3, 1); + Sprite::MakeSprite(bloodflamesprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .3, 1); + victim->DoBloodBig(200, 180); + victim->DoBloodBig(200, 215); + victim->bloodloss += 10000; + victim->velocity = 0; + emit_sound_at(fleshstabsound, victim->coords); + if (bloodtoggle) + weapons[weaponids[weaponactive]].bloody = 2; + weapons[weaponids[weaponactive]].blooddrip += 5; + } + } + + if (hasvictim && animTarget == swordsneakattackanim && Animation::animations[animTarget].frames[frameCurrent].label == 6) { + escapednum = 0; + victim->velocity = 0; + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity = 0; + } + if (weaponactive != -1) { + emit_sound_at(fleshstabremovesound, victim->coords); + if (bloodtoggle) + weapons[weaponids[weaponactive]].bloody = 2; + weapons[weaponids[weaponactive]].blooddrip += 5; + + XYZ footvel, footpoint; + footvel = 0; + footpoint = weapons[weaponids[0]].tippoint; + if (bloodtoggle) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); + footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position) * -1; + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); + } + } + + if (animTarget == sweepreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { + escapednum = 0; + if (id == 0) + camerashake += .4; + if (Random() % 2) { + victim->spurt = 1; + DoBlood(.2, 240); + } + if (weaponactive == -1) { + if (tutoriallevel != 1) { + emit_sound_at(heavyimpactsound, victim->coords, 128.); + } + } + bool doslice; + doslice = 0; + if (weaponactive != -1 || creature == wolftype) + doslice = 1; + if (creature == rabbittype && weaponactive != -1) + if (weapons[weaponids[0]].getType() == staff) + doslice = 0; + if (doslice) { + if (weaponactive != -1) { + victim->DoBloodBig(2 / victim->armorhead, 225); + emit_sound_at(knifeslicesound, victim->coords); + if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) + weapons[weaponids[weaponactive]].bloody = 1; + weapons[weaponids[weaponactive]].blooddrip += 3; + } + if (weaponactive == -1 && creature == wolftype) { + emit_sound_at(clawslicesound, victim->coords, 128.); + victim->spurt = 1; + victim->DoBloodBig(2 / victim->armorhead, 175); + } + } + + award_bonus(id, Reversal); + + victim->Puff(neck); + + XYZ relative; + relative = facing * -1; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, 90, 0); + relative.y = .5; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 20; + } + victim->jointVel(head) += relative * damagemult * 200; + if (victim->damage < victim->damagetolerance - 100) + victim->velocity = relative * 200; + victim->DoDamage(damagemult * 100 / victim->protectionhead); + victim->velocity = 0; + } + + if (animTarget == sweepreversalanim && ((Animation::animations[animTarget].frames[frameCurrent].label == 9 && victim->damage < victim->damagetolerance) || (Animation::animations[animTarget].frames[frameCurrent].label == 7 && victim->damage > victim->damagetolerance))) { + escapednum = 0; + victim->RagDoll(0); + XYZ relative; + relative = facing * -1; + relative.y = 0; + Normalise(&relative); + relative = DoRotation(relative, 0, 90, 0); + relative.y = .5; + Normalise(&relative); + for (int i = 0; i < victim->skeleton.joints.size(); i++) { + victim->skeleton.joints[i].velocity += relative * damagemult * 20; + } + victim->jointVel(head) += relative * damagemult * 200; + } + + if (hasvictim && (animTarget == spinkickreversalanim || animTarget == sweepreversalanim || animTarget == rabbitkickreversalanim || animTarget == upunchreversalanim || animTarget == jumpreversalanim || animTarget == swordslashreversalanim || animTarget == knifeslashreversalanim || animTarget == rabbittacklereversal || animTarget == wolftacklereversal || animTarget == staffhitreversalanim || animTarget == staffspinhitreversalanim)) + if (victim->damage > victim->damagetolerance && bonus != reverseko) { + award_bonus(id, reverseko); + } + } + + + //Animation end + if (frameTarget > Animation::animations[animCurrent].frames.size() - 1) { + frameTarget = 0; + if (wasStop()) { + animTarget = getIdle(); + FootLand(leftfoot, 1); + FootLand(rightfoot, 1); + } + if (animCurrent == rabbittackleanim || animCurrent == rabbittacklinganim) { + animTarget = rollanim; + frameTarget = 3; + emit_sound_at(movewhooshsound, coords, 128.); + } + if (animCurrent == staggerbackhighanim) { + animTarget = getIdle(); + } + if (animCurrent == staggerbackhardanim) { + animTarget = getIdle(); + } + if (animCurrent == removeknifeanim) { + animTarget = getIdle(); + } + if (animCurrent == crouchremoveknifeanim) { + animTarget = getCrouch(); + } + if (animCurrent == backhandspringanim) { + animTarget = getIdle(); + } + if (animCurrent == dodgebackanim) { + animTarget = getIdle(); + } + if (animCurrent == drawleftanim) { + animTarget = getIdle(); + } + if (animCurrent == drawrightanim || animCurrent == crouchdrawrightanim) { + animTarget = getIdle(); + if (animCurrent == crouchdrawrightanim) { + animTarget = getCrouch(); + } + if (weaponactive == -1) + weaponactive = 0; + else if (weaponactive == 0) { + weaponactive = -1; + if (num_weapons == 2) { + int buffer; + buffer = weaponids[0]; + weaponids[0] = weaponids[1]; + weaponids[1] = buffer; + } + } + + if (weaponactive == -1) { + emit_sound_at(knifesheathesound, coords, 128.); + } + if (weaponactive != -1) { + emit_sound_at(knifedrawsound, coords, 128.); + } + } + if (animCurrent == rollanim) { + animTarget = getCrouch(); + FootLand(leftfoot, 1); + FootLand(rightfoot, 1); + } + if (isFlip()) { + if (animTarget == walljumprightkickanim) { + targetrot = -190; + } + if (animTarget == walljumpleftkickanim) { + targetrot = 190; + } + animTarget = jumpdownanim; + } + if (animCurrent == climbanim) { + animTarget = getCrouch(); + frameTarget = 1; + coords += facing * .1; + if (!isnormal(coords.x)) + coords = oldcoords; + oldcoords = coords; + collided = 0; + targetoffset = 0; + currentoffset = 0; + grabdelay = 1; + velocity = 0; + collided = 0; + avoidcollided = 0; + } + if (animTarget == rabbitkickreversalanim) { + animTarget = getCrouch(); + lastfeint = 0; + } + if (animTarget == jumpreversalanim) { + animTarget = getCrouch(); + lastfeint = 0; + } + if (animTarget == walljumprightanim || animTarget == walljumpbackanim || animTarget == walljumpfrontanim) { + if (attackkeydown && animTarget != walljumpfrontanim) { + int closest = -1; + float closestdist = -1; + float distance; + if (Person::players.size() > 1) + for (unsigned i = 0; i < Person::players.size(); i++) { + if (id != i && Person::players[i]->coords.y < coords.y && !Person::players[i]->skeleton.free) { + distance = distsq(&Person::players[i]->coords, &coords); + if (closestdist == -1 || distance < closestdist) { + closestdist = distance; + closest = i; + } + } + } + if (closestdist > 0 && closest >= 0 && closestdist < 16) { + victim = Person::players[closest]; + animTarget = walljumprightkickanim; + frameTarget = 0; + XYZ rotatetarget = victim->coords - coords; + Normalise(&rotatetarget); + yaw = -asin(0 - rotatetarget.x); + yaw *= 360 / 6.28; + if (rotatetarget.z < 0) + yaw = 180 - yaw; + targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; + velocity = (victim->coords - coords) * 4; + velocity.y += 2; + transspeed = 40; + } + } + if (animTarget == walljumpbackanim) { + animTarget = backflipanim; + frameTarget = 3; + velocity = facing * -8; + velocity.y = 4; + if (id == 0) + resume_stream(whooshsound); + } + if (animTarget == walljumprightanim) { + animTarget = rightflipanim; + frameTarget = 4; + targetyaw -= 90; + yaw -= 90; + velocity = DoRotation(facing, 0, 30, 0) * -8; + velocity.y = 4; + } + if (animTarget == walljumpfrontanim) { + animTarget = frontflipanim; + frameTarget = 2; + //targetyaw-=180; + ////yaw-=180; + velocity = facing * 8; + velocity.y = 4; + } + if (id == 0) + resume_stream(whooshsound); + } + if (animTarget == walljumpleftanim) { + if (attackkeydown) { + int closest = -1; + float closestdist = -1; + float distance; + if (Person::players.size() > 1) + for (unsigned i = 0; i < Person::players.size(); i++) { + if (id != i && Person::players[i]->coords.y < coords.y && !Person::players[i]->skeleton.free) { + distance = distsq(&Person::players[i]->coords, &coords); + if (closestdist == -1 || distance < closestdist) { + closestdist = distance; + closest = i; + } + } + } + if (closestdist > 0 && closest >= 0 && closestdist < 16) { + victim = Person::players[closest]; + animTarget = walljumpleftkickanim; + frameTarget = 0; + XYZ rotatetarget = victim->coords - coords; + Normalise(&rotatetarget); + yaw = -asin(0 - rotatetarget.x); + yaw *= 360 / 6.28; + if (rotatetarget.z < 0) + yaw = 180 - yaw; + targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; + velocity = (victim->coords - coords) * 4; + velocity.y += 2; + transspeed = 40; + } + } + if (animTarget != walljumpleftkickanim) { + animTarget = leftflipanim; + frameTarget = 4; + targetyaw += 90; + yaw += 90; + velocity = DoRotation(facing, 0, -30, 0) * -8; + velocity.y = 4; + } + if (id == 0) + resume_stream(whooshsound); + } + if (animTarget == sneakattackanim) { + animCurrent = getCrouch(); + animTarget = getCrouch(); + frameTarget = 1; + frameCurrent = 0; + targetyaw += 180; + yaw += 180; + targettilt2 *= -1; + tilt2 *= -1; + transspeed = 1000000; + targetheadyaw += 180; + coords -= facing * .7; + if (onterrain) + coords.y = terrain.getHeight(coords.x, coords.z); + + lastfeint = 0; + } + if (animTarget == knifesneakattackanim || animTarget == swordsneakattackanim) { + animTarget = getIdle(); + frameTarget = 0; + if (onterrain) + coords.y = terrain.getHeight(coords.x, coords.z); + + lastfeint = 0; + } + if (animCurrent == knifefollowanim) { + animTarget = getIdle(); + lastfeint = 0; + } + if (Animation::animations[animTarget].attack == reversal && animCurrent != sneakattackanim && animCurrent != knifesneakattackanim && animCurrent != swordsneakattackanim && animCurrent != knifefollowanim) { + float ycoords = oldcoords.y; + animTarget = getStop(); + targetyaw += 180; + yaw += 180; + targettilt2 *= -1; + tilt2 *= -1; + transspeed = 1000000; + targetheadyaw += 180; + if (!isnormal(coords.x)) + coords = oldcoords; + if (animCurrent == spinkickreversalanim || animCurrent == swordslashreversalanim) + oldcoords = coords + facing * .5; + else if (animCurrent == sweepreversalanim) + oldcoords = coords + facing * 1.1; + else if (animCurrent == upunchreversalanim) { + oldcoords = coords + facing * 1.5; + targetyaw += 180; + yaw += 180; + targetheadyaw += 180; + targettilt2 *= -1; + tilt2 *= -1; + } else if (animCurrent == knifeslashreversalanim) { + oldcoords = coords + facing * .5; + targetyaw += 90; + yaw += 90; + targetheadyaw += 90; + targettilt2 = 0; + tilt2 = 0; + } else if (animCurrent == staffspinhitreversalanim) { + targetyaw += 180; + yaw += 180; + targetheadyaw += 180; + targettilt2 = 0; + tilt2 = 0; + } + if (onterrain) + oldcoords.y = terrain.getHeight(oldcoords.x, oldcoords.z); + else + oldcoords.y = ycoords; + currentoffset = coords - oldcoords; + targetoffset = 0; + coords = oldcoords; + + lastfeint = 0; + } + if (animCurrent == knifesneakattackedanim || animCurrent == swordsneakattackedanim) { + velocity = 0; + velocity.y = -5; + RagDoll(0); + } + if (Animation::animations[animTarget].attack == reversed) { + escapednum++; + if (animTarget == sweepreversedanim) + targetyaw += 90; + animTarget = backhandspringanim; + frameTarget = 2; + emit_sound_at(landsound, coords, 128); + + if (animCurrent == upunchreversedanim || animCurrent == swordslashreversedanim) { + animTarget = rollanim; + frameTarget = 5; + oldcoords = coords; + coords += (DoRotation(jointPos(leftfoot), 0, yaw, 0) + DoRotation(jointPos(rightfoot), 0, yaw, 0)) / 2 * scale; + coords.y = oldcoords.y; + } + if (animCurrent == knifeslashreversedanim) { + animTarget = rollanim; + frameTarget = 0; + targetyaw += 90; + yaw += 90; + oldcoords = coords; + coords += (DoRotation(jointPos(leftfoot), 0, yaw, 0) + DoRotation(jointPos(rightfoot), 0, yaw, 0)) / 2 * scale; + coords.y = oldcoords.y; + } + } + if (wasFlip()) { + animTarget = jumpdownanim; + } + if (wasLanding()) + animTarget = getIdle(); + if (wasLandhard()) + animTarget = getIdle(); + if (animCurrent == spinkickanim || animCurrent == getupfrombackanim || animCurrent == getupfromfrontanim || animCurrent == lowkickanim) { + animTarget = getIdle(); + oldcoords = coords; + coords += (DoRotation(jointPos(leftfoot), 0, yaw, 0) + DoRotation(jointPos(rightfoot), 0, yaw, 0)) / 2 * scale; + coords.y = oldcoords.y; + //coords+=DoRotation(Animation::animations[animCurrent].offset,0,yaw,0)*scale; + targetoffset.y = coords.y; + if (onterrain) + targetoffset.y = terrain.getHeight(coords.x, coords.z); + currentoffset = DoRotation(Animation::animations[animCurrent].offset * -1, 0, yaw, 0) * scale; + currentoffset.y -= (coords.y - targetoffset.y); + coords.y = targetoffset.y; + targetoffset = 0; + normalsupdatedelay = 0; + } + if (animCurrent == upunchanim) { + animTarget = getStop(); + normalsupdatedelay = 0; + lastfeint = 0; + } + if (animCurrent == rabbitkickanim && animTarget != backflipanim) { + targetyaw = yaw; + bool hasstaff; + hasstaff = 0; + if (num_weapons > 0) + if (weapons[0].getType() == staff) + hasstaff = 1; + if (!hasstaff) + DoDamage(35); + RagDoll(0); + lastfeint = 0; + rabbitkickragdoll = 1; + } + if (animCurrent == rabbitkickreversedanim) { + if (!feint) { + velocity = 0; + velocity.y = -10; + //DoDamage(100); + RagDoll(0); + skeleton.spinny = 0; + SolidHitBonus(!id); // FIXME: tricky id + } + if (feint) { + escapednum++; + animTarget = rollanim; + coords += facing; + if (id == 0) + pause_sound(whooshsound); + } + lastfeint = 0; + } + if (animCurrent == rabbittackledbackanim || animCurrent == rabbittackledfrontanim) { + velocity = 0; + velocity.y = -10; + RagDoll(0); + skeleton.spinny = 0; + } + if (animCurrent == jumpreversedanim) { + if (!feint) { + velocity = 0; + velocity.y = -10; + //DoDamage(100); + RagDoll(0); + skeleton.spinny = 0; + SolidHitBonus(!id); // FIXME: tricky id + } + if (feint) { + escapednum++; + animTarget = rollanim; + coords += facing * 2; + if (id == 0) + pause_sound(whooshsound); + } + lastfeint = 0; + } + + if (Animation::animations[animCurrent].attack == normalattack && !victim->skeleton.free && victim->animTarget != staggerbackhighanim && victim->animTarget != staggerbackhardanim && animTarget != winduppunchblockedanim && animTarget != blockhighleftanim && animTarget != swordslashparryanim && animTarget != swordslashparriedanim && animTarget != crouchstabanim && animTarget != swordgroundstabanim) { + animTarget = getupfromfrontanim; + lastfeint = 0; + } else if (Animation::animations[animCurrent].attack == normalattack) { + animTarget = getIdle(); + lastfeint = 0; + } + if (animCurrent == blockhighleftanim && aitype != playercontrolled) { + animTarget = blockhighleftstrikeanim; + } + if (animCurrent == knifeslashstartanim || animCurrent == knifethrowanim || animCurrent == swordslashanim || animCurrent == staffhitanim || animCurrent == staffgroundsmashanim || animCurrent == staffspinhitanim) { + animTarget = getIdle(); + lastfeint = 0; + } + if (animCurrent == spinkickanim && victim->skeleton.free) { + if (creature == rabbittype) + animTarget = fightidleanim; + } + } + target = 0; + + if (isIdle() && !wasIdle()) + normalsupdatedelay = 0; + + if (animCurrent == jumpupanim && velocity.y < 0 && !isFlip()) { + animTarget = jumpdownanim; + } + } + if (!skeleton.free) { + oldtarget = target; + if (!transspeed && Animation::animations[animTarget].attack != 2 && Animation::animations[animTarget].attack != 3) { + if (!isRun() || !wasRun()) { + if (targetFrame().speed > currentFrame().speed) + target += multiplier * targetFrame().speed * speed * 2; + if (targetFrame().speed <= currentFrame().speed) + target += multiplier * currentFrame().speed * speed * 2; + } + if (isRun() && wasRun()) { + float tempspeed; + tempspeed = velspeed; + if (tempspeed < 10 * speedmult) + tempspeed = 10 * speedmult; + /* FIXME - mixed of target and current here, is that intended? */ + target += multiplier * Animation::animations[animTarget].frames[frameCurrent].speed * speed * 1.7 * tempspeed / (speed * 45 * scale); + } + } else if (transspeed) + target += multiplier * transspeed * speed * 2; + else { + if (!isRun() || !wasRun()) { + if (targetFrame().speed > currentFrame().speed) + target += multiplier * targetFrame().speed * 2; + if (targetFrame().speed <= currentFrame().speed) + target += multiplier * currentFrame().speed * 2; + } + } + + if (animCurrent != animTarget) + target = (target + oldtarget) / 2; + + if (target > 1) { + frameCurrent = frameTarget; + target = 1; + } + oldrot = rot; + rot = targetrot * target; + yaw += rot - oldrot; + if (target == 1) { + rot = 0; + oldrot = 0; + targetrot = 0; + } + if (frameCurrent >= Animation::animations[animCurrent].frames.size()) { + frameCurrent = Animation::animations[animCurrent].frames.size() - 1; + } + if (animCurrent != oldanimCurrent || animTarget != oldanimTarget || ((frameCurrent != oldframeCurrent || frameTarget != oldframeTarget) && !calcrot)) { + //Old rotates + for (int i = 0; i < skeleton.joints.size(); i++) { + skeleton.joints[i].position = currentFrame().joints[i].position; + } + + skeleton.FindForwards(); + + for (int i = 0; i < skeleton.muscles.size(); i++) { + if (skeleton.muscles[i].visible) { + skeleton.FindRotationMuscle(i, animTarget); + } + } + for (int i = 0; i < skeleton.muscles.size(); i++) { + if (skeleton.muscles[i].visible) { + if (isnormal((float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100)) + skeleton.muscles[i].oldrotate1 = (float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100; + if (isnormal((float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100)) + skeleton.muscles[i].oldrotate2 = (float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100; + if (isnormal((float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100)) + skeleton.muscles[i].oldrotate3 = (float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100; + } + } + + //New rotates + for (int i = 0; i < skeleton.joints.size(); i++) { + skeleton.joints[i].position = targetFrame().joints[i].position; + } + + skeleton.FindForwards(); + + for (int i = 0; i < skeleton.muscles.size(); i++) { + if (skeleton.muscles[i].visible) { + skeleton.FindRotationMuscle(i, animTarget); + } + } + for (int i = 0; i < skeleton.muscles.size(); i++) { + if (skeleton.muscles[i].visible) { + if (isnormal((float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100)) + skeleton.muscles[i].newrotate1 = (float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100; + if (isnormal((float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100)) + skeleton.muscles[i].newrotate2 = (float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100; + if (isnormal((float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100)) + skeleton.muscles[i].newrotate3 = (float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100; + if (skeleton.muscles[i].newrotate3 > skeleton.muscles[i].oldrotate3 + 180) skeleton.muscles[i].newrotate3 -= 360; + if (skeleton.muscles[i].newrotate3 < skeleton.muscles[i].oldrotate3 - 180) skeleton.muscles[i].newrotate3 += 360; + if (skeleton.muscles[i].newrotate2 > skeleton.muscles[i].oldrotate2 + 180) skeleton.muscles[i].newrotate2 -= 360; + if (skeleton.muscles[i].newrotate2 < skeleton.muscles[i].oldrotate2 - 180) skeleton.muscles[i].newrotate2 += 360; + if (skeleton.muscles[i].newrotate1 > skeleton.muscles[i].oldrotate1 + 180) skeleton.muscles[i].newrotate1 -= 360; + if (skeleton.muscles[i].newrotate1 < skeleton.muscles[i].oldrotate1 - 180) skeleton.muscles[i].newrotate1 += 360; + } + } + } + + oldanimCurrent = animCurrent; + oldanimTarget = animTarget; + oldframeTarget = frameTarget; + oldframeCurrent = frameCurrent; + + for (int i = 0; i < skeleton.joints.size(); i++) { + skeleton.joints[i].velocity = (currentFrame().joints[i].position * (1 - target) + targetFrame().joints[i].position * (target) - skeleton.joints[i].position) / multiplier; + skeleton.joints[i].position = currentFrame().joints[i].position * (1 - target) + targetFrame().joints[i].position * (target); + } + offset = currentoffset * (1 - target) + targetoffset * target; + for (int i = 0; i < skeleton.muscles.size(); i++) { + if (skeleton.muscles[i].visible) { + skeleton.muscles[i].rotate1 = skeleton.muscles[i].oldrotate1 * (1 - target) + skeleton.muscles[i].newrotate1 * (target); + skeleton.muscles[i].rotate2 = skeleton.muscles[i].oldrotate2 * (1 - target) + skeleton.muscles[i].newrotate2 * (target); + skeleton.muscles[i].rotate3 = skeleton.muscles[i].oldrotate3 * (1 - target) + skeleton.muscles[i].newrotate3 * (target); + } + } + } + + if (isLanding() && landhard) { + if (id == 0) + camerashake += .4; + animTarget = getLandhard(); + frameTarget = 0; + target = 0; + landhard = 0; + transspeed = 15; + } + } +} + +/* EFFECT + * MONSTER + * TODO + */ +void Person::DoStuff() +{ + static XYZ terrainnormal; + static XYZ flatfacing; + static XYZ flatvelocity; + static float flatvelspeed; + static int i, j, l; + static XYZ average; + static int howmany; + static int bloodsize; + static int startx, starty, endx, endy; + static GLubyte color; + static XYZ bloodvel; + + onfiredelay -= multiplier; + if (onfiredelay < 0 && onfire) { + if (Random() % 2 == 0) { + crouchkeydown = 1; + } + onfiredelay = 0.3; + } + + crouchkeydowntime += multiplier; + if (!crouchkeydown) + crouchkeydowntime = 0; + jumpkeydowntime += multiplier; + if (!jumpkeydown && skeleton.free) + jumpkeydowntime = 0; + + if (hostile || damage > 0 || bloodloss > 0) + immobile = 0; + + if (isIdle() || isRun()) + targetoffset = 0; + + if (num_weapons == 1 && weaponactive != -1) + weaponstuck = -1; + + if (id == 0) + blooddimamount -= multiplier * .3; + speechdelay -= multiplier; + texupdatedelay -= multiplier; + interestdelay -= multiplier; + flamedelay -= multiplier; + parriedrecently -= multiplier; + if (!victim) { + victim = this->shared_from_this(); + hasvictim = 0; + } + + if (id == 0) + speed = 1.1 * speedmult; + else + speed = 1.0 * speedmult; + if (!skeleton.free) + rabbitkickragdoll = 0; + + speed *= speedmult; + + if (id != 0 && (creature == rabbittype || difficulty != 2)) + superruntoggle = 0; + if (id != 0 && creature == wolftype && difficulty == 2) { + superruntoggle = 0; + if (aitype != passivetype) { + superruntoggle = 1; + if (aitype == attacktypecutoff && (Person::players[0]->isIdle() || Person::players[0]->isCrouch() || Person::players[0]->skeleton.free || Person::players[0]->animTarget == getupfrombackanim || Person::players[0]->animTarget == getupfromfrontanim || Person::players[0]->animTarget == sneakanim) && distsq(&coords, &Person::players[0]->coords) < 16) { + superruntoggle = 0; + } + } + if (scale < 0.2) + superruntoggle = 0; + if (animTarget == wolfrunninganim && !superruntoggle) { + animTarget = getRun(); + frameTarget = 0; + } + } + if (weaponactive == -1 && num_weapons > 0) { + if (weapons[weaponids[0]].getType() == staff) { + weaponactive = 0; + } + } + + if (onfire) { + burnt += multiplier; + deathbleeding = 1; + if (burnt > .6) + burnt = .6; + OPENAL_SetVolume(channels[stream_firesound], 256 + 256 * findLength(&velocity) / 3); + + if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { + float gLoc[3]; + float vel[3]; + gLoc[0] = coords.x; + gLoc[1] = coords.y; + gLoc[2] = coords.z; + vel[0] = velocity.x; + vel[1] = velocity.y; + vel[2] = velocity.z; + + if (id == 0) { + OPENAL_3D_SetAttributes(channels[whooshsound], gLoc, vel); + OPENAL_SetVolume(channels[whooshsound], 64 * findLength(&velocity) / 5); + } + } + } + while (flamedelay < 0 && onfire) { + flamedelay += .006; + howmany = abs(Random() % (skeleton.joints.size())); + if (skeleton.free) { + flatvelocity = skeleton.joints[howmany].velocity * scale / 2; + flatfacing = skeleton.joints[howmany].position * scale + coords; + } else { + flatfacing = DoRotation(DoRotation(DoRotation(skeleton.joints[howmany].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; + flatvelocity = (coords - oldcoords) / multiplier / 2; + } + Sprite::MakeSprite(flamesprite, flatfacing, flatvelocity, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1); + } + + while (flamedelay < 0 && !onfire && tutoriallevel == 1 && id != 0) { + flamedelay += .05; + howmany = abs(Random() % (skeleton.joints.size())); + if (skeleton.free) { + flatvelocity = skeleton.joints[howmany].velocity * scale / 2; + flatfacing = skeleton.joints[howmany].position * scale + coords; + } else { + flatvelocity = (coords - oldcoords) / multiplier / 2; + flatfacing = DoRotation(DoRotation(DoRotation(skeleton.joints[howmany].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; + } + Sprite::MakeSprite(breathsprite, flatfacing, flatvelocity, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, .3); + } + + if (bleeding > 0) { + bleeding -= multiplier * .3; + if (bloodtoggle == 2) { + skeleton.drawmodel.textureptr.bind(); + if ((bleeding <= 0) && (detail != 2)) + DoMipmaps(); + } + } + + if (neckspurtamount > 0) { + neckspurtamount -= multiplier; + neckspurtdelay -= multiplier * 3; + neckspurtparticledelay -= multiplier * 3; + if (neckspurtparticledelay < 0 && neckspurtdelay > 2) { + spurt = 0; + bloodvel = 0; + if (skeleton.free) { + bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 40, ((float)(Random() % 100)) / 40, 0); + bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 40, yaw + ((float)(Random() % 100)) / 40, 0) * scale; + Sprite::MakeSprite(bloodsprite, (jointPos(neck) + (jointPos(neck) - jointPos(head)) / 5)*scale + coords, bloodvel, 1, 1, 1, .05, .9); + } else { + bloodvel.z = 5 * neckspurtamount; + bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 40, yaw + ((float)(Random() % 100)) / 40, 0) * scale; + bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 40, ((float)(Random() % 100)) / 40, 0) * scale; + Sprite::MakeSprite(bloodsprite, DoRotation(jointPos(neck) + (jointPos(neck) - jointPos(head)) / 5, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, .9); + } + neckspurtparticledelay = .05; + } + if (neckspurtdelay < 0) { + neckspurtdelay = 3; + } + } + + if (deathbleeding > 0 && dead != 2) { + if (deathbleeding < 5) + bleeddelay -= deathbleeding * multiplier / 4; + else + bleeddelay -= 5 * multiplier / 4; + if (bleeddelay < 0 && bloodtoggle) { + bleeddelay = 1; + XYZ bloodvel; + if (bloodtoggle) { + bloodvel = 0; + if (skeleton.free) { + bloodvel += DoRotation(jointVel(abdomen), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, jointPos(abdomen) * scale + coords, bloodvel, 1, 1, 1, .05, 1); + } else { + bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; + Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(abdomen) + jointPos(abdomen)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); + } + } + } + bloodloss += deathbleeding * multiplier * 80; + deathbleeding -= multiplier * 1.6; + if (deathbleeding < 0) + deathbleeding = 0; + if (bloodloss > damagetolerance && Animation::animations[animTarget].attack == neutral) { + if (weaponactive != -1) { + weapons[weaponids[0]].drop(velocity * scale * -.3, velocity * scale); + weapons[weaponids[0]].velocity.x += .01; + num_weapons--; + if (num_weapons) { + weaponids[0] = weaponids[num_weapons]; + if (weaponstuck == num_weapons) + weaponstuck = 0; + } + weaponactive = -1; + for (unsigned i = 0; i < Person::players.size(); i++) { + Person::players[i]->wentforweapon = 0; + } + + if (id == 0) { + Game::flash(.5, 0); + } + } + + if (!dead && creature == wolftype) { + award_bonus(0, Wolfbonus); + } + dead = 2; + if (animTarget == knifefollowedanim && !skeleton.free) { + for (int i = 0; i < skeleton.joints.size(); i++) { + skeleton.joints[i].velocity = 0; + skeleton.joints[i].velocity.y = -2; + } + } + if (id != 0 && unconscioustime > .1) { + numafterkill++; + } + + RagDoll(0); + } + } + + if (texupdatedelay < 0 && bleeding > 0 && bloodtoggle == 2 && distsq(&viewer, &coords) < 9) { + texupdatedelay = .12; + + bloodsize = 5 - realtexdetail; + + startx = 0; + starty = 0; + startx = bleedy; //abs(Random()%(skeleton.skinsize-bloodsize-1)); + starty = bleedx; //abs(Random()%(skeleton.skinsize-bloodsize-1)); + endx = startx + bloodsize; + endy = starty + bloodsize; + + if (startx < 0) { + startx = 0; + bleeding = 0; + } + if (starty < 0) { + starty = 0; + bleeding = 0; + } + if (endx > skeleton.skinsize - 1) { + endx = skeleton.skinsize - 1; + bleeding = 0; + } + if (endy > skeleton.skinsize - 1) { + endy = skeleton.skinsize - 1; + bleeding = 0; + } + if (endx < startx) + endx = startx; + if (endy < starty) + endy = starty; + + for (i = startx; i < endx; i++) { + for (j = starty; j < endy; j++) { + if (Random() % 2 == 0) { + color = Random() % 85 + 170; + if (skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 0] > color / 2) + skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 0] = color / 2; + skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 1] = 0; + skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 2] = 0; + } + } + } + if (detail > 1) { + skeleton.drawmodel.textureptr.bind(); + DoMipmaps(); + } + + if (skeleton.free) { + bleedx += 4 * direction / realtexdetail; + if (detail == 2) + bleedy += (abs(Random() % 3) - 1) * 2 / realtexdetail; + else + bleedy += (abs(Random() % 3) - 1) * 4 / realtexdetail; + } else { + bleedy -= 4 / realtexdetail; + if (detail == 2) + bleedx += (abs(Random() % 3) - 1) * 2 / realtexdetail; + else + bleedx += (abs(Random() % 3) - 1) * 4 / realtexdetail; + } + } + + if (abs(righthandmorphness - targetrighthandmorphness) < multiplier * 4) { + righthandmorphness = targetrighthandmorphness; + righthandmorphstart = righthandmorphend; + } else if (righthandmorphness > targetrighthandmorphness) { + righthandmorphness -= multiplier * 4; + } else if (righthandmorphness < targetrighthandmorphness) { + righthandmorphness += multiplier * 4; + } + + if (abs(lefthandmorphness - targetlefthandmorphness) < multiplier * 4) { + lefthandmorphness = targetlefthandmorphness; + lefthandmorphstart = lefthandmorphend; + } else if (lefthandmorphness > targetlefthandmorphness) { + lefthandmorphness -= multiplier * 4; + } else if (lefthandmorphness < targetlefthandmorphness) { + lefthandmorphness += multiplier * 4; + } + + if (creature == rabbittype || targettailmorphness == 5 || targettailmorphness == 0) { + if (abs(tailmorphness - targettailmorphness) < multiplier * 10) { + tailmorphness = targettailmorphness; + tailmorphstart = tailmorphend; + } else if (tailmorphness > targettailmorphness) { + tailmorphness -= multiplier * 10; + } else if (tailmorphness < targettailmorphness) { + tailmorphness += multiplier * 10; + } + } + + if (creature == wolftype) { + if (abs(tailmorphness - targettailmorphness) < multiplier * 4) { + tailmorphness = targettailmorphness; + tailmorphstart = tailmorphend; + } else if (tailmorphness > targettailmorphness) { + tailmorphness -= multiplier * 2; + } else if (tailmorphness < targettailmorphness) { + tailmorphness += multiplier * 2; + } + } + + if (headmorphend == 3 || headmorphstart == 3) { + if (abs(headmorphness - targetheadmorphness) < multiplier * 7) { + headmorphness = targetheadmorphness; + headmorphstart = headmorphend; + } else if (headmorphness > targetheadmorphness) { + headmorphness -= multiplier * 7; + } else if (headmorphness < targetheadmorphness) { + headmorphness += multiplier * 7; + } + } else if (headmorphend == 5 || headmorphstart == 5) { + if (abs(headmorphness - targetheadmorphness) < multiplier * 10) { + headmorphness = targetheadmorphness; + headmorphstart = headmorphend; + } else if (headmorphness > targetheadmorphness) { + headmorphness -= multiplier * 10; + } else if (headmorphness < targetheadmorphness) { + headmorphness += multiplier * 10; + } + } else { + if (abs(headmorphness - targetheadmorphness) < multiplier * 4) { + headmorphness = targetheadmorphness; + headmorphstart = headmorphend; + } else if (headmorphness > targetheadmorphness) { + headmorphness -= multiplier * 4; + } else if (headmorphness < targetheadmorphness) { + headmorphness += multiplier * 4; + } + } + + if (abs(chestmorphness - targetchestmorphness) < multiplier) { + chestmorphness = targetchestmorphness; + chestmorphstart = chestmorphend; + } else if (chestmorphness > targetchestmorphness) { + chestmorphness -= multiplier; + } else if (chestmorphness < targetchestmorphness) { + chestmorphness += multiplier; + } + + if (dead != 2 && howactive <= typesleeping) { + if (chestmorphstart == 0 && chestmorphend == 0) { + chestmorphness = 0; + targetchestmorphness = 1; + chestmorphend = 3; + } + if (chestmorphstart != 0 && chestmorphend != 0) { + chestmorphness = 0; + targetchestmorphness = 1; + chestmorphend = 0; + if (environment == snowyenvironment) { + XYZ footpoint; + XYZ footvel; + if (skeleton.free) { + footvel = skeleton.specialforward[0] * -1; + footpoint = ((jointPos(head) + jointPos(neck)) / 2) * scale + coords; + } else { + footvel = DoRotation(skeleton.specialforward[0], 0, yaw, 0) * -1; + footpoint = DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0) * scale + coords; + } + if (animTarget == sleepanim) + footvel = DoRotation(footvel, 0, 90, 0); + Sprite::MakeSprite(breathsprite, footpoint + footvel * .2, footvel * .4, 1, 1, 1, .4, .3); + } + } + + if (!dead && howactive < typesleeping) { + blinkdelay -= multiplier * 2; + if (headmorphstart == 0 && headmorphend == 0 && blinkdelay <= 0) { + headmorphness = 0; + targetheadmorphness = 1; + headmorphend = 3; + blinkdelay = (float)(abs(Random() % 40)) / 5; + } + if (headmorphstart == 3 && headmorphend == 3) { + headmorphness = 0; + targetheadmorphness = 1; + headmorphend = 0; + } + } + if (!dead) { + twitchdelay -= multiplier * 1.5; + if (animTarget != hurtidleanim) { + if (headmorphstart == 0 && headmorphend == 0 && twitchdelay <= 0) { + headmorphness = 0; + targetheadmorphness = 1; + headmorphend = 5; + twitchdelay = (float)(abs(Random() % 40)) / 5; + } + if (headmorphstart == 5 && headmorphend == 5) { + headmorphness = 0; + targetheadmorphness = 1; + headmorphend = 0; + } + } + if ((isIdle() || isCrouch()) && animTarget != hurtidleanim) { + twitchdelay3 -= multiplier * 1; + if (Random() % 2 == 0) { + if (righthandmorphstart == 0 && righthandmorphend == 0 && twitchdelay3 <= 0) { + righthandmorphness = 0; + targetrighthandmorphness = 1; + righthandmorphend = 1; + if (Random() % 2 == 0)twitchdelay3 = (float)(abs(Random() % 40)) / 5; + } + if (righthandmorphstart == 1 && righthandmorphend == 1) { + righthandmorphness = 0; + targetrighthandmorphness = 1; + righthandmorphend = 0; + } + } + if (Random() % 2 == 0) { + if (lefthandmorphstart == 0 && lefthandmorphend == 0 && twitchdelay3 <= 0) { + lefthandmorphness = 0; + targetlefthandmorphness = 1; + lefthandmorphend = 1; + twitchdelay3 = (float)(abs(Random() % 40)) / 5; + } + if (lefthandmorphstart == 1 && lefthandmorphend == 1) { + lefthandmorphness = 0; + targetlefthandmorphness = 1; + lefthandmorphend = 0; + } + } + } + } + if (!dead) { + if (creature == rabbittype) { + if (howactive < typesleeping) + twitchdelay2 -= multiplier * 1.5; + else + twitchdelay2 -= multiplier * 0.5; + if (howactive <= typesleeping) { + if (tailmorphstart == 0 && tailmorphend == 0 && twitchdelay2 <= 0) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 1; + twitchdelay2 = (float)(abs(Random() % 40)) / 5; + } + if (tailmorphstart == 1 && tailmorphend == 1) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 2; + } + if (tailmorphstart == 2 && tailmorphend == 2) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 0; + } + } + } + } + } + if (creature == wolftype) { + twitchdelay2 -= multiplier * 1.5; + if (tailmorphend != 0) + if ((isRun() || animTarget == jumpupanim || animTarget == jumpdownanim || animTarget == backflipanim) && !skeleton.free) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 0; + twitchdelay2 = .1; + } + if (tailmorphend != 5) + if (animTarget == flipanim || animTarget == frontflipanim || animTarget == rollanim || skeleton.free) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 5; + twitchdelay2 = .1; + } + if (twitchdelay2 <= 0) { + if (((tailmorphstart == 0 && tailmorphend == 0) || (tailmorphstart == 5 && tailmorphend == 5))) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 1; + } + if (tailmorphstart == 1 && tailmorphend == 1) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 2; + } + if (tailmorphstart == 2 && tailmorphend == 2) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 3; + } + if (tailmorphstart == 3 && tailmorphend == 3) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 4; + } + if (tailmorphstart == 4 && tailmorphend == 4) { + tailmorphness = 0; + targettailmorphness = 1; + tailmorphend = 1; + } + } + } + + if (dead != 1) + unconscioustime = 0; + + if (dead == 1 || howactive == typesleeping) { + unconscioustime += multiplier; + //If unconscious, close eyes and mouth + if (righthandmorphend != 0) + righthandmorphness = 0; + righthandmorphend = 0; + targetrighthandmorphness = 1; + + if (lefthandmorphend != 0) + lefthandmorphness = 0; + lefthandmorphend = 0; + targetlefthandmorphness = 1; + + if (headmorphend != 3 && headmorphend != 5) + headmorphness = 0; + headmorphend = 3; + targetheadmorphness = 1; + } + + + if (howactive > typesleeping) { + XYZ headpoint; + headpoint = coords; + if (bloodtoggle && !bled) { + terrain.MakeDecal(blooddecalslow, headpoint, .8, .5, 0); + } + if (bloodtoggle && !bled) + for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { + j = terrain.patchobjects[whichpatchx][whichpatchz][l]; + XYZ point = DoRotation(headpoint - objects.position[j], 0, -objects.yaw[j], 0); + float size = .8; + float opacity = .6; + float yaw = 0; + objects.model[j].MakeDecal(blooddecalslow, &point, &size, &opacity, &yaw); + } + bled = 1; + } + + if (dead == 2 || howactive > typesleeping) { + //If dead, open mouth and hands + if (righthandmorphend != 0) + righthandmorphness = 0; + righthandmorphend = 0; + targetrighthandmorphness = 1; + + if (lefthandmorphend != 0) + lefthandmorphness = 0; + lefthandmorphend = 0; + targetlefthandmorphness = 1; + + if (headmorphend != 2) + headmorphness = 0; + headmorphend = 2; + targetheadmorphness = 1; + } + + if (stunned > 0 && !dead && headmorphend != 2) { + if (headmorphend != 4) + headmorphness = 0; + headmorphend = 4; + targetheadmorphness = 1; + } + + if (damage > damagetolerance && !dead) { + + dead = 1; + unconscioustime = 0; + + if (creature == wolftype) { + award_bonus(0, Wolfbonus); + } + + RagDoll(0); + + if (weaponactive != -1) { + weapons[weaponids[0]].drop(velocity * scale * -.3, velocity * scale); + weapons[weaponids[0]].velocity.x += .01; + num_weapons--; + if (num_weapons) { + weaponids[0] = weaponids[num_weapons]; + if (weaponstuck == num_weapons) + weaponstuck = 0; + } + weaponactive = -1; + for (unsigned i = 0; i < Person::players.size(); i++) { + Person::players[i]->wentforweapon = 0; + } + } + + + + if ((id == 0 || distsq(&coords, &viewer) < 50) && autoslomo) { + slomo = 1; + slomodelay = .2; + } + + damage += 20; + } + + if (!dead) + damage -= multiplier * 13; + if (!dead) + permanentdamage -= multiplier * 4; + if (isIdle() || isCrouch()) { + if (!dead) + permanentdamage -= multiplier * 4; + } + if (damage < 0) + damage = 0; + if (permanentdamage < 0) + permanentdamage = 0; + if (superpermanentdamage < 0) + superpermanentdamage = 0; + if (permanentdamage < superpermanentdamage) { + permanentdamage = superpermanentdamage; + } + if (damage < permanentdamage) { + damage = permanentdamage; + } + if (dead == 1 && damage < damagetolerance) { + dead = 0; + skeleton.free = 1; + damage -= 20; + for (int i = 0; i < skeleton.joints.size(); i++) { + skeleton.joints[i].velocity = 0; + } + } + if (permanentdamage > damagetolerance && dead != 2) { + DoBlood(1, 255); + + if (weaponactive != -1) { + weapons[weaponids[0]].drop(velocity * scale * -.3, velocity * scale); + weapons[weaponids[0]].velocity.x += .01; + num_weapons--; + if (num_weapons) { + weaponids[0] = weaponids[num_weapons]; + if (weaponstuck == num_weapons) + weaponstuck = 0; + } + weaponactive = -1; + for (unsigned i = 0; i < Person::players.size(); i++) { + Person::players[i]->wentforweapon = 0; + } + } + + bled = 0; + + if (!dead && creature == wolftype) { + award_bonus(0, Wolfbonus); + } + + if (unconscioustime < .1 && (bonus != spinecrusher || bonustime > 1) && (bonus != FinishedBonus || bonustime > 1) && bloodloss < damagetolerance) + award_bonus(id, touchofdeath); + if (id != 0 && unconscioustime > .1) { + numafterkill++; + } + + dead = 2; + + skeleton.free = 1; + + emit_sound_at(breaksound, coords); + } + + if (skeleton.free == 1) { + if (id == 0) + pause_sound(whooshsound); + + if (!dead) { + //If knocked over, open hands and close mouth + if (righthandmorphend != 0) + righthandmorphness = 0; + righthandmorphend = 0; + targetrighthandmorphness = 1; + + if (lefthandmorphend != 0) + lefthandmorphness = 0; + lefthandmorphend = 0; + targetlefthandmorphness = 1; + + if (headmorphend != 3 && headmorphend != 5 && headmorphstart != 3 && headmorphstart != 5) { + if (headmorphend != 0) + headmorphness = 0; + headmorphend = 0; + targetheadmorphness = 1; + } + } + + skeleton.DoGravity(&scale); + float damageamount; + damageamount = skeleton.DoConstraints(&coords, &scale) * 5; + if (damage > damagetolerance - damageamount && !dead && (bonus != spinecrusher || bonustime > 1) && (bonus != style || bonustime > 1) && (bonus != cannon || bonustime > 1)) + award_bonus(id, deepimpact); + DoDamage(damageamount / ((protectionhigh + protectionhead + protectionlow) / 3)); + + average = 0; + howmany = 0; + for (j = 0; j < skeleton.joints.size(); j++) { + average += skeleton.joints[j].position; + howmany++; + } + average /= howmany; + coords += average * scale; + for (j = 0; j < skeleton.joints.size(); j++) { + skeleton.joints[j].position -= average; + } + average /= multiplier; + + velocity = 0; + for (int i = 0; i < skeleton.joints.size(); i++) { + velocity += skeleton.joints[i].velocity * scale; + } + velocity /= skeleton.joints.size(); + + if (!isnormal(velocity.x) && velocity.x) { + velocity = 0; + } + + if (findLength(&average) < 10 && dead && skeleton.free) { + skeleton.longdead += (2000 - findLength(&average)) * multiplier + multiplier; + if (skeleton.longdead > 2000) { + if (skeleton.longdead > 6000) { + if (id == 0) + pause_sound(whooshsound); + skeleton.free = 3; + DrawSkeleton(); + skeleton.free = 2; + } + if (dead == 2 && bloodloss < damagetolerance) { + XYZ headpoint; + headpoint = (jointPos(head) + jointPos(neck)) / 2 * scale + coords; + DoBlood(1, 255); + if (bloodtoggle && !bled) { + terrain.MakeDecal(blooddecal, headpoint, .2 * 1.2, .5, 0); + } + if (bloodtoggle && !bled) + for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { + j = terrain.patchobjects[whichpatchx][whichpatchz][l]; + XYZ point = DoRotation(headpoint - objects.position[j], 0, -objects.yaw[j], 0); + float size = .2 * 1.2; + float opacity = .6; + float yaw = 0; + objects.model[j].MakeDecal(blooddecal, &point, &size, &opacity, &yaw); + } + bled = 1; + } + if (dead == 2 && bloodloss >= damagetolerance) { + XYZ headpoint; + headpoint = (jointPos(abdomen) + jointPos(neck)) / 2 * scale + coords; + if (bleeding <= 0) + DoBlood(1, 255); + if (bloodtoggle && !bled) { + terrain.MakeDecal(blooddecalslow, headpoint, .8, .5, 0); + } + if (bloodtoggle && !bled) + for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { + j = terrain.patchobjects[whichpatchx][whichpatchz][l]; + XYZ point = DoRotation(headpoint - objects.position[j], 0, -objects.yaw[j], 0); + float size = .8; + float opacity = .6; + float yaw = 0; + objects.model[j].MakeDecal(blooddecalslow, &point, &size, &opacity, &yaw); + } + bled = 1; + } + } + } + + if (!dead && crouchkeydown && skeleton.freetime > .5 && id == 0 && skeleton.free) { + bool canrecover = 1; + XYZ startpoint, endpoint, colpoint, colviewer, coltarget; + startpoint = coords; + endpoint = coords; + endpoint.y -= .7; + if (terrain.lineTerrain(startpoint, endpoint, &colpoint) != -1) + canrecover = 0; + if (velocity.y < -30) + canrecover = 0; + for (i = 0; i < objects.numobjects; i++) { + if (objects.type[i] != treeleavestype && objects.type[i] != bushtype && objects.type[i] != firetype) { + colviewer = startpoint; + coltarget = endpoint; + if (objects.model[i].LineCheck(&colviewer, &coltarget, &colpoint, &objects.position[i], &objects.yaw[i]) != -1) + canrecover = 0; + } + } + if (canrecover) { + skeleton.free = 0; + XYZ middle; + middle = 0; + + terrainnormal = jointPos(groin) - jointPos(abdomen); + if (joint(groin).locked && joint(abdomen).locked) { + terrainnormal = jointPos(groin) - jointPos(abdomen); + middle = (jointPos(groin) + jointPos(abdomen)) / 2; + } + if (joint(abdomen).locked && joint(neck).locked) { + terrainnormal = jointPos(abdomen) - jointPos(neck); + middle = (jointPos(neck) + jointPos(abdomen)) / 2; + } + if (joint(groin).locked && joint(neck).locked) { + terrainnormal = jointPos(groin) - jointPos(neck); + middle = (jointPos(groin) + jointPos(neck)) / 2; + } + Normalise(&terrainnormal); + + targetyaw = -asin(0 - terrainnormal.x); + targetyaw *= 360 / 6.28; + if (terrainnormal.z < 0) + targetyaw = 180 - targetyaw; + yaw = targetyaw; + + frameTarget = 0; + animTarget = flipanim; + crouchtogglekeydown = 1; + target = 0; + tilt2 = 0; + targettilt2 = 0; + + animCurrent = tempanim; + frameCurrent = 0; + target = 0; + + for (int i = 0; i < skeleton.joints.size(); i++) { + tempanimation.frames[0].joints[i].position = skeleton.joints[i].position; + tempanimation.frames[0].joints[i].position = DoRotation(tempanimation.frames[0].joints[i].position, 0, -yaw, 0); + } + } + } + + if (findLength(&average) < 10 && !dead && skeleton.free) { + skeleton.longdead += (2000 - findLength(&average)) * multiplier + multiplier; + if (skeleton.longdead > (damage + 500) * 1.5) { + if (id == 0) + pause_sound(whooshsound); + skeleton.free = 0; + velocity = 0; + XYZ middle; + middle = 0; + + terrainnormal = jointPos(groin) - jointPos(abdomen); + if (joint(groin).locked && joint(abdomen).locked) { + terrainnormal = jointPos(groin) - jointPos(abdomen); + middle = (jointPos(groin) + jointPos(abdomen)) / 2; + } + if (joint(abdomen).locked && joint(neck).locked) { + terrainnormal = jointPos(abdomen) - jointPos(neck); + middle = (jointPos(neck) + jointPos(abdomen)) / 2; + } + if (joint(groin).locked && joint(neck).locked) { + terrainnormal = jointPos(groin) - jointPos(neck); + middle = (jointPos(groin) + jointPos(neck)) / 2; + } + Normalise(&terrainnormal); + + targetyaw = -asin(0 - terrainnormal.x); + targetyaw *= 360 / 6.28; + if (terrainnormal.z < 0) + targetyaw = 180 - targetyaw; + yaw = targetyaw; + + targettilt2 = asin(terrainnormal.y) * 180 / 3.14 * -1; + + + if (skeleton.forward.y < 0) { + animTarget = getupfrombackanim; + frameTarget = 0; + targettilt2 = 0; + } + if (skeleton.forward.y > -.3) { + animTarget = getupfromfrontanim; + yaw += 180; + targetyaw += 180; + targettilt2 *= -1; + frameTarget = 0; + targettilt2 = 0; + } + + if ((Random() % 8 == 0 && id != 0 && creature == rabbittype) || (Random() % 2 == 0 && id != 0 && creature == wolftype) || (id == 0 && crouchkeydown && (forwardkeydown || backkeydown || leftkeydown || rightkeydown))) { + animTarget = rollanim; + targetyaw = lookyaw; + if (id == 0) { + if (rightkeydown) { + targetyaw -= 90; + if (forwardkeydown) + targetyaw += 45; + if (backkeydown) + targetyaw -= 45; + } + if (leftkeydown) { + targetyaw += 90; + if (forwardkeydown) + targetyaw -= 45; + if (backkeydown) + targetyaw += 45; + } + if (backkeydown) { + if ( !leftkeydown && !rightkeydown) + targetyaw += 180; + } + targetyaw += 180; + } + } + + if (abs(targettilt2) > 50) + targettilt2 = 0; + animCurrent = tempanim; + frameCurrent = 0; + target = 0; + tilt2 = targettilt2; + + if (middle.y > 0 && animTarget != rollanim) + targetoffset.y = middle.y + 1; + + for (int i = 0; i < skeleton.joints.size(); i++) { + tempanimation.frames[0].joints[i].position = skeleton.joints[i].position; + tempanimation.frames[0].joints[i].position = DoRotation(tempanimation.frames[0].joints[i].position, 0, -yaw, 0); + } + } + } + + bool hasstaff; + hasstaff = 0; + if (num_weapons > 0) + if (weapons[0].getType() == staff) + hasstaff = 1; + if (!skeleton.freefall && freefall && ((jumpkeydown && jumpkeydowntime < .2) || (hasstaff && rabbitkickragdoll)) && !dead) { + if (velocity.y > -30) { + XYZ tempvelocity; + tempvelocity = velocity; + Normalise(&tempvelocity); + targetyaw = -asin(0 - tempvelocity.x); + targetyaw *= 360 / 6.28; + if (velocity.z < 0) + targetyaw = 180 - targetyaw; + //targetyaw+=180; + + skeleton.free = 0; + if (dotproduct(&skeleton.forward, &tempvelocity) < 0) { + animTarget = rollanim; + frameTarget = 2; + } else { + animTarget = backhandspringanim; + targetyaw += 180; + frameTarget = 6; + } + target = 0; + + emit_sound_at(movewhooshsound, coords, 128.); + + animCurrent = animTarget; + frameCurrent = frameTarget - 1; + target = 0; + + velocity = 0; + + yaw = targetyaw; + tilt = 0; + targettilt = 0; + tilt2 = 0; + targettilt2 = 0; + } + } + if (skeleton.freefall == 0) + freefall = 0; + + } + + if (aitype != passivetype || skeleton.free == 1) + if (findLengthfast(&velocity) > .1) + for (i = 0; i < objects.numobjects; i++) { + if (objects.type[i] == firetype) + if (distsqflat(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 12 && distsq(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 49) { + if (onfire) { + if (!objects.onfire[i]) { + emit_sound_at(firestartsound, objects.position[i]); + } + objects.onfire[i] = 1; + } + if (!onfire) { + if (objects.onfire[i]) { + CatchFire(); + } + } + } + if (objects.type[i] == bushtype) + if (distsqflat(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 12 && distsq(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 49) { + if (onfire) { + if (!objects.onfire[i]) { + emit_sound_at(firestartsound, objects.position[i]); + } + objects.onfire[i] = 1; + } + + if (!onfire) { + if (objects.onfire[i]) { + CatchFire(); + } + } + if (objects.messedwith[i] <= 0) { + XYZ tempvel; + XYZ pos; + + emit_sound_at(bushrustle, coords, 40 * findLength(&velocity)); + + if (id == 0) { + addEnvSound(coords, 4 * findLength(&velocity)); + } + + int howmany; + if (environment == grassyenvironment) + howmany = findLength(&velocity) * 4; + if (environment == snowyenvironment) + howmany = findLength(&velocity) * 2; + if (detail == 2) + if (environment != desertenvironment) + for (j = 0; j < howmany; j++) { + tempvel.x = float(abs(Random() % 100) - 50) / 20; + tempvel.y = float(abs(Random() % 100) - 50) / 20; + tempvel.z = float(abs(Random() % 100) - 50) / 20; + pos = coords; + pos.y += 1; + pos.x += float(abs(Random() % 100) - 50) / 200; + pos.y += float(abs(Random() % 100) - 50) / 200; + pos.z += float(abs(Random() % 100) - 50) / 200; + Sprite::MakeSprite(splintersprite, pos, tempvel * .5 + velocity * float(abs(Random() % 100)) / 100, 165 / 255 + float(abs(Random() % 100) - 50) / 400, 0, 0, .2 + float(abs(Random() % 100) - 50) / 1300, 1); + Sprite::setLastSpriteSpecial(1); + } + howmany = findLength(&velocity) * 4; + if (detail == 2) + if (environment == snowyenvironment) + for (j = 0; j < howmany; j++) { + tempvel.x = float(abs(Random() % 100) - 50) / 20; + tempvel.y = float(abs(Random() % 100) - 50) / 20; + tempvel.z = float(abs(Random() % 100) - 50) / 20; + pos = coords; + pos.y += 1; + pos.x += float(abs(Random() % 100) - 50) / 200; + pos.y += float(abs(Random() % 100) - 50) / 200; + pos.z += float(abs(Random() % 100) - 50) / 200; + Sprite::MakeSprite(splintersprite, pos, tempvel * .3 + velocity * float(abs(Random() % 100)) / 100 / 2, 1, 1, 1, .1, 1); + Sprite::setLastSpriteSpecial(2); + } + } + objects.rotx[i] += velocity.x * multiplier * 6; + objects.roty[i] += velocity.z * multiplier * 6; + objects.messedwith[i] = .5; + } + XYZ tempcoord; + if (objects.type[i] == treeleavestype && environment != desertenvironment) { + if (objects.pitch[i] == 0) + tempcoord = coords; + else { + tempcoord = coords - objects.position[i]; + tempcoord = DoRotation(tempcoord, 0, -objects.yaw[i], 0); + tempcoord = DoRotation(tempcoord, -objects.pitch[i], 0, 0); + tempcoord += objects.position[i]; + } + if (distsqflat(&tempcoord, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 8 && distsq(&tempcoord, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 300 && tempcoord.y > objects.position[i].y + 3 * objects.scale[i]) { + if (objects.messedwith[i] <= 0) { + XYZ tempvel; + XYZ pos; + + emit_sound_at(bushrustle, coords, 40 * findLength(&velocity)); + + if (id == 0) { + addEnvSound(coords, 4 * findLength(&velocity)); + } + + int howmany; + if (environment == grassyenvironment) + howmany = findLength(&velocity) * 4; + if (environment == snowyenvironment) + howmany = findLength(&velocity) * 2; + if (detail == 2) + if (environment != desertenvironment) + for (j = 0; j < howmany; j++) { + tempvel.x = float(abs(Random() % 100) - 50) / 20; + tempvel.y = float(abs(Random() % 100) - 50) / 20; + tempvel.z = float(abs(Random() % 100) - 50) / 20; + pos = coords; + pos += velocity * .1; + pos.y += 1; + pos.x += float(abs(Random() % 100) - 50) / 150; + pos.y += float(abs(Random() % 100) - 50) / 150; + pos.z += float(abs(Random() % 100) - 50) / 150; + Sprite::MakeSprite(splintersprite, pos, tempvel * .5 + velocity * float(abs(Random() % 100)) / 100, 165 / 255 + float(abs(Random() % 100) - 50) / 400, 0, 0, .2 + float(abs(Random() % 100) - 50) / 1300, 1); + Sprite::setLastSpriteSpecial(1); + } + howmany = findLength(&velocity) * 4; + if (detail == 2) + if (environment == snowyenvironment) + for (j = 0; j < howmany; j++) { + tempvel.x = float(abs(Random() % 100) - 50) / 20; + tempvel.y = float(abs(Random() % 100) - 50) / 20; + tempvel.z = float(abs(Random() % 100) - 50) / 20; + pos = coords; + pos += velocity * .1; + pos.y += 1; + pos.x += float(abs(Random() % 100) - 50) / 150; + pos.y += float(abs(Random() % 100) - 50) / 150; + pos.z += float(abs(Random() % 100) - 50) / 150; + Sprite::MakeSprite(splintersprite, pos, tempvel * .3 + velocity * float(abs(Random() % 100)) / 100 / 2, 1, 1, 1, .1, 1); + Sprite::setLastSpriteSpecial(2); + } + } + objects.messedwith[i] = .5; + } + } + } + + if (!skeleton.free) { + bool play; + play = 0; + if ((stunned > 0 || surprised > 0) && Person::players.size() > 2 && aitype != passivetype) + play = 1; + if (hasvictim) + if (aitype != passivetype && victim->skeleton.free && !victim->dead) + play = 1; + if (tutoriallevel == 1 && id != 0) + play = 0; + if (play && aitype != playercontrolled) { + int whichsound = -1; + i = abs(Random() % 4); + if (speechdelay <= 0) { + if (creature == rabbittype) { + if (i == 0) + whichsound = rabbitchitter; + if (i == 1) + whichsound = rabbitchitter2; + } + if (creature == wolftype) { + if (i == 0) + whichsound = growlsound; + if (i == 1) + whichsound = growl2sound; + } + } + speechdelay = .3; + + if (whichsound != -1) { + emit_sound_at(whichsound, coords); + } + } + + if (animTarget == staggerbackhighanim) + staggerdelay = 1; + if (animTarget == staggerbackhardanim) + staggerdelay = 1; + staggerdelay -= multiplier; + if (animTarget != crouchstabanim && animTarget != swordgroundstabanim && animTarget != staffgroundsmashanim) + hasvictim = 1; + if (velocity.y < -30 && animTarget == jumpdownanim) + RagDoll(0); + if (animCurrent != getIdle() && wasIdle() && animTarget != getIdle() && isIdle()) { + animTarget = getIdle(); + frameTarget = 0; + target = 0; + } + weaponmissdelay -= multiplier; + highreversaldelay -= multiplier; + lowreversaldelay -= multiplier; + lastcollide -= multiplier; + skiddelay -= multiplier; + if (!isnormal(velocity.x) && velocity.x) { + velocity = 0; + } + if (!isnormal(targettilt) && targettilt) { + targettilt = 0; + } + if (!isnormal(targettilt2) && targettilt2) { + targettilt2 = 0; + } + if (!isnormal(targetyaw) && targetyaw) { + targetyaw = 0; + } + + if (animTarget == bounceidleanim || animTarget == wolfidle || animTarget == walkanim || animTarget == drawrightanim || animTarget == crouchdrawrightanim || animTarget == drawleftanim || animTarget == fightidleanim || animTarget == fightsidestep || animTarget == hanganim || isCrouch() || animTarget == backhandspringanim) { + //open hands and close mouth + if (righthandmorphend != 0 && righthandmorphness == targetrighthandmorphness) { + righthandmorphness = 0; + righthandmorphend = 0; + targetrighthandmorphness = 1; + } + + if (lefthandmorphend != 0 && lefthandmorphness == targetlefthandmorphness) { + lefthandmorphness = 0; + lefthandmorphend = 0; + targetlefthandmorphness = 1; + } + + if (headmorphend != 3 && headmorphend != 5 && headmorphstart != 3 && headmorphstart != 5 && headmorphend != 0 && headmorphness == targetheadmorphness) { + headmorphness = 0; + headmorphend = 0; + targetheadmorphness = 1; + } + } + + if (animTarget == rollanim || animTarget == dodgebackanim || animTarget == removeknifeanim || animTarget == knifefightidleanim || animTarget == swordfightidleanim || animTarget == blockhighleftstrikeanim || animTarget == crouchremoveknifeanim || animTarget == sneakanim || animTarget == sweepanim || animTarget == spinkickreversedanim || animTarget == jumpdownanim || isWallJump() || isFlip() || animTarget == climbanim || isRun() || animTarget == getupfrombackanim || animTarget == getupfromfrontanim) { + //open hands and mouth + if (righthandmorphend != 0 && righthandmorphness == targetrighthandmorphness) { + righthandmorphness = 0; + righthandmorphend = 0; + targetrighthandmorphness = 1; + } + + if (lefthandmorphend != 0 && lefthandmorphness == targetlefthandmorphness) { + lefthandmorphness = 0; + lefthandmorphend = 0; + targetlefthandmorphness = 1; + } + + if (headmorphend != 1 && headmorphness == targetheadmorphness) { + headmorphness = 0; + headmorphend = 1; + targetheadmorphness = 1; + } + } + + if (animTarget == jumpupanim || animTarget == crouchstabanim || animTarget == swordgroundstabanim || animTarget == swordfightidlebothanim || animTarget == blockhighleftanim) { + //close hands and mouth + if (righthandmorphend != 1 && righthandmorphness == targetrighthandmorphness) { + righthandmorphness = 0; + righthandmorphend = 1; + targetrighthandmorphness = 1; + } + + if (lefthandmorphend != 1 && lefthandmorphness == targetlefthandmorphness) { + lefthandmorphness = 0; + lefthandmorphend = 1; + targetlefthandmorphness = 1; + } + + if (headmorphend != 0 && headmorphness == targetheadmorphness) { + headmorphness = 0; + headmorphend = 0; + targetheadmorphness = 1; + } + } + + if (animTarget == spinkickanim || animTarget == staffspinhitreversalanim || animTarget == staffspinhitreversedanim || animTarget == staffhitreversalanim || animTarget == staffhitreversedanim || animTarget == hurtidleanim || animTarget == winduppunchanim || animTarget == swordslashreversalanim || animTarget == swordslashreversedanim || animTarget == knifeslashreversalanim || animTarget == knifeslashreversedanim || animTarget == knifethrowanim || animTarget == knifefollowanim || animTarget == knifefollowedanim || animTarget == killanim || animTarget == dropkickanim || animTarget == upunchanim || animTarget == knifeslashstartanim || animTarget == swordslashanim || animTarget == staffhitanim || animTarget == staffspinhitanim || animTarget == staffgroundsmashanim || animTarget == spinkickreversalanim || animTarget == sweepreversalanim || animTarget == lowkickanim || animTarget == sweepreversedanim || animTarget == rabbitkickreversalanim || animTarget == rabbitkickreversedanim || animTarget == jumpreversalanim || animTarget == jumpreversedanim) { + //close hands and yell + if (righthandmorphend != 1 && righthandmorphness == targetrighthandmorphness) { + righthandmorphness = 0; + righthandmorphend = 1; + targetrighthandmorphness = 1; + } + + if (lefthandmorphend != 1 && lefthandmorphness == targetlefthandmorphness) { + lefthandmorphness = 0; + lefthandmorphend = 1; + targetlefthandmorphness = 1; + } + + if (headmorphend != 2 && headmorphness == targetheadmorphness) { + headmorphness = 1; + headmorphend = 2; + targetheadmorphness = 1; + } + } + + bool behind; + behind = 0; + if (hasvictim) { + if ((victim != this->shared_from_this()) && !victim->dead && (victim->aitype != passivetype) && + (victim->aitype != searchtype) && (aitype != passivetype) && + (aitype != searchtype) && (victim->id < Person::players.size())) { + behind = (normaldotproduct(facing, coords - victim->coords) > 0); + } + } + + if (!dead && animTarget != hurtidleanim) + if (behind || animTarget == killanim || animTarget == knifethrowanim || animTarget == knifefollowanim || animTarget == spinkickreversalanim || animTarget == rabbitkickreversedanim || animTarget == jumpreversedanim) { + if (headmorphend != 4 || headmorphness == targetheadmorphness) { + headmorphend = 4; + //headmorphness=1; + targetheadmorphness = 1; + } + } + + if (weaponactive != -1) { + if (weapons[weaponids[weaponactive]].getType() != staff) { + righthandmorphstart = 1; + righthandmorphend = 1; + } + if (weapons[weaponids[weaponactive]].getType() == staff) { + righthandmorphstart = 2; + righthandmorphend = 2; + } + targetrighthandmorphness = 1; + } + + terrainnormal = terrain.getNormal(coords.x, coords.z); + + if (Animation::animations[animTarget].attack != reversal) { + if (!isnormal(coords.x)) + coords = oldcoords; + oldcoords = coords; + } + + flatfacing = 0; + flatfacing.z = 1; + + flatfacing = DoRotation(flatfacing, 0, yaw, 0); + facing = flatfacing; + ReflectVector(&facing, terrainnormal); + Normalise(&facing); + + if (isRun() || animTarget == sneakanim || animTarget == rollanim || animTarget == walkanim) { + if (onterrain) + targettilt2 = -facing.y * 20; + else + targettilt2 = 0; + } + onterrain = 0; + if (!isRun() && !Animation::animations[animTarget].attack && animTarget != getupfromfrontanim && animTarget != getupfrombackanim && animTarget != sneakanim) + targettilt2 = 0; + if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { + flatvelocity = velocity; + flatvelocity.y = 0; + flatvelspeed = findLength(&flatvelocity); + targettilt = flatvelspeed * fast_sqrt(abs(velocity.y) * .7) * normaldotproduct(DoRotation(flatfacing, 0, -90, 0), flatvelocity); + targettilt2 = flatvelspeed * fast_sqrt(abs(velocity.y) * .7) * normaldotproduct(flatfacing, flatvelocity); + if (velocity.y < 0) + targettilt2 *= -1; + if (velocity.y < 0) + targettilt *= -1; + if (targettilt > 25) + targettilt = 25; + if (targettilt < -25) + targettilt = -25; + } + + if (targettilt2 > 45) + targettilt2 = 45; + if (targettilt2 < -45) + targettilt2 = -45; + if (abs(tilt2 - targettilt2) < multiplier * 400) + tilt2 = targettilt2; + else if (tilt2 > targettilt2) { + tilt2 -= multiplier * 400; + } else if (tilt2 < targettilt2) { + tilt2 += multiplier * 400; + } + if (!Animation::animations[animTarget].attack && animTarget != getupfrombackanim && animTarget != getupfromfrontanim) { + if (tilt2 > 25) + tilt2 = 25; + if (tilt2 < -25) + tilt2 = -25; + } + + if (!isnormal(targettilt) && targettilt) { + targettilt = 0; + } + if (!isnormal(targettilt2) && targettilt2) { + targettilt2 = 0; + } + + //Running velocity + if (animTarget == rabbittackleanim) { + velocity += facing * multiplier * speed * 700 * scale; + velspeed = findLength(&velocity); + if (velspeed > speed * 65 * scale) { + velocity /= velspeed; + velspeed = speed * 65 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed; + } + if (animTarget != rabbitrunninganim && animTarget != wolfrunninganim) { + if (isRun() || animTarget == rabbitkickanim) { + velocity += facing * multiplier * speed * 700 * scale; + velspeed = findLength(&velocity); + if (velspeed > speed * 45 * scale) { + velocity /= velspeed; + velspeed = speed * 45 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + if (velspeed < speed * 30 * scale) + velspeed = speed * 30 * scale; + velocity = flatfacing * velspeed; + } + } else if (isRun()) { + velocity += facing * multiplier * speed * 700 * scale; + velspeed = findLength(&velocity); + if (creature == rabbittype) { + if (velspeed > speed * 55 * scale) { + velocity /= velspeed; + velspeed = speed * 55 * scale; + velocity *= velspeed; + } + } + if (creature == wolftype) { + if (velspeed > speed * 75 * scale) { + velocity /= velspeed; + velspeed = speed * 75 * scale; + velocity *= velspeed; + } + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed; + } + + if (animTarget == rollanim && targetFrame().label != 6) { + velocity += facing * multiplier * speed * 700 * scale; + velspeed = findLength(&velocity); + if (velspeed > speed * 45 * scale) { + velocity /= velspeed; + velspeed = speed * 45 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed; + } + + if (animTarget == sneakanim || animTarget == walkanim) { + velocity += facing * multiplier * speed * 700 * scale; + velspeed = findLength(&velocity); + if (velspeed > speed * 12 * scale) { + velocity /= velspeed; + velspeed = speed * 12 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed; + } + + if ((animTarget == fightidleanim || animTarget == knifefightidleanim) && (animCurrent == bounceidleanim || animCurrent == hurtidleanim)) { + velocity += facing * multiplier * speed * 700 * scale; + velspeed = findLength(&velocity); + if (velspeed > speed * 2 * scale) { + velocity /= velspeed; + velspeed = speed * 2 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed; + } + + + if ((animTarget == bounceidleanim || animCurrent == hurtidleanim) && (animCurrent == fightidleanim || animCurrent == knifefightidleanim)) { + velocity -= facing * multiplier * speed * 700 * scale; + velspeed = findLength(&velocity); + if (velspeed > speed * 2 * scale) { + velocity /= velspeed; + velspeed = speed * 2 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed * -1; + } + + if (animTarget == fightsidestep) { + velocity += DoRotation(facing * multiplier * speed * 700 * scale, 0, -90, 0); + velspeed = findLength(&velocity); + if (velspeed > speed * 12 * scale) { + velocity /= velspeed; + velspeed = speed * 12 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = DoRotation(flatfacing * velspeed, 0, -90, 0); + } + + if (animTarget == staggerbackhighanim) { + coords -= facing * multiplier * speed * 16 * scale; + velocity = 0; + } + if (animTarget == staggerbackhardanim && Animation::animations[staggerbackhardanim].frames[frameTarget].label != 6) { + coords -= facing * multiplier * speed * 20 * scale; + velocity = 0; + } + + if (animTarget == backhandspringanim) { + //coords-=facing*multiplier*50*scale; + velocity += facing * multiplier * speed * 700 * scale * -1; + velspeed = findLength(&velocity); + if (velspeed > speed * 50 * scale) { + velocity /= velspeed; + velspeed = speed * 50 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed * -1; + } + if (animTarget == dodgebackanim) { + //coords-=facing*multiplier*50*scale; + velocity += facing * multiplier * speed * 700 * scale * -1; + velspeed = findLength(&velocity); + if (velspeed > speed * 60 * scale) { + velocity /= velspeed; + velspeed = speed * 60 * scale; + velocity *= velspeed; + } + velocity.y += gravity * multiplier * 20; + ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); + velspeed = findLength(&velocity); + velocity = flatfacing * velspeed * -1; + } + + if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { + velspeed = findLength(&velocity); + } + + + if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { + velocity.y += gravity * multiplier; + } + + if (animTarget != climbanim && animTarget != hanganim && !isWallJump()) + coords += velocity * multiplier; + + if (coords.y < terrain.getHeight(coords.x, coords.z) && (animTarget == jumpdownanim || animTarget == jumpupanim || isFlip())) { + if (isFlip() && targetFrame().label == 7) + RagDoll(0); + + if (animTarget == jumpupanim) { + jumppower = -4; + animTarget = getIdle(); + } + target = 0; + frameTarget = 0; + onterrain = 1; + + if (id == 0) { + pause_sound(whooshsound); + OPENAL_SetVolume(channels[whooshsound], 0); + } + + if (animTarget == jumpdownanim || isFlip()) { + if (isFlip())jumppower = -4; + animTarget = getLanding(); + emit_sound_at(landsound, coords, 128.); + + if (id == 0) { + addEnvSound(coords); + } + } + } + + if (animTarget != jumpupanim && animTarget != jumpdownanim && !isFlip() && animTarget != climbanim && animTarget != hanganim && !isWallJump()) + coords.y += gravity * multiplier * 2; + if (animTarget != jumpupanim && animTarget != jumpdownanim && !isFlip() && coords.y < terrain.getHeight(coords.x, coords.z)) { + coords.y = terrain.getHeight(coords.x, coords.z); + onterrain = 1; + } + + + if (isIdle() || animTarget == drawrightanim || animTarget == drawleftanim || animTarget == crouchdrawrightanim || animTarget == crouchstabanim || animTarget == swordgroundstabanim || isStop() || animTarget == removeknifeanim || animTarget == crouchremoveknifeanim || isLanding() || isCrouch() || Animation::animations[animTarget].attack || (animTarget == rollanim && targetFrame().label == 6)) { + velspeed = findLength(&velocity); + velocity.y = 0; + if (velspeed < multiplier * 300 * scale) { + velocity = 0; + } else + velocity -= velocity / velspeed * multiplier * 300 * scale; + if (velspeed > 5 && (isLanding() || isLandhard())) { + skiddingdelay += multiplier; + if (skiddelay <= 0) { + FootLand(leftfoot, .5); + FootLand(rightfoot, .5); + skiddelay = .02; + } + } else + skiddingdelay = 0; + } + + if (isLandhard()) { + velspeed = findLength(&velocity); + velocity.y = 0; + if (velspeed < multiplier * 600 * scale) { + velocity = 0; + } else + velocity -= velocity / velspeed * multiplier * 600 * scale; + velocity = 0; + if (velspeed > 5 && (isLanding() || isLandhard())) { + skiddingdelay += multiplier; + if (skiddelay <= 0) { + FootLand(leftfoot, .5); + FootLand(rightfoot, .5); + skiddelay = .02; + } + } else + skiddingdelay = 0; + } + + if (skiddingdelay < 0) + skiddingdelay += multiplier; + if (skiddingdelay > .02 && !forwardkeydown && !backkeydown && !leftkeydown && !rightkeydown && !jumpkeydown && isLanding() && !landhard) { + skiddingdelay = -1; + if (!onterrain || environment == grassyenvironment) { + emit_sound_at(skidsound, coords, 128 * velspeed / 10); + } else { + emit_sound_at(snowskidsound, coords, 128 * velspeed / 10); + } + } + + if (Animation::animations[animTarget].attack == normalattack && animTarget != rabbitkickanim && !victim->skeleton.free) { + terrainnormal = victim->coords - coords; + Normalise(&terrainnormal); + targetyaw = -asin(0 - terrainnormal.x); + targetyaw *= 360 / 6.28; + if (terrainnormal.z < 0) + targetyaw = 180 - targetyaw; + targettilt2 = -asin(terrainnormal.y) * 360 / 6.28; //*-70; + } + + if (Animation::animations[animTarget].attack == reversal && animTarget != rabbittacklinganim) { + targetyaw = victim->targetyaw; + } + if (animTarget == rabbittacklinganim) { + coords = victim->coords; + } + } + skeleton.oldfree = skeleton.free; + + XYZ midterrain; + midterrain = 0; + midterrain.x = terrain.size * terrain.scale / 2; + midterrain.z = terrain.size * terrain.scale / 2; + if (distsqflat(&coords, &midterrain) > (terrain.size * terrain.scale / 2 - viewdistance) * (terrain.size * terrain.scale / 2 - viewdistance)) { + XYZ tempposit; + tempposit = coords - midterrain; + tempposit.y = 0; + Normalise(&tempposit); + tempposit *= (terrain.size * terrain.scale / 2 - viewdistance); + coords.x = tempposit.x + midterrain.x; + coords.z = tempposit.z + midterrain.z; + } +} + + +/* EFFECT + * inverse kinematics helper function + */ +void IKHelper(Person *p, float interp) +{ + XYZ point, change, change2; + float heightleft, heightright; + + // TODO: implement localToWorld and worldToLocal + // but keep in mind it won't be the same math if player is ragdolled or something + // - localToWorldStanding / worldToLocalStanding (or crouching or...?) + // then comb through code for places where to use it + + // point = localToWorld(jointPos(leftfoot)) + point = DoRotation(p->jointPos(leftfoot), 0, p->yaw, 0) * p->scale + p->coords; + // adjust height of foot + heightleft = terrain.getHeight(point.x, point.z) + .04; + point.y = heightleft; + change = p->jointPos(leftankle) - p->jointPos(leftfoot); + change2 = p->jointPos(leftknee) - p->jointPos(leftfoot); + // jointPos(leftfoot) = interpolate(worldToLocal(point), jointPos(leftfoot), interp) + p->jointPos(leftfoot) = DoRotation((point - p->coords) / p->scale, 0, -p->yaw, 0) * interp + p->jointPos(leftfoot) * (1 - interp); + // move ankle along with foot + p->jointPos(leftankle) = p->jointPos(leftfoot) + change; + // average knee pos between old and new pos + p->jointPos(leftknee) = (p->jointPos(leftfoot) + change2) / 2 + (p->jointPos(leftknee)) / 2; + + // do same as above for right leg + point = DoRotation(p->jointPos(rightfoot), 0, p->yaw, 0) * p->scale + p->coords; + heightright = terrain.getHeight(point.x, point.z) + .04; + point.y = heightright; + change = p->jointPos(rightankle) - p->jointPos(rightfoot); + change2 = p->jointPos(rightknee) - p->jointPos(rightfoot); + p->jointPos(rightfoot) = DoRotation((point - p->coords) / p->scale, 0, -p->yaw, 0) * interp + p->jointPos(rightfoot) * (1 - interp); + p->jointPos(rightankle) = p->jointPos(rightfoot) + change; + p->jointPos(rightknee) = (p->jointPos(rightfoot) + change2) / 2 + (p->jointPos(rightknee)) / 2; + + // fix up skeleton now that we've moved body parts? + p->skeleton.DoConstraints(&p->coords, &p->scale); +} + +/* EFFECT + * MONSTER + * TODO: ??? + */ +int Person::DrawSkeleton() +{ + int oldplayerdetail; + if ((frustum.SphereInFrustum(coords.x, coords.y + scale * 3, coords.z, scale * 8) && distsq(&viewer, &coords) < viewdistance * viewdistance) || skeleton.free == 3) { + if (onterrain && (isIdle() || isCrouch() || wasIdle() || wasCrouch()) && !skeleton.free) { + calcrot = 1; + } + + if (headless) { + headmorphness = 0; + headmorphstart = 6; + headmorphend = 6; + } + + glAlphaFunc(GL_GREATER, 0.0001); + XYZ terrainlight; + float terrainheight; + float distance; + if (!isnormal(yaw)) + yaw = 0; + if (!isnormal(tilt)) + tilt = 0; + if (!isnormal(tilt2)) + tilt2 = 0; + oldplayerdetail = playerdetail; + playerdetail = 0; + if (distsq(&viewer, &coords) < viewdistance * viewdistance / 32 && detail == 2) { + playerdetail = 1; + } + if (distsq(&viewer, &coords) < viewdistance * viewdistance / 128 && detail == 1) { + playerdetail = 1; + } + if (distsq(&viewer, &coords) < viewdistance * viewdistance / 256 && (detail != 1 && detail != 2)) { + playerdetail = 1; + } + if (id == 0) + playerdetail = 1; + if (playerdetail != oldplayerdetail) { + updatedelay = 0; + normalsupdatedelay = 0; + } + static float updatedelaychange; + static float morphness; + static float framemult; + if (calcrot) { + skeleton.FindForwards(); + if (howactive == typesittingwall) { + skeleton.specialforward[1] = 0; + skeleton.specialforward[1].z = 1; + } + } + static XYZ mid; + static float M[16]; + static int i, j, k; + static int weaponattachmuscle; + static int weaponrotatemuscle; + static XYZ weaponpoint; + static int start, endthing; + if ((dead != 2 || skeleton.free != 2) && updatedelay <= 0) { + if (!isSleeping() && !isSitting()) { + // TODO: give these meaningful names + const bool cond1 = (isIdle() || isCrouch() || isLanding() || isLandhard() + || animTarget == drawrightanim || animTarget == drawleftanim || animTarget == crouchdrawrightanim); + const bool cond2 = (wasIdle() || wasCrouch() || wasLanding() || wasLandhard() + || animCurrent == drawrightanim || animCurrent == drawleftanim || animCurrent == crouchdrawrightanim); + + if (onterrain && (cond1 && cond2) && !skeleton.free) { + IKHelper(this, 1); + if (creature == wolftype) + IKHelper(this, 1); + } + + if (onterrain && (cond1 && !cond2) && !skeleton.free) { + IKHelper(this, target); + if (creature == wolftype) + IKHelper(this, target); + } + + if (onterrain && (!cond1 && cond2) && !skeleton.free) { + IKHelper(this, 1 - target); + if (creature == wolftype) + IKHelper(this, 1 - target); + } + } + + if (!skeleton.free && (!Animation::animations[animTarget].attack && animTarget != getupfrombackanim && ((animTarget != rollanim && !isFlip()) || targetFrame().label == 6) && animTarget != getupfromfrontanim && animTarget != wolfrunninganim && animTarget != rabbitrunninganim && animTarget != backhandspringanim && animTarget != walljumpfrontanim && animTarget != hurtidleanim && !isLandhard() && !isSleeping())) + DoHead(); + else { + targetheadyaw = -targetyaw; + targetheadpitch = 0; + if (Animation::animations[animTarget].attack == 3) + targetheadyaw += 180; + } + for (i = 0; i < skeleton.drawmodel.vertexNum; i++) { + skeleton.drawmodel.vertex[i] = 0; + skeleton.drawmodel.vertex[i].y = 999; + } + for (i = 0; i < skeleton.drawmodellow.vertexNum; i++) { + skeleton.drawmodellow.vertex[i] = 0; + skeleton.drawmodellow.vertex[i].y = 999; + } + for (i = 0; i < skeleton.drawmodelclothes.vertexNum; i++) { + skeleton.drawmodelclothes.vertex[i] = 0; + skeleton.drawmodelclothes.vertex[i].y = 999; + } + for (int i = 0; i < skeleton.muscles.size(); i++) { + // convenience renames + const int p1 = skeleton.muscles[i].parent1->label; + const int p2 = skeleton.muscles[i].parent2->label; + + if ((skeleton.muscles[i].vertices.size() > 0 && playerdetail) || (skeleton.muscles[i].verticeslow.size() > 0 && !playerdetail)) { + morphness = 0; + start = 0; + endthing = 0; + + if (p1 == righthand || p2 == righthand) { + morphness = righthandmorphness; + start = righthandmorphstart; + endthing = righthandmorphend; + } + if (p1 == lefthand || p2 == lefthand) { + morphness = lefthandmorphness; + start = lefthandmorphstart; + endthing = lefthandmorphend; + } + if (p1 == head || p2 == head) { + morphness = headmorphness; + start = headmorphstart; + endthing = headmorphend; + } + if ((p1 == neck && p2 == abdomen) || (p2 == neck && p1 == abdomen)) { + morphness = chestmorphness; + start = chestmorphstart; + endthing = chestmorphend; + } + if ((p1 == groin && p2 == abdomen) || (p2 == groin && p1 == abdomen)) { + morphness = tailmorphness; + start = tailmorphstart; + endthing = tailmorphend; + } + if (calcrot) + skeleton.FindRotationMuscle(i, animTarget); + mid = (skeleton.muscles[i].parent1->position + skeleton.muscles[i].parent2->position) / 2; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + if (!skeleton.free) + glRotatef(tilt2, 1, 0, 0); + if (!skeleton.free) + glRotatef(tilt, 0, 0, 1); + + + glTranslatef(mid.x, mid.y, mid.z); + + skeleton.muscles[i].lastrotate1 = skeleton.muscles[i].rotate1; + glRotatef(-skeleton.muscles[i].lastrotate1 + 90, 0, 1, 0); + + skeleton.muscles[i].lastrotate2 = skeleton.muscles[i].rotate2; + glRotatef(-skeleton.muscles[i].lastrotate2 + 90, 0, 0, 1); + + skeleton.muscles[i].lastrotate3 = skeleton.muscles[i].rotate3; + glRotatef(-skeleton.muscles[i].lastrotate3, 0, 1, 0); + + if (playerdetail || skeleton.free == 3) { + for (j = 0; j < skeleton.muscles[i].vertices.size(); j++) { + XYZ &v0 = skeleton.model[start].vertex[skeleton.muscles[i].vertices[j]]; + XYZ &v1 = skeleton.model[endthing].vertex[skeleton.muscles[i].vertices[j]]; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (p1 == abdomen || p2 == abdomen) + glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionbody.x, + (v0.y * (1 - morphness) + v1.y * morphness) * proportionbody.y, + (v0.z * (1 - morphness) + v1.z * morphness) * proportionbody.z); + if (p1 == lefthand || p1 == righthand || p1 == leftwrist || p1 == rightwrist || p1 == leftelbow || p1 == rightelbow || p2 == leftelbow || p2 == rightelbow) + glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionarms.x, + (v0.y * (1 - morphness) + v1.y * morphness) * proportionarms.y, + (v0.z * (1 - morphness) + v1.z * morphness) * proportionarms.z); + if (p1 == leftfoot || p1 == rightfoot || p1 == leftankle || p1 == rightankle || p1 == leftknee || p1 == rightknee || p2 == leftknee || p2 == rightknee) + glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionlegs.x, + (v0.y * (1 - morphness) + v1.y * morphness) * proportionlegs.y, + (v0.z * (1 - morphness) + v1.z * morphness) * proportionlegs.z); + if (p1 == head || p2 == head) + glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionhead.x, + (v0.y * (1 - morphness) + v1.y * morphness) * proportionhead.y, + (v0.z * (1 - morphness) + v1.z * morphness) * proportionhead.z); + glGetFloatv(GL_MODELVIEW_MATRIX, M); + skeleton.drawmodel.vertex[skeleton.muscles[i].vertices[j]].x = M[12] * scale; + skeleton.drawmodel.vertex[skeleton.muscles[i].vertices[j]].y = M[13] * scale; + skeleton.drawmodel.vertex[skeleton.muscles[i].vertices[j]].z = M[14] * scale; + glPopMatrix(); + } + } + if (!playerdetail || skeleton.free == 3) { + for (j = 0; j < skeleton.muscles[i].verticeslow.size(); j++) { + XYZ &v0 = skeleton.modellow.vertex[skeleton.muscles[i].verticeslow[j]]; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (p1 == abdomen || p2 == abdomen) + glTranslatef(v0.x * proportionbody.x, + v0.y * proportionbody.y, + v0.z * proportionbody.z); + if (p1 == lefthand || p1 == righthand || p1 == leftwrist || p1 == rightwrist || p1 == leftelbow || p1 == rightelbow || p2 == leftelbow || p2 == rightelbow) + glTranslatef(v0.x * proportionarms.x, + v0.y * proportionarms.y, + v0.z * proportionarms.z); + if (p1 == leftfoot || p1 == rightfoot || p1 == leftankle || p1 == rightankle || p1 == leftknee || p1 == rightknee || p2 == leftknee || p2 == rightknee) + glTranslatef(v0.x * proportionlegs.x, + v0.y * proportionlegs.y, + v0.z * proportionlegs.z); + if (p1 == head || p2 == head) + glTranslatef(v0.x * proportionhead.x, + v0.y * proportionhead.y, + v0.z * proportionhead.z); + + glGetFloatv(GL_MODELVIEW_MATRIX, M); + skeleton.drawmodellow.vertex[skeleton.muscles[i].verticeslow[j]].x = M[12] * scale; + skeleton.drawmodellow.vertex[skeleton.muscles[i].verticeslow[j]].y = M[13] * scale; + skeleton.drawmodellow.vertex[skeleton.muscles[i].verticeslow[j]].z = M[14] * scale; + glPopMatrix(); + } + } + glPopMatrix(); + } + if (skeleton.clothes && skeleton.muscles[i].verticesclothes.size() > 0) { + mid = (skeleton.muscles[i].parent1->position + skeleton.muscles[i].parent2->position) / 2; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + if (!skeleton.free) + glRotatef(tilt2, 1, 0, 0); + if (!skeleton.free) + glRotatef(tilt, 0, 0, 1); + glTranslatef(mid.x, mid.y, mid.z); + skeleton.muscles[i].lastrotate1 = skeleton.muscles[i].rotate1; + glRotatef(-skeleton.muscles[i].lastrotate1 + 90, 0, 1, 0); + + skeleton.muscles[i].lastrotate2 = skeleton.muscles[i].rotate2; + glRotatef(-skeleton.muscles[i].lastrotate2 + 90, 0, 0, 1); + + skeleton.muscles[i].lastrotate3 = skeleton.muscles[i].rotate3; + glRotatef(-skeleton.muscles[i].lastrotate3, 0, 1, 0); + + for (j = 0; j < skeleton.muscles[i].verticesclothes.size(); j++) { + XYZ &v0 = skeleton.modelclothes.vertex[skeleton.muscles[i].verticesclothes[j]]; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (p1 == abdomen || p2 == abdomen) + glTranslatef(v0.x * proportionbody.x, + v0.y * proportionbody.y, + v0.z * proportionbody.z); + if (p1 == lefthand || p1 == righthand || p1 == leftwrist || p1 == rightwrist || p1 == leftelbow || p1 == rightelbow || p2 == leftelbow || p2 == rightelbow) + glTranslatef(v0.x * proportionarms.x, + v0.y * proportionarms.y, + v0.z * proportionarms.z); + if (p1 == leftfoot || p1 == rightfoot || p1 == leftankle || p1 == rightankle || p1 == leftknee || p1 == rightknee || p2 == leftknee || p2 == rightknee) + glTranslatef(v0.x * proportionlegs.x, + v0.y * proportionlegs.y, + v0.z * proportionlegs.z); + if (p1 == head || p2 == head) + glTranslatef(v0.x * proportionhead.x, + v0.y * proportionhead.y, + v0.z * proportionhead.z); + glGetFloatv(GL_MODELVIEW_MATRIX, M); + skeleton.drawmodelclothes.vertex[skeleton.muscles[i].verticesclothes[j]].x = M[12] * scale; + skeleton.drawmodelclothes.vertex[skeleton.muscles[i].verticesclothes[j]].y = M[13] * scale; + skeleton.drawmodelclothes.vertex[skeleton.muscles[i].verticesclothes[j]].z = M[14] * scale; + glPopMatrix(); + } + glPopMatrix(); + } + updatedelay = 1 + (float)(Random() % 100) / 1000; + } + if (skeleton.free != 2 && (skeleton.free == 1 || skeleton.free == 3 || id == 0 || (normalsupdatedelay <= 0) || animTarget == getupfromfrontanim || animTarget == getupfrombackanim || animCurrent == getupfromfrontanim || animCurrent == getupfrombackanim)) { + normalsupdatedelay = 1; + if (playerdetail || skeleton.free == 3) + skeleton.drawmodel.CalculateNormals(0); + if (!playerdetail || skeleton.free == 3) + skeleton.drawmodellow.CalculateNormals(0); + if (skeleton.clothes) + skeleton.drawmodelclothes.CalculateNormals(0); + } else { + if (playerdetail || skeleton.free == 3) + skeleton.drawmodel.UpdateVertexArrayNoTexNoNorm(); + if (!playerdetail || skeleton.free == 3) + skeleton.drawmodellow.UpdateVertexArrayNoTexNoNorm(); + if (skeleton.clothes) { + skeleton.drawmodelclothes.UpdateVertexArrayNoTexNoNorm(); + } + } + } + framemult = .01; + updatedelaychange = -framemult * 4 * (45 - findDistance(&viewer, &coords) * 1); + if (updatedelaychange > -realmultiplier * 30) + updatedelaychange = -realmultiplier * 30; + if (updatedelaychange > -framemult * 4) + updatedelaychange = -framemult * 4; + if (skeleton.free == 1) + updatedelaychange *= 6; + if (id == 0) + updatedelaychange *= 8; + updatedelay += updatedelaychange; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(coords.x, coords.y - .02, coords.z); + if (!skeleton.free) { + glTranslatef(offset.x * scale, offset.y * scale, offset.z * scale); + glRotatef(yaw, 0, 1, 0); + } + if (showpoints) { + glPointSize(5); + glColor4f(.4, 1, .4, 1); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glBegin(GL_POINTS); + if (playerdetail) + for (i = 0; i < skeleton.drawmodel.vertexNum; i++) { + XYZ &v0 = skeleton.drawmodel.vertex[i]; + glVertex3f(v0.x, v0.y, v0.z); + } + glEnd(); + glBegin(GL_LINES); + + if (playerdetail) + for (i = 0; i < skeleton.drawmodel.TriangleNum; i++) { + XYZ &v0 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[0]]; + XYZ &v1 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[1]]; + XYZ &v2 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[2]]; + glVertex3f(v0.x, v0.y, v0.z); + glVertex3f(v1.x, v1.y, v1.z); + glVertex3f(v1.x, v1.y, v1.z); + glVertex3f(v2.x, v2.y, v2.z); + glVertex3f(v2.x, v2.y, v2.z); + glVertex3f(v0.x, v0.y, v0.z); + } + + glEnd(); + } + + terrainlight = terrain.getLighting(coords.x, coords.z); + distance = distsq(&viewer, &coords); + distance = (viewdistance * viewdistance - (distance - (viewdistance * viewdistance * fadestart)) * (1 / (1 - fadestart))) / viewdistance / viewdistance; + if (distance > 1) + distance = 1; + if (distance > 0) { + terrainheight = (coords.y - terrain.getHeight(coords.x, coords.z)) / 3 + 1; + if (terrainheight < 1) + terrainheight = 1; + if (terrainheight > 1.7) + terrainheight = 1.7; + + glColor4f((1 - (1 - terrainlight.x) / terrainheight) - burnt, (1 - (1 - terrainlight.y) / terrainheight) - burnt, (1 - (1 - terrainlight.z) / terrainheight) - burnt, distance); + glDisable(GL_BLEND); + glAlphaFunc(GL_GREATER, 0.0001); + glEnable(GL_TEXTURE_2D); + if (cellophane) { + glDisable(GL_TEXTURE_2D); + glColor4f(.7, .35, 0, .5); + glDepthMask(0); + glEnable(GL_LIGHTING); + glEnable(GL_BLEND); + } + if (tutoriallevel && id != 0) { + glColor4f(.7, .7, .7, 0.6); + glDepthMask(0); + glEnable(GL_LIGHTING); + glEnable(GL_BLEND); + if (canattack && cananger) + if (Animation::animations[animTarget].attack == normalattack || Animation::animations[animTarget].attack == reversed) { + glDisable(GL_TEXTURE_2D); + glColor4f(1, 0, 0, 0.8); + } + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glTranslatef(0, -smoketex, 0); + glTranslatef(-smoketex, 0, 0); + } + if (playerdetail) { + if (!showpoints) { + if ((tutoriallevel && id != 0)) + skeleton.drawmodel.drawdifftex(Sprite::cloudimpacttexture); + else + skeleton.drawmodel.draw(); + } + } + if (!playerdetail) { + if ((tutoriallevel && id != 0)) + skeleton.drawmodellow.drawdifftex(Sprite::cloudimpacttexture); + else + skeleton.drawmodellow.drawdifftex(skeleton.drawmodel.textureptr); + } + + if (!(Animation::animations[animTarget].attack == normalattack || Animation::animations[animTarget].attack == reversed)) + if (tutoriallevel && id != 0) { + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glEnable(GL_TEXTURE_2D); + glColor4f(.7, .7, .7, 0.6); + glDepthMask(0); + glEnable(GL_LIGHTING); + glEnable(GL_BLEND); + if (canattack && cananger) + if (Animation::animations[animTarget].attack == normalattack || Animation::animations[animTarget].attack == reversed) { + glDisable(GL_TEXTURE_2D); + glColor4f(1, 0, 0, 0.8); + } + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glTranslatef(0, -smoketex * .6, 0); + glTranslatef(smoketex * .6, 0, 0); + if (playerdetail) { + if (!showpoints) { + if ((tutoriallevel && id != 0)) + skeleton.drawmodel.drawdifftex(Sprite::cloudimpacttexture); + else + skeleton.drawmodel.draw(); + } + } + if (!playerdetail) { + if ((tutoriallevel && id != 0)) + skeleton.drawmodellow.drawdifftex(Sprite::cloudimpacttexture); + else + skeleton.drawmodellow.drawdifftex(skeleton.drawmodel.textureptr); + } + } + + + if (tutoriallevel && id != 0) { + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glEnable(GL_TEXTURE_2D); + } + if (skeleton.clothes) { + glDepthMask(0); + glEnable(GL_BLEND); + if (!immediate) + skeleton.drawmodelclothes.draw(); + if (immediate) + skeleton.drawmodelclothes.drawimmediate(); + glDepthMask(1); + } + } + glPopMatrix(); + + if (num_weapons > 0) { + for (k = 0; k < num_weapons; k++) { + i = weaponids[k]; + if (weaponactive == k) { + if (weapons[i].getType() != staff) { + for (j = 0; j < skeleton.muscles.size(); j++) { + if ((skeleton.muscles[j].parent1->label == righthand || skeleton.muscles[j].parent2->label == righthand) && skeleton.muscles[j].vertices.size() > 0) { + weaponattachmuscle = j; + } + } + for (j = 0; j < skeleton.muscles.size(); j++) { + if ((skeleton.muscles[j].parent1->label == rightwrist || skeleton.muscles[j].parent2->label == rightwrist) && (skeleton.muscles[j].parent1->label != righthand && skeleton.muscles[j].parent2->label != righthand) && skeleton.muscles[j].vertices.size() > 0) { + weaponrotatemuscle = j; + } + } + weaponpoint = (skeleton.muscles[weaponattachmuscle].parent1->position + skeleton.muscles[weaponattachmuscle].parent2->position) / 2; + if (creature == wolftype) + weaponpoint = (jointPos(rightwrist) * .7 + jointPos(righthand) * .3); + } + if (weapons[i].getType() == staff) { + for (j = 0; j < skeleton.muscles.size(); j++) { + if ((skeleton.muscles[j].parent1->label == righthand || skeleton.muscles[j].parent2->label == righthand) && skeleton.muscles[j].vertices.size() > 0) { + weaponattachmuscle = j; + } + } + for (j = 0; j < skeleton.muscles.size(); j++) { + if ((skeleton.muscles[j].parent1->label == rightelbow || skeleton.muscles[j].parent2->label == rightelbow) && (skeleton.muscles[j].parent1->label != rightshoulder && skeleton.muscles[j].parent2->label != rightshoulder) && skeleton.muscles[j].vertices.size() > 0) { + weaponrotatemuscle = j; + } + } + //weaponpoint=jointPos(rightwrist); + weaponpoint = (skeleton.muscles[weaponattachmuscle].parent1->position + skeleton.muscles[weaponattachmuscle].parent2->position) / 2; + //weaponpoint+=skeleton.specialforward[1]*.1+(jointPos(rightwrist)-jointPos(rightelbow)); + XYZ tempnormthing, vec1, vec2; + vec1 = (jointPos(rightwrist) - jointPos(rightelbow)); + vec2 = (jointPos(rightwrist) - jointPos(rightshoulder)); + CrossProduct(&vec1, &vec2, &tempnormthing); + Normalise(&tempnormthing); + if (animTarget != staffhitanim && animCurrent != staffhitanim && animTarget != staffgroundsmashanim && animCurrent != staffgroundsmashanim && animTarget != staffspinhitanim && animCurrent != staffspinhitanim) + weaponpoint += tempnormthing * .1 - skeleton.specialforward[1] * .3 + (jointPos(rightwrist) - jointPos(rightelbow)); + } + } + if (weaponactive != k && weaponstuck != k) { + if (weapons[i].getType() == knife) + weaponpoint = jointPos(abdomen) + (jointPos(righthip) - jointPos(lefthip)) * .1 + (jointPos(rightshoulder) - jointPos(leftshoulder)) * .35; + if (weapons[i].getType() == sword) + weaponpoint = jointPos(abdomen) + (jointPos(lefthip) - jointPos(righthip)) * .09 + (jointPos(leftshoulder) - jointPos(rightshoulder)) * .33; + if (weapons[i].getType() == staff) + weaponpoint = jointPos(abdomen) + (jointPos(lefthip) - jointPos(righthip)) * .09 + (jointPos(leftshoulder) - jointPos(rightshoulder)) * .33; + for (j = 0; j < skeleton.muscles.size(); j++) { + if ((skeleton.muscles[j].parent1->label == abdomen || skeleton.muscles[j].parent2->label == abdomen) && (skeleton.muscles[j].parent1->label == neck || skeleton.muscles[j].parent2->label == neck) && skeleton.muscles[j].vertices.size() > 0) { + weaponrotatemuscle = j; + } + } + } + if (weaponstuck == k) { + if (weaponstuckwhere == 0) + weaponpoint = jointPos(abdomen) * .5 + jointPos(neck) * .5 - skeleton.forward * .8; + else + weaponpoint = jointPos(abdomen) * .5 + jointPos(neck) * .5 + skeleton.forward * .8; + for (j = 0; j < skeleton.muscles.size(); j++) { + if ((skeleton.muscles[j].parent1->label == abdomen || skeleton.muscles[j].parent2->label == abdomen) && (skeleton.muscles[j].parent1->label == neck || skeleton.muscles[j].parent2->label == neck) && skeleton.muscles[j].vertices.size() > 0) { + weaponrotatemuscle = j; + } + } + } + if (skeleton.free) { + weapons[i].position = weaponpoint * scale + coords; + weapons[i].bigrotation = 0; + weapons[i].bigtilt = 0; + weapons[i].bigtilt2 = 0; + } else { + weapons[i].position = DoRotation(DoRotation(DoRotation(weaponpoint, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords + currentoffset * (1 - target) * scale + targetoffset * target * scale; + weapons[i].bigrotation = yaw; + weapons[i].bigtilt = tilt; + weapons[i].bigtilt2 = tilt2; + } + weapons[i].rotation1 = skeleton.muscles[weaponrotatemuscle].lastrotate1; + weapons[i].rotation2 = skeleton.muscles[weaponrotatemuscle].lastrotate2; + weapons[i].rotation3 = skeleton.muscles[weaponrotatemuscle].lastrotate3; + if (weaponactive == k) { + if (weapons[i].getType() == knife) { + weapons[i].smallrotation = 180; + weapons[i].smallrotation2 = 0; + if (isCrouch() || wasCrouch()) { + weapons[i].smallrotation2 = 20; + } + if (animTarget == hurtidleanim) { + weapons[i].smallrotation2 = 50; + } + if ((animCurrent == crouchstabanim && animTarget == crouchstabanim) || (animCurrent == backhandspringanim && animTarget == backhandspringanim)) { + XYZ temppoint1, temppoint2; + float distance; + + temppoint1 = jointPos(righthand); + temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); + distance = findDistance(&temppoint1, &temppoint2); + weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); + weapons[i].rotation2 *= 360 / 6.28; + temppoint1.y = 0; + temppoint2.y = 0; + weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); + weapons[i].rotation1 *= 360 / 6.28; + weapons[i].rotation3 = 0; + weapons[i].smallrotation = -90; + weapons[i].smallrotation2 = 0; + if (temppoint1.x > temppoint2.x) + weapons[i].rotation1 = 360 - weapons[i].rotation1; + } + if ((animCurrent == knifeslashreversalanim && animTarget == knifeslashreversalanim) || (animCurrent == knifeslashreversedanim && animTarget == knifeslashreversedanim)) { + XYZ temppoint1, temppoint2; + float distance; + + temppoint1 = jointPos(righthand); + temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); + distance = findDistance(&temppoint1, &temppoint2); + weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); + weapons[i].rotation2 *= 360 / 6.28; + temppoint1.y = 0; + temppoint2.y = 0; + weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); + weapons[i].rotation1 *= 360 / 6.28; + weapons[i].rotation3 = 0; + weapons[i].smallrotation = 90; + weapons[i].smallrotation2 = 0; + if (temppoint1.x > temppoint2.x) + weapons[i].rotation1 = 360 - weapons[i].rotation1; + } + if (animTarget == knifethrowanim) { + weapons[i].smallrotation = 90; + //weapons[i].smallrotation2=-90; + weapons[i].smallrotation2 = 0; + weapons[i].rotation1 = 0; + weapons[i].rotation2 = 0; + weapons[i].rotation3 = 0; + } + if (animTarget == knifesneakattackanim && frameTarget < 5) { + weapons[i].smallrotation = -90; + weapons[i].rotation1 = 0; + weapons[i].rotation2 = 0; + weapons[i].rotation3 = 0; + } + } + if (weapons[i].getType() == sword) { + weapons[i].smallrotation = 0; + weapons[i].smallrotation2 = 0; + if (animTarget == knifethrowanim) { + weapons[i].smallrotation = -90; + weapons[i].smallrotation2 = 0; + weapons[i].rotation1 = 0; + weapons[i].rotation2 = 0; + weapons[i].rotation3 = 0; + } + if ((animTarget == swordgroundstabanim && animCurrent == swordgroundstabanim) || (animTarget == swordsneakattackanim && animCurrent == swordsneakattackanim) || (animTarget == swordslashparryanim && animCurrent == swordslashparryanim) || (animTarget == swordslashparriedanim && animCurrent == swordslashparriedanim) || (animTarget == swordslashreversalanim && animCurrent == swordslashreversalanim) || (animTarget == swordslashreversedanim && animCurrent == swordslashreversedanim) || (animTarget == knifeslashreversalanim && animCurrent == knifeslashreversalanim) || (animTarget == knifeslashreversedanim && animCurrent == knifeslashreversedanim) || (animTarget == swordslashanim && animCurrent == swordslashanim) || (animTarget == drawleftanim && animCurrent == drawleftanim) || (animCurrent == backhandspringanim && animTarget == backhandspringanim)) { + XYZ temppoint1, temppoint2; + float distance; + + temppoint1 = currentFrame().joints[skeleton.jointlabels[righthand]].position * (1 - target) + targetFrame().joints[skeleton.jointlabels[righthand]].position * (target); //jointPos(righthand); + temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); + distance = findDistance(&temppoint1, &temppoint2); + weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); + weapons[i].rotation2 *= 360 / 6.28; + temppoint1.y = 0; + temppoint2.y = 0; + weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); + weapons[i].rotation1 *= 360 / 6.28; + weapons[i].rotation3 = 0; + weapons[i].smallrotation = 90; + weapons[i].smallrotation2 = 0; + if (temppoint1.x > temppoint2.x) + weapons[i].rotation1 = 360 - weapons[i].rotation1; + } + } + if (weapons[i].getType() == staff) { + weapons[i].smallrotation = 100; + weapons[i].smallrotation2 = 0; + if ((animTarget == staffhitanim && animCurrent == staffhitanim) || (animTarget == staffhitreversedanim && animCurrent == staffhitreversedanim) || (animTarget == staffspinhitreversedanim && animCurrent == staffspinhitreversedanim) || (animTarget == staffgroundsmashanim && animCurrent == staffgroundsmashanim) || (animTarget == staffspinhitanim && animCurrent == staffspinhitanim)) { + XYZ temppoint1, temppoint2; + float distance; + + temppoint1 = currentFrame().joints[skeleton.jointlabels[righthand]].position * (1 - target) + targetFrame().joints[skeleton.jointlabels[righthand]].position * (target); //jointPos(righthand); + temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); + distance = findDistance(&temppoint1, &temppoint2); + weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); + weapons[i].rotation2 *= 360 / 6.28; + temppoint1.y = 0; + temppoint2.y = 0; + weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); + weapons[i].rotation1 *= 360 / 6.28; + weapons[i].rotation3 = 0; + weapons[i].smallrotation = 90; + weapons[i].smallrotation2 = 0; + if (temppoint1.x > temppoint2.x) + weapons[i].rotation1 = 360 - weapons[i].rotation1; + } + } + } + if (weaponactive != k && weaponstuck != k) { + if (weapons[i].getType() == knife) { + weapons[i].smallrotation = -70; + weapons[i].smallrotation2 = 10; + } + if (weapons[i].getType() == sword) { + weapons[i].smallrotation = -100; + weapons[i].smallrotation2 = -8; + } + if (weapons[i].getType() == staff) { + weapons[i].smallrotation = -100; + weapons[i].smallrotation2 = -8; + } + } + if (weaponstuck == k) { + if (weaponstuckwhere == 0) + weapons[i].smallrotation = 180; + else + weapons[i].smallrotation = 0; + weapons[i].smallrotation2 = 10; + } + } + } + } + + calcrot = 0; + if (skeleton.free) + calcrot = 1; + if (Animation::animations[animTarget].attack || isRun() || animTarget == staggerbackhardanim || isFlip() || animTarget == climbanim || animTarget == sneakanim || animTarget == rollanim || animTarget == walkanim || animTarget == backhandspringanim || isWallJump()) + calcrot = 1; + if (animCurrent != animTarget) + calcrot = 1; + if (skeleton.free == 2) + calcrot = 0; + + return 0; +} + + +/* FUNCTION? + */ +int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate, Model *model) +{ + static int i, j; + static float distance; + static float olddistance; + static int intersecting; + static int firstintersecting; + static XYZ point; + static XYZ oldp1; + static XYZ start, end; + static float slopethreshold = -.4; + + firstintersecting = -1; + + oldp1 = *p1; + *p1 = *p1 - *move; + if (distsq(p1, &model->boundingspherecenter) > radius * radius + model->boundingsphereradius * model->boundingsphereradius) + return -1; + if (*rotate) + *p1 = DoRotation(*p1, 0, -*rotate, 0); + for (i = 0; i < 4; i++) { + for (j = 0; j < model->TriangleNum; j++) { + if (model->facenormals[j].y <= slopethreshold) { + intersecting = 0; + distance = abs((model->facenormals[j].x * p1->x) + (model->facenormals[j].y * p1->y) + (model->facenormals[j].z * p1->z) - ((model->facenormals[j].x * model->vertex[model->Triangles[j].vertex[0]].x) + (model->facenormals[j].y * model->vertex[model->Triangles[j].vertex[0]].y) + (model->facenormals[j].z * model->vertex[model->Triangles[j].vertex[0]].z))); + if (distance < radius) { + point = *p1 - model->facenormals[j] * distance; + if (PointInTriangle( &point, model->facenormals[j], &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]])) + intersecting = 1; + if (!intersecting) + intersecting = sphere_line_intersection(&model->vertex[model->Triangles[j].vertex[0]], + &model->vertex[model->Triangles[j].vertex[1]], + p1, &radius); + if (!intersecting) + intersecting = sphere_line_intersection(&model->vertex[model->Triangles[j].vertex[1]], + &model->vertex[model->Triangles[j].vertex[2]], + p1, &radius); + if (!intersecting) + intersecting = sphere_line_intersection(&model->vertex[model->Triangles[j].vertex[0]], + &model->vertex[model->Triangles[j].vertex[2]], + p1, &radius); + end = *p1 - point; + if (dotproduct(&model->facenormals[j], &end) > 0 && intersecting) { + start = *p1; + end = *p1; + end.y -= radius; + if (LineFacetd(&start, &end, &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]], &model->facenormals[j], &point)) { + p1->y = point.y + radius; + if ((animTarget == jumpdownanim || isFlip())) { + if (isFlip() && (frameTarget < 5 || targetFrame().label == 7 || targetFrame().label == 4)) + RagDoll(0); + + if (animTarget == jumpupanim) { + jumppower = -4; + animTarget = getIdle(); + } + target = 0; + frameTarget = 0; + onterrain = 1; + + if (id == 0) { + pause_sound(whooshsound); + OPENAL_SetVolume(channels[whooshsound], 0); + } + + if ((animTarget == jumpdownanim || isFlip()) && !wasLanding() && !wasLandhard()) { + if (isFlip()) + jumppower = -4; + animTarget = getLanding(); + emit_sound_at(landsound, coords, 128.); + + if (id == 0) { + addEnvSound(coords); + } + } + } + } + } + } + if ((distance < olddistance || firstintersecting == -1) && intersecting) { + olddistance = distance; + firstintersecting = j; + *p = point; + } + } + } + for (j = 0; j < model->TriangleNum; j++) { + if (model->facenormals[j].y > slopethreshold) { + intersecting = 0; + start = *p1; + start.y -= radius / 4; + XYZ &v0 = model->vertex[model->Triangles[j].vertex[0]]; + XYZ &v1 = model->vertex[model->Triangles[j].vertex[1]]; + XYZ &v2 = model->vertex[model->Triangles[j].vertex[2]]; + distance = abs((model->facenormals[j].x * start.x) + + (model->facenormals[j].y * start.y) + + (model->facenormals[j].z * start.z) + - ((model->facenormals[j].x * v0.x) + + (model->facenormals[j].y * v0.y) + + (model->facenormals[j].z * v0.z))); + if (distance < radius * .5) { + point = start - model->facenormals[j] * distance; + if (PointInTriangle( &point, model->facenormals[j], &v0, &v1, &v2)) + intersecting = 1; + if (!intersecting) + intersecting = sphere_line_intersection(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, p1->x, p1->y, p1->z, radius / 2); + if (!intersecting) + intersecting = sphere_line_intersection(v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, p1->x, p1->y, p1->z, radius / 2); + if (!intersecting) + intersecting = sphere_line_intersection(v0.x, v0.y, v0.z, v2.x, v2.y, v2.z, p1->x, p1->y, p1->z, radius / 2); + end = *p1 - point; + if (dotproduct(&model->facenormals[j], &end) > 0 && intersecting) { + if ((animTarget == jumpdownanim || animTarget == jumpupanim || isFlip())) { + start = velocity; + velocity -= DoRotation(model->facenormals[j], 0, *rotate, 0) * findLength(&velocity) * abs(normaldotproduct(velocity, DoRotation(model->facenormals[j], 0, *rotate, 0))); //(distance-radius*.5)/multiplier; + if (findLengthfast(&start) < findLengthfast(&velocity)) + velocity = start; + } + *p1 += model->facenormals[j] * (distance - radius * .5); + } + } + if ((distance < olddistance || firstintersecting == -1) && intersecting) { + olddistance = distance; + firstintersecting = j; + *p = point; + } + } + } + } + if (*rotate) + *p = DoRotation(*p, 0, *rotate, 0); + *p = *p + *move; + if (*rotate) + *p1 = DoRotation(*p1, 0, *rotate, 0); + *p1 += *move; + return firstintersecting; +} + +void Person::takeWeapon(int weaponId) +{ + weaponactive = 0; + weapons[weaponId].owner = id; + if (num_weapons > 0) { + weaponids[num_weapons] = weaponids[0]; + } + num_weapons++; + weaponids[0] = weaponId; +} + +void Person::addClothes() +{ + if (numclothes > 0) { + for (int i = 0; i < numclothes; i++) { + addClothes(i); + } + DoMipmaps(); + } +} + +bool Person::addClothes(const int& clothesId) +{ + LOGFUNC; + const std::string fileName = clothes[clothesId]; + + GLubyte* array = &skeleton.skinText[0]; + + //Load Image + ImageRec texture; + bool opened = load_image(Folders::getResourcePath(fileName).c_str(), texture); + + float alphanum; + //Is it valid? + if (opened) { + float tintr = clothestintr[clothesId]; + float tintg = clothestintg[clothesId]; + float tintb = clothestintb[clothesId]; + + if (tintr > 1) tintr = 1; + if (tintg > 1) tintg = 1; + if (tintb > 1) tintb = 1; + + if (tintr < 0) tintr = 0; + if (tintg < 0) tintg = 0; + if (tintb < 0) tintb = 0; + + int bytesPerPixel = texture.bpp / 8; + + int tempnum = 0; + alphanum = 255; + for (int i = 0; i < (int)(texture.sizeY * texture.sizeX * bytesPerPixel); i++) { + if (bytesPerPixel == 3) + alphanum = 255; + else if ((i + 1) % 4 == 0) + alphanum = texture.data[i]; + if ((i + 1) % 4 || bytesPerPixel == 3) { + if ((i % 4) == 0) + texture.data[i] *= tintr; + if ((i % 4) == 1) + texture.data[i] *= tintg; + if ((i % 4) == 2) + texture.data[i] *= tintb; + array[tempnum] = (float)array[tempnum] * (1 - alphanum / 255) + (float)texture.data[i] * (alphanum / 255); + tempnum++; + } + } + return 1; + } else { + return 0; + } +} diff --git a/Source/Objects/Person.h b/Source/Objects/Person.h new file mode 100644 index 0000000..4fa115f --- /dev/null +++ b/Source/Objects/Person.h @@ -0,0 +1,403 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _PERSON_H_ +#define _PERSON_H_ + +/**> HEADER FILES <**/ +#include "Animation/Animation.h" +#include "Animation/Skeleton.h" +#include "Environment/Terrain.h" +#include "Graphic/gamegl.h" +#include "Graphic/Models.h" +#include "Graphic/Sprite.h" +#include "Math/Quaternions.h" +#include "Objects/Weapons.h" + +#include +#include + +#define passivetype 0 +#define guardtype 1 +#define searchtype 2 +#define attacktype 3 +#define attacktypecutoff 4 +#define playercontrolled 5 +#define gethelptype 6 +#define getweapontype 7 +#define pathfindtype 8 + +#define rabbittype 0 +#define wolftype 1 + +struct InvalidPersonException : public exception { + const char * what () const throw () { + return "Invalid weapon number"; + } +}; + +class Person : public enable_shared_from_this +{ +public: + static std::vector> players; + + int whichpatchx; + int whichpatchz; + + // animCurrent and animTarget are used to interpolate between different animations + // (and for a bunch of other things). + // animations interpolate with one another at various speeds. + // animTarget seems to determine the combat state? + int animCurrent; + int animTarget; + + // frameCurrent and frameTarget are used to interpolate between the frames of an animation + // (e.g. the crouched animation has only two frames, lerped back and forth slowly). + // animations advance at various speeds. + int frameCurrent; + int frameTarget; + + int oldanimCurrent; + int oldanimTarget; + int oldframeCurrent; + int oldframeTarget; + + int howactive; + + float parriedrecently; + + bool superruntoggle; + + int lastattack, lastattack2, lastattack3; + + XYZ currentoffset, targetoffset, offset; + float target; + float transspeed; + + XYZ realoldcoords; + XYZ oldcoords; + XYZ coords; + XYZ velocity; + + XYZ proportionhead; + XYZ proportionlegs; + XYZ proportionarms; + XYZ proportionbody; + + float unconscioustime; + + bool immobile; + + float velspeed; + float targetyaw; + float targetrot; + float rot; + float oldrot; + float lookyaw; + float lookpitch; + float yaw; + float pitch; + float lowyaw; + float tilt; + float targettilt; + float tilt2; + float targettilt2; + bool rabbitkickenabled; + + float bloodloss; + float bleeddelay; + float skiddelay; + float skiddingdelay; + float deathbleeding; + float tempdeltav; + + float damagetolerance; + float damage; + float permanentdamage; + float superpermanentdamage; + float lastcollide; + /* Seems to be 0 = alive, 1 = unconscious, 2 = dead */ + int dead; + + float jumppower; + bool onground; + + int wentforweapon; + + bool calcrot; + + XYZ facing; + + float bleeding; + float bleedx, bleedy; + int direction; + float texupdatedelay; + + float headyaw, headpitch; + float targetheadyaw, targetheadpitch; + + bool onterrain; + bool pause; + + float grabdelay; + + std::shared_ptr victim; + bool hasvictim; + + float updatedelay; + float normalsupdatedelay; + + bool jumpstart; + + bool forwardkeydown; + bool forwardstogglekeydown; + bool rightkeydown; + bool leftkeydown; + bool backkeydown; + bool jumpkeydown; + bool jumptogglekeydown; + bool crouchkeydown; + bool crouchtogglekeydown; + bool drawkeydown; + bool drawtogglekeydown; + bool throwkeydown; + bool throwtogglekeydown; + bool attackkeydown; + bool feint; + bool lastfeint; + bool headless; + + float crouchkeydowntime; + float jumpkeydowntime; + bool freefall; + + + float turnspeed; + + int aitype; + float aiupdatedelay; + float losupdatedelay; + int ally; + float collide; + float collided; + float avoidcollided; + bool loaded; + bool whichdirection; + float whichdirectiondelay; + bool avoidsomething; + XYZ avoidwhere; + float blooddimamount; + + float staggerdelay; + float blinkdelay; + float twitchdelay; + float twitchdelay2; + float twitchdelay3; + float lefthandmorphness; + float righthandmorphness; + float headmorphness; + float chestmorphness; + float tailmorphness; + float targetlefthandmorphness; + float targetrighthandmorphness; + float targetheadmorphness; + float targetchestmorphness; + float targettailmorphness; + int lefthandmorphstart, lefthandmorphend; + int righthandmorphstart, righthandmorphend; + int headmorphstart, headmorphend; + int chestmorphstart, chestmorphend; + int tailmorphstart, tailmorphend; + + float weaponmissdelay; + float highreversaldelay; + float lowreversaldelay; + + int creature; + + unsigned id; + + Skeleton skeleton; + + float speed; + float scale; + float power; + float speedmult; + + float protectionhead; + float protectionhigh; + float protectionlow; + float armorhead; + float armorhigh; + float armorlow; + bool metalhead; + bool metalhigh; + bool metallow; + + int numclothes; + char clothes[10][256]; + float clothestintr[10]; + float clothestintg[10]; + float clothestintb[10]; + + bool landhard; + bool bled; + bool spurt; + bool onfire; + float onfiredelay; + float burnt; + + float flamedelay; + + int playerdetail; + + int num_weapons; + int weaponids[4]; + /* Key of weaponids which is the weapon in hand, if any. -1 otherwise. + * Always 0 or -1 as activeweapon is moved to position 0 when taken */ + int weaponactive; + int weaponstuck; + /* 0 or 1 to say if weapon is stuck in the front or the back */ + int weaponstuckwhere; + + int numwaypoints; + XYZ waypoints[90]; + int waypointtype[90]; + float pausetime; + + XYZ headtarget; + float interestdelay; + + XYZ finalfinaltarget; + XYZ finaltarget; + int finalpathfindpoint; + int targetpathfindpoint; + int lastpathfindpoint; + int lastpathfindpoint2; + int lastpathfindpoint3; + int lastpathfindpoint4; + + int waypoint; + + XYZ lastseen; + float lastseentime; + float lastchecktime; + float stunned; + float surprised; + float runninghowlong; + int occluded; + int lastoccluded; + int laststanding; + int escapednum; + + float speechdelay; + float neckspurtdelay; + float neckspurtparticledelay; + float neckspurtamount; + + int whichskin; + bool rabbitkickragdoll; + + Animation tempanimation; + + bool jumpclimb; + + Person(); + Person(FILE*, int, unsigned); + + void skeletonLoad(bool clothes = false); + + // convenience functions + inline Joint& joint(int bodypart) { return skeleton.joints[skeleton.jointlabels[bodypart]]; } + inline XYZ& jointPos(int bodypart) { return joint(bodypart).position; } + inline XYZ& jointVel(int bodypart) { return joint(bodypart).velocity; } + inline AnimationFrame& currentFrame() { return Animation::animations.at(animCurrent).frames.at(frameCurrent); } + inline AnimationFrame& targetFrame() { return Animation::animations.at(animTarget).frames.at(frameTarget); } + + + void CheckKick(); + void CatchFire(); + void DoBlood(float howmuch, int which); + void DoBloodBig(float howmuch, int which); + bool DoBloodBigWhere(float howmuch, int which, XYZ where); + + bool wasIdle() { return animation_bits[animCurrent] & ab_idle; } + bool isIdle() { return animation_bits[animTarget] & ab_idle; } + int getIdle(); + + bool isSitting() { return animation_bits[animTarget] & ab_sit; } + + bool isSleeping() { return animation_bits[animTarget] & ab_sleep; } + + bool wasCrouch() { return animation_bits[animCurrent] & ab_crouch; } + bool isCrouch() { return animation_bits[animTarget] & ab_crouch; } + int getCrouch(); + + bool wasStop() { return animation_bits[animCurrent] & ab_stop; } + bool isStop() { return animation_bits[animTarget] & ab_stop; } + int getStop(); + + bool wasSneak(); + bool isSneak(); + int getSneak(); + + bool wasRun() { return animation_bits[animCurrent] & ab_run; } + bool isRun() { return animation_bits[animTarget] & ab_run; } + int getRun(); + + bool wasLanding() { return animation_bits[animCurrent] & ab_land; } + bool isLanding() { return animation_bits[animTarget] & ab_land; } + int getLanding(); + + bool wasLandhard() { return animation_bits[animCurrent] & ab_landhard; } + bool isLandhard() { return animation_bits[animTarget] & ab_landhard; } + int getLandhard(); + + bool wasFlip() { return animation_bits[animCurrent] & ab_flip; } + bool isFlip() { return animation_bits[animTarget] & ab_flip; } + + bool isWallJump() { return animation_bits[animTarget] & ab_walljump; } + void Reverse(); + void DoDamage(float howmuch); + void DoHead(); + void DoMipmaps() { + skeleton.drawmodel.textureptr.bind(); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, skeleton.skinsize, skeleton.skinsize, 0, GL_RGB, GL_UNSIGNED_BYTE, &skeleton.skinText[0]); + } + + int SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate, Model *model); + int DrawSkeleton(); + void Puff(int whichlabel); + void FootLand(bodypart whichfoot, float opacity); + void DoStuff(); + void setAnimation(int); + void DoAnimations(); + void RagDoll(bool checkcollision); + + void takeWeapon (int weaponId); + + bool addClothes(const int& clothesId); + void addClothes(); +}; + +const int maxplayers = 10; + +#endif diff --git a/Source/Objects/Weapons.cpp b/Source/Objects/Weapons.cpp new file mode 100644 index 0000000..c56779d --- /dev/null +++ b/Source/Objects/Weapons.cpp @@ -0,0 +1,1124 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +/**> HEADER FILES <**/ +#include "Animation/Animation.h" +#include "Audio/openal_wrapper.h" +#include "Audio/Sounds.h" +#include "Level/Awards.h" +#include "Objects/Weapons.h" + +#include "Game.h" + +extern float multiplier; +extern Terrain terrain; +extern float gravity; +extern int environment; +extern int detail; +extern FRUSTUM frustum; +extern XYZ viewer; +extern float realmultiplier; +extern int slomo; +extern float slomodelay; +extern bool cellophane; +extern float texdetail; +extern GLubyte bloodText[512 * 512 * 3]; +extern int bloodtoggle; +extern Objects objects; +extern bool autoslomo; +extern float camerashake; +extern float woozy; +extern float viewdistance; +extern float blackout; +extern bool freeze; +extern int tutoriallevel; +extern int numthrowkill; + +Model Weapon::throwingknifemodel; +Texture Weapon::knifetextureptr; +Texture Weapon::lightbloodknifetextureptr; +Texture Weapon::bloodknifetextureptr; + +Model Weapon::swordmodel; +Texture Weapon::swordtextureptr; +Texture Weapon::lightbloodswordtextureptr; +Texture Weapon::bloodswordtextureptr; + +Model Weapon::staffmodel; +Texture Weapon::stafftextureptr; + +Weapon::Weapon(int t, int o) : owner(o) +{ + setType(t); + bloody = 0; + blooddrip = 0; + blooddripdelay = 0; + onfire = 0; + flamedelay = 0; + damage = 0; + position = -1000; + tippoint = -1000; +} + +void Weapon::setType(int t) +{ + type = t; + if (type == sword) { + mass = 1.5; + tipmass = 1; + length = .8; + } + if (type == staff) { + mass = 2; + tipmass = 1; + length = 1.5; + } + if (type == knife) { + mass = 1; + tipmass = 1.2; + length = .25; + } +} + +void Weapon::DoStuff(int i) +{ + //~ cout << position.x << "," << position.y << "," << position.z << "|" << tippoint.x << "," << tippoint.y << "," << tippoint.z << endl; + static int whichpatchx, whichpatchz, whichhit; + static XYZ start, end, colpoint, normalrot, footvel, footpoint; + static XYZ terrainnormal; + static XYZ vel; + static XYZ midp; + static XYZ newpoint1, newpoint2; + static float friction = 3.5; + static float elasticity = .4; + static XYZ bounceness; + static float frictionness; + static float closestdistance; + static float distance; + static XYZ point[3]; + static XYZ closestpoint; + static XYZ closestswordpoint; + static float tempmult; + + if (owner != -1) { + oldowner = owner; + } + if (damage >= 2 && type == staff && owner != -1) { // the staff breaks + emit_sound_at(staffbreaksound, tippoint); + XYZ tempvel; + for (int j = 0; j < 40; j++) { + tempvel.x = float(abs(Random() % 100) - 50) / 20; + tempvel.y = float(abs(Random() % 100) - 50) / 20; + tempvel.z = float(abs(Random() % 100) - 50) / 20; + Sprite::MakeSprite(splintersprite, position + (tippoint - position) * ((float)j - 8) / 32, tempvel * .5, 115 / 255, 73 / 255, 12 / 255, .1, 1); + } + if (owner != -1) { + Person::players[owner]->weaponactive = -1; + Person::players[owner]->num_weapons--; + if (Person::players[owner]->num_weapons) { + Person::players[owner]->weaponids[0] = Person::players[owner]->weaponids[Person::players[owner]->num_weapons]; + if (Person::players[owner]->weaponstuck == Person::players[owner]->num_weapons) + Person::players[owner]->weaponstuck = 0; + } + } + owner = -1; + hitsomething = 0; + missed = 1; + freetime = 0; + firstfree = 1; + position = 0; + physics = 0; + } + oldposition = position; + oldtippoint = tippoint; + if (owner == -1 && (velocity.x || velocity.y || velocity.z) && !physics) { // if the weapon is flying + position += velocity * multiplier; + tippoint += velocity * multiplier; + whichpatchx = position.x / (terrain.size / subdivision * terrain.scale); + whichpatchz = position.z / (terrain.size / subdivision * terrain.scale); + if (whichpatchx > 0 && whichpatchz > 0 && whichpatchx < subdivision && whichpatchz < subdivision) { + if (terrain.patchobjectnum[whichpatchx][whichpatchz]) { // if there are objects where the weapon is + for (int j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { // check for collision + int k = terrain.patchobjects[whichpatchx][whichpatchz][j]; + start = oldtippoint; + end = tippoint; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + if (objects.type[k] == treetrunktype) { + objects.model[k].MakeDecal(breakdecal, DoRotation(colpoint - objects.position[k], 0, -objects.yaw[k], 0), .1, 1, Random() % 360); + normalrot = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0); + velocity = 0; + if (type == knife) + position = colpoint - normalrot * .1; + else if (type == sword) + position = colpoint - normalrot * .2; + else if (type == staff) + position = colpoint - normalrot * .2; + XYZ temppoint1, temppoint2; + float distance; + + temppoint1 = 0; + temppoint2 = normalrot; + distance = findDistance(&temppoint1, &temppoint2); + rotation2 = asin((temppoint1.y - temppoint2.y) / distance); + rotation2 *= 360 / 6.28; + temppoint1.y = 0; + temppoint2.y = 0; + rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); + rotation1 *= 360 / 6.28; + if (temppoint1.x > temppoint2.x) + rotation1 = 360 - rotation1; + + rotation3 = 0; + smallrotation = 90; + smallrotation2 = 0; + bigtilt = 0; + bigtilt2 = 0; + bigrotation = 0; + + emit_sound_at(knifesheathesound, position, 128.); + + bloody = 0; + + Sprite::MakeSprite(cloudimpactsprite, position, velocity, 1, 1, 1, .8, .3); + } else { + physics = 1; + firstfree = 1; + position -= velocity * multiplier; + tippoint -= velocity * multiplier; + tipvelocity = velocity; + } + } + } + } + } + + if (velocity.x || velocity.y || velocity.z) { + for (unsigned j = 0; j < Person::players.size(); j++) { + footvel = 0; + footpoint = DoRotation((Person::players[j]->jointPos(abdomen) + Person::players[j]->jointPos(neck)) / 2, 0, Person::players[j]->yaw, 0) * Person::players[j]->scale + Person::players[j]->coords; + if (owner == -1 && distsqflat(&position, &Person::players[j]->coords) < 1.5 && + distsq(&position, &Person::players[j]->coords) < 4 && Person::players[j]->weaponstuck == -1 && + !Person::players[j]->skeleton.free && (int(j) != oldowner)) { + if ((Person::players[j]->aitype != attacktypecutoff || abs(Random() % 6) == 0 || (Person::players[j]->animTarget != backhandspringanim && Person::players[j]->animTarget != rollanim && Person::players[j]->animTarget != flipanim && Random() % 2 == 0)) && !missed) { + if ( (Person::players[j]->creature == wolftype && Random() % 3 != 0 && Person::players[j]->weaponactive == -1 && (Person::players[j]->isIdle() || Person::players[j]->isRun() || Person::players[j]->animTarget == walkanim)) || + (Person::players[j]->creature == rabbittype && Random() % 2 == 0 && Person::players[j]->aitype == attacktypecutoff && Person::players[j]->weaponactive == -1)) { + emit_sound_at(knifedrawsound, Person::players[j]->coords, 128.); + + Person::players[j]->animTarget = removeknifeanim; + Person::players[j]->frameTarget = 1; + Person::players[j]->target = 1; + Person::players[j]->takeWeapon(i); + + Person::players[j]->aitype = attacktypecutoff; + } else { + if (j != 0) + numthrowkill++; + Person::players[j]->num_weapons++; + Person::players[j]->weaponstuck = Person::players[j]->num_weapons - 1; + if (normaldotproduct(Person::players[j]->facing, velocity) > 0) + Person::players[j]->weaponstuckwhere = 1; + else + Person::players[j]->weaponstuckwhere = 0; + + Person::players[j]->weaponids[Person::players[j]->num_weapons - 1] = i; + + Person::players[j]->RagDoll(0); + Person::players[j]->jointVel(abdomen) += velocity * 2; + Person::players[j]->jointVel(neck) += velocity * 2; + Person::players[j]->jointVel(rightshoulder) += velocity * 2; + Person::players[j]->jointVel(leftshoulder) += velocity * 2; + if (bloodtoggle && tutoriallevel != 1) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .8, .3); + if (tutoriallevel == 1) + Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .8, .3); + footvel = tippoint - position; + Normalise(&footvel); + if (bloodtoggle && tutoriallevel != 1) + Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * -1, 1, 0, 0, .6, 1); + + if (tutoriallevel != 1) { + if (Person::players[j]->weaponstuckwhere == 0) + Person::players[j]->DoBloodBig(2, 205); + if (Person::players[j]->weaponstuckwhere == 1) + Person::players[j]->DoBloodBig(2, 200); + Person::players[j]->damage += 200 / Person::players[j]->armorhigh; + Person::players[j]->deathbleeding = 1; + Person::players[j]->bloodloss += (200 + abs((float)(Random() % 40)) - 20) / Person::players[j]->armorhigh; + owner = j; + bloody = 2; + blooddrip = 5; + } + + emit_sound_at(fleshstabsound, position, 128.); + + if (Animation::animations[Person::players[0]->animTarget].height == highheight) + award_bonus(0, ninja); + else + award_bonus(0, Bullseyebonus); + } + } else { + missed = 1; + } + } + } + } + if (position.y < terrain.getHeight(position.x, position.z)) { + if (terrain.getOpacity(position.x, position.z) < .2) { + velocity = 0; + if (terrain.lineTerrain(oldposition, position, &colpoint) != -1) { + position = colpoint * terrain.scale; + } else { + position.y = terrain.getHeight(position.x, position.z); + } + + terrain.MakeDecal(shadowdecalpermanent, position, .06, .5, 0); + normalrot = terrain.getNormal(position.x, position.z) * -1; + velocity = 0; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + GLfloat M[16]; + glLoadIdentity(); + glRotatef(bigrotation, 0, 1, 0); + glRotatef(bigtilt2, 1, 0, 0); + glRotatef(bigtilt, 0, 0, 1); + glRotatef(-rotation1 + 90, 0, 1, 0); + glRotatef(-rotation2 + 90, 0, 0, 1); + glRotatef(-rotation3, 0, 1, 0); + glRotatef(smallrotation, 1, 0, 0); + glRotatef(smallrotation2, 0, 1, 0); + glTranslatef(0, 0, 1); + glGetFloatv(GL_MODELVIEW_MATRIX, M); + tippoint.x = M[12]; + tippoint.y = M[13]; + tippoint.z = M[14]; + glPopMatrix(); + position -= tippoint * .15; + XYZ temppoint1, temppoint2; + + rotation3 = 0; + smallrotation = 90; + smallrotation2 = 0; + bigtilt = 0; + bigtilt2 = 0; + bigrotation = 0; + + emit_sound_at(knifesheathesound, position, 128.); + + XYZ terrainlight; + terrainlight = terrain.getLighting(position.x, position.z); + if (environment == snowyenvironment) { + if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7); + } else if (environment == grassyenvironment) { + if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5); + } else if (environment == desertenvironment) { + if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7); + } + + bloody = 0; + } else { + physics = 1; + firstfree = 1; + position -= velocity * multiplier; + tippoint -= velocity * multiplier; + tipvelocity = velocity; + } + } + if (velocity.x != 0 || velocity.z != 0 || velocity.y != 0) { + velocity.y += gravity * multiplier; + + XYZ temppoint1, temppoint2; + float distance; + + temppoint1 = 0; + temppoint2 = velocity; + distance = findDistance(&temppoint1, &temppoint2); + rotation2 = asin((temppoint1.y - temppoint2.y) / distance); + rotation2 *= 360 / 6.28; + temppoint1.y = 0; + temppoint2.y = 0; + rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); + rotation1 *= 360 / 6.28; + rotation3 = 0; + smallrotation = 90; + smallrotation2 = 0; + bigtilt = 0; + bigtilt2 = 0; + bigrotation = 0; + if (temppoint1.x > temppoint2.x) + rotation1 = 360 - rotation1; + } + + } + + //Sword physics + XYZ mid; + XYZ oldmid; + XYZ oldmid2; + + tempmult = multiplier; + multiplier /= 10; + for (int l = 0; l < 10; l++) { + if (owner == -1 && (velocity.x || velocity.y || velocity.z) && physics) { + //move + position += velocity * multiplier; + tippoint += tipvelocity * multiplier; + + //Length constrain + midp = (position * mass + tippoint * tipmass) / (mass + tipmass); + vel = tippoint - midp; + Normalise(&vel); + newpoint1 = midp - vel * length * (tipmass / (mass + tipmass)); + newpoint2 = midp + vel * length * (mass / (mass + tipmass)); + if (!freeze) { + if (freetime > .04) { + velocity = velocity + (newpoint1 - position) / multiplier; + tipvelocity = tipvelocity + (newpoint2 - tippoint) / multiplier; + } + } + position = newpoint1; + tippoint = newpoint2; + + + //Object collisions + whichpatchx = (position.x) / (terrain.size / subdivision * terrain.scale); + whichpatchz = (position.z) / (terrain.size / subdivision * terrain.scale); + if (whichpatchx > 0 && whichpatchz > 0 && whichpatchx < subdivision && whichpatchz < subdivision) + if (terrain.patchobjectnum[whichpatchx][whichpatchz]) { + for (int j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { + int k = terrain.patchobjects[whichpatchx][whichpatchz][j]; + + if (firstfree) { + if (type == staff) { + start = tippoint - (position - tippoint) / 5; + end = position + (position - tippoint) / 30; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + XYZ diff; + diff = (colpoint - position); + Normalise(&diff); + hitsomething = 1; + + tippoint += (colpoint - position) + diff * .05; + position = colpoint + diff * .05; + oldtippoint = tippoint; + oldposition = tippoint; + } + } else { + start = position - (tippoint - position) / 5; + end = tippoint + (tippoint - position) / 30; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + XYZ diff; + diff = (colpoint - tippoint); + Normalise(&diff); + hitsomething = 1; + + position += (colpoint - tippoint) + diff * .05; + tippoint = colpoint + diff * .05; + oldposition = position; + oldtippoint = tippoint; + } + } + } + + start = oldposition; + end = position; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + hitsomething = 1; + position = colpoint; + terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; + ReflectVector(&velocity, &terrainnormal); + position += terrainnormal * .002; + + bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); + if (findLengthfast(&velocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(velocity, terrainnormal)); + velocity -= bounceness; + if (1 - friction * frictionness > 0) + velocity *= 1 - friction * frictionness; + else + velocity = 0; + velocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + else + whichsound = clank1sound + abs(Random() % 4); + emit_sound_at(whichsound, position, 128 * findLengthfast(&bounceness)); + } + } + start = oldtippoint; + end = tippoint; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + hitsomething = 1; + tippoint = colpoint; + terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; + ReflectVector(&tipvelocity, &terrainnormal); + tippoint += terrainnormal * .002; + + bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); + if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); + tipvelocity -= bounceness; + if (1 - friction * frictionness > 0) + tipvelocity *= 1 - friction * frictionness; + else + tipvelocity = 0; + tipvelocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + else + whichsound = clank1sound + abs(Random() % 4); + emit_sound_at(whichsound, position, 128 * findLengthfast(&bounceness)); + } + } + + if ((objects.type[k] != boxtype && objects.type[k] != platformtype && objects.type[k] != walltype && objects.type[k] != weirdtype) || objects.pitch[k] != 0) + for (int m = 0; m < 2; m++) { + mid = (position * (21 + (float)m * 10) + tippoint * (19 - (float)m * 10)) / 40; + oldmid2 = mid; + oldmid = (oldposition * (21 + (float)m * 10) + oldtippoint * (19 - (float)m * 10)) / 40; + + start = oldmid; + end = mid; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + hitsomething = 1; + mid = colpoint; + terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; + ReflectVector(&velocity, &terrainnormal); + + bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); + if (findLengthfast(&velocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(velocity, terrainnormal)); + velocity -= bounceness; + if (1 - friction * frictionness > 0) + velocity *= 1 - friction * frictionness; + else + velocity = 0; + velocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + else + whichsound = clank1sound + abs(Random() % 4); + emit_sound_at(whichsound, mid, 128 * findLengthfast(&bounceness)); + } + position += (mid - oldmid2) * (20 / (1 + (float)m * 10)); + } + + mid = (position * (19 - (float)m * 10) + tippoint * (21 + (float)m * 10)) / 40; + oldmid2 = mid; + oldmid = (oldposition * (19 - (float)m * 10) + oldtippoint * (21 + (float)m * 10)) / 40; + + start = oldmid; + end = mid; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + hitsomething = 1; + mid = colpoint; + terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; + ReflectVector(&tipvelocity, &terrainnormal); + + bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); + if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); + tipvelocity -= bounceness; + if (1 - friction * frictionness > 0) + tipvelocity *= 1 - friction * frictionness; + else + tipvelocity = 0; + tipvelocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + else + whichsound = clank1sound + abs(Random() % 4); + emit_sound_at(whichsound, mid, 128 * findLengthfast(&bounceness)); + } + tippoint += (mid - oldmid2) * (20 / (1 + (float)m * 10)); + } + } + else { + start = position; + end = tippoint; + whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); + if (whichhit != -1) { + hitsomething = 1; + closestdistance = -1; + closestswordpoint = colpoint; //(position+tippoint)/2; + point[0] = DoRotation(objects.model[k].vertex[objects.model[k].Triangles[whichhit].vertex[0]], 0, objects.yaw[k], 0) + objects.position[k]; + point[1] = DoRotation(objects.model[k].vertex[objects.model[k].Triangles[whichhit].vertex[1]], 0, objects.yaw[k], 0) + objects.position[k]; + point[2] = DoRotation(objects.model[k].vertex[objects.model[k].Triangles[whichhit].vertex[2]], 0, objects.yaw[k], 0) + objects.position[k]; + if (DistancePointLine(&closestswordpoint, &point[0], &point[1], &distance, &colpoint )) { + if (distance < closestdistance || closestdistance == -1) { + closestpoint = colpoint; + closestdistance = distance; + } + } + if (DistancePointLine(&closestswordpoint, &point[1], &point[2], &distance, &colpoint )) { + if (distance < closestdistance || closestdistance == -1) { + closestpoint = colpoint; + closestdistance = distance; + } + } + if (DistancePointLine(&closestswordpoint, &point[2], &point[0], &distance, &colpoint )) { + if (distance < closestdistance || closestdistance == -1) { + closestpoint = colpoint; + closestdistance = distance; + } + } + if (closestdistance != -1 && isnormal(closestdistance)) { + if (DistancePointLine(&closestpoint, &position, &tippoint, &distance, &colpoint )) { + closestswordpoint = colpoint; + velocity += (closestpoint - closestswordpoint); + tipvelocity += (closestpoint - closestswordpoint); + position += (closestpoint - closestswordpoint); + tippoint += (closestpoint - closestswordpoint); + } + } + } + } + } + } + //Terrain collisions + whichhit = terrain.lineTerrain(oldposition, position, &colpoint); + if (whichhit != -1 || position.y < terrain.getHeight(position.x, position.z)) { + hitsomething = 1; + if (whichhit != -1) + position = colpoint * terrain.scale; + else + position.y = terrain.getHeight(position.x, position.z); + + terrainnormal = terrain.getNormal(position.x, position.z); + ReflectVector(&velocity, &terrainnormal); + position += terrainnormal * .002; + bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); + if (findLengthfast(&velocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(velocity, terrainnormal)); + velocity -= bounceness; + if (1 - friction * frictionness > 0) + velocity *= 1 - friction * frictionness; + else + velocity = 0; + if (terrain.getOpacity(position.x, position.z) < .2) + velocity += bounceness * elasticity * .3; + else + velocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (terrain.getOpacity(position.x, position.z) > .2) { + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + else + whichsound = clank1sound + abs(Random() % 4); + } else { + whichsound = footstepsound + abs(Random() % 2); + } + emit_sound_at(whichsound, position, + findLengthfast(&bounceness) + * (terrain.getOpacity(position.x, position.z) > .2 ? 128. : 32.)); + + if (terrain.getOpacity(position.x, position.z) < .2) { + XYZ terrainlight; + terrainlight = terrain.getLighting(position.x, position.z); + if (environment == snowyenvironment) { + if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7); + } else if (environment == grassyenvironment) { + if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5); + } else if (environment == desertenvironment) { + if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7); + } + } + } + } + whichhit = terrain.lineTerrain(oldtippoint, tippoint, &colpoint); + if (whichhit != -1 || tippoint.y < terrain.getHeight(tippoint.x, tippoint.z)) { + if (whichhit != -1) + tippoint = colpoint * terrain.scale; + else + tippoint.y = terrain.getHeight(tippoint.x, tippoint.z); + + terrainnormal = terrain.getNormal(tippoint.x, tippoint.z); + ReflectVector(&tipvelocity, &terrainnormal); + tippoint += terrainnormal * .002; + bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); + if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); + tipvelocity -= bounceness; + if (1 - friction * frictionness > 0) + tipvelocity *= 1 - friction * frictionness; + else + tipvelocity = 0; + if (terrain.getOpacity(tippoint.x, tippoint.z) < .2) + tipvelocity += bounceness * elasticity * .3; + else + tipvelocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (terrain.getOpacity(tippoint.x, tippoint.z) > .2) { + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + else + whichsound = clank1sound + abs(Random() % 4); + } else { + whichsound = footstepsound + abs(Random() % 2); + } + emit_sound_at(whichsound, tippoint, + findLengthfast(&bounceness) + * (terrain.getOpacity(tippoint.x, tippoint.z) > .2 ? 128. : 32.)); + + if (terrain.getOpacity(tippoint.x, tippoint.z) < .2) { + XYZ terrainlight; + terrainlight = terrain.getLighting(tippoint.x, tippoint.z); + if (environment == snowyenvironment) { + if (distsq(&tippoint, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, tippoint, tipvelocity, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7); + } else if (environment == grassyenvironment) { + if (distsq(&tippoint, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, tippoint, tipvelocity, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5); + } else if (environment == desertenvironment) { + if (distsq(&tippoint, &viewer) < viewdistance * viewdistance / 4) + Sprite::MakeSprite(cloudsprite, tippoint, tipvelocity, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7); + } + } + } + } + + //Edges + mid = position + tippoint; + mid /= 2; + mid += (position - mid) / 20; + oldmid = mid; + if (mid.y < terrain.getHeight(mid.x, mid.z)) { + hitsomething = 1; + mid.y = terrain.getHeight(mid.x, mid.z); + + terrainnormal = terrain.getNormal(mid.x, mid.z); + ReflectVector(&velocity, &terrainnormal); + //mid+=terrainnormal*.002; + bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); + if (findLengthfast(&velocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(velocity, terrainnormal)); + velocity -= bounceness; + if (1 - friction * frictionness > 0) + velocity *= 1 - friction * frictionness; + else + velocity = 0; + if (terrain.getOpacity(mid.x, mid.z) < .2) + velocity += bounceness * elasticity * .3; + else + velocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (terrain.getOpacity(mid.x, mid.z) > .2) { + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + if (type != staff) + whichsound = clank1sound + abs(Random() % 4); + } else { + whichsound = footstepsound + abs(Random() % 2); + } + emit_sound_at(whichsound, mid, + findLengthfast(&bounceness) + * (terrain.getOpacity(position.x, position.z) > .2 + ? 128. + : 32.)); + } + position += (mid - oldmid) * 20; + } + + mid = position + tippoint; + mid /= 2; + mid += (tippoint - mid) / 20; + oldmid = mid; + if (mid.y < terrain.getHeight(mid.x, mid.z)) { + hitsomething = 1; + mid.y = terrain.getHeight(mid.x, mid.z); + + terrainnormal = terrain.getNormal(mid.x, mid.z); + ReflectVector(&tipvelocity, &terrainnormal); + //mid+=terrainnormal*.002; + bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); + if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) + bounceness = 0; + frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); + tipvelocity -= bounceness; + if (1 - friction * frictionness > 0) + tipvelocity *= 1 - friction * frictionness; + else + tipvelocity = 0; + if (terrain.getOpacity(mid.x, mid.z) < .2) + tipvelocity += bounceness * elasticity * .3; + else + tipvelocity += bounceness * elasticity; + + if (findLengthfast(&bounceness) > 1) { + int whichsound; + if (terrain.getOpacity(mid.x, mid.z) > .2) { + if (type == staff) + whichsound = footstepsound3 + abs(Random() % 2); + if (type != staff) + whichsound = clank1sound + abs(Random() % 4); + } else { + whichsound = footstepsound + abs(Random() % 2); + } + emit_sound_at(whichsound, mid, + findLengthfast(&bounceness) + * (terrain.getOpacity(position.x, position.z) > .2 + ? 128. + : 32.)); + } + tippoint += (mid - oldmid) * 20; + } + //Gravity + velocity.y += gravity * multiplier; + tipvelocity.y += gravity * multiplier; + + //Rotation + XYZ temppoint1, temppoint2; + float distance; + + temppoint1 = position; + temppoint2 = tippoint; + distance = findDistance(&temppoint1, &temppoint2); + rotation2 = asin((temppoint1.y - temppoint2.y) / distance); + rotation2 *= 360 / 6.28; + temppoint1.y = 0; + temppoint2.y = 0; + rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); + rotation1 *= 360 / 6.28; + rotation3 = 0; + smallrotation = 90; + smallrotation2 = 0; + bigtilt = 0; + bigtilt2 = 0; + bigrotation = 0; + if (temppoint1.x > temppoint2.x) + rotation1 = 360 - rotation1; + + //Stop moving + if (findLengthfast(&velocity) < .3 && findLengthfast(&tipvelocity) < .3 && hitsomething) { + freetime += multiplier; + } + + if (freetime > .4) { + velocity = 0; + tipvelocity = 0; + } + firstfree = 0; + } + } + multiplier = tempmult; + if (blooddrip && bloody) { + blooddripdelay -= blooddrip * multiplier / 2; + blooddrip -= multiplier; + if (blooddrip < 0) + blooddrip = 0; + if (blooddrip > 5) + blooddrip = 5; + if (blooddripdelay < 0 && bloodtoggle) { + blooddripdelay = 1; + XYZ bloodvel; + XYZ bloodloc; + bloodloc = position + (tippoint - position) * .7; + bloodloc.y -= .05; + if (bloodtoggle) { + bloodvel = 0; + Sprite::MakeSprite(bloodsprite, bloodloc, bloodvel, 1, 1, 1, .03, 1); + } + } + } + if (onfire) { + flamedelay -= multiplier; + if (onfire && flamedelay <= 0) { + flamedelay = .020; + flamedelay -= multiplier; + normalrot = 0; + if (owner != -1) { + normalrot = Person::players[owner]->velocity; + } + normalrot.y += 1; + if (owner != -1) { + if (Person::players[owner]->onterrain) { + normalrot.y = 1; + } + } + Sprite::MakeSprite(weaponflamesprite, position + tippoint * (((float)abs(Random() % 100)) / 600 + .05), normalrot, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 1 / 3, 1); + Sprite::setLastSpriteSpeed(4); + Sprite::setLastSpriteAlivetime(.3); + } + } + + if (!onfire && owner == -1 && type != staff) { + flamedelay -= multiplier; + if (flamedelay <= 0) { + flamedelay = .020; + flamedelay -= multiplier; + normalrot = 0; + if (Random() % 50 == 0 && distsq(&position, &viewer) > 80) { + XYZ shinepoint; + shinepoint = position + (tippoint - position) * (((float)abs(Random() % 100)) / 100); + Sprite::MakeSprite(weaponshinesprite, shinepoint, normalrot, 1, 1, 1, (.1 + (float)abs(Random() % 100) / 200 - .25) * 1 / 3 * fast_sqrt(findDistance(&shinepoint, &viewer)), 1); + Sprite::setLastSpriteSpeed(4); + Sprite::setLastSpriteAlivetime(.3); + } + } + } +} + +void Weapons::DoStuff() +{ + //Move + int i = 0; + for (std::vector::iterator weapon = begin(); weapon != end(); ++weapon) { + weapon->DoStuff(i++); + } +} + +void Weapon::Draw() +{ + static XYZ terrainlight; + static GLfloat M[16]; + + if ((frustum.SphereInFrustum(position.x, position.y, position.z, 1) && + distsq(&viewer, &position) < viewdistance * viewdistance)) { + bool draw = false; + if (owner == -1) { + draw = true; + if (velocity.x && !physics) + drawhowmany = 10; + else + drawhowmany = 1; + } else { + if (Person::players[owner]->occluded < 25) + if ((frustum.SphereInFrustum(Person::players[owner]->coords.x, Person::players[owner]->coords.y + Person::players[owner]->scale * 3, Person::players[owner]->coords.z, Person::players[owner]->scale * 8) && distsq(&viewer, &Person::players[owner]->coords) < viewdistance * viewdistance) || Person::players[owner]->skeleton.free == 3) + draw = true; + if ( + (Person::players[owner]->animTarget == knifeslashstartanim || + Person::players[owner]->animTarget == swordsneakattackanim || + (Person::players[owner]->animCurrent == staffhitanim && Person::players[owner]->frameCurrent > 1) || + (Person::players[owner]->animCurrent == staffhitreversedanim && Person::players[owner]->frameCurrent > 1) || + (Person::players[owner]->animCurrent == staffspinhitanim && Person::players[owner]->frameCurrent > 1) || + (Person::players[owner]->animCurrent == staffspinhitreversedanim && Person::players[owner]->frameCurrent > 1) || + (Person::players[owner]->animCurrent == staffgroundsmashanim && Person::players[owner]->frameCurrent > 1) || + (Person::players[owner]->animTarget == swordslashanim && Person::players[owner]->frameTarget < 7) || + Person::players[owner]->animTarget == crouchstabanim || + Person::players[owner]->animTarget == swordslashreversalanim || + Person::players[owner]->animTarget == swordslashreversedanim || + Person::players[owner]->animTarget == knifefollowanim || + Person::players[owner]->animTarget == swordgroundstabanim || + Person::players[owner]->animTarget == knifethrowanim) && + Person::players[owner]->animTarget == lastdrawnanim && + !Person::players[owner]->skeleton.free + ) { + drawhowmany = 10; + } else { + drawhowmany = 1; + } + if (Person::players[owner]->animTarget == swordgroundstabanim) { + lastdrawnrotation1 = rotation1; + lastdrawnrotation2 = rotation2; + lastdrawnrotation3 = rotation3; + lastdrawnbigrotation = bigrotation; + lastdrawnbigtilt = bigtilt; + lastdrawnbigtilt2 = bigtilt2; + lastdrawnsmallrotation = smallrotation; + lastdrawnsmallrotation2 = smallrotation2; + } + } + if (draw) { + terrainlight = terrain.getLighting(position.x, position.z); + if (drawhowmany > 0) { + glAlphaFunc(GL_GREATER, 0.01); + } + for (int j = drawhowmany; j > 0; j--) { + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, j / drawhowmany); + if (owner == -1) + glTranslatef(position.x * (((float)(j)) / drawhowmany) + lastdrawnposition.x * (1 - ((float)(j)) / drawhowmany), position.y * (((float)(j)) / drawhowmany) + lastdrawnposition.y * (1 - ((float)(j)) / drawhowmany), position.z * (((float)(j)) / drawhowmany) + lastdrawnposition.z * (1 - ((float)(j)) / drawhowmany)); + else + glTranslatef(position.x * (((float)(j)) / drawhowmany) + lastdrawnposition.x * (1 - ((float)(j)) / drawhowmany), position.y * (((float)(j)) / drawhowmany) - .02 + lastdrawnposition.y * (1 - ((float)(j)) / drawhowmany), position.z * (((float)(j)) / drawhowmany) + lastdrawnposition.z * (1 - ((float)(j)) / drawhowmany)); + glRotatef(bigrotation * (((float)(j)) / drawhowmany) + lastdrawnbigrotation * (1 - ((float)(j)) / drawhowmany), 0, 1, 0); + glRotatef(bigtilt2 * (((float)(j)) / drawhowmany) + lastdrawnbigtilt2 * (1 - ((float)(j)) / drawhowmany), 1, 0, 0); + glRotatef(bigtilt * (((float)(j)) / drawhowmany) + lastdrawnbigtilt * (1 - ((float)(j)) / drawhowmany), 0, 0, 1); + glRotatef(-rotation1 * (((float)(j)) / drawhowmany) - lastdrawnrotation1 * (1 - ((float)(j)) / drawhowmany) + 90, 0, 1, 0); + glRotatef(-rotation2 * (((float)(j)) / drawhowmany) - lastdrawnrotation2 * (1 - ((float)(j)) / drawhowmany) + 90, 0, 0, 1); + glRotatef(-rotation3 * (((float)(j)) / drawhowmany) - lastdrawnrotation3 * (1 - ((float)(j)) / drawhowmany), 0, 1, 0); + glRotatef(smallrotation * (((float)(j)) / drawhowmany) + lastdrawnsmallrotation * (1 - ((float)(j)) / drawhowmany), 1, 0, 0); + glRotatef(smallrotation2 * (((float)(j)) / drawhowmany) + lastdrawnsmallrotation2 * (1 - ((float)(j)) / drawhowmany), 0, 1, 0); + + if (owner != -1) { + if (Person::players[owner]->animTarget == staffhitanim || Person::players[owner]->animCurrent == staffhitanim || Person::players[owner]->animTarget == staffhitreversedanim || Person::players[owner]->animCurrent == staffhitreversedanim) { + glTranslatef(0, 0, -.3); + } + if (Person::players[owner]->animTarget == staffgroundsmashanim || Person::players[owner]->animCurrent == staffgroundsmashanim || Person::players[owner]->animTarget == staffspinhitreversedanim || Person::players[owner]->animCurrent == staffspinhitreversedanim || Person::players[owner]->animTarget == staffspinhitanim || Person::players[owner]->animCurrent == staffspinhitanim) { + glTranslatef(0, 0, -.1); + } + } + + glEnable(GL_LIGHTING); + switch (type) { + case knife: + if (!bloody || !bloodtoggle) + throwingknifemodel.drawdifftex(knifetextureptr); + if (bloodtoggle) { + if (bloody == 1) + throwingknifemodel.drawdifftex(lightbloodknifetextureptr); + if (bloody == 2) + throwingknifemodel.drawdifftex(bloodknifetextureptr); + } + break; + case sword: + if (!bloody || !bloodtoggle) + swordmodel.drawdifftex(swordtextureptr); + if (bloodtoggle) { + if (bloody == 1) + swordmodel.drawdifftex(lightbloodswordtextureptr); + if (bloody == 2) + swordmodel.drawdifftex(bloodswordtextureptr); + } + break; + case staff: + staffmodel.drawdifftex(stafftextureptr); + break; + } + + glPopMatrix(); + } + + lastdrawnposition = position; + lastdrawntippoint = tippoint; + lastdrawnrotation1 = rotation1; + lastdrawnrotation2 = rotation2; + lastdrawnrotation3 = rotation3; + lastdrawnbigrotation = bigrotation; + lastdrawnbigtilt = bigtilt; + lastdrawnbigtilt2 = bigtilt2; + lastdrawnsmallrotation = smallrotation; + lastdrawnsmallrotation2 = smallrotation2; + if (owner != -1) + lastdrawnanim = Person::players[owner]->animCurrent; + } + if (owner != -1) { + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(position.x, position.y - .02, position.z); + glRotatef(bigrotation, 0, 1, 0); + glRotatef(bigtilt2, 1, 0, 0); + glRotatef(bigtilt, 0, 0, 1); + glRotatef(-rotation1 + 90, 0, 1, 0); + glRotatef(-rotation2 + 90, 0, 0, 1); + glRotatef(-rotation3, 0, 1, 0); + glRotatef(smallrotation, 1, 0, 0); + glRotatef(smallrotation2, 0, 1, 0); + glTranslatef(0, 0, length); + glGetFloatv(GL_MODELVIEW_MATRIX, M); + tippoint.x = M[12]; + tippoint.y = M[13]; + tippoint.z = M[14]; + glPopMatrix(); + } + } +} + +void Weapon::drop(XYZ v, XYZ tv, bool sethitsomething) +{ + owner = -1; + velocity = v; + tipvelocity = tv; + missed = 1; + if (sethitsomething) { + hitsomething = 0; + } + freetime = 0; + firstfree = 1; + physics = 1; +} + +void Weapon::thrown(XYZ v, bool sethitsomething) +{ + drop(v, v, sethitsomething); + missed = 0; + physics = 0; +} + +int Weapons::Draw() +{ + glAlphaFunc(GL_GREATER, 0.9); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + glDepthMask(1); + + for (std::vector::iterator weapon = begin(); weapon != end(); ++weapon) { + weapon->Draw(); + } + return 0; +} + +Weapons::Weapons() +{ +} + +Weapons::~Weapons() +{ + Weapon::stafftextureptr.destroy(); + Weapon::knifetextureptr.destroy(); + Weapon::lightbloodknifetextureptr.destroy(); + Weapon::bloodknifetextureptr.destroy(); + Weapon::swordtextureptr.destroy(); + Weapon::lightbloodswordtextureptr.destroy(); + Weapon::bloodswordtextureptr.destroy(); +} + diff --git a/Source/Objects/Weapons.h b/Source/Objects/Weapons.h new file mode 100644 index 0000000..51ab000 --- /dev/null +++ b/Source/Objects/Weapons.h @@ -0,0 +1,133 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _WEAPONS_H_ +#define _WEAPONS_H_ + +/**> HEADER FILES <**/ +#include "Animation/Skeleton.h" +#include "Environment/Terrain.h" +#include "Graphic/gamegl.h" +#include "Graphic/Models.h" +#include "Graphic/Sprite.h" +#include "Graphic/Texture.h" +#include "Math/Quaternions.h" +#include "Objects/Person.h" + +#include + +#define max_weapons 30 +#define max_weaponinstances 20 + +#define knife 1 +#define sword 2 +#define staff 3 + +class Weapon +{ +public: + Weapon(int type, int owner); + + static Model throwingknifemodel; + static Texture knifetextureptr; + static Texture lightbloodknifetextureptr; + static Texture bloodknifetextureptr; + + static Model swordmodel; + static Texture swordtextureptr; + static Texture lightbloodswordtextureptr; + static Texture bloodswordtextureptr; + + static Model staffmodel; + static Texture stafftextureptr; + + void Draw(); + void DoStuff(int); + + int getType() { + return type; + } + void setType(int); + + void drop(XYZ velocity, XYZ tipvelocity, bool sethitsomething = true); + void thrown(XYZ velocity, bool sethitsomething = true); + + int owner; + XYZ position; + XYZ tippoint; + XYZ velocity; + XYZ tipvelocity; + bool missed; + bool hitsomething; + float freetime; + bool firstfree; + bool physics; + + float damage; + int bloody; + float blooddrip; + float blooddripdelay; + + float rotation1; + float rotation2; + float rotation3; + float bigrotation; + float bigtilt; + float bigtilt2; + float smallrotation; + float smallrotation2; +private: + int type; + + XYZ oldtippoint; + XYZ oldposition; + int oldowner; + bool onfire; + float flamedelay; + float mass; + float tipmass; + float length; + float drawhowmany; + + XYZ lastdrawnposition; + XYZ lastdrawntippoint; + float lastdrawnrotation1; + float lastdrawnrotation2; + float lastdrawnrotation3; + float lastdrawnbigrotation; + float lastdrawnbigtilt; + float lastdrawnbigtilt2; + float lastdrawnsmallrotation; + float lastdrawnsmallrotation2; + int lastdrawnanim; +}; + +class Weapons : public std::vector +{ +public: + Weapons(); + ~Weapons(); + + int Draw(); + void DoStuff(); +}; + +extern Weapons weapons; +#endif diff --git a/Source/Person.cpp b/Source/Person.cpp deleted file mode 100644 index fb3c445..0000000 --- a/Source/Person.cpp +++ /dev/null @@ -1,6892 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -/**> HEADER FILES <**/ -#include "Person.h" -#include "openal_wrapper.h" -#include "Animation/Animation.h" -#include "Sounds.h" -#include "Awards.h" -#include "Game.h" -#include "Dialog.h" -#include "Utils/Folders.h" - -extern float multiplier; -extern Terrain terrain; -extern float gravity; -extern int environment; -extern int detail; -extern FRUSTUM frustum; -extern XYZ viewer; -extern float realmultiplier; -extern int slomo; -extern float slomodelay; -extern bool cellophane; -extern float texdetail; -extern float realtexdetail; -extern GLubyte bloodText[512 * 512 * 3]; -extern GLubyte wolfbloodText[512 * 512 * 3]; -extern int bloodtoggle; -extern Objects objects; -extern bool autoslomo; -extern float camerashake; -extern float woozy; -extern float viewdistance; -extern float blackout; -extern int difficulty; -extern bool decals; -extern float fadestart; -extern bool freeze; -extern bool winfreeze; -extern bool showpoints; -extern bool immediate; -extern int tutoriallevel; -extern float smoketex; -extern int tutorialstage; -extern bool reversaltrain; -extern bool canattack; -extern bool cananger; -extern float damagedealt; -extern int hostile; -extern float hostiletime; - -extern bool gamestarted; - -std::vector> Person::players(1, std::shared_ptr(new Person())); - -Person::Person() : - whichpatchx(0), - whichpatchz(0), - animCurrent(bounceidleanim), - animTarget(bounceidleanim), - frameCurrent(0), - frameTarget(1), - oldanimCurrent(0), - oldanimTarget(0), - oldframeCurrent(0), - oldframeTarget(0), - howactive(typeactive), - parriedrecently(0), - superruntoggle(false), - lastattack(0), lastattack2(0), lastattack3(0), - currentoffset(), targetoffset(), offset(), - target(0), - transspeed(0), - - realoldcoords(), - oldcoords(), - coords(), - velocity(), - - proportionhead(), - proportionlegs(), - proportionarms(), - proportionbody(), - - unconscioustime(0), - - immobile(false), - - velspeed(0), - targetyaw(0), - targetrot(0), - rot(0), - oldrot(0), - lookyaw(0), - lookpitch(0), - yaw(0), - pitch(0), - lowyaw(0), - tilt(0), - targettilt(0), - tilt2(0), - targettilt2(0), - rabbitkickenabled(false), - - bloodloss(0), - bleeddelay(0), - skiddelay(0), - skiddingdelay(0), - deathbleeding(0), - tempdeltav(0), - - damagetolerance(200), - damage(0), - permanentdamage(0), - superpermanentdamage(0), - lastcollide(0), - dead(0), - - jumppower(5), - onground(false), - - wentforweapon(0), - - calcrot(false), - - facing(), - - bleeding(0), - bleedx(0), bleedy(0), - direction(0), - texupdatedelay(0), - - headyaw(0), headpitch(0), - targetheadyaw(0), targetheadpitch(0), - - onterrain(false), - pause(false), - - grabdelay(0), - - victim(nullptr), - hasvictim(false), - - updatedelay(0), - normalsupdatedelay(0), - - jumpstart(false), - forwardkeydown(false), - forwardstogglekeydown(false), - rightkeydown(false), - leftkeydown(false), - backkeydown(false), - jumpkeydown(false), - jumptogglekeydown(false), - crouchkeydown(false), - crouchtogglekeydown(false), - drawkeydown(false), - drawtogglekeydown(false), - throwkeydown(false), - throwtogglekeydown(false), - attackkeydown(false), - feint(false), - lastfeint(false), - headless(false), - - crouchkeydowntime(0), - jumpkeydowntime(0), - freefall(false), - - turnspeed(0), - - aitype(passivetype), - aiupdatedelay(0), - losupdatedelay(0), - ally(0), - collide(0), - collided(-10), - avoidcollided(0), - loaded(false), - whichdirection(false), - whichdirectiondelay(0), - avoidsomething(false), - avoidwhere(), - blooddimamount(0), - - staggerdelay(0), - blinkdelay(0), - twitchdelay(0), - twitchdelay2(0), - twitchdelay3(0), - lefthandmorphness(0), - righthandmorphness(0), - headmorphness(0), - chestmorphness(0), - tailmorphness(0), - targetlefthandmorphness(0), - targetrighthandmorphness(0), - targetheadmorphness(1), - targetchestmorphness(0), - targettailmorphness(0), - lefthandmorphstart(0), lefthandmorphend(0), - righthandmorphstart(0), righthandmorphend(0), - headmorphstart(0), headmorphend(0), - chestmorphstart(0), chestmorphend(0), - tailmorphstart(0), tailmorphend(0), - - weaponmissdelay(0), - highreversaldelay(0), - lowreversaldelay(0), - - creature(rabbittype), - - id(0), - - skeleton(), - - speed(0), - scale(-1), - power(0), - speedmult(0), - - protectionhead(0), - protectionhigh(0), - protectionlow(0), - armorhead(0), - armorhigh(0), - armorlow(0), - metalhead(false), - metalhigh(false), - metallow(false), - - numclothes(0), - - landhard(false), - bled(false), - spurt(false), - onfire(false), - onfiredelay(0), - burnt(0), - - flamedelay(0), - - playerdetail(0), - - num_weapons(0), - weaponactive(-1), - weaponstuck(-1), - weaponstuckwhere(0), - - numwaypoints(0), - pausetime(0), - - headtarget(), - interestdelay(0), - - finalfinaltarget(), - finaltarget(), - finalpathfindpoint(0), - targetpathfindpoint(0), - lastpathfindpoint(0), - lastpathfindpoint2(0), - lastpathfindpoint3(0), - lastpathfindpoint4(0), - - waypoint(0), - - lastseen(), - lastseentime(0), - lastchecktime(0), - stunned(0), - surprised(0), - runninghowlong(0), - occluded(0), - lastoccluded(0), - laststanding(0), - escapednum(0), - - speechdelay(0), - neckspurtdelay(0), - neckspurtparticledelay(0), - neckspurtamount(0), - - whichskin(0), - rabbitkickragdoll(false), - - tempanimation(), - - jumpclimb(false) -{ -} - -/* Read a person in tfile. Throws an error if it’s not valid */ -Person::Person(FILE *tfile, int mapvers, unsigned i) : Person() -{ - id = i; - funpackf(tfile, "Bi Bi Bf Bf Bf Bi", &whichskin, &creature, &coords.x, &coords.y, &coords.z, &num_weapons); - if (mapvers >= 5) { - funpackf(tfile, "Bi", &howactive); - } else { - howactive = typeactive; - } - if (mapvers >= 3) { - funpackf(tfile, "Bf", &scale); - } else { - scale = -1; - } - if (mapvers >= 11) { - funpackf(tfile, "Bb", &immobile); - } else { - immobile = 0; - } - if (mapvers >= 12) { - funpackf(tfile, "Bf", &yaw); - } else { - yaw = 0; - } - targetyaw = yaw; - if (num_weapons < 0 || num_weapons > 5) { - throw InvalidPersonException(); - } - if (num_weapons > 0 && num_weapons < 5) { - for (int j = 0; j < num_weapons; j++) { - weaponids[j] = weapons.size(); - int type; - funpackf(tfile, "Bi", &type); - weapons.push_back(Weapon(type, id)); - } - } - funpackf(tfile, "Bi", &numwaypoints); - for (int j = 0; j < numwaypoints; j++) { - funpackf(tfile, "Bf", &waypoints[j].x); - funpackf(tfile, "Bf", &waypoints[j].y); - funpackf(tfile, "Bf", &waypoints[j].z); - if (mapvers >= 5) { - funpackf(tfile, "Bi", &waypointtype[j]); - } else { - waypointtype[j] = wpkeepwalking; - } - } - - funpackf(tfile, "Bi", &waypoint); - if (waypoint > (numwaypoints - 1)) { - waypoint = 0; - } - - funpackf(tfile, "Bf Bf Bf", &armorhead, &armorhigh, &armorlow); - funpackf(tfile, "Bf Bf Bf", &protectionhead, &protectionhigh, &protectionlow); - funpackf(tfile, "Bf Bf Bf", &metalhead, &metalhigh, &metallow); - funpackf(tfile, "Bf Bf", &power, &speedmult); - - float headprop, legprop, armprop, bodyprop; - - if (mapvers >= 4) { - funpackf(tfile, "Bf Bf Bf Bf", &headprop, &bodyprop, &armprop, &legprop); - } else { - headprop = 1; - bodyprop = 1; - armprop = 1; - legprop = 1; - } - - if (creature == wolftype) { - proportionhead = 1.1 * headprop; - proportionbody = 1.1 * bodyprop; - proportionarms = 1.1 * armprop; - proportionlegs = 1.1 * legprop; - } else if (creature == rabbittype) { - proportionhead = 1.2 * headprop; - proportionbody = 1.05 * bodyprop; - proportionarms = 1.00 * armprop; - proportionlegs = 1.1 * legprop; - proportionlegs.y = 1.05 * legprop; - } - - funpackf(tfile, "Bi", &numclothes); - for (int k = 0; k < numclothes; k++) { - int templength; - funpackf(tfile, "Bi", &templength); - for (int l = 0; l < templength; l++) - funpackf(tfile, "Bb", &clothes[k][l]); - clothes[k][templength] = '\0'; - funpackf(tfile, "Bf Bf Bf", &clothestintr[k], &clothestintg[k], &clothestintb[k]); - } - - loaded = true; - - if (scale < 0) { - if (creature == wolftype) { - scale = .23; - damagetolerance = 300; - } else { - scale = .2; - } - } - - oldcoords = coords; - realoldcoords = coords; -} - -void Person::skeletonLoad(bool clothes) -{ - skeleton.id = id; - if (creature != wolftype) { - skeleton.Load( - "Skeleton/BasicFigure", - "Skeleton/BasicFigureLow", - "Skeleton/RabbitBelt", - "Models/Body.solid", - "Models/Body2.solid", - "Models/Body3.solid", - "Models/Body4.solid", - "Models/Body5.solid", - "Models/Body6.solid", - "Models/Body7.solid", - "Models/BodyLow.solid", - "Models/Belt.solid", - clothes - ); - } else { - skeleton.Load( - "Skeleton/BasicFigureWolf", - "Skeleton/BasicFigureWolfLow", - "Skeleton/RabbitBelt", - "Models/Wolf.solid", - "Models/Wolf2.solid", - "Models/Wolf3.solid", - "Models/Wolf4.solid", - "Models/Wolf5.solid", - "Models/Wolf6.solid", - "Models/Wolf7.solid", - "Models/WolfLow.solid", - "Models/Belt.solid", - clothes - ); - } - - skeleton.drawmodel.textureptr.load(creatureskin[creature][whichskin], 1, &skeleton.skinText[0], &skeleton.skinsize); -} - -/* EFFECT - * - * USES: - * GameTick/doPlayerCollisions - */ -void Person::CheckKick() -{ - if (!(hasvictim - && (animTarget == rabbitkickanim - && victim - && victim != this->shared_from_this() - && frameCurrent >= 2 - && animCurrent == rabbitkickanim) - && distsq(&coords, &victim->coords) < 1.2 - && !victim->skeleton.free)) - return; - - if (Animation::animations[victim->animTarget].height != lowheight) { - float damagemult = (creature == wolftype ? 2.5 : 1.) * power * power; - XYZ relative = velocity; - relative.y = 0; - Normalise(&relative); - - victim->spurt = 1; - DoBlood(.2, 250); - if (tutoriallevel != 1) - emit_sound_at(heavyimpactsound, victim->coords); - victim->RagDoll(0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * 120 * damagemult; - } - victim->Puff(neck); - victim->DoDamage(100 * damagemult / victim->protectionhigh); - if (id == 0) - camerashake += .4; - - target = 0; - frameCurrent = 3; - animTarget = backflipanim; - frameTarget = 4; - velocity = facing * -10; - velocity.y = 5; - skeleton.free = 0; - if (id == 0) - resume_stream(whooshsound); - - award_bonus(id, cannon); - } else if (victim->isCrouch()) { - animTarget = rabbitkickreversedanim; - animCurrent = rabbitkickreversedanim; - victim->animCurrent = rabbitkickreversalanim; - victim->animTarget = rabbitkickreversalanim; - targettilt2 = 0; - frameCurrent = 0; - frameTarget = 1; - target = 0; - velocity = 0; - victim->oldcoords = victim->coords; - coords = victim->coords; - victim->targetyaw = targetyaw; - victim->victim = this->shared_from_this(); - } -} - -/* EFFECT - * - * USES: - * GameTick/doPlayerCollisions - spread fire between players - * GameTick/doDevKeys - press f to ignite - * Person::DoStuff - spread fire from lit campfires and bushes - */ -void Person::CatchFire() -{ - XYZ flatfacing, flatvelocity; - int howmany; - for (int i = 0; i < 10; i++) { - howmany = abs(Random() % (skeleton.joints.size())); - if (skeleton.free) { - flatvelocity = skeleton.joints[howmany].velocity; - flatfacing = skeleton.joints[howmany].position * scale + coords; - } else { - flatvelocity = velocity; - flatfacing = DoRotation(DoRotation(DoRotation(skeleton.joints[howmany].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; - } - Sprite::MakeSprite(flamesprite, flatfacing, flatvelocity, 1, 1, 1, 2, 1); - } - - onfiredelay = 0.5; - - emit_sound_at(firestartsound, coords); - - emit_stream_at(stream_firesound, coords); - - flamedelay = 0; - - onfire = 1; -} - -/* FUNCTION - * idle animation for this creature (depending on status) - */ -int Person::getIdle() -{ - if (Dialog::inDialog() && (howactive == typeactive) && (creature == rabbittype)) - return talkidleanim; - if (hasvictim && (victim != this->shared_from_this())/*||(id==0&&attackkeydown)*/) - if (/*(id==0&&attackkeydown)||*/(!victim->dead && victim->aitype != passivetype && - victim->aitype != searchtype && aitype != passivetype && aitype != searchtype && - victim->id < Person::players.size())) { - if ((aitype == playercontrolled && stunned <= 0 && weaponactive == -1) || pause) { - if (creature == rabbittype) - return fightidleanim; - if (creature == wolftype) - return wolfidle; - } - if (aitype == playercontrolled && stunned <= 0 && weaponactive != -1) { - if (weapons[weaponids[weaponactive]].getType() == knife) - return knifefightidleanim; - if (weapons[weaponids[weaponactive]].getType() == sword && victim->weaponactive != -1) - return swordfightidlebothanim; - if (weapons[weaponids[weaponactive]].getType() == sword) - return swordfightidleanim; - if (weapons[weaponids[weaponactive]].getType() == staff) - return swordfightidleanim; - } - if (aitype != playercontrolled && stunned <= 0 && creature != wolftype && !pause) - return fightsidestep; - } - if ((damage > permanentdamage || damage > damagetolerance * .8 || deathbleeding > 0) && creature != wolftype) - return hurtidleanim; - if (howactive == typesitting) return sitanim; - if (howactive == typesittingwall) return sitwallanim; - if (howactive == typesleeping) return sleepanim; - if (howactive == typedead1) return dead1anim; - if (howactive == typedead2) return dead2anim; - if (howactive == typedead3) return dead3anim; - if (howactive == typedead4) return dead4anim; - if (creature == rabbittype) return bounceidleanim; - if (creature == wolftype) return wolfidle; - return 0; -} - -/* FUNCTION - * crouch animation for this creature - */ -int Person::getCrouch() -{ - if (creature == rabbittype) - return crouchanim; - if (creature == wolftype) - return wolfcrouchanim; - return 0; -} - -/* FUNCTION - * running animation for this creature (can be upright or all fours) - */ -int Person::getRun() -{ - if (creature == rabbittype && (!superruntoggle || weaponactive != -1)) - return runanim; - if (creature == wolftype && (!superruntoggle)) - return wolfrunanim; - - if (creature == rabbittype && (superruntoggle && weaponactive == -1)) - return rabbitrunninganim; - if (creature == wolftype && (superruntoggle)) - return wolfrunninganim; - return 0; -} - -/* FUNCTION - */ -int Person::getStop() -{ - if (creature == rabbittype) - return stopanim; - if (creature == wolftype) - return wolfstopanim; - return 0; -} - -/* FUNCTION - */ -int Person::getLanding() -{ - if (creature == rabbittype) - return landanim; - if (creature == wolftype) - return wolflandanim; - return 0; -} - -/* FUNCTION - */ -int Person::getLandhard() -{ - if (creature == rabbittype) - return landhardanim; - if (creature == wolftype) - return wolflandhardanim; - return 0; -} - -/* EFFECT - * - * USES: - * Person::DoAnimations - */ -static void -SolidHitBonus(int playerid) -{ - if (bonustime < 1.5 && bonus >= solidhit && bonus <= megacombo) - award_bonus(playerid, bonus == megacombo ? bonus : bonus + 1); - else - award_bonus(playerid, solidhit); -} - -/* EFFECT - * spawns blood effects - */ -void Person::DoBlood(float howmuch, int which) -{ - // FIXME: should abstract out inputs - static int bleedxint, bleedyint; - static XYZ bloodvel; - if (bloodtoggle && tutoriallevel != 1) { - if (bleeding <= 0 && spurt) { - spurt = 0; - for (int i = 0; i < 3; i++) { - // emit blood particles - bloodvel = 0; - if (skeleton.free) { - bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); - bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); - Sprite::MakeSprite(bloodflamesprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .3, 1); - } else { - bloodvel.z = 10; - bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); - Sprite::MakeSprite(bloodflamesprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .3, 1); - } - } - if (Random() % 2 == 0) // 50% chance - for (int i = 0; i < 3; i++) { - if (Random() % 2 != 0) { - // emit teeth particles - bloodvel = 0; - if (skeleton.free) { - bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); - bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - } else { - bloodvel.z = 10; - bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; - } - bloodvel *= .2; - if (skeleton.free) { - Sprite::MakeSprite(splintersprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); - } else { - Sprite::MakeSprite(splintersprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); - } - Sprite::setLastSpriteSpecial(3); // sets it to teeth - } - } - } - if (decals) { - // FIXME: manipulating attributes - bleeding = howmuch + (float)abs(Random() % 100) / 200 - .25; - bleedxint = 0; - bleedyint = 0; - if (creature == rabbittype) - while (bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { - bleedxint = abs(Random() % 512); - bleedyint = abs(Random() % 512); - } - if (creature == wolftype) - while (wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { - bleedxint = abs(Random() % 512); - bleedyint = abs(Random() % 512); - } - bleedy = bleedxint; - bleedx = bleedyint; - bleedy /= realtexdetail; - bleedx /= realtexdetail; - direction = abs(Random() % 2) * 2 - 1; - } - - } - if (bleeding > 2) - bleeding = 2; -} - -/* EFFECT - * spawns big blood effects and ??? - * modifies character's skin texture - */ -void Person::DoBloodBig(float howmuch, int which) -{ - static int bleedxint, bleedyint, i, j; - static XYZ bloodvel; - if (howmuch && id == 0) - blooddimamount = 1; - - if (tutoriallevel != 1 || id == 0) - if (aitype != playercontrolled && howmuch > 0) { - // play pain sounds - int whichsound = -1; - - if (creature == wolftype) { - int i = abs(Random() % 2); - if (i == 0) - whichsound = snarlsound; - if (i == 1) - whichsound = snarl2sound; - } - if (creature == rabbittype) { - int i = abs(Random() % 2); - if (i == 0) - whichsound = rabbitpainsound; - if (i == 1 && howmuch >= 2) - whichsound = rabbitpain1sound; - } - - if (whichsound != -1) { - emit_sound_at(whichsound, coords); - addEnvSound(coords); - } - } - - if (id == 0 && howmuch > 0) { - Game::flash(.5, 0); - } - - if (bloodtoggle && decals && tutoriallevel != 1) { - if (bleeding <= 0 && spurt) { - spurt = 0; - for (int i = 0; i < 3; i++) { - // emit blood particles - // FIXME: copypaste from above - bloodvel = 0; - if (skeleton.free) { - bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); - bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); - Sprite::MakeSprite(bloodflamesprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .3, 1); - } else { - bloodvel.z = 10; - bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); - Sprite::MakeSprite(bloodflamesprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .3, 1); - } - } - } - - // weird texture manipulation code follows. - // looks like this is painting blood onto the character's skin texture - // FIXME: surely there's a better way - - int offsetx = 0, offsety = 0; - if (which == 225) { - offsety = Random() % 40; - offsetx = abs(Random() % 60); - } - if (which == 190 || which == 185) { - offsety = Random() % 40; - offsetx = abs(Random() % 100) - 20; - } - if (which == 175) { - offsety = Random() % 10; - offsetx = Random() % 10; - } - if (which == 170) { - offsety = Random() % 20; - offsetx = Random() % 20; - } - if (which == 220 || which == 215) { - offsetx = 20; - } - - - int startx = 512; - int starty = 512; - int endx = 0; - int endy = 0; - GLubyte color; - if (creature == rabbittype) - for (i = 0; i < 512; i++) { - for (j = 0; j < 512; j++) { - if (bloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && bloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { - if (i < startx) startx = i; - if (j < starty) starty = j; - if (i > endx) endx = i; - if (j > endy) endy = j; - } - } - } - if (creature == wolftype) - for (i = 0; i < 512; i++) { - for (j = 0; j < 512; j++) { - if (wolfbloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && wolfbloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { - if (i < startx) startx = i; - if (j < starty) starty = j; - if (i > endx) endx = i; - if (j > endy) endy = j; - } - } - } - - startx += offsetx; - endx += offsetx; - starty += offsety; - endy += offsety; - - if (startx < 0) startx = 0; - if (starty < 0) starty = 0; - if (endx > 512 - 1) endx = 512 - 1; - if (endy > 512 - 1) endy = 512 - 1; - if (endx < startx) endx = startx; - if (endy < starty) endy = starty; - - startx /= realtexdetail; - starty /= realtexdetail; - endx /= realtexdetail; - endy /= realtexdetail; - - int texdetailint = realtexdetail; - int where; - if (creature == rabbittype) - for (i = startx; i < endx; i++) { - for (j = starty; j < endy; j++) { - if (bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { - color = Random() % 85 + 170; - where = i * skeleton.skinsize * 3 + j * 3; - if (skeleton.skinText[where + 0] > color / 2) - skeleton.skinText[where + 0] = color / 2; - skeleton.skinText[where + 1] = 0; - skeleton.skinText[where + 2] = 0; - } - } - } - if (creature == wolftype) - for (i = startx; i < endx; i++) { - for (j = starty; j < endy; j++) { - if (wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { - color = Random() % 85 + 170; - where = i * skeleton.skinsize * 3 + j * 3; - if (skeleton.skinText[where + 0] > color / 2) - skeleton.skinText[where + 0] = color / 2; - skeleton.skinText[where + 1] = 0; - skeleton.skinText[where + 2] = 0; - } - } - } - skeleton.drawmodel.textureptr.bind(); - DoMipmaps(); - - bleedxint = 0; - bleedyint = 0; - if (creature == rabbittype) - while (bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || bloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { - bleedxint = abs(Random() % 512); - bleedyint = abs(Random() % 512); - } - if (creature == wolftype) - while (wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] > which + 4 || wolfbloodText[bleedxint * 512 * 3 + bleedyint * 3 + 0] < which - 4 || bleedxint < 10 || bleedyint < 10 || bleedxint > 500 || bleedyint > 500) { - bleedxint = abs(Random() % 512); - bleedyint = abs(Random() % 512); - } - bleedy = bleedxint + offsetx; - bleedx = bleedyint + offsety; - bleedy /= realtexdetail; - bleedx /= realtexdetail; - if (bleedx < 0) - bleedx = 0; - if (bleedy < 0) - bleedy = 0; - if (bleedx > skeleton.skinsize - 1) - bleedx = skeleton.skinsize - 1; - if (bleedy > skeleton.skinsize - 1) - bleedy = skeleton.skinsize - 1; - direction = abs(Random() % 2) * 2 - 1; - - } - bleeding = howmuch + (float)abs(Random() % 100) / 200 - .25; - deathbleeding += bleeding; - bloodloss += bleeding * 3; - - if (tutoriallevel != 1 && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) { - if (abs(Random() % 2) == 0) { - aitype = gethelptype; - lastseentime = 12; - } else - aitype = attacktypecutoff; - ally = 0; - } - if (bleeding > 2) - bleeding = 2; -} - -/* EFFECT - * similar to DoBloodBig - */ -bool Person::DoBloodBigWhere(float howmuch, int which, XYZ where) -{ - static int i, j; - static XYZ bloodvel; - static XYZ startpoint, endpoint, colpoint, movepoint; - static float rotationpoint; - static int whichtri; - static XYZ p1, p2, p3, p0; - XYZ bary; - XYZ gxx, gyy; - float coordsx, coordsy; - float total; - - if (bloodtoggle && decals && tutoriallevel != 1) { - where -= coords; - if (!skeleton.free) - where = DoRotation(where, 0, -yaw, 0); - //where=scale; - startpoint = where; - startpoint.y += 100; - endpoint = where; - endpoint.y -= 100; - movepoint = 0; - rotationpoint = 0; - // ray testing for a tri in the character model - whichtri = skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &colpoint, &movepoint, &rotationpoint); - if (whichtri != -1) { - // low level geometry math - p0 = colpoint; - p1 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[whichtri].vertex[0]]; - p2 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[whichtri].vertex[1]]; - p3 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[whichtri].vertex[2]]; - - bary.x = distsq(&p0, &p1); - bary.y = distsq(&p0, &p2); - bary.z = distsq(&p0, &p3); - - total = bary.x + bary.y + bary.z; - bary.x /= total; - bary.y /= total; - bary.z /= total; - - bary.x = 1 - bary.x; - bary.y = 1 - bary.y; - bary.z = 1 - bary.z; - - total = bary.x + bary.y + bary.z; - bary.x /= total; - bary.y /= total; - bary.z /= total; - - - gxx.x = skeleton.drawmodel.Triangles[whichtri].gx[0]; - gxx.y = skeleton.drawmodel.Triangles[whichtri].gx[1]; - gxx.z = skeleton.drawmodel.Triangles[whichtri].gx[2]; - gyy.x = skeleton.drawmodel.Triangles[whichtri].gy[0]; - gyy.y = skeleton.drawmodel.Triangles[whichtri].gy[1]; - gyy.z = skeleton.drawmodel.Triangles[whichtri].gy[2]; - coordsx = skeleton.drawmodel.Triangles[whichtri].gx[0] * bary.x + skeleton.drawmodel.Triangles[whichtri].gx[1] * bary.y + skeleton.drawmodel.Triangles[whichtri].gx[2] * bary.z; - coordsy = skeleton.drawmodel.Triangles[whichtri].gy[0] * bary.x + skeleton.drawmodel.Triangles[whichtri].gy[1] * bary.y + skeleton.drawmodel.Triangles[whichtri].gy[2] * bary.z; - - if (bleeding <= 0 && spurt) { - spurt = 0; - for (int i = 0; i < 3; i++) { - // emit blood particles - // FIXME: more copypaste code - bloodvel = 0; - if (skeleton.free) { - bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0); - bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .05, 1); - Sprite::MakeSprite(bloodflamesprite, jointPos(head) * scale + coords, bloodvel, 1, 1, 1, .3, 1); - } else { - bloodvel.z = 10; - bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); - Sprite::MakeSprite(bloodflamesprite, DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .3, 1); - } - } - } - - // texture manipulation follows - - int offsetx = 0, offsety = 0; - offsetx = (1 + coordsy) * 512 - 291; - offsety = coordsx * 512 - 437; - - int startx = 512; - int starty = 512; - int endx = 0; - int endy = 0; - GLubyte color; - if (creature == rabbittype) - for (i = 0; i < 512; i++) { - for (j = 0; j < 512; j++) { - if (bloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && bloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { - if (i < startx) startx = i; - if (j < starty) starty = j; - if (i > endx) endx = i; - if (j > endy) endy = j; - } - } - } - if (creature == wolftype) - for (i = 0; i < 512; i++) { - for (j = 0; j < 512; j++) { - if (wolfbloodText[i * 512 * 3 + j * 3 + 0] <= which + 4 && wolfbloodText[i * 512 * 3 + j * 3 + 0] >= which - 4) { - if (i < startx) startx = i; - if (j < starty) starty = j; - if (i > endx) endx = i; - if (j > endy) endy = j; - } - } - } - startx += offsetx; - endx += offsetx; - starty += offsety; - endy += offsety; - - if (startx < 0) startx = 0; - if (starty < 0) starty = 0; - if (endx > 512 - 1) endx = 512 - 1; - if (endy > 512 - 1) endy = 512 - 1; - if (endx < startx) endx = startx; - if (endy < starty) endy = starty; - - startx /= realtexdetail; - starty /= realtexdetail; - endx /= realtexdetail; - endy /= realtexdetail; - - int texdetailint = realtexdetail; - int where; - if (creature == rabbittype) - for (i = startx; i < endx; i++) { - for (j = starty; j < endy; j++) { - if (bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { - color = Random() % 85 + 170; - where = i * skeleton.skinsize * 3 + j * 3; - if (skeleton.skinText[where + 0] > color / 2) - skeleton.skinText[where + 0] = color / 2; - skeleton.skinText[where + 1] = 0; - skeleton.skinText[where + 2] = 0; - } else if (bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= 160 + 4 && bloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= 160 - 4) { - color = Random() % 85 + 170; - where = i * skeleton.skinsize * 3 + j * 3; - if (skeleton.skinText[where + 0] > color / 2) - skeleton.skinText[where + 0] = color / 2; - skeleton.skinText[where + 1] = 0; - skeleton.skinText[where + 2] = 0; - } - } - } - if (creature == wolftype) - for (i = startx; i < endx; i++) { - for (j = starty; j < endy; j++) { - if (wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= which + 4 && wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= which - 4) { - color = Random() % 85 + 170; - where = i * skeleton.skinsize * 3 + j * 3; - if (skeleton.skinText[where + 0] > color / 2) - skeleton.skinText[where + 0] = color / 2; - skeleton.skinText[where + 1] = 0; - skeleton.skinText[where + 2] = 0; - } else if (wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] <= 160 + 4 && wolfbloodText[(i * texdetailint - offsetx) * 512 * 3 + (j * texdetailint - offsety) * 3 + 0] >= 160 - 4) { - color = Random() % 85 + 170; - where = i * skeleton.skinsize * 3 + j * 3; - if (skeleton.skinText[where + 0] > color / 2) - skeleton.skinText[where + 0] = color / 2; - skeleton.skinText[where + 1] = 0; - skeleton.skinText[where + 2] = 0; - } - } - } - skeleton.drawmodel.textureptr.bind(); - DoMipmaps(); - - bleedy = (1 + coordsy) * 512; - bleedx = coordsx * 512; - bleedy /= realtexdetail; - bleedx /= realtexdetail; - if (bleedx < 0) - bleedx = 0; - if (bleedy < 0) - bleedy = 0; - if (bleedx > skeleton.skinsize - 1) - bleedx = skeleton.skinsize - 1; - if (bleedy > skeleton.skinsize - 1) - bleedy = skeleton.skinsize - 1; - direction = abs(Random() % 2) * 2 - 1; - } - if (whichtri == -1) - return 0; - } - bleeding = howmuch + (float)abs(Random() % 100) / 200 - .25; - deathbleeding += bleeding; - bloodloss += bleeding * 3; - - if (tutoriallevel != 1 && aitype != playercontrolled && bloodloss > damagetolerance * 2 / 3 && bloodloss < damagetolerance && creature == rabbittype) { - if (abs(Random() % 2) == 0) { - aitype = gethelptype; - lastseentime = 12; - } else - aitype = attacktypecutoff; - ally = 0; - } - if (bleeding > 2) - bleeding = 2; - return 1; -} - - - -/* EFFECT - * guessing this performs a reversal - */ -void Person::Reverse() -{ - if (!((victim->aitype == playercontrolled - || hostiletime > 1 - || staggerdelay <= 0) - && victim->animTarget != jumpupanim - && victim->animTarget != jumpdownanim - && (tutoriallevel != 1 || cananger) - && hostile)) - return; - - if (normaldotproduct (victim->facing, victim->coords - coords) > 0 - && (victim->id != 0 || difficulty >= 2) - && (creature != wolftype || victim->creature == wolftype)) - return; - - if (animTarget == sweepanim) { - animTarget = sweepreversedanim; - animCurrent = sweepreversedanim; - victim->animCurrent = sweepreversalanim; - victim->animTarget = sweepreversalanim; - } - if (animTarget == spinkickanim) { - animTarget = spinkickreversedanim; - animCurrent = spinkickreversedanim; - victim->animCurrent = spinkickreversalanim; - victim->animTarget = spinkickreversalanim; - } - if (animTarget == upunchanim || animTarget == rabbittacklinganim) { - if (animTarget == rabbittacklinganim) { - frameCurrent = 6; - frameTarget = 7; - victim->frameCurrent = 6; - victim->frameTarget = 7; - } - animTarget = upunchreversedanim; - animCurrent = upunchreversedanim; - victim->animCurrent = upunchreversalanim; - victim->animTarget = upunchreversalanim; - } - if (animTarget == staffhitanim && distsq(&victim->coords, &coords) < 2 && ((victim->id == 0 && victim->crouchkeydown) || Random() % 4 == 0)) { - if (victim->weaponactive != -1) { - victim->throwtogglekeydown = 1; - XYZ tempVelocity = victim->velocity * .2; - if (tempVelocity.x == 0) - tempVelocity.x = .1; - weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); - victim->num_weapons--; - if (victim->num_weapons) { - victim->weaponids[0] = victim->weaponids[victim->num_weapons]; - if (victim->weaponstuck == victim->num_weapons) - victim->weaponstuck = 0; - } - - victim->weaponactive = -1; - for (unsigned j = 0; j < Person::players.size(); j++) { - Person::players[j]->wentforweapon = 0; - } - } - - animTarget = staffhitreversedanim; - animCurrent = staffhitreversedanim; - victim->animCurrent = staffhitreversalanim; - victim->animTarget = staffhitreversalanim; - } - if (animTarget == staffspinhitanim && distsq(&victim->coords, &coords) < 2 && ((victim->id == 0 && victim->crouchkeydown) || Random() % 2 == 0)) { - if (victim->weaponactive != -1) { - victim->throwtogglekeydown = 1; - XYZ tempVelocity = victim->velocity * .2; - if (tempVelocity.x == 0) - tempVelocity.x = .1; - weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); - victim->num_weapons--; - if (victim->num_weapons) { - victim->weaponids[0] = victim->weaponids[victim->num_weapons]; - if (victim->weaponstuck == victim->num_weapons) - victim->weaponstuck = 0; - } - - victim->weaponactive = -1; - for (unsigned j = 0; j < Person::players.size(); j++) { - Person::players[j]->wentforweapon = 0; - } - } - animTarget = staffspinhitreversedanim; - animCurrent = staffspinhitreversedanim; - victim->animCurrent = staffspinhitreversalanim; - victim->animTarget = staffspinhitreversalanim; - } - if (animTarget == swordslashanim && distsq(&victim->coords, &coords) < 2 && ((victim->id == 0 && victim->crouchkeydown) || Random() % 4 == 0)) { - if (victim->weaponactive != -1) { - victim->throwtogglekeydown = 1; - XYZ tempVelocity = victim->velocity * .2; - if (tempVelocity.x == 0) - tempVelocity.x = .1; - weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); - victim->num_weapons--; - if (victim->num_weapons) { - victim->weaponids[0] = victim->weaponids[victim->num_weapons]; - if (victim->weaponstuck == victim->num_weapons) - victim->weaponstuck = 0; - } - - victim->weaponactive = -1; - for (unsigned j = 0; j < Person::players.size(); j++) { - Person::players[j]->wentforweapon = 0; - } - } - animTarget = swordslashreversedanim; - animCurrent = swordslashreversedanim; - victim->animCurrent = swordslashreversalanim; - victim->animTarget = swordslashreversalanim; - } - if (animTarget == knifeslashstartanim && distsq(&victim->coords, &coords) < 2 && (victim->id == 0 || Random() % 4 == 0)) { - if (victim->weaponactive != -1) { - victim->throwtogglekeydown = 1; - XYZ tempVelocity = victim->velocity * .2; - if (tempVelocity.x == 0) - tempVelocity.x = .1; - weapons[victim->weaponids[0]].drop(tempVelocity, tempVelocity, false); - victim->num_weapons--; - if (victim->num_weapons) { - victim->weaponids[0] = victim->weaponids[victim->num_weapons]; - if (victim->weaponstuck == victim->num_weapons) - victim->weaponstuck = 0; - } - - victim->weaponactive = -1; - for (unsigned j = 0; j < Person::players.size(); j++) { - Person::players[j]->wentforweapon = 0; - } - } - animTarget = knifeslashreversedanim; - animCurrent = knifeslashreversedanim; - victim->animCurrent = knifeslashreversalanim; - victim->animTarget = knifeslashreversalanim; - } - if (animTarget != knifeslashstartanim && animTarget != staffhitanim && animTarget != staffspinhitanim && animTarget != winduppunchanim && animTarget != wolfslapanim && animTarget != swordslashanim) { - victim->targettilt2 = targettilt2; - victim->frameCurrent = frameCurrent; - victim->frameTarget = frameTarget; - victim->target = target; - victim->velocity = 0; - victim->oldcoords = victim->coords; - victim->coords = coords; - victim->targetyaw = targetyaw; - victim->yaw = targetyaw; - victim->victim = this->shared_from_this(); - } - if (animTarget == winduppunchanim) { - animTarget = winduppunchblockedanim; - victim->animTarget = blockhighleftanim; - victim->frameTarget = 1; - victim->target = .5; - victim->victim = this->shared_from_this(); - victim->targetyaw = targetyaw + 180; - } - if (animTarget == wolfslapanim) { - animTarget = winduppunchblockedanim; - victim->animTarget = blockhighleftanim; - victim->frameTarget = 1; - victim->target = .5; - victim->victim = this->shared_from_this(); - victim->targetyaw = targetyaw + 180; - } - if ((animTarget == swordslashanim || animTarget == staffhitanim || animTarget == staffspinhitanim) && victim->weaponactive != -1) { - animTarget = swordslashparriedanim; - parriedrecently = .4; - victim->parriedrecently = 0; - victim->animTarget = swordslashparryanim; - victim->frameTarget = 1; - victim->target = .5; - victim->victim = this->shared_from_this(); - victim->targetyaw = targetyaw + 180; - - if (abs(Random() % 20) == 0 || weapons[victim->weaponids[victim->weaponactive]].getType() == knife) { - if (victim->weaponactive != -1) { - if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { - if (weapons[victim->weaponids[0]].getType() == staff) - weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - if (weapons[weaponids[0]].getType() == staff) - weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - emit_sound_at(swordstaffsound, victim->coords); - } else { - emit_sound_at(metalhitsound, victim->coords); - } - } - XYZ aim; - victim->Puff(righthand); - victim->target = 0; - victim->frameTarget = 0; - victim->animTarget = staggerbackhighanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - aim = DoRotation(facing, 0, 90, 0) * 21; - aim.y += 7; - weapons[victim->weaponids[0]].drop(aim * -.2, aim); - victim->num_weapons--; - if (victim->num_weapons) { - victim->weaponids[0] = victim->weaponids[num_weapons]; - if (victim->weaponstuck == victim->num_weapons) - victim->weaponstuck = 0; - } - victim->weaponactive = -1; - for (unsigned i = 0; i < Person::players.size(); i++) { - Person::players[i]->wentforweapon = 0; - } - } - - if (abs(Random() % 20) == 0) { - if (weaponactive != -1) { - if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { - if (weapons[victim->weaponids[0]].getType() == staff) - weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - if (weapons[weaponids[0]].getType() == staff) - weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - - emit_sound_at(swordstaffsound, coords); - } else { - emit_sound_at(metalhitsound, coords); - } - } - - XYZ aim; - Puff(righthand); - target = 0; - frameTarget = 0; - animTarget = staggerbackhighanim; - targetyaw = targetyaw + 180; - target = 0; - aim = DoRotation(facing, 0, 90, 0) * 21; - aim.y += 7; - weapons[victim->weaponids[0]].drop(aim * -.2, aim); - num_weapons--; - if (num_weapons) { - weaponids[0] = weaponids[num_weapons]; - if (weaponstuck == num_weapons) - weaponstuck = 0; - } - weaponactive = -1; - for (unsigned i = 0; i < Person::players.size(); i++) { - Person::players[i]->wentforweapon = 0; - } - - - } - } - if (hasvictim) - if (animTarget == knifeslashstartanim || animTarget == swordslashanim || animTarget == staffhitanim || animTarget == staffspinhitanim) { - if ((animTarget != staffhitanim && animTarget != staffspinhitanim) || distsq(&coords, &victim->coords) > .2) { - victim->animTarget = dodgebackanim; - victim->frameTarget = 0; - victim->target = 0; - - XYZ rotatetarget; - rotatetarget = coords - victim->coords; - Normalise(&rotatetarget); - victim->targetyaw = -asin(0 - rotatetarget.x); - victim->targetyaw *= 360 / 6.28; - if (rotatetarget.z < 0) - victim->targetyaw = 180 - victim->targetyaw; - - victim->targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; //*-70; - - victim->lastattack3 = victim->lastattack2; - victim->lastattack2 = victim->lastattack; - victim->lastattack = victim->animTarget; - } else { - victim->animTarget = sweepanim; - victim->frameTarget = 0; - victim->target = 0; - - XYZ rotatetarget; - rotatetarget = coords - victim->coords; - Normalise(&rotatetarget); - victim->targetyaw = -asin(0 - rotatetarget.x); - victim->targetyaw *= 360 / 6.28; - if (rotatetarget.z < 0) - victim->targetyaw = 180 - victim->targetyaw; - - victim->targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; //*-70; - - victim->lastattack3 = victim->lastattack2; - victim->lastattack2 = victim->lastattack; - victim->lastattack = victim->animTarget; - } - } - - velocity = 0; - victim->velocity = 0; - - if (aitype != playercontrolled) { - feint = 0; - if (escapednum < 2) { - int chances = ((difficulty == 2) ? 3 : ((difficulty == 1) ? 5 : 10)); - if ((Random() % chances) == 0) { - feint = 1; - } - } - } - - if (victim->id == 0 && Animation::animations[victim->animTarget].attack == reversal) - numreversals++; -} - -/* EFFECT - * get hurt - */ -void Person::DoDamage(float howmuch) -{ - // subtract health (temporary?) - if (tutoriallevel != 1) - damage += howmuch / power; - // stats? - if (id != 0) - damagedealt += howmuch / power; - if (id == 0) - damagetaken += howmuch / power; - - // reset bonuses - if (id == 0 && (bonus == solidhit || bonus == twoxcombo || bonus == threexcombo || bonus == fourxcombo || bonus == megacombo)) - bonus = 0; - // subtract health - if (tutoriallevel != 1) - permanentdamage += howmuch / 2 / power; - if (tutoriallevel != 1) - superpermanentdamage += howmuch / 4 / power; - // visual effects - if (permanentdamage > damagetolerance / 2 && permanentdamage - howmuch < damagetolerance / 2 && Random() % 2) - DoBlood(1, 255); - if ((permanentdamage > damagetolerance * .8 && Random() % 2 && !deathbleeding) || spurt) - DoBlood(1, 255); - spurt = 0; - if (id == 0) - camerashake += howmuch / 100; - if (id == 0 && ((howmuch > 50 && damage > damagetolerance / 2))) - blackout = damage / damagetolerance; - if (blackout > 1) - blackout = 1; - - // cancel attack? - if (aitype == passivetype && damage < damagetolerance && ((tutoriallevel != 1 || cananger) && hostile)) - aitype = attacktypecutoff; - if (tutoriallevel != 1 && aitype != playercontrolled && damage < damagetolerance && damage > damagetolerance * 2 / 3 && creature == rabbittype) { - if (abs(Random() % 2) == 0) { - aitype = gethelptype; - lastseentime = 12; - } else - aitype = attacktypecutoff; - ally = 0; - } - - if (howmuch > damagetolerance * 50 && skeleton.free != 2) { - XYZ flatvelocity2; - XYZ flatfacing2; - for (int i = 0; i < skeleton.joints.size(); i++) { - if (skeleton.free) { - flatvelocity2 = skeleton.joints[i].velocity; - flatfacing2 = skeleton.joints[i].position * scale + coords; - } else { - flatvelocity2 = velocity; - flatfacing2 = DoRotation(DoRotation(DoRotation(skeleton.joints[i].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; - } - flatvelocity2.x += (float)(abs(Random() % 100) - 50) / 10; - flatvelocity2.y += (float)(abs(Random() % 100) - 50) / 10; - flatvelocity2.z += (float)(abs(Random() % 100) - 50) / 10; - Sprite::MakeSprite(bloodflamesprite, flatfacing2, flatvelocity2, 1, 1, 1, 3, 1); - Sprite::MakeSprite(bloodsprite, flatfacing2, flatvelocity2, 1, 1, 1, .4, 1); - Sprite::MakeSprite(cloudsprite, flatfacing2, flatvelocity2 * 0, .6, 0, 0, 1, .5); - } - - emit_sound_at(splattersound, coords); - - skeleton.free = 2; - DoDamage(10000); - RagDoll(0); - if (!dead && creature == wolftype) { - award_bonus(0, Wolfbonus); - } - dead = 2; - coords = 20; - } - - // play sounds - if (tutoriallevel != 1 || id == 0) - if (speechdelay <= 0 && !dead && aitype != playercontrolled) { - int whichsound = -1; - - if (creature == wolftype) { - int i = abs(Random() % 2); - if (i == 0) - whichsound = snarlsound; - if (i == 1) - whichsound = snarl2sound; - } - if (creature == rabbittype) { - int i = abs(Random() % 2); - if (i == 0) - whichsound = rabbitpainsound; - if (i == 1 && damage > damagetolerance) - whichsound = rabbitpain1sound; - } - - if (whichsound != -1) { - emit_sound_at(whichsound, coords); - addEnvSound(coords); - } - } - speechdelay = .3; -} - -/* EFFECT - * calculate/animate head facing direction? - */ -void Person::DoHead() -{ - static XYZ rotatearound; - static XYZ facing; - static float lookspeed = 500; - - if (!freeze && !winfreeze) { - - //head facing - targetheadyaw = (float)((int)((0 - yaw - targetheadyaw + 180) * 100) % 36000) / 100; - targetheadpitch = (float)((int)(targetheadpitch * 100) % 36000) / 100; - - while (targetheadyaw > 180)targetheadyaw -= 360; - while (targetheadyaw < -180)targetheadyaw += 360; - - if (targetheadyaw > 160) - targetheadpitch = targetheadpitch * -1; - if (targetheadyaw < -160) - targetheadpitch = targetheadpitch * -1; - if (targetheadyaw > 160) - targetheadyaw = targetheadyaw - 180; - if (targetheadyaw < -160) - targetheadyaw = targetheadyaw + 180; - - if (targetheadpitch > 120) - targetheadpitch = 120; - if (targetheadpitch < -120) - targetheadpitch = -120; - if (targetheadyaw > 120) - targetheadyaw = 120; - if (targetheadyaw < -120) - targetheadyaw = -120; - - if (!isIdle()) - targetheadpitch = 0; - if (isIdle()) { - if (targetheadyaw > 80) - targetheadyaw = 80; - if (targetheadyaw < -80) - targetheadyaw = -80; - if (targetheadpitch > 50) - targetheadpitch = 50; - if (targetheadpitch < -50) - targetheadpitch = -50; - } - - if (abs(headyaw - targetheadyaw) < multiplier * lookspeed) - headyaw = targetheadyaw; - else if (headyaw > targetheadyaw) { - headyaw -= multiplier * lookspeed; - } else if (headyaw < targetheadyaw) { - headyaw += multiplier * lookspeed; - } - - if (abs(headpitch - targetheadpitch) < multiplier * lookspeed / 2) - headpitch = targetheadpitch; - else if (headpitch > targetheadpitch) { - headpitch -= multiplier * lookspeed / 2; - } else if (headpitch < targetheadpitch) { - headpitch += multiplier * lookspeed / 2; - } - - rotatearound = jointPos(neck); - jointPos(head) = rotatearound + DoRotation(jointPos(head) - rotatearound, headpitch, 0, 0); - - facing = 0; - facing.z = -1; - if (animTarget != bounceidleanim && animTarget != fightidleanim && animTarget != wolfidle && animTarget != knifefightidleanim && animTarget != drawrightanim && animTarget != drawleftanim && animTarget != walkanim) { - facing = DoRotation(facing, headpitch * .4, 0, 0); - facing = DoRotation(facing, 0, headyaw * .4, 0); - } - - if (animTarget == bounceidleanim || animTarget == fightidleanim || animTarget == wolfidle || animTarget == knifefightidleanim || animTarget == drawrightanim || animTarget == drawleftanim) { - facing = DoRotation(facing, headpitch * .8, 0, 0); - facing = DoRotation(facing, 0, headyaw * .8, 0); - } - - if (animTarget == walkanim) { - facing = DoRotation(facing, headpitch * .6, 0, 0); - facing = DoRotation(facing, 0, headyaw * .6, 0); - } - - skeleton.specialforward[0] = facing; - //skeleton.specialforward[0]=DoRotation(facing,0,yaw,0); - for (int i = 0; i < skeleton.muscles.size(); i++) { - if (skeleton.muscles[i].visible && (skeleton.muscles[i].parent1->label == head || skeleton.muscles[i].parent2->label == head)) { - skeleton.FindRotationMuscle(i, animTarget); - } - } - } -} - -/* EFFECT - * ragdolls character? - */ -void Person::RagDoll(bool checkcollision) -{ - static XYZ change; - static int l, i, j; - static float speed; - if (!skeleton.free) { - if (id == 0) - numfalls++; - if (id == 0 && isFlip()) - numflipfail++; - - escapednum = 0; - - facing = 0; - facing.z = 1; - facing = DoRotation(facing, 0, yaw, 0); - - skeleton.freetime = 0; - - skeleton.longdead = 0; - - skeleton.free = 1; - skeleton.broken = 0; - skeleton.spinny = 1; - freefall = 1; - skeleton.freefall = 1; - - if (!isnormal(velocity.x)) velocity.x = 0; - if (!isnormal(velocity.y)) velocity.y = 0; - if (!isnormal(velocity.z)) velocity.z = 0; - if (!isnormal(yaw)) yaw = 0; - if (!isnormal(coords.x)) coords = 0; - if (!isnormal(tilt)) tilt = 0; - if (!isnormal(tilt2)) tilt2 = 0; - - for (int i = 0; i < skeleton.joints.size(); i++) { - skeleton.joints[i].delay = 0; - skeleton.joints[i].locked = 0; - skeleton.joints[i].position = DoRotation(DoRotation(DoRotation(skeleton.joints[i].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0); - if (!isnormal(skeleton.joints[i].position.x)) skeleton.joints[i].position = DoRotation(skeleton.joints[i].position, 0, yaw, 0); - if (!isnormal(skeleton.joints[i].position.x)) skeleton.joints[i].position = coords; - skeleton.joints[i].position.y += .1; - skeleton.joints[i].oldposition = skeleton.joints[i].position; - skeleton.joints[i].realoldposition = skeleton.joints[i].position * scale + coords; - } - - for (int i = 0; i < skeleton.joints.size(); i++) { - skeleton.joints[i].velocity = 0; - skeleton.joints[i].velchange = 0; - } - skeleton.DoConstraints(&coords, &scale); - if (Animation::animations[animCurrent].height == lowheight || Animation::animations[animTarget].height == lowheight) { - skeleton.DoConstraints(&coords, &scale); - skeleton.DoConstraints(&coords, &scale); - skeleton.DoConstraints(&coords, &scale); - skeleton.DoConstraints(&coords, &scale); - } - - speed = targetFrame().speed * 2; - if (currentFrame().speed > targetFrame().speed) { - speed = currentFrame().speed * 2; - } - if (transspeed) - speed = transspeed * 2; - - speed *= speedmult; - - for (int i = 0; i < skeleton.joints.size(); i++) { - if ((Animation::animations[animCurrent].attack != reversed || animCurrent == swordslashreversedanim) && animCurrent != rabbitkickanim && !isLanding() && !wasLanding() && Animation::animations[animCurrent].height == Animation::animations[animTarget].height) - skeleton.joints[i].velocity = velocity / scale + facing * 5 + DoRotation(DoRotation(DoRotation((targetFrame().joints[i].position - currentFrame().joints[i].position) * speed, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0); - else - skeleton.joints[i].velocity = velocity / scale + facing * 5; - change.x = (float)(Random() % 100) / 100; - change.y = (float)(Random() % 100) / 100; - change.z = (float)(Random() % 100) / 100; - skeleton.joints[i].velocity += change; - skeleton.joints[abs(Random() % skeleton.joints.size())].velocity -= change; - - change.x = (float)(Random() % 100) / 100; - change.y = (float)(Random() % 100) / 100; - change.z = (float)(Random() % 100) / 100; - skeleton.joints[i].velchange += change; - skeleton.joints[abs(Random() % skeleton.joints.size())].velchange -= change; - } - - if (checkcollision) { - XYZ average; - XYZ lowpoint; - XYZ colpoint; - int howmany; - average = 0; - howmany = 0; - for (j = 0; j < skeleton.joints.size(); j++) { - average += skeleton.joints[j].position; - howmany++; - } - average /= howmany; - coords += average * scale; - for (j = 0; j < skeleton.joints.size(); j++) { - skeleton.joints[j].position -= average; - } - - whichpatchx = coords.x / (terrain.size / subdivision * terrain.scale); - whichpatchz = coords.z / (terrain.size / subdivision * terrain.scale); - if (terrain.patchobjectnum[whichpatchx][whichpatchz]) - for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { - i = terrain.patchobjects[whichpatchx][whichpatchz][l]; - lowpoint = coords; - lowpoint.y += 1; - if (SphereCheck(&lowpoint, 3, &colpoint, &objects.position[i], &objects.yaw[i], &objects.model[i]) != -1) { - coords.x = lowpoint.x; - coords.z = lowpoint.z; - } - } - } - - yaw = 0; - updatedelay = 0; - - velocity = 0; - for (int i = 0; i < skeleton.joints.size(); i++) { - velocity += skeleton.joints[i].velocity * scale; - } - velocity /= skeleton.joints.size(); - - // drop weapon - if (Random() % 2 == 0) { - if (weaponactive != -1 && animTarget != rabbitkickanim && num_weapons > 0) { - weapons[weaponids[0]].drop(jointVel(righthand) * scale * -.3, jointVel(righthand) * scale); - weapons[weaponids[0]].velocity.x += .01; - num_weapons--; - if (num_weapons) { - weaponids[0] = weaponids[num_weapons]; - if (weaponstuck == num_weapons) - weaponstuck = 0; - } - weaponactive = -1; - for (unsigned i = 0; i < Person::players.size(); i++) { - Person::players[i]->wentforweapon = 0; - } - } - } - - animTarget = bounceidleanim; - animCurrent = bounceidleanim; - frameTarget = 0; - frameCurrent = 0; - } -} - - - -/* EFFECT - */ -void Person::FootLand(bodypart whichfoot, float opacity) -{ - if ((whichfoot != leftfoot) && (whichfoot != rightfoot)) { - cerr << "FootLand called on wrong bodypart" << endl; - return; - } - static XYZ terrainlight; - static XYZ footvel, footpoint; - if (opacity >= 1 || skiddelay <= 0) { - if (opacity > 1) { - footvel = 0; - footpoint = DoRotation(jointPos(whichfoot), 0, yaw, 0) * scale + coords; - if (distsq(&footpoint, &viewer)) - Sprite::MakeSprite(cloudsprite, footpoint, footvel, 1, 1, 1, .5, .2 * opacity); - } else if (onterrain && terrain.getOpacity(coords.x, coords.z) < .2) { - footvel = velocity / 5; - if (footvel.y < .8) - footvel.y = .8; - footpoint = DoRotation(jointPos(whichfoot), 0, yaw, 0) * scale + coords; - footpoint.y = terrain.getHeight(footpoint.x, footpoint.z); - terrainlight = terrain.getLighting(footpoint.x, footpoint.z); - if (distsq(&footpoint, &viewer) < viewdistance * viewdistance / 4) { - if (environment == snowyenvironment) { - Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7 * opacity); - if (detail == 2) { - terrain.MakeDecal(footprintdecal, footpoint, .2, 1 * opacity, yaw); - } - } else if (environment == grassyenvironment) { - Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5 * opacity); - } else if (environment == desertenvironment) { - Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7 * opacity); - if (detail == 2) { - terrain.MakeDecal(footprintdecal, footpoint, .2, .25 * opacity, yaw); - } - } - } - } else if (isLanding() || (animTarget == jumpupanim) || isLandhard()) { - footvel = velocity / 5; - if (footvel.y < .8) - footvel.y = .8; - footpoint = DoRotation(jointPos(whichfoot), 0, yaw, 0) * scale + coords; - if (distsq(&footpoint, &viewer) < viewdistance * viewdistance / 4) { - Sprite::MakeSprite(cloudsprite, footpoint, footvel * .6, 1, 1, 1, .5, .2 * opacity); - } - } - } -} - -/* EFFECT - * make a puff effect at a body part (dust effect?) - */ -void Person::Puff(int whichlabel) -{ - static XYZ footvel, footpoint; - - footvel = 0; - footpoint = DoRotation(jointPos(whichlabel), 0, yaw, 0) * scale + coords; - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .9, .3); -} - -/* EFFECT - * I think I added this in an attempt to clean up code - */ -void Person::setAnimation(int animation) -{ - animTarget = animation; - frameTarget = 0; - target = 0; -} - -/* EFFECT - * MONSTER - * TODO: ??? - */ -void Person::DoAnimations() -{ - if (!skeleton.free) { - static float oldtarget; - - if (isIdle() && animCurrent != getIdle()) - normalsupdatedelay = 0; - - if (animTarget == tempanim || animCurrent == tempanim) { - Animation::animations[tempanim] = tempanimation; - } - if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { - float gLoc[3]; - float vel[3]; - gLoc[0] = coords.x; - gLoc[1] = coords.y; - gLoc[2] = coords.z; - vel[0] = velocity.x; - vel[1] = velocity.y; - vel[2] = velocity.z; - - if (id == 0) { - OPENAL_3D_SetAttributes(channels[whooshsound], gLoc, vel); - OPENAL_SetVolume(channels[whooshsound], 64 * findLength(&velocity) / 5); - } - if (((velocity.y < -15) || (crouchkeydown && velocity.y < -8)) && abs(velocity.y) * 4 > fast_sqrt(velocity.x * velocity.x * velocity.z * velocity.z)) - landhard = 1; - if (!crouchkeydown && velocity.y >= -15) - landhard = 0; - } - if ((animCurrent == jumpupanim || animTarget == jumpdownanim)/*&&velocity.y<40*/ && !isFlip() && (!isLanding() && !isLandhard()) && ((crouchkeydown && !crouchtogglekeydown))) { - XYZ targfacing; - targfacing = 0; - targfacing.z = 1; - - targfacing = DoRotation(targfacing, 0, targetyaw, 0); - - if (normaldotproduct(targfacing, velocity) >= -.3) - animTarget = flipanim; - else - animTarget = backflipanim; - crouchtogglekeydown = 1; - frameTarget = 0; - target = 0; - - if (id == 0) - numflipped++; - } - - if (Animation::animations[animTarget].attack != reversed) - feint = 0; - if (!crouchkeydown || (isLanding() || isLandhard()) || (wasLanding() || wasLandhard())) { - crouchtogglekeydown = 0; - if (aitype == playercontrolled) - feint = 0; - } else { - if (!crouchtogglekeydown && Animation::animations[animTarget].attack == reversed && aitype == playercontrolled && (escapednum < 2 || reversaltrain)) - feint = 1; - if (!isFlip()) - crouchtogglekeydown = 1; - } - - - if (Animation::animations[animTarget].attack || animCurrent == getupfrombackanim || animCurrent == getupfromfrontanim) { - if (detail) - normalsupdatedelay = 0; - } - - if (target >= 1) { - if (animTarget == rollanim && frameTarget == 3 && onfire) { - onfire = 0; - emit_sound_at(fireendsound, coords); - pause_sound(stream_firesound); - deathbleeding = 0; - } - - if (animTarget == rabbittacklinganim && frameTarget == 1) { - if (victim->aitype == attacktypecutoff && victim->stunned <= 0 && victim->surprised <= 0 && victim->id != 0) - Reverse(); - if (animTarget == rabbittacklinganim && frameTarget == 1 && !victim->isCrouch() && victim->animTarget != backhandspringanim) { - if (normaldotproduct(victim->facing, facing) > 0) - victim->animTarget = rabbittackledbackanim; - else - victim->animTarget = rabbittackledfrontanim; - victim->frameTarget = 2; - victim->target = 0; - victim->yaw = yaw; - victim->targetyaw = yaw; - if (victim->aitype == gethelptype) - victim->DoDamage(victim->damagetolerance - victim->damage); - //victim->DoDamage(30); - if (creature == wolftype) { - DoBloodBig(0, 255); - emit_sound_at(clawslicesound, victim->coords); - victim->spurt = 1; - victim->DoBloodBig(1 / victim->armorhead, 210); - } - award_bonus(id, TackleBonus, - victim->aitype == gethelptype ? 50 : 0); - } - } - - if (!drawtogglekeydown && drawkeydown && (weaponactive == -1 || num_weapons == 1) && (targetFrame().label || (animTarget != animCurrent && animCurrent == rollanim)) && num_weapons > 0 && creature != wolftype) { - if (weapons[weaponids[0]].getType() == knife) { - if (weaponactive == -1) - weaponactive = 0; - else if (weaponactive == 0) - weaponactive = -1; - - if (weaponactive == -1) { - emit_sound_at(knifesheathesound, coords); - } - if (weaponactive != -1) { - emit_sound_at(knifedrawsound, coords, 128); - } - } - drawtogglekeydown = 1; - } - //Footstep sounds - if (tutoriallevel != 1 || id == 0) - if ((targetFrame().label && (targetFrame().label < 5 || targetFrame().label == 8))) { - int whichsound; - if (onterrain) { - if (terrain.getOpacity(coords.x, coords.z) < .2) { - if (targetFrame().label == 1) - whichsound = footstepsound; - else - whichsound = footstepsound2; - if (targetFrame().label == 1) - FootLand(leftfoot, 1); - if (targetFrame().label == 2) - FootLand(rightfoot, 1); - if (targetFrame().label == 3 && isRun()) { - FootLand(rightfoot, 1); - FootLand(leftfoot, 1); - } - - } - if (terrain.getOpacity(coords.x, coords.z) >= .2) { - if (targetFrame().label == 1) - whichsound = footstepsound3; - else - whichsound = footstepsound4; - } - } - if (!onterrain) { - if (targetFrame().label == 1) - whichsound = footstepsound3; - else - whichsound = footstepsound4; - } - if (targetFrame().label == 4 && (weaponactive == -1 || (animTarget != knifeslashstartanim && animTarget != knifethrowanim && animTarget != crouchstabanim && animTarget != swordgroundstabanim && animTarget != knifefollowanim))) { - if (Animation::animations[animTarget].attack != neutral) { - unsigned r = abs(Random() % 3); - if (r == 0) - whichsound = lowwhooshsound; - if (r == 1) - whichsound = midwhooshsound; - if (r == 2) - whichsound = highwhooshsound; - } - if (Animation::animations[animTarget].attack == neutral) - whichsound = movewhooshsound; - } else if (targetFrame().label == 4) - whichsound = knifeswishsound; - if (targetFrame().label == 8 && tutoriallevel != 1) - whichsound = landsound2; - - emit_sound_at(whichsound, coords, 256.); - - if (id == 0) - if (whichsound == footstepsound || whichsound == footstepsound2 || whichsound == footstepsound3 || whichsound == footstepsound4) { - if (animTarget == wolfrunninganim || animTarget == rabbitrunninganim) { - addEnvSound(coords, 15); - } else { - addEnvSound(coords, 6); - } - } - - if (targetFrame().label == 3) { - whichsound--; - emit_sound_at(whichsound, coords, 128.); - } - } - - //Combat sounds - if (tutoriallevel != 1 || id == 0) - if (speechdelay <= 0) - if (animTarget != crouchstabanim && animTarget != swordgroundstabanim && animTarget != staffgroundsmashanim) - if ((targetFrame().label && (targetFrame().label < 5 || targetFrame().label == 8))) { - int whichsound = -1; - if (targetFrame().label == 4 && aitype != playercontrolled) { - if (Animation::animations[animTarget].attack != neutral) { - unsigned r = abs(Random() % 4); - if (creature == rabbittype) { - if (r == 0) whichsound = rabbitattacksound; - if (r == 1) whichsound = rabbitattack2sound; - if (r == 2) whichsound = rabbitattack3sound; - if (r == 3) whichsound = rabbitattack4sound; - } - if (creature == wolftype) { - if (r == 0) whichsound = barksound; - if (r == 1) whichsound = bark2sound; - if (r == 2) whichsound = bark3sound; - if (r == 3) whichsound = barkgrowlsound; - } - speechdelay = .3; - } - } - - if (whichsound != -1) { - emit_sound_at(whichsound, coords); - } - } - - - - if ((!wasLanding() && !wasLandhard()) && animCurrent != getIdle() && (isLanding() || isLandhard())) { - FootLand(leftfoot, 1); - FootLand(rightfoot, 1); - } - - transspeed = 0; - currentoffset = targetoffset; - frameTarget = frameCurrent; - animCurrent = animTarget; - frameTarget++; - - if (animTarget == removeknifeanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - for (unsigned i = 0; i < weapons.size(); i++) { - if (weapons[i].owner == -1) - if (distsqflat(&coords, &weapons[i].position) < 4 && weaponactive == -1) { - if (distsq(&coords, &weapons[i].position) >= 1) { - if (weapons[i].getType() != staff) { - emit_sound_at(knifedrawsound, coords, 128.); - } - - takeWeapon(i); - } - } - } - } - - if (animTarget == crouchremoveknifeanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - for (unsigned i = 0; i < weapons.size(); i++) { - bool willwork = true; - if (weapons[i].owner != -1) - if (Person::players[weapons[i].owner]->weaponstuck != -1) - if (Person::players[weapons[i].owner]->weaponids[Person::players[weapons[i].owner]->weaponstuck] == int(i)) - if (Person::players[weapons[i].owner]->num_weapons > 1) - willwork = 0; - if ((weapons[i].owner == -1) || (hasvictim && (weapons[i].owner == int(victim->id)) && victim->skeleton.free)) - if (willwork && distsqflat(&coords, &weapons[i].position) < 3 && weaponactive == -1) { - if (distsq(&coords, &weapons[i].position) < 1 || hasvictim) { - bool fleshstuck = false; - if (weapons[i].owner != -1) - if (victim->weaponstuck != -1) { - if (victim->weaponids[victim->weaponstuck] == int(i)) { - fleshstuck = true; - } - } - if (fleshstuck) { - emit_sound_at(fleshstabremovesound, coords, 128.); - } else { - if (weapons[i].getType() != staff) { - emit_sound_at(knifedrawsound, coords, 128.); - } - } - if (weapons[i].owner != -1) { - victim = Person::players[weapons[i].owner]; - if (victim->num_weapons == 1) - victim->num_weapons = 0; - else - victim->num_weapons = 1; - - //victim->weaponactive=-1; - victim->skeleton.longdead = 0; - victim->skeleton.free = 1; - victim->skeleton.broken = 0; - - for (int j = 0; j < victim->skeleton.joints.size(); j++) { - victim->skeleton.joints[j].velchange = 0; - victim->skeleton.joints[j].locked = 0; - } - - XYZ relative; - relative = 0; - relative.y = 10; - Normalise(&relative); - XYZ footvel, footpoint; - footvel = 0; - footpoint = weapons[i].position; - if (victim->weaponstuck != -1) { - if (victim->weaponids[victim->weaponstuck] == int(i)) { - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .8, .3); - weapons[i].bloody = 2; - weapons[i].blooddrip = 5; - victim->weaponstuck = -1; - } - } - if (victim->num_weapons > 0) { - if (victim->weaponstuck != 0 && victim->weaponstuck != -1) - victim->weaponstuck = 0; - if (victim->weaponids[0] == int(i)) - victim->weaponids[0] = victim->weaponids[victim->num_weapons]; - } - - victim->jointVel(abdomen) += relative * 6; - victim->jointVel(neck) += relative * 6; - victim->jointVel(rightshoulder) += relative * 6; - victim->jointVel(leftshoulder) += relative * 6; - } - takeWeapon(i); - } - } - } - } - - if (animCurrent == drawleftanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (weaponactive == -1) - weaponactive = 0; - else if (weaponactive == 0) { - weaponactive = -1; - if (num_weapons == 2) { - int buffer; - buffer = weaponids[0]; - weaponids[0] = weaponids[1]; - weaponids[1] = buffer; - } - } - if (weaponactive == -1) { - emit_sound_at(knifesheathesound, coords, 128.); - } - if (weaponactive != -1) { - emit_sound_at(knifedrawsound, coords, 128.); - } - } - - - if ((animCurrent == walljumprightkickanim && animTarget == walljumprightkickanim) || (animCurrent == walljumpleftkickanim && animTarget == walljumpleftkickanim)) { - XYZ rotatetarget = DoRotation(skeleton.forward, 0, yaw, 0); - Normalise(&rotatetarget); - targetyaw = -asin(0 - rotatetarget.x); - targetyaw *= 360 / 6.28; - if (rotatetarget.z < 0) - targetyaw = 180 - targetyaw; - - if (animTarget == walljumprightkickanim) - targetyaw += 40; - if (animTarget == walljumpleftkickanim) - targetyaw -= 40; - } - - bool dojumpattack; - dojumpattack = 0; - if ((animTarget == rabbitrunninganim || animTarget == wolfrunninganim) && frameTarget == 3 && (jumpkeydown || attackkeydown || id != 0)) - dojumpattack = 1; - if (hasvictim) - if (distsq(&victim->coords, &/*Person::players[i]->*/coords) < 5 && victim->aitype == gethelptype && (attackkeydown) && !victim->skeleton.free && victim->isRun() && victim->runninghowlong >= 1) - dojumpattack = 1; - if (!hostile) - dojumpattack = 0; - if (dojumpattack) { - if ((animTarget == rabbitrunninganim || animTarget == wolfrunninganim) && id == 0) { - animTarget = rabbittackleanim; - frameTarget = 0; - emit_sound_at(jumpsound, coords); - } - - float closestdist; - closestdist = 0; - int closestid; - closestid = -1; - XYZ targetloc; - targetloc = velocity; - Normalise(&targetloc); - targetloc += coords; - for (unsigned i = 0; i < Person::players.size(); i++) { - if (i != id) - if (distsq(&targetloc, &Person::players[i]->coords) < closestdist || closestdist == 0) { - closestdist = distsq(&targetloc, &Person::players[i]->coords); - closestid = i; - } - } - if (closestid != -1) - if (closestdist < 5 && !Person::players[closestid]->dead && Animation::animations[Person::players[closestid]->animTarget].height != lowheight && Person::players[closestid]->animTarget != backhandspringanim) { - hasvictim = 1; - victim = Person::players[closestid]; - coords = victim->coords; - animCurrent = rabbittacklinganim; - animTarget = rabbittacklinganim; - frameCurrent = 0; - frameTarget = 1; - XYZ rotatetarget; - if (coords.z != victim->coords.z || coords.x != victim->coords.x) { - rotatetarget = coords - victim->coords; - Normalise(&rotatetarget); - targetyaw = -asin(0 - rotatetarget.x); - targetyaw *= 360 / 6.28; - if (rotatetarget.z < 0) - targetyaw = 180 - targetyaw; - } - if (animTarget != rabbitrunninganim) { - emit_sound_at(jumpsound, coords, 128.); - } - } - } - - //Move impacts - float damagemult = 1 * power; - if (creature == wolftype) - damagemult = 2.5 * power; - if (hasvictim) { - damagemult /= victim->damagetolerance / 200; - } - if ((Animation::animations[animTarget].attack == normalattack || animTarget == walljumprightkickanim || animTarget == walljumpleftkickanim) && (!feint) && (victim->skeleton.free != 2 || animTarget == killanim || animTarget == dropkickanim || animTarget == crouchstabanim || animTarget == swordgroundstabanim || animTarget == staffgroundsmashanim)) { - if (animTarget == spinkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && 3 && Animation::animations[victim->animTarget].height != lowheight) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2 || creature == wolftype) { - victim->spurt = 1; - DoBlood(.2, 250); - if (creature == wolftype) - DoBloodBig(0, 250); - } - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 128.); - } - if (creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhead, 175); - } - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, -90, 0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->jointVel(head) += relative * damagemult * 200; - victim->Puff(head); - victim->DoDamage(damagemult * 100 / victim->protectionhead); - - SolidHitBonus(id); - } - } - - if (animTarget == wolfslapanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && 3 && Animation::animations[victim->animTarget].height != lowheight) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2 || creature == wolftype) { - victim->spurt = 1; - if (creature == wolftype) - DoBloodBig(0, 235); - } - emit_sound_at(whooshhitsound, victim->coords); - if (creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2, 175); - } - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - relative.y -= 1; - Normalise(&relative); - relative = DoRotation(relative, 0, 90, 0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 20; - } - victim->jointVel(head) += relative * damagemult * 100; - victim->Puff(head); - victim->DoDamage(damagemult * 50 / victim->protectionhead); - } - } - - if (animTarget == walljumprightkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) { - escapednum = 0; - if (id == 0) - camerashake += .4; - victim->spurt = 1; - DoBlood(.2, 250); - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 160.); - } - if (creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhead, 175); - } - victim->RagDoll(0); - XYZ relative; - relative = facing; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, -90, 0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->jointVel(head) += relative * damagemult * 200; - victim->Puff(head); - victim->DoDamage(damagemult * 150 / victim->protectionhead); - - if (victim->damage > victim->damagetolerance) - award_bonus(id, style); - else - SolidHitBonus(id); - } - } - - if (animTarget == walljumpleftkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) { - escapednum = 0; - if (id == 0) - camerashake += .4; - victim->spurt = 1; - DoBlood(.2, 250); - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 160.); - } - if (creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhead, 175); - } - victim->RagDoll(0); - XYZ relative; - relative = facing; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, 90, 0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->jointVel(head) += relative * damagemult * 200; - victim->Puff(head); - victim->DoDamage(damagemult * 150 / victim->protectionhead); - - if (victim->damage > victim->damagetolerance) - award_bonus(id, style); - else - SolidHitBonus(id); - } - } - - if (animTarget == blockhighleftstrikeanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != lowheight) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 235); - } - emit_sound_at(whooshhitsound, victim->coords); - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 30; - } - victim->jointVel(head) += relative * damagemult * 100; - victim->Puff(head); - victim->DoDamage(damagemult * 50 / victim->protectionhead); - } - } - - if (animTarget == killanim && Animation::animations[animTarget].frames[frameCurrent].label == 8) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && victim->dead) { - escapednum = 0; - if (id == 0) - camerashake += .2; - emit_sound_at(whooshhitsound, victim->coords, 128.); - - victim->skeleton.longdead = 0; - victim->skeleton.free = 1; - victim->skeleton.broken = 0; - victim->skeleton.spinny = 1; - - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velchange = 0; - victim->skeleton.joints[i].delay = 0; - victim->skeleton.joints[i].locked = 0; - //victim->skeleton.joints[i].velocity=0; - } - - XYZ relative; - relative = 0; - relative.y = 1; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity.y = relative.y * 10; - victim->skeleton.joints[i].position.y += relative.y * .3; - victim->skeleton.joints[i].oldposition.y += relative.y * .3; - victim->skeleton.joints[i].realoldposition.y += relative.y * .3; - } - victim->Puff(abdomen); - victim->jointVel(abdomen).y = relative.y * 400; - } - } - - if (animTarget == killanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 9 && victim->dead) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, coords, 128.); - } - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 90; - } - victim->Puff(abdomen); - if (victim->dead != 2 && victim->permanentdamage > victim->damagetolerance - 250 && autoslomo) { - slomo = 1; - slomodelay = .2; - } - victim->DoDamage(damagemult * 500 / victim->protectionhigh); - victim->jointVel(abdomen) += relative * damagemult * 300; - } - } - - if (animTarget == dropkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 9 && victim->skeleton.free) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (tutoriallevel != 1) { - emit_sound_at(thudsound, coords); - } - - victim->skeleton.longdead = 0; - victim->skeleton.free = 1; - victim->skeleton.broken = 0; - victim->skeleton.spinny = 1; - - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velchange = 0; - //victim->skeleton.joints[i].delay=0; - victim->skeleton.joints[i].locked = 0; - } - XYZ relative; - relative = victim->coords - coords; - Normalise(&relative); - relative.y += .3; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 20; - } - if (!victim->dead) - SolidHitBonus(id); - - victim->Puff(abdomen); - victim->DoDamage(damagemult * 20 / victim->protectionhigh); - victim->jointVel(abdomen) += relative * damagemult * 200; - staggerdelay = .5; - if (!victim->dead) - staggerdelay = 1.2; - - - } - } - - if ((animTarget == crouchstabanim || animTarget == swordgroundstabanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - - if (hasvictim) - if (!victim->skeleton.free) - hasvictim = 0; - - if (!hasvictim) { - terrain.MakeDecal(blooddecalfast, (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2), .08, .6, Random() % 360); - emit_sound_at(knifesheathesound, coords, 128.); - } - - if (victim && hasvictim) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) { - - XYZ where, startpoint, endpoint, movepoint, colpoint; - float rotationpoint; - int whichtri; - if (weapons[weaponids[weaponactive]].getType() == knife) { - where = (weapons[weaponids[weaponactive]].tippoint * .6 + weapons[weaponids[weaponactive]].position * .4); - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - //where=scale; - startpoint = where; - startpoint.y += 100; - endpoint = where; - endpoint.y -= 100; - } - if (weapons[weaponids[weaponactive]].getType() == sword) { - where = weapons[weaponids[weaponactive]].position; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - startpoint = where; - where = weapons[weaponids[weaponactive]].tippoint; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - endpoint = where; - } - if (weapons[weaponids[weaponactive]].getType() == staff) { - where = weapons[weaponids[weaponactive]].position; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - startpoint = where; - where = weapons[weaponids[weaponactive]].tippoint; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - endpoint = where; - } - movepoint = 0; - rotationpoint = 0; - whichtri = victim->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &colpoint, &movepoint, &rotationpoint); - - if (whichtri != -1) { - if (victim->dead != 2) { - victim->DoDamage(abs((victim->damagetolerance - victim->permanentdamage) * 2)); - if (!victim->dead) - award_bonus(id, FinishedBonus); - } - if (bloodtoggle) - weapons[weaponids[weaponactive]].bloody = 2; - - victim->skeleton.longdead = 0; - victim->skeleton.free = 1; - victim->skeleton.broken = 0; - - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velchange = 0; - victim->skeleton.joints[i].locked = 0; - //victim->skeleton.joints[i].velocity=0; - } - emit_sound_at(fleshstabsound, coords, 128); - - } - if (whichtri != -1 || weapons[weaponids[weaponactive]].bloody) { - weapons[weaponids[weaponactive]].blooddrip += 5; - weapons[weaponids[weaponactive]].blooddripdelay = 0; - } - if (whichtri == -1) { - hasvictim = 0; - emit_sound_at(knifesheathesound, coords, 128.); - } - } - } - } - - if ((animTarget == crouchstabanim || animTarget == swordgroundstabanim) && Animation::animations[animTarget].frames[frameCurrent].label == 6) { - if (!hasvictim) { - emit_sound_at(knifedrawsound, coords, 128); - } - - if (victim && hasvictim) { - XYZ footvel, footpoint; - - emit_sound_at(fleshstabremovesound, coords, 128.); - - footvel = 0; - footpoint = (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2); - - if (weapons[weaponids[weaponactive]].getType() == sword) { - XYZ where, startpoint, endpoint, movepoint; - float rotationpoint; - int whichtri; - - where = weapons[weaponids[weaponactive]].position; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - startpoint = where; - where = weapons[weaponids[weaponactive]].tippoint; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - endpoint = where; - - movepoint = 0; - rotationpoint = 0; - whichtri = victim->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &footpoint, &movepoint, &rotationpoint); - footpoint += victim->coords; - - if (whichtri == -1) { - footpoint = (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2); - } - } - if (weapons[weaponids[weaponactive]].getType() == staff) { - XYZ where, startpoint, endpoint, movepoint; - float rotationpoint; - int whichtri; - - where = weapons[weaponids[weaponactive]].position; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - startpoint = where; - where = weapons[weaponids[weaponactive]].tippoint; - where -= victim->coords; - if (!victim->skeleton.free) - where = DoRotation(where, 0, -victim->yaw, 0); - endpoint = where; - - movepoint = 0; - rotationpoint = 0; - whichtri = victim->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &footpoint, &movepoint, &rotationpoint); - footpoint += victim->coords; - - if (whichtri == -1) { - footpoint = (weapons[weaponids[weaponactive]].tippoint * .8 + weapons[weaponids[weaponactive]].position * .2); - } - } - hasvictim = victim->DoBloodBigWhere(2, 220, footpoint); - if (hasvictim) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) { - victim->skeleton.longdead = 0; - victim->skeleton.free = 1; - victim->skeleton.broken = 0; - - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velchange = 0; - victim->skeleton.joints[i].locked = 0; - //victim->skeleton.joints[i].velocity=0; - } - - XYZ relative; - relative = 0; - relative.y = 10; - Normalise(&relative); - //victim->Puff(abdomen); - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .8, .3); - - if (victim->bloodloss < victim->damagetolerance) { - victim->bloodloss += 1000; - victim->bled = 0; - } - - victim->jointVel(abdomen) += relative * damagemult * 20; - } - } - } - if (!hasvictim && onterrain) { - weapons[weaponids[weaponactive]].bloody = 0; - weapons[weaponids[weaponactive]].blooddrip = 0; - } - } - - if (animTarget == upunchanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 235); - } - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 128); - } - - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity = relative * 30; - } - victim->jointVel(head) += relative * damagemult * 150; - - victim->frameTarget = 0; - victim->animTarget = staggerbackhardanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - victim->stunned = 1; - - victim->Puff(head); - victim->Puff(abdomen); - victim->DoDamage(damagemult * 60 / victim->protectionhigh); - - SolidHitBonus(id); - } - } - - - if (animTarget == winduppunchanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 2) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (victim->damage <= victim->damagetolerance - 60 && normaldotproduct(victim->facing, victim->coords - coords) < (scale * 5) * (scale * 5) * 0 && Animation::animations[victim->animTarget].height != lowheight) { - if (tutoriallevel != 1) { - emit_sound_at(thudsound, victim->coords); - } - } else if (victim->damage <= victim->damagetolerance - 60 && normaldotproduct(victim->facing, victim->coords - coords) < (scale * 5) * (scale * 5) * 0 && Animation::animations[victim->animTarget].height == lowheight) { - if (tutoriallevel != 1) { - emit_sound_at(whooshhitsound, victim->coords); - } - } else { - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords); - } - } - - if (victim->damage > victim->damagetolerance - 60 || normaldotproduct(victim->facing, victim->coords - coords) > 0 || Animation::animations[victim->animTarget].height == lowheight) - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - relative.y = .3; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity = relative * 5; - } - victim->jointVel(abdomen) += relative * damagemult * 400; - - victim->frameTarget = 0; - victim->animTarget = staggerbackhardanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - victim->stunned = 1; - - victim->Puff(abdomen); - victim->DoDamage(damagemult * 60 / victim->protectionhigh); - - SolidHitBonus(id); - } - } - - if (animTarget == blockhighleftanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 4) { - if (victim->id == 0) - camerashake += .4; - emit_sound_at(landsound2, victim->coords); - - Puff(righthand); - } - } - - if (animTarget == swordslashparryanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 4) { - if (victim->id == 0) - camerashake += .4; - - if (weaponactive != -1) { - if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { - if (weapons[victim->weaponids[0]].getType() == staff) - weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - if (weapons[weaponids[0]].getType() == staff) - weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - - emit_sound_at(swordstaffsound, victim->coords); - } else { - emit_sound_at(metalhitsound, victim->coords); - } - } - - //Puff(righthand); - } - } - - if (animTarget == knifethrowanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (weaponactive != -1) { - escapednum = 0; - XYZ aim; - aim = victim->coords + DoRotation(victim->jointPos(abdomen), 0, victim->yaw, 0) * victim->scale + victim->velocity * findDistance(&victim->coords, &coords) / 50 - (coords + DoRotation(jointPos(righthand), 0, yaw, 0) * scale); - Normalise(&aim); - weapons[weaponids[0]].thrown(aim * 50); - num_weapons--; - if (num_weapons) { - weaponids[0] = weaponids[num_weapons]; - } - weaponactive = -1; - } - } - - if (animTarget == knifeslashstartanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (hasvictim) - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 4.5 &&/*Animation::animations[victim->animTarget].height!=lowheight&&*/victim->animTarget != dodgebackanim && victim->animTarget != rollanim) { - escapednum = 0; - if (tutoriallevel != 1) - victim->DoBloodBig(1.5 / victim->armorhigh, 225); - - award_bonus(id, Slicebonus); - if (tutoriallevel != 1) { - emit_sound_at(knifeslicesound, victim->coords); - } - //victim->jointVel(abdomen)+=relative*damagemult*200; - if (Animation::animations[victim->animTarget].attack && (victim->aitype != playercontrolled || victim->animTarget == knifeslashstartanim) && (victim->creature == rabbittype || victim->deathbleeding <= 0)) { - if (victim->id != 0 || difficulty == 2) { - victim->frameTarget = 0; - victim->animTarget = staggerbackhardanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - } - } - victim->lowreversaldelay = 0; - victim->highreversaldelay = 0; - if (aitype != playercontrolled) - weaponmissdelay = .6; - - if (tutoriallevel != 1) - if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) - weapons[weaponids[weaponactive]].bloody = 1; - if (tutoriallevel != 1) - weapons[weaponids[weaponactive]].blooddrip += 3; - - XYZ footvel, footpoint; - footvel = 0; - if (skeleton.free) { - footpoint = (victim->jointPos(abdomen) + victim->jointPos(neck)) / 2 * victim->scale + victim->coords; - } else { - footpoint = DoRotation((victim->jointPos(abdomen) + victim->jointPos(neck)) / 2, 0, victim->yaw, 0) * victim->scale + victim->coords; - } - if (tutoriallevel != 1) { - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .6, .3); - footvel = DoRotation(facing, 0, 90, 0) * .8; - //footvel.y-=.3; - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .2, 1); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .2, 1); - } - if (tutoriallevel == 1) { - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .6, .3); - } - victim->DoDamage(damagemult * 0); - } - } - if (animTarget == swordslashanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim) { - if (victim->weaponactive == -1 || normaldotproduct(victim->facing, victim->coords - coords) > 0 || (Random() % 2 == 0)) { - award_bonus(id, Slashbonus); - escapednum = 0; - if (tutoriallevel != 1) { - if (normaldotproduct(victim->facing, victim->coords - coords) < 0) - victim->DoBloodBig(2 / victim->armorhigh, 190); - else - victim->DoBloodBig(2 / victim->armorhigh, 185); - victim->deathbleeding = 1; - emit_sound_at(swordslicesound, victim->coords); - } - //victim->jointVel(abdomen)+=relative*damagemult*200; - if (tutoriallevel != 1) { - victim->frameTarget = 0; - victim->animTarget = staggerbackhardanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - } - - if (tutoriallevel != 1) { - if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) - weapons[weaponids[weaponactive]].bloody = 1; - weapons[weaponids[weaponactive]].blooddrip += 3; - - float bloodlossamount; - bloodlossamount = 200 + abs((float)(Random() % 40)) - 20; - victim->bloodloss += bloodlossamount / victim->armorhigh; - //victim->bloodloss+=100*(6.5-distsq(&coords,&victim->coords)); - victim->DoDamage(damagemult * 0); - - XYZ footvel, footpoint; - footvel = 0; - if (skeleton.free) { - footpoint = (victim->jointPos(abdomen) + victim->jointPos(neck)) / 2 * victim->scale + victim->coords; - } else { - footpoint = DoRotation((victim->jointPos(abdomen) + victim->jointPos(neck)) / 2, 0, victim->yaw, 0) * victim->scale + victim->coords; - } - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); - footvel = DoRotation(facing, 0, 90, 0) * .8; - footvel.y -= .3; - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); - } - } else { - if (victim->weaponactive != -1) { - if (weapons[victim->weaponids[0]].getType() == staff || weapons[weaponids[0]].getType() == staff) { - if (weapons[victim->weaponids[0]].getType() == staff) - weapons[victim->weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - if (weapons[weaponids[0]].getType() == staff) - weapons[weaponids[0]].damage += .2 + float(abs(Random() % 100) - 50) / 250; - - emit_sound_at(swordstaffsound, victim->coords); - } else { - emit_sound_at(metalhitsound, victim->coords); - } - } - - - XYZ aim; - victim->Puff(righthand); - victim->target = 0; - victim->frameTarget = 0; - victim->animTarget = staggerbackhighanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - aim = DoRotation(facing, 0, 90, 0) * 21; - aim.y += 7; - weapons[victim->weaponids[0]].drop(aim * -.2, aim); - victim->num_weapons--; - if (victim->num_weapons) { - victim->weaponids[0] = victim->weaponids[num_weapons]; - if (victim->weaponstuck == victim->num_weapons) - victim->weaponstuck = 0; - } - victim->weaponactive = -1; - for (unsigned i = 0; i < Person::players.size(); i++) { - Person::players[i]->wentforweapon = 0; - } - - } - } - } - - if (animTarget == staffhitanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim && victim->animTarget != sweepanim) { - if (tutoriallevel != 1) { - weapons[weaponids[0]].damage += .4 + float(abs(Random() % 100) - 50) / 250; - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2 || creature == wolftype) { - victim->spurt = 1; - } - emit_sound_at(staffheadsound, victim->coords); - } - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, 90, 0); - relative.y -= 1; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 60; - } - victim->jointVel(head) += relative * damagemult * 230; - victim->jointVel(neck) += relative * damagemult * 230; - victim->Puff(head); - if (tutoriallevel != 1) { - victim->DoDamage(damagemult * 120 / victim->protectionhigh); - - award_bonus(id, solidhit, 30); - } - } - } - - if (animTarget == staffspinhitanim && Animation::animations[animTarget].frames[frameCurrent].label == 5 && victim->animTarget != rollanim) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5 && victim->animTarget != dodgebackanim && victim->animTarget != sweepanim) { - if (tutoriallevel != 1) { - weapons[weaponids[0]].damage += .6 + float(abs(Random() % 100) - 50) / 250; - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2 || creature == wolftype) { - victim->spurt = 1; - } - emit_sound_at(staffheadsound, victim->coords); - } - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, -90, 0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->jointVel(head) += relative * damagemult * 220; - victim->jointVel(neck) += relative * damagemult * 220; - victim->Puff(head); - if (tutoriallevel != 1) { - victim->DoDamage(damagemult * 350 / victim->protectionhead); - - award_bonus(id, solidhit, 60); - } - } - } - - if (animTarget == staffgroundsmashanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 6.5) { - escapednum = 0; - if (tutoriallevel != 1) { - if (!victim->dead) - weapons[weaponids[0]].damage += .4 + float(abs(Random() % 100) - 50) / 500; - if (id == 0) - camerashake += .4; - if (Random() % 2 || creature == wolftype) { - victim->spurt = 1; - } - emit_sound_at(staffbodysound, victim->coords); - } - victim->skeleton.longdead = 0; - victim->skeleton.free = 1; - victim->skeleton.broken = 0; - - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velchange = 0; - victim->skeleton.joints[i].locked = 0; - //victim->skeleton.joints[i].velocity=0; - } - - victim->RagDoll(0); - XYZ relative; - relative = 0; - relative.y = -1; - Normalise(&relative); - if (!victim->dead) { - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity = relative * damagemult * 40; - } - victim->jointVel(abdomen) += relative * damagemult * 40; - } - if (victim->dead) { - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity = relative * damagemult * abs(Random() % 20); - } - } - victim->Puff(abdomen); - if (tutoriallevel != 1) { - victim->DoDamage(damagemult * 100 / victim->protectionhigh); - - if (!victim->dead) { - award_bonus(id, solidhit, 40); - } - } - } - } - - if (animTarget == lowkickanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3 && Animation::animations[victim->animTarget].height != highheight) { - escapednum = 0; - if (id == 0) - camerashake += .4; - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - - SolidHitBonus(id); - - if (Animation::animations[victim->animTarget].height == lowheight) { - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 250); - } - victim->RagDoll(0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->jointVel(head) += relative * damagemult * 200; - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 128.); - } - victim->Puff(head); - victim->DoDamage(damagemult * 100 / victim->protectionhead); - if (victim->howactive == typesleeping) - victim->DoDamage(damagemult * 150 / victim->protectionhead); - if (creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhead, 175); - } - } else { - if (victim->damage >= victim->damagetolerance) - victim->RagDoll(0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 10; - } - victim->jointVel(abdomen) += relative * damagemult * 200; - victim->frameTarget = 0; - victim->animTarget = staggerbackhighanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - if (tutoriallevel != 1) { - emit_sound_at(landsound2, victim->coords, 128.); - } - victim->Puff(abdomen); - victim->DoDamage(damagemult * 30 / victim->protectionhigh); - if (creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhigh, 170); - } - } - - } - } - - if (animTarget == sweepanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if ((victim->animTarget != jumpupanim) && - (distsq(&coords, &victim->coords) < (scale * 5) * (scale * 5) * 3) && - (victim != this->shared_from_this())) { - escapednum = 0; - if (id == 0) - camerashake += .2; - if (tutoriallevel != 1) { - emit_sound_at(landsound2, victim->coords, 128.); - } - XYZ relative; - relative = victim->coords - coords; - relative.y = 0; - Normalise(&relative); - - if (Animation::animations[victim->animTarget].height == middleheight || Animation::animations[victim->animCurrent].height == middleheight || victim->damage >= victim->damagetolerance - 40) { - victim->RagDoll(0); - - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 15; - } - relative = DoRotation(relative, 0, -90, 0); - relative.y += .1; - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - if (victim->skeleton.joints[i].label == leftfoot || victim->skeleton.joints[i].label == rightfoot || victim->skeleton.joints[i].label == leftankle || victim->skeleton.joints[i].label == rightankle) - victim->skeleton.joints[i].velocity = relative * 80; - } - victim->Puff(rightankle); - victim->Puff(leftankle); - victim->DoDamage(damagemult * 40 / victim->protectionlow); - } else { - if (victim->damage >= victim->damagetolerance) - victim->RagDoll(0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 10; - } - relative = DoRotation(relative, 0, -90, 0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - if (victim->skeleton.joints[i].label == leftfoot || victim->skeleton.joints[i].label == rightfoot || victim->skeleton.joints[i].label == leftankle || victim->skeleton.joints[i].label == rightankle) - victim->skeleton.joints[i].velocity += relative * damagemult * 80; - } - victim->jointVel(abdomen) += relative * damagemult * 200; - victim->frameTarget = 0; - victim->animTarget = staggerbackhighanim; - victim->targetyaw = targetyaw + 180; - victim->target = 0; - if (tutoriallevel != 1) { - emit_sound_at(landsound2, victim->coords, 128.); - } - victim->Puff(abdomen); - victim->DoDamage(damagemult * 30 / victim->protectionlow); - } - - SolidHitBonus(id); - - } - } - } - if (Animation::animations[animTarget].attack == reversal && (!victim->feint || (victim->lastattack == victim->lastattack2 && victim->lastattack2 == victim->lastattack3 && Random() % 2) || animTarget == knifefollowanim)) { - if (animTarget == spinkickreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 230); - } - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 128.); - } - if (creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhigh, 170); - } - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - oldcoords; - relative.y = 0; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->jointVel(abdomen) += relative * damagemult * 200; - victim->Puff(abdomen); - victim->DoDamage(damagemult * 150 / victim->protectionhigh); - - award_bonus(id, Reversal); - } - - if ((animTarget == swordslashreversalanim || animTarget == knifeslashreversalanim || animTarget == staffhitreversalanim || animTarget == staffspinhitreversalanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (victim->weaponactive != -1 && victim->num_weapons > 0) { - if (weapons[victim->weaponids[victim->weaponactive]].owner == int(victim->id)) { - takeWeapon(victim->weaponids[victim->weaponactive]); - victim->num_weapons--; - if (victim->num_weapons > 0) { - victim->weaponids[victim->weaponactive] = victim->weaponids[victim->num_weapons]; - } - victim->weaponactive = -1; - } - } - } - - if (animTarget == staffhitreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 230); - } - emit_sound_at(whooshhitsound, victim->coords, 128.); - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - oldcoords; - relative.y = 0; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 30; - } - victim->jointVel(abdomen) += relative * damagemult * 200; - victim->Puff(head); - victim->DoDamage(damagemult * 70 / victim->protectionhigh); - } - - if (animTarget == staffspinhitreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 230); - } - - award_bonus(id, staffreversebonus); - - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 128.); - } - victim->RagDoll(0); - award_bonus(id, staffreversebonus); // Huh, again? - - XYZ relative; - relative = victim->coords - oldcoords; - relative.y = 0; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 30; - } - victim->jointVel(abdomen) += relative * damagemult * 200; - victim->Puff(head); - victim->DoDamage(damagemult * 70 / victim->protectionhigh); - } - - if (animTarget == upunchreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - escapednum = 0; - victim->RagDoll(1); - XYZ relative; - relative = facing; - relative.y = 0; - Normalise(&relative); - relative.y -= .1; - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 70; - } - victim->jointVel(lefthand) *= .1; - victim->jointVel(leftwrist) *= .2; - victim->jointVel(leftelbow) *= .5; - victim->jointVel(leftshoulder) *= .7; - victim->jointVel(righthand) *= .1; - victim->jointVel(rightwrist) *= .2; - victim->jointVel(rightelbow) *= .5; - victim->jointVel(rightshoulder) *= .7; - - victim->Puff(abdomen); - victim->DoDamage(damagemult * 90 / victim->protectionhigh); - - award_bonus(id, Reversal); - - bool doslice; - doslice = 0; - if (weaponactive != -1 || creature == wolftype) - doslice = 1; - if (creature == rabbittype && weaponactive != -1) - if (weapons[weaponids[0]].getType() == staff) - doslice = 0; - if (doslice) { - if (weaponactive != -1) { - victim->DoBloodBig(2 / victim->armorhigh, 225); - emit_sound_at(knifeslicesound, victim->coords); - if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) - weapons[weaponids[weaponactive]].bloody = 1; - weapons[weaponids[weaponactive]].blooddrip += 3; - } - if (weaponactive == -1 && creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhigh, 175); - } - } - } - - - - if (animTarget == swordslashreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - escapednum = 0; - victim->RagDoll(1); - XYZ relative; - relative = facing; - relative.y = 0; - Normalise(&relative); - relative.y -= .1; - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 70; - } - victim->jointVel(lefthand) *= .1 - 1; - victim->jointVel(leftwrist) *= .2 - 1; - victim->jointVel(leftelbow) *= .5 - 1; - victim->jointVel(leftshoulder) *= .7 - 1; - victim->jointVel(righthand) *= .1 - 1; - victim->jointVel(rightwrist) *= .2 - 1; - victim->jointVel(rightelbow) *= .5 - 1; - victim->jointVel(rightshoulder) *= .7 - 1; - - award_bonus(id, swordreversebonus); - } - - if (hasvictim && animTarget == knifeslashreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 230); - } - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 128.); - } - victim->RagDoll(0); - XYZ relative; - relative = victim->coords - oldcoords; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, -90, 0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->jointVel(abdomen) += relative * damagemult * 200; - victim->Puff(abdomen); - victim->DoDamage(damagemult * 30 / victim->protectionhigh); - - award_bonus(id, Reversal); - } - - if (hasvictim && animTarget == sneakattackanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - escapednum = 0; - victim->RagDoll(0); - victim->skeleton.spinny = 0; - XYZ relative; - relative = facing * -1; - relative.y = -3; - Normalise(&relative); - if (victim->id == 0) - relative /= 30; - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 40; - } - victim->damage = victim->damagetolerance; - victim->permanentdamage = victim->damagetolerance - 1; - bool doslice; - doslice = 0; - if (weaponactive != -1 || creature == wolftype) - doslice = 1; - if (creature == rabbittype && weaponactive != -1) - if (weapons[weaponids[0]].getType() == staff) - doslice = 0; - if (doslice) { - if (weaponactive != -1) { - victim->DoBloodBig(200, 225); - emit_sound_at(knifeslicesound, victim->coords); - if (bloodtoggle) - weapons[weaponids[weaponactive]].bloody = 2; - weapons[weaponids[weaponactive]].blooddrip += 5; - } - - if (creature == wolftype && weaponactive == -1) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2, 175); - } - } - award_bonus(id, spinecrusher); - } - - if (hasvictim && (animTarget == knifefollowanim || animTarget == knifesneakattackanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (weaponactive != -1 && victim->bloodloss < victim->damagetolerance) { - escapednum = 0; - if (animTarget == knifefollowanim) - victim->DoBloodBig(200, 210); - if (animTarget == knifesneakattackanim) { - XYZ footvel, footpoint; - footvel = 0; - footpoint = weapons[weaponids[0]].tippoint; - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); - footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); - victim->DoBloodBig(200, 195); - award_bonus(id, tracheotomy); - } - if (animTarget == knifefollowanim) { - award_bonus(id, Stabbonus); - XYZ footvel, footpoint; - footvel = 0; - footpoint = weapons[weaponids[0]].tippoint; - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); - footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position) * -1; - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .2, 1); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .2, 1); - - } - victim->bloodloss += 10000; - victim->velocity = 0; - emit_sound_at(fleshstabsound, victim->coords); - if (bloodtoggle) - weapons[weaponids[weaponactive]].bloody = 2; - weapons[weaponids[weaponactive]].blooddrip += 5; - } - } - - if (hasvictim && (animTarget == knifefollowanim || animTarget == knifesneakattackanim) && Animation::animations[animTarget].frames[frameCurrent].label == 6) { - escapednum = 0; - victim->velocity = 0; - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity = 0; - } - if (animTarget == knifefollowanim) { - victim->RagDoll(0); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity = 0; - } - } - if (weaponactive != -1 && Animation::animations[victim->animTarget].attack != reversal) { - emit_sound_at(fleshstabremovesound, victim->coords); - if (bloodtoggle) - weapons[weaponids[weaponactive]].bloody = 2; - weapons[weaponids[weaponactive]].blooddrip += 5; - - XYZ footvel, footpoint; - footvel = 0; - footpoint = weapons[weaponids[0]].tippoint; - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); - footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position) * -1; - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); - } - } - - if (hasvictim && (animTarget == swordsneakattackanim) && Animation::animations[animTarget].frames[frameCurrent].label == 5) { - if (weaponactive != -1 && victim->bloodloss < victim->damagetolerance) { - award_bonus(id, backstab); - - escapednum = 0; - - XYZ footvel, footpoint; - footvel = 0; - footpoint = (weapons[weaponids[0]].tippoint + weapons[weaponids[0]].position) / 2; - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); - footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodflamesprite, footpoint, DoRotation(footvel * 5, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .3, 1); - Sprite::MakeSprite(bloodflamesprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .3, 1); - victim->DoBloodBig(200, 180); - victim->DoBloodBig(200, 215); - victim->bloodloss += 10000; - victim->velocity = 0; - emit_sound_at(fleshstabsound, victim->coords); - if (bloodtoggle) - weapons[weaponids[weaponactive]].bloody = 2; - weapons[weaponids[weaponactive]].blooddrip += 5; - } - } - - if (hasvictim && animTarget == swordsneakattackanim && Animation::animations[animTarget].frames[frameCurrent].label == 6) { - escapednum = 0; - victim->velocity = 0; - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity = 0; - } - if (weaponactive != -1) { - emit_sound_at(fleshstabremovesound, victim->coords); - if (bloodtoggle) - weapons[weaponids[weaponactive]].bloody = 2; - weapons[weaponids[weaponactive]].blooddrip += 5; - - XYZ footvel, footpoint; - footvel = 0; - footpoint = weapons[weaponids[0]].tippoint; - if (bloodtoggle) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .9, .3); - footvel = (weapons[weaponids[0]].tippoint - weapons[weaponids[0]].position) * -1; - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 7, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodsprite, footpoint, DoRotation(footvel * 3, (float)(Random() % 20), (float)(Random() % 20), 0), 1, 1, 1, .05, .9); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 5, 1, 1, 1, .3, 1); - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * 2, 1, 1, 1, .3, 1); - } - } - - if (animTarget == sweepreversalanim && Animation::animations[animTarget].frames[frameCurrent].label == 7) { - escapednum = 0; - if (id == 0) - camerashake += .4; - if (Random() % 2) { - victim->spurt = 1; - DoBlood(.2, 240); - } - if (weaponactive == -1) { - if (tutoriallevel != 1) { - emit_sound_at(heavyimpactsound, victim->coords, 128.); - } - } - bool doslice; - doslice = 0; - if (weaponactive != -1 || creature == wolftype) - doslice = 1; - if (creature == rabbittype && weaponactive != -1) - if (weapons[weaponids[0]].getType() == staff) - doslice = 0; - if (doslice) { - if (weaponactive != -1) { - victim->DoBloodBig(2 / victim->armorhead, 225); - emit_sound_at(knifeslicesound, victim->coords); - if (bloodtoggle && !weapons[weaponids[weaponactive]].bloody) - weapons[weaponids[weaponactive]].bloody = 1; - weapons[weaponids[weaponactive]].blooddrip += 3; - } - if (weaponactive == -1 && creature == wolftype) { - emit_sound_at(clawslicesound, victim->coords, 128.); - victim->spurt = 1; - victim->DoBloodBig(2 / victim->armorhead, 175); - } - } - - award_bonus(id, Reversal); - - victim->Puff(neck); - - XYZ relative; - relative = facing * -1; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, 90, 0); - relative.y = .5; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 20; - } - victim->jointVel(head) += relative * damagemult * 200; - if (victim->damage < victim->damagetolerance - 100) - victim->velocity = relative * 200; - victim->DoDamage(damagemult * 100 / victim->protectionhead); - victim->velocity = 0; - } - - if (animTarget == sweepreversalanim && ((Animation::animations[animTarget].frames[frameCurrent].label == 9 && victim->damage < victim->damagetolerance) || (Animation::animations[animTarget].frames[frameCurrent].label == 7 && victim->damage > victim->damagetolerance))) { - escapednum = 0; - victim->RagDoll(0); - XYZ relative; - relative = facing * -1; - relative.y = 0; - Normalise(&relative); - relative = DoRotation(relative, 0, 90, 0); - relative.y = .5; - Normalise(&relative); - for (int i = 0; i < victim->skeleton.joints.size(); i++) { - victim->skeleton.joints[i].velocity += relative * damagemult * 20; - } - victim->jointVel(head) += relative * damagemult * 200; - } - - if (hasvictim && (animTarget == spinkickreversalanim || animTarget == sweepreversalanim || animTarget == rabbitkickreversalanim || animTarget == upunchreversalanim || animTarget == jumpreversalanim || animTarget == swordslashreversalanim || animTarget == knifeslashreversalanim || animTarget == rabbittacklereversal || animTarget == wolftacklereversal || animTarget == staffhitreversalanim || animTarget == staffspinhitreversalanim)) - if (victim->damage > victim->damagetolerance && bonus != reverseko) { - award_bonus(id, reverseko); - } - } - - - //Animation end - if (frameTarget > Animation::animations[animCurrent].frames.size() - 1) { - frameTarget = 0; - if (wasStop()) { - animTarget = getIdle(); - FootLand(leftfoot, 1); - FootLand(rightfoot, 1); - } - if (animCurrent == rabbittackleanim || animCurrent == rabbittacklinganim) { - animTarget = rollanim; - frameTarget = 3; - emit_sound_at(movewhooshsound, coords, 128.); - } - if (animCurrent == staggerbackhighanim) { - animTarget = getIdle(); - } - if (animCurrent == staggerbackhardanim) { - animTarget = getIdle(); - } - if (animCurrent == removeknifeanim) { - animTarget = getIdle(); - } - if (animCurrent == crouchremoveknifeanim) { - animTarget = getCrouch(); - } - if (animCurrent == backhandspringanim) { - animTarget = getIdle(); - } - if (animCurrent == dodgebackanim) { - animTarget = getIdle(); - } - if (animCurrent == drawleftanim) { - animTarget = getIdle(); - } - if (animCurrent == drawrightanim || animCurrent == crouchdrawrightanim) { - animTarget = getIdle(); - if (animCurrent == crouchdrawrightanim) { - animTarget = getCrouch(); - } - if (weaponactive == -1) - weaponactive = 0; - else if (weaponactive == 0) { - weaponactive = -1; - if (num_weapons == 2) { - int buffer; - buffer = weaponids[0]; - weaponids[0] = weaponids[1]; - weaponids[1] = buffer; - } - } - - if (weaponactive == -1) { - emit_sound_at(knifesheathesound, coords, 128.); - } - if (weaponactive != -1) { - emit_sound_at(knifedrawsound, coords, 128.); - } - } - if (animCurrent == rollanim) { - animTarget = getCrouch(); - FootLand(leftfoot, 1); - FootLand(rightfoot, 1); - } - if (isFlip()) { - if (animTarget == walljumprightkickanim) { - targetrot = -190; - } - if (animTarget == walljumpleftkickanim) { - targetrot = 190; - } - animTarget = jumpdownanim; - } - if (animCurrent == climbanim) { - animTarget = getCrouch(); - frameTarget = 1; - coords += facing * .1; - if (!isnormal(coords.x)) - coords = oldcoords; - oldcoords = coords; - collided = 0; - targetoffset = 0; - currentoffset = 0; - grabdelay = 1; - velocity = 0; - collided = 0; - avoidcollided = 0; - } - if (animTarget == rabbitkickreversalanim) { - animTarget = getCrouch(); - lastfeint = 0; - } - if (animTarget == jumpreversalanim) { - animTarget = getCrouch(); - lastfeint = 0; - } - if (animTarget == walljumprightanim || animTarget == walljumpbackanim || animTarget == walljumpfrontanim) { - if (attackkeydown && animTarget != walljumpfrontanim) { - int closest = -1; - float closestdist = -1; - float distance; - if (Person::players.size() > 1) - for (unsigned i = 0; i < Person::players.size(); i++) { - if (id != i && Person::players[i]->coords.y < coords.y && !Person::players[i]->skeleton.free) { - distance = distsq(&Person::players[i]->coords, &coords); - if (closestdist == -1 || distance < closestdist) { - closestdist = distance; - closest = i; - } - } - } - if (closestdist > 0 && closest >= 0 && closestdist < 16) { - victim = Person::players[closest]; - animTarget = walljumprightkickanim; - frameTarget = 0; - XYZ rotatetarget = victim->coords - coords; - Normalise(&rotatetarget); - yaw = -asin(0 - rotatetarget.x); - yaw *= 360 / 6.28; - if (rotatetarget.z < 0) - yaw = 180 - yaw; - targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; - velocity = (victim->coords - coords) * 4; - velocity.y += 2; - transspeed = 40; - } - } - if (animTarget == walljumpbackanim) { - animTarget = backflipanim; - frameTarget = 3; - velocity = facing * -8; - velocity.y = 4; - if (id == 0) - resume_stream(whooshsound); - } - if (animTarget == walljumprightanim) { - animTarget = rightflipanim; - frameTarget = 4; - targetyaw -= 90; - yaw -= 90; - velocity = DoRotation(facing, 0, 30, 0) * -8; - velocity.y = 4; - } - if (animTarget == walljumpfrontanim) { - animTarget = frontflipanim; - frameTarget = 2; - //targetyaw-=180; - ////yaw-=180; - velocity = facing * 8; - velocity.y = 4; - } - if (id == 0) - resume_stream(whooshsound); - } - if (animTarget == walljumpleftanim) { - if (attackkeydown) { - int closest = -1; - float closestdist = -1; - float distance; - if (Person::players.size() > 1) - for (unsigned i = 0; i < Person::players.size(); i++) { - if (id != i && Person::players[i]->coords.y < coords.y && !Person::players[i]->skeleton.free) { - distance = distsq(&Person::players[i]->coords, &coords); - if (closestdist == -1 || distance < closestdist) { - closestdist = distance; - closest = i; - } - } - } - if (closestdist > 0 && closest >= 0 && closestdist < 16) { - victim = Person::players[closest]; - animTarget = walljumpleftkickanim; - frameTarget = 0; - XYZ rotatetarget = victim->coords - coords; - Normalise(&rotatetarget); - yaw = -asin(0 - rotatetarget.x); - yaw *= 360 / 6.28; - if (rotatetarget.z < 0) - yaw = 180 - yaw; - targettilt2 = -asin(rotatetarget.y) * 360 / 6.28; - velocity = (victim->coords - coords) * 4; - velocity.y += 2; - transspeed = 40; - } - } - if (animTarget != walljumpleftkickanim) { - animTarget = leftflipanim; - frameTarget = 4; - targetyaw += 90; - yaw += 90; - velocity = DoRotation(facing, 0, -30, 0) * -8; - velocity.y = 4; - } - if (id == 0) - resume_stream(whooshsound); - } - if (animTarget == sneakattackanim) { - animCurrent = getCrouch(); - animTarget = getCrouch(); - frameTarget = 1; - frameCurrent = 0; - targetyaw += 180; - yaw += 180; - targettilt2 *= -1; - tilt2 *= -1; - transspeed = 1000000; - targetheadyaw += 180; - coords -= facing * .7; - if (onterrain) - coords.y = terrain.getHeight(coords.x, coords.z); - - lastfeint = 0; - } - if (animTarget == knifesneakattackanim || animTarget == swordsneakattackanim) { - animTarget = getIdle(); - frameTarget = 0; - if (onterrain) - coords.y = terrain.getHeight(coords.x, coords.z); - - lastfeint = 0; - } - if (animCurrent == knifefollowanim) { - animTarget = getIdle(); - lastfeint = 0; - } - if (Animation::animations[animTarget].attack == reversal && animCurrent != sneakattackanim && animCurrent != knifesneakattackanim && animCurrent != swordsneakattackanim && animCurrent != knifefollowanim) { - float ycoords = oldcoords.y; - animTarget = getStop(); - targetyaw += 180; - yaw += 180; - targettilt2 *= -1; - tilt2 *= -1; - transspeed = 1000000; - targetheadyaw += 180; - if (!isnormal(coords.x)) - coords = oldcoords; - if (animCurrent == spinkickreversalanim || animCurrent == swordslashreversalanim) - oldcoords = coords + facing * .5; - else if (animCurrent == sweepreversalanim) - oldcoords = coords + facing * 1.1; - else if (animCurrent == upunchreversalanim) { - oldcoords = coords + facing * 1.5; - targetyaw += 180; - yaw += 180; - targetheadyaw += 180; - targettilt2 *= -1; - tilt2 *= -1; - } else if (animCurrent == knifeslashreversalanim) { - oldcoords = coords + facing * .5; - targetyaw += 90; - yaw += 90; - targetheadyaw += 90; - targettilt2 = 0; - tilt2 = 0; - } else if (animCurrent == staffspinhitreversalanim) { - targetyaw += 180; - yaw += 180; - targetheadyaw += 180; - targettilt2 = 0; - tilt2 = 0; - } - if (onterrain) - oldcoords.y = terrain.getHeight(oldcoords.x, oldcoords.z); - else - oldcoords.y = ycoords; - currentoffset = coords - oldcoords; - targetoffset = 0; - coords = oldcoords; - - lastfeint = 0; - } - if (animCurrent == knifesneakattackedanim || animCurrent == swordsneakattackedanim) { - velocity = 0; - velocity.y = -5; - RagDoll(0); - } - if (Animation::animations[animTarget].attack == reversed) { - escapednum++; - if (animTarget == sweepreversedanim) - targetyaw += 90; - animTarget = backhandspringanim; - frameTarget = 2; - emit_sound_at(landsound, coords, 128); - - if (animCurrent == upunchreversedanim || animCurrent == swordslashreversedanim) { - animTarget = rollanim; - frameTarget = 5; - oldcoords = coords; - coords += (DoRotation(jointPos(leftfoot), 0, yaw, 0) + DoRotation(jointPos(rightfoot), 0, yaw, 0)) / 2 * scale; - coords.y = oldcoords.y; - } - if (animCurrent == knifeslashreversedanim) { - animTarget = rollanim; - frameTarget = 0; - targetyaw += 90; - yaw += 90; - oldcoords = coords; - coords += (DoRotation(jointPos(leftfoot), 0, yaw, 0) + DoRotation(jointPos(rightfoot), 0, yaw, 0)) / 2 * scale; - coords.y = oldcoords.y; - } - } - if (wasFlip()) { - animTarget = jumpdownanim; - } - if (wasLanding()) - animTarget = getIdle(); - if (wasLandhard()) - animTarget = getIdle(); - if (animCurrent == spinkickanim || animCurrent == getupfrombackanim || animCurrent == getupfromfrontanim || animCurrent == lowkickanim) { - animTarget = getIdle(); - oldcoords = coords; - coords += (DoRotation(jointPos(leftfoot), 0, yaw, 0) + DoRotation(jointPos(rightfoot), 0, yaw, 0)) / 2 * scale; - coords.y = oldcoords.y; - //coords+=DoRotation(Animation::animations[animCurrent].offset,0,yaw,0)*scale; - targetoffset.y = coords.y; - if (onterrain) - targetoffset.y = terrain.getHeight(coords.x, coords.z); - currentoffset = DoRotation(Animation::animations[animCurrent].offset * -1, 0, yaw, 0) * scale; - currentoffset.y -= (coords.y - targetoffset.y); - coords.y = targetoffset.y; - targetoffset = 0; - normalsupdatedelay = 0; - } - if (animCurrent == upunchanim) { - animTarget = getStop(); - normalsupdatedelay = 0; - lastfeint = 0; - } - if (animCurrent == rabbitkickanim && animTarget != backflipanim) { - targetyaw = yaw; - bool hasstaff; - hasstaff = 0; - if (num_weapons > 0) - if (weapons[0].getType() == staff) - hasstaff = 1; - if (!hasstaff) - DoDamage(35); - RagDoll(0); - lastfeint = 0; - rabbitkickragdoll = 1; - } - if (animCurrent == rabbitkickreversedanim) { - if (!feint) { - velocity = 0; - velocity.y = -10; - //DoDamage(100); - RagDoll(0); - skeleton.spinny = 0; - SolidHitBonus(!id); // FIXME: tricky id - } - if (feint) { - escapednum++; - animTarget = rollanim; - coords += facing; - if (id == 0) - pause_sound(whooshsound); - } - lastfeint = 0; - } - if (animCurrent == rabbittackledbackanim || animCurrent == rabbittackledfrontanim) { - velocity = 0; - velocity.y = -10; - RagDoll(0); - skeleton.spinny = 0; - } - if (animCurrent == jumpreversedanim) { - if (!feint) { - velocity = 0; - velocity.y = -10; - //DoDamage(100); - RagDoll(0); - skeleton.spinny = 0; - SolidHitBonus(!id); // FIXME: tricky id - } - if (feint) { - escapednum++; - animTarget = rollanim; - coords += facing * 2; - if (id == 0) - pause_sound(whooshsound); - } - lastfeint = 0; - } - - if (Animation::animations[animCurrent].attack == normalattack && !victim->skeleton.free && victim->animTarget != staggerbackhighanim && victim->animTarget != staggerbackhardanim && animTarget != winduppunchblockedanim && animTarget != blockhighleftanim && animTarget != swordslashparryanim && animTarget != swordslashparriedanim && animTarget != crouchstabanim && animTarget != swordgroundstabanim) { - animTarget = getupfromfrontanim; - lastfeint = 0; - } else if (Animation::animations[animCurrent].attack == normalattack) { - animTarget = getIdle(); - lastfeint = 0; - } - if (animCurrent == blockhighleftanim && aitype != playercontrolled) { - animTarget = blockhighleftstrikeanim; - } - if (animCurrent == knifeslashstartanim || animCurrent == knifethrowanim || animCurrent == swordslashanim || animCurrent == staffhitanim || animCurrent == staffgroundsmashanim || animCurrent == staffspinhitanim) { - animTarget = getIdle(); - lastfeint = 0; - } - if (animCurrent == spinkickanim && victim->skeleton.free) { - if (creature == rabbittype) - animTarget = fightidleanim; - } - } - target = 0; - - if (isIdle() && !wasIdle()) - normalsupdatedelay = 0; - - if (animCurrent == jumpupanim && velocity.y < 0 && !isFlip()) { - animTarget = jumpdownanim; - } - } - if (!skeleton.free) { - oldtarget = target; - if (!transspeed && Animation::animations[animTarget].attack != 2 && Animation::animations[animTarget].attack != 3) { - if (!isRun() || !wasRun()) { - if (targetFrame().speed > currentFrame().speed) - target += multiplier * targetFrame().speed * speed * 2; - if (targetFrame().speed <= currentFrame().speed) - target += multiplier * currentFrame().speed * speed * 2; - } - if (isRun() && wasRun()) { - float tempspeed; - tempspeed = velspeed; - if (tempspeed < 10 * speedmult) - tempspeed = 10 * speedmult; - /* FIXME - mixed of target and current here, is that intended? */ - target += multiplier * Animation::animations[animTarget].frames[frameCurrent].speed * speed * 1.7 * tempspeed / (speed * 45 * scale); - } - } else if (transspeed) - target += multiplier * transspeed * speed * 2; - else { - if (!isRun() || !wasRun()) { - if (targetFrame().speed > currentFrame().speed) - target += multiplier * targetFrame().speed * 2; - if (targetFrame().speed <= currentFrame().speed) - target += multiplier * currentFrame().speed * 2; - } - } - - if (animCurrent != animTarget) - target = (target + oldtarget) / 2; - - if (target > 1) { - frameCurrent = frameTarget; - target = 1; - } - oldrot = rot; - rot = targetrot * target; - yaw += rot - oldrot; - if (target == 1) { - rot = 0; - oldrot = 0; - targetrot = 0; - } - if (frameCurrent >= Animation::animations[animCurrent].frames.size()) { - frameCurrent = Animation::animations[animCurrent].frames.size() - 1; - } - if (animCurrent != oldanimCurrent || animTarget != oldanimTarget || ((frameCurrent != oldframeCurrent || frameTarget != oldframeTarget) && !calcrot)) { - //Old rotates - for (int i = 0; i < skeleton.joints.size(); i++) { - skeleton.joints[i].position = currentFrame().joints[i].position; - } - - skeleton.FindForwards(); - - for (int i = 0; i < skeleton.muscles.size(); i++) { - if (skeleton.muscles[i].visible) { - skeleton.FindRotationMuscle(i, animTarget); - } - } - for (int i = 0; i < skeleton.muscles.size(); i++) { - if (skeleton.muscles[i].visible) { - if (isnormal((float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100)) - skeleton.muscles[i].oldrotate1 = (float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100; - if (isnormal((float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100)) - skeleton.muscles[i].oldrotate2 = (float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100; - if (isnormal((float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100)) - skeleton.muscles[i].oldrotate3 = (float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100; - } - } - - //New rotates - for (int i = 0; i < skeleton.joints.size(); i++) { - skeleton.joints[i].position = targetFrame().joints[i].position; - } - - skeleton.FindForwards(); - - for (int i = 0; i < skeleton.muscles.size(); i++) { - if (skeleton.muscles[i].visible) { - skeleton.FindRotationMuscle(i, animTarget); - } - } - for (int i = 0; i < skeleton.muscles.size(); i++) { - if (skeleton.muscles[i].visible) { - if (isnormal((float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100)) - skeleton.muscles[i].newrotate1 = (float)((int)(skeleton.muscles[i].rotate1 * 100) % 36000) / 100; - if (isnormal((float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100)) - skeleton.muscles[i].newrotate2 = (float)((int)(skeleton.muscles[i].rotate2 * 100) % 36000) / 100; - if (isnormal((float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100)) - skeleton.muscles[i].newrotate3 = (float)((int)(skeleton.muscles[i].rotate3 * 100) % 36000) / 100; - if (skeleton.muscles[i].newrotate3 > skeleton.muscles[i].oldrotate3 + 180) skeleton.muscles[i].newrotate3 -= 360; - if (skeleton.muscles[i].newrotate3 < skeleton.muscles[i].oldrotate3 - 180) skeleton.muscles[i].newrotate3 += 360; - if (skeleton.muscles[i].newrotate2 > skeleton.muscles[i].oldrotate2 + 180) skeleton.muscles[i].newrotate2 -= 360; - if (skeleton.muscles[i].newrotate2 < skeleton.muscles[i].oldrotate2 - 180) skeleton.muscles[i].newrotate2 += 360; - if (skeleton.muscles[i].newrotate1 > skeleton.muscles[i].oldrotate1 + 180) skeleton.muscles[i].newrotate1 -= 360; - if (skeleton.muscles[i].newrotate1 < skeleton.muscles[i].oldrotate1 - 180) skeleton.muscles[i].newrotate1 += 360; - } - } - } - - oldanimCurrent = animCurrent; - oldanimTarget = animTarget; - oldframeTarget = frameTarget; - oldframeCurrent = frameCurrent; - - for (int i = 0; i < skeleton.joints.size(); i++) { - skeleton.joints[i].velocity = (currentFrame().joints[i].position * (1 - target) + targetFrame().joints[i].position * (target) - skeleton.joints[i].position) / multiplier; - skeleton.joints[i].position = currentFrame().joints[i].position * (1 - target) + targetFrame().joints[i].position * (target); - } - offset = currentoffset * (1 - target) + targetoffset * target; - for (int i = 0; i < skeleton.muscles.size(); i++) { - if (skeleton.muscles[i].visible) { - skeleton.muscles[i].rotate1 = skeleton.muscles[i].oldrotate1 * (1 - target) + skeleton.muscles[i].newrotate1 * (target); - skeleton.muscles[i].rotate2 = skeleton.muscles[i].oldrotate2 * (1 - target) + skeleton.muscles[i].newrotate2 * (target); - skeleton.muscles[i].rotate3 = skeleton.muscles[i].oldrotate3 * (1 - target) + skeleton.muscles[i].newrotate3 * (target); - } - } - } - - if (isLanding() && landhard) { - if (id == 0) - camerashake += .4; - animTarget = getLandhard(); - frameTarget = 0; - target = 0; - landhard = 0; - transspeed = 15; - } - } -} - -/* EFFECT - * MONSTER - * TODO - */ -void Person::DoStuff() -{ - static XYZ terrainnormal; - static XYZ flatfacing; - static XYZ flatvelocity; - static float flatvelspeed; - static int i, j, l; - static XYZ average; - static int howmany; - static int bloodsize; - static int startx, starty, endx, endy; - static GLubyte color; - static XYZ bloodvel; - - onfiredelay -= multiplier; - if (onfiredelay < 0 && onfire) { - if (Random() % 2 == 0) { - crouchkeydown = 1; - } - onfiredelay = 0.3; - } - - crouchkeydowntime += multiplier; - if (!crouchkeydown) - crouchkeydowntime = 0; - jumpkeydowntime += multiplier; - if (!jumpkeydown && skeleton.free) - jumpkeydowntime = 0; - - if (hostile || damage > 0 || bloodloss > 0) - immobile = 0; - - if (isIdle() || isRun()) - targetoffset = 0; - - if (num_weapons == 1 && weaponactive != -1) - weaponstuck = -1; - - if (id == 0) - blooddimamount -= multiplier * .3; - speechdelay -= multiplier; - texupdatedelay -= multiplier; - interestdelay -= multiplier; - flamedelay -= multiplier; - parriedrecently -= multiplier; - if (!victim) { - victim = this->shared_from_this(); - hasvictim = 0; - } - - if (id == 0) - speed = 1.1 * speedmult; - else - speed = 1.0 * speedmult; - if (!skeleton.free) - rabbitkickragdoll = 0; - - speed *= speedmult; - - if (id != 0 && (creature == rabbittype || difficulty != 2)) - superruntoggle = 0; - if (id != 0 && creature == wolftype && difficulty == 2) { - superruntoggle = 0; - if (aitype != passivetype) { - superruntoggle = 1; - if (aitype == attacktypecutoff && (Person::players[0]->isIdle() || Person::players[0]->isCrouch() || Person::players[0]->skeleton.free || Person::players[0]->animTarget == getupfrombackanim || Person::players[0]->animTarget == getupfromfrontanim || Person::players[0]->animTarget == sneakanim) && distsq(&coords, &Person::players[0]->coords) < 16) { - superruntoggle = 0; - } - } - if (scale < 0.2) - superruntoggle = 0; - if (animTarget == wolfrunninganim && !superruntoggle) { - animTarget = getRun(); - frameTarget = 0; - } - } - if (weaponactive == -1 && num_weapons > 0) { - if (weapons[weaponids[0]].getType() == staff) { - weaponactive = 0; - } - } - - if (onfire) { - burnt += multiplier; - deathbleeding = 1; - if (burnt > .6) - burnt = .6; - OPENAL_SetVolume(channels[stream_firesound], 256 + 256 * findLength(&velocity) / 3); - - if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { - float gLoc[3]; - float vel[3]; - gLoc[0] = coords.x; - gLoc[1] = coords.y; - gLoc[2] = coords.z; - vel[0] = velocity.x; - vel[1] = velocity.y; - vel[2] = velocity.z; - - if (id == 0) { - OPENAL_3D_SetAttributes(channels[whooshsound], gLoc, vel); - OPENAL_SetVolume(channels[whooshsound], 64 * findLength(&velocity) / 5); - } - } - } - while (flamedelay < 0 && onfire) { - flamedelay += .006; - howmany = abs(Random() % (skeleton.joints.size())); - if (skeleton.free) { - flatvelocity = skeleton.joints[howmany].velocity * scale / 2; - flatfacing = skeleton.joints[howmany].position * scale + coords; - } else { - flatfacing = DoRotation(DoRotation(DoRotation(skeleton.joints[howmany].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; - flatvelocity = (coords - oldcoords) / multiplier / 2; - } - Sprite::MakeSprite(flamesprite, flatfacing, flatvelocity, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1); - } - - while (flamedelay < 0 && !onfire && tutoriallevel == 1 && id != 0) { - flamedelay += .05; - howmany = abs(Random() % (skeleton.joints.size())); - if (skeleton.free) { - flatvelocity = skeleton.joints[howmany].velocity * scale / 2; - flatfacing = skeleton.joints[howmany].position * scale + coords; - } else { - flatvelocity = (coords - oldcoords) / multiplier / 2; - flatfacing = DoRotation(DoRotation(DoRotation(skeleton.joints[howmany].position, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords; - } - Sprite::MakeSprite(breathsprite, flatfacing, flatvelocity, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, .3); - } - - if (bleeding > 0) { - bleeding -= multiplier * .3; - if (bloodtoggle == 2) { - skeleton.drawmodel.textureptr.bind(); - if ((bleeding <= 0) && (detail != 2)) - DoMipmaps(); - } - } - - if (neckspurtamount > 0) { - neckspurtamount -= multiplier; - neckspurtdelay -= multiplier * 3; - neckspurtparticledelay -= multiplier * 3; - if (neckspurtparticledelay < 0 && neckspurtdelay > 2) { - spurt = 0; - bloodvel = 0; - if (skeleton.free) { - bloodvel -= DoRotation(skeleton.forward * 10 * scale, ((float)(Random() % 100)) / 40, ((float)(Random() % 100)) / 40, 0); - bloodvel += DoRotation(jointVel(head), ((float)(Random() % 100)) / 40, yaw + ((float)(Random() % 100)) / 40, 0) * scale; - Sprite::MakeSprite(bloodsprite, (jointPos(neck) + (jointPos(neck) - jointPos(head)) / 5)*scale + coords, bloodvel, 1, 1, 1, .05, .9); - } else { - bloodvel.z = 5 * neckspurtamount; - bloodvel = DoRotation(bloodvel, ((float)(Random() % 100)) / 40, yaw + ((float)(Random() % 100)) / 40, 0) * scale; - bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 40, ((float)(Random() % 100)) / 40, 0) * scale; - Sprite::MakeSprite(bloodsprite, DoRotation(jointPos(neck) + (jointPos(neck) - jointPos(head)) / 5, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, .9); - } - neckspurtparticledelay = .05; - } - if (neckspurtdelay < 0) { - neckspurtdelay = 3; - } - } - - if (deathbleeding > 0 && dead != 2) { - if (deathbleeding < 5) - bleeddelay -= deathbleeding * multiplier / 4; - else - bleeddelay -= 5 * multiplier / 4; - if (bleeddelay < 0 && bloodtoggle) { - bleeddelay = 1; - XYZ bloodvel; - if (bloodtoggle) { - bloodvel = 0; - if (skeleton.free) { - bloodvel += DoRotation(jointVel(abdomen), ((float)(Random() % 100)) / 4, yaw + ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, jointPos(abdomen) * scale + coords, bloodvel, 1, 1, 1, .05, 1); - } else { - bloodvel += DoRotation(velocity, ((float)(Random() % 100)) / 4, ((float)(Random() % 100)) / 4, 0) * scale; - Sprite::MakeSprite(bloodsprite, DoRotation((jointPos(abdomen) + jointPos(abdomen)) / 2, 0, yaw, 0)*scale + coords, bloodvel, 1, 1, 1, .05, 1); - } - } - } - bloodloss += deathbleeding * multiplier * 80; - deathbleeding -= multiplier * 1.6; - if (deathbleeding < 0) - deathbleeding = 0; - if (bloodloss > damagetolerance && Animation::animations[animTarget].attack == neutral) { - if (weaponactive != -1) { - weapons[weaponids[0]].drop(velocity * scale * -.3, velocity * scale); - weapons[weaponids[0]].velocity.x += .01; - num_weapons--; - if (num_weapons) { - weaponids[0] = weaponids[num_weapons]; - if (weaponstuck == num_weapons) - weaponstuck = 0; - } - weaponactive = -1; - for (unsigned i = 0; i < Person::players.size(); i++) { - Person::players[i]->wentforweapon = 0; - } - - if (id == 0) { - Game::flash(.5, 0); - } - } - - if (!dead && creature == wolftype) { - award_bonus(0, Wolfbonus); - } - dead = 2; - if (animTarget == knifefollowedanim && !skeleton.free) { - for (int i = 0; i < skeleton.joints.size(); i++) { - skeleton.joints[i].velocity = 0; - skeleton.joints[i].velocity.y = -2; - } - } - if (id != 0 && unconscioustime > .1) { - numafterkill++; - } - - RagDoll(0); - } - } - - if (texupdatedelay < 0 && bleeding > 0 && bloodtoggle == 2 && distsq(&viewer, &coords) < 9) { - texupdatedelay = .12; - - bloodsize = 5 - realtexdetail; - - startx = 0; - starty = 0; - startx = bleedy; //abs(Random()%(skeleton.skinsize-bloodsize-1)); - starty = bleedx; //abs(Random()%(skeleton.skinsize-bloodsize-1)); - endx = startx + bloodsize; - endy = starty + bloodsize; - - if (startx < 0) { - startx = 0; - bleeding = 0; - } - if (starty < 0) { - starty = 0; - bleeding = 0; - } - if (endx > skeleton.skinsize - 1) { - endx = skeleton.skinsize - 1; - bleeding = 0; - } - if (endy > skeleton.skinsize - 1) { - endy = skeleton.skinsize - 1; - bleeding = 0; - } - if (endx < startx) - endx = startx; - if (endy < starty) - endy = starty; - - for (i = startx; i < endx; i++) { - for (j = starty; j < endy; j++) { - if (Random() % 2 == 0) { - color = Random() % 85 + 170; - if (skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 0] > color / 2) - skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 0] = color / 2; - skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 1] = 0; - skeleton.skinText[i * skeleton.skinsize * 3 + j * 3 + 2] = 0; - } - } - } - if (detail > 1) { - skeleton.drawmodel.textureptr.bind(); - DoMipmaps(); - } - - if (skeleton.free) { - bleedx += 4 * direction / realtexdetail; - if (detail == 2) - bleedy += (abs(Random() % 3) - 1) * 2 / realtexdetail; - else - bleedy += (abs(Random() % 3) - 1) * 4 / realtexdetail; - } else { - bleedy -= 4 / realtexdetail; - if (detail == 2) - bleedx += (abs(Random() % 3) - 1) * 2 / realtexdetail; - else - bleedx += (abs(Random() % 3) - 1) * 4 / realtexdetail; - } - } - - if (abs(righthandmorphness - targetrighthandmorphness) < multiplier * 4) { - righthandmorphness = targetrighthandmorphness; - righthandmorphstart = righthandmorphend; - } else if (righthandmorphness > targetrighthandmorphness) { - righthandmorphness -= multiplier * 4; - } else if (righthandmorphness < targetrighthandmorphness) { - righthandmorphness += multiplier * 4; - } - - if (abs(lefthandmorphness - targetlefthandmorphness) < multiplier * 4) { - lefthandmorphness = targetlefthandmorphness; - lefthandmorphstart = lefthandmorphend; - } else if (lefthandmorphness > targetlefthandmorphness) { - lefthandmorphness -= multiplier * 4; - } else if (lefthandmorphness < targetlefthandmorphness) { - lefthandmorphness += multiplier * 4; - } - - if (creature == rabbittype || targettailmorphness == 5 || targettailmorphness == 0) { - if (abs(tailmorphness - targettailmorphness) < multiplier * 10) { - tailmorphness = targettailmorphness; - tailmorphstart = tailmorphend; - } else if (tailmorphness > targettailmorphness) { - tailmorphness -= multiplier * 10; - } else if (tailmorphness < targettailmorphness) { - tailmorphness += multiplier * 10; - } - } - - if (creature == wolftype) { - if (abs(tailmorphness - targettailmorphness) < multiplier * 4) { - tailmorphness = targettailmorphness; - tailmorphstart = tailmorphend; - } else if (tailmorphness > targettailmorphness) { - tailmorphness -= multiplier * 2; - } else if (tailmorphness < targettailmorphness) { - tailmorphness += multiplier * 2; - } - } - - if (headmorphend == 3 || headmorphstart == 3) { - if (abs(headmorphness - targetheadmorphness) < multiplier * 7) { - headmorphness = targetheadmorphness; - headmorphstart = headmorphend; - } else if (headmorphness > targetheadmorphness) { - headmorphness -= multiplier * 7; - } else if (headmorphness < targetheadmorphness) { - headmorphness += multiplier * 7; - } - } else if (headmorphend == 5 || headmorphstart == 5) { - if (abs(headmorphness - targetheadmorphness) < multiplier * 10) { - headmorphness = targetheadmorphness; - headmorphstart = headmorphend; - } else if (headmorphness > targetheadmorphness) { - headmorphness -= multiplier * 10; - } else if (headmorphness < targetheadmorphness) { - headmorphness += multiplier * 10; - } - } else { - if (abs(headmorphness - targetheadmorphness) < multiplier * 4) { - headmorphness = targetheadmorphness; - headmorphstart = headmorphend; - } else if (headmorphness > targetheadmorphness) { - headmorphness -= multiplier * 4; - } else if (headmorphness < targetheadmorphness) { - headmorphness += multiplier * 4; - } - } - - if (abs(chestmorphness - targetchestmorphness) < multiplier) { - chestmorphness = targetchestmorphness; - chestmorphstart = chestmorphend; - } else if (chestmorphness > targetchestmorphness) { - chestmorphness -= multiplier; - } else if (chestmorphness < targetchestmorphness) { - chestmorphness += multiplier; - } - - if (dead != 2 && howactive <= typesleeping) { - if (chestmorphstart == 0 && chestmorphend == 0) { - chestmorphness = 0; - targetchestmorphness = 1; - chestmorphend = 3; - } - if (chestmorphstart != 0 && chestmorphend != 0) { - chestmorphness = 0; - targetchestmorphness = 1; - chestmorphend = 0; - if (environment == snowyenvironment) { - XYZ footpoint; - XYZ footvel; - if (skeleton.free) { - footvel = skeleton.specialforward[0] * -1; - footpoint = ((jointPos(head) + jointPos(neck)) / 2) * scale + coords; - } else { - footvel = DoRotation(skeleton.specialforward[0], 0, yaw, 0) * -1; - footpoint = DoRotation((jointPos(head) + jointPos(neck)) / 2, 0, yaw, 0) * scale + coords; - } - if (animTarget == sleepanim) - footvel = DoRotation(footvel, 0, 90, 0); - Sprite::MakeSprite(breathsprite, footpoint + footvel * .2, footvel * .4, 1, 1, 1, .4, .3); - } - } - - if (!dead && howactive < typesleeping) { - blinkdelay -= multiplier * 2; - if (headmorphstart == 0 && headmorphend == 0 && blinkdelay <= 0) { - headmorphness = 0; - targetheadmorphness = 1; - headmorphend = 3; - blinkdelay = (float)(abs(Random() % 40)) / 5; - } - if (headmorphstart == 3 && headmorphend == 3) { - headmorphness = 0; - targetheadmorphness = 1; - headmorphend = 0; - } - } - if (!dead) { - twitchdelay -= multiplier * 1.5; - if (animTarget != hurtidleanim) { - if (headmorphstart == 0 && headmorphend == 0 && twitchdelay <= 0) { - headmorphness = 0; - targetheadmorphness = 1; - headmorphend = 5; - twitchdelay = (float)(abs(Random() % 40)) / 5; - } - if (headmorphstart == 5 && headmorphend == 5) { - headmorphness = 0; - targetheadmorphness = 1; - headmorphend = 0; - } - } - if ((isIdle() || isCrouch()) && animTarget != hurtidleanim) { - twitchdelay3 -= multiplier * 1; - if (Random() % 2 == 0) { - if (righthandmorphstart == 0 && righthandmorphend == 0 && twitchdelay3 <= 0) { - righthandmorphness = 0; - targetrighthandmorphness = 1; - righthandmorphend = 1; - if (Random() % 2 == 0)twitchdelay3 = (float)(abs(Random() % 40)) / 5; - } - if (righthandmorphstart == 1 && righthandmorphend == 1) { - righthandmorphness = 0; - targetrighthandmorphness = 1; - righthandmorphend = 0; - } - } - if (Random() % 2 == 0) { - if (lefthandmorphstart == 0 && lefthandmorphend == 0 && twitchdelay3 <= 0) { - lefthandmorphness = 0; - targetlefthandmorphness = 1; - lefthandmorphend = 1; - twitchdelay3 = (float)(abs(Random() % 40)) / 5; - } - if (lefthandmorphstart == 1 && lefthandmorphend == 1) { - lefthandmorphness = 0; - targetlefthandmorphness = 1; - lefthandmorphend = 0; - } - } - } - } - if (!dead) { - if (creature == rabbittype) { - if (howactive < typesleeping) - twitchdelay2 -= multiplier * 1.5; - else - twitchdelay2 -= multiplier * 0.5; - if (howactive <= typesleeping) { - if (tailmorphstart == 0 && tailmorphend == 0 && twitchdelay2 <= 0) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 1; - twitchdelay2 = (float)(abs(Random() % 40)) / 5; - } - if (tailmorphstart == 1 && tailmorphend == 1) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 2; - } - if (tailmorphstart == 2 && tailmorphend == 2) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 0; - } - } - } - } - } - if (creature == wolftype) { - twitchdelay2 -= multiplier * 1.5; - if (tailmorphend != 0) - if ((isRun() || animTarget == jumpupanim || animTarget == jumpdownanim || animTarget == backflipanim) && !skeleton.free) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 0; - twitchdelay2 = .1; - } - if (tailmorphend != 5) - if (animTarget == flipanim || animTarget == frontflipanim || animTarget == rollanim || skeleton.free) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 5; - twitchdelay2 = .1; - } - if (twitchdelay2 <= 0) { - if (((tailmorphstart == 0 && tailmorphend == 0) || (tailmorphstart == 5 && tailmorphend == 5))) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 1; - } - if (tailmorphstart == 1 && tailmorphend == 1) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 2; - } - if (tailmorphstart == 2 && tailmorphend == 2) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 3; - } - if (tailmorphstart == 3 && tailmorphend == 3) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 4; - } - if (tailmorphstart == 4 && tailmorphend == 4) { - tailmorphness = 0; - targettailmorphness = 1; - tailmorphend = 1; - } - } - } - - if (dead != 1) - unconscioustime = 0; - - if (dead == 1 || howactive == typesleeping) { - unconscioustime += multiplier; - //If unconscious, close eyes and mouth - if (righthandmorphend != 0) - righthandmorphness = 0; - righthandmorphend = 0; - targetrighthandmorphness = 1; - - if (lefthandmorphend != 0) - lefthandmorphness = 0; - lefthandmorphend = 0; - targetlefthandmorphness = 1; - - if (headmorphend != 3 && headmorphend != 5) - headmorphness = 0; - headmorphend = 3; - targetheadmorphness = 1; - } - - - if (howactive > typesleeping) { - XYZ headpoint; - headpoint = coords; - if (bloodtoggle && !bled) { - terrain.MakeDecal(blooddecalslow, headpoint, .8, .5, 0); - } - if (bloodtoggle && !bled) - for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { - j = terrain.patchobjects[whichpatchx][whichpatchz][l]; - XYZ point = DoRotation(headpoint - objects.position[j], 0, -objects.yaw[j], 0); - float size = .8; - float opacity = .6; - float yaw = 0; - objects.model[j].MakeDecal(blooddecalslow, &point, &size, &opacity, &yaw); - } - bled = 1; - } - - if (dead == 2 || howactive > typesleeping) { - //If dead, open mouth and hands - if (righthandmorphend != 0) - righthandmorphness = 0; - righthandmorphend = 0; - targetrighthandmorphness = 1; - - if (lefthandmorphend != 0) - lefthandmorphness = 0; - lefthandmorphend = 0; - targetlefthandmorphness = 1; - - if (headmorphend != 2) - headmorphness = 0; - headmorphend = 2; - targetheadmorphness = 1; - } - - if (stunned > 0 && !dead && headmorphend != 2) { - if (headmorphend != 4) - headmorphness = 0; - headmorphend = 4; - targetheadmorphness = 1; - } - - if (damage > damagetolerance && !dead) { - - dead = 1; - unconscioustime = 0; - - if (creature == wolftype) { - award_bonus(0, Wolfbonus); - } - - RagDoll(0); - - if (weaponactive != -1) { - weapons[weaponids[0]].drop(velocity * scale * -.3, velocity * scale); - weapons[weaponids[0]].velocity.x += .01; - num_weapons--; - if (num_weapons) { - weaponids[0] = weaponids[num_weapons]; - if (weaponstuck == num_weapons) - weaponstuck = 0; - } - weaponactive = -1; - for (unsigned i = 0; i < Person::players.size(); i++) { - Person::players[i]->wentforweapon = 0; - } - } - - - - if ((id == 0 || distsq(&coords, &viewer) < 50) && autoslomo) { - slomo = 1; - slomodelay = .2; - } - - damage += 20; - } - - if (!dead) - damage -= multiplier * 13; - if (!dead) - permanentdamage -= multiplier * 4; - if (isIdle() || isCrouch()) { - if (!dead) - permanentdamage -= multiplier * 4; - } - if (damage < 0) - damage = 0; - if (permanentdamage < 0) - permanentdamage = 0; - if (superpermanentdamage < 0) - superpermanentdamage = 0; - if (permanentdamage < superpermanentdamage) { - permanentdamage = superpermanentdamage; - } - if (damage < permanentdamage) { - damage = permanentdamage; - } - if (dead == 1 && damage < damagetolerance) { - dead = 0; - skeleton.free = 1; - damage -= 20; - for (int i = 0; i < skeleton.joints.size(); i++) { - skeleton.joints[i].velocity = 0; - } - } - if (permanentdamage > damagetolerance && dead != 2) { - DoBlood(1, 255); - - if (weaponactive != -1) { - weapons[weaponids[0]].drop(velocity * scale * -.3, velocity * scale); - weapons[weaponids[0]].velocity.x += .01; - num_weapons--; - if (num_weapons) { - weaponids[0] = weaponids[num_weapons]; - if (weaponstuck == num_weapons) - weaponstuck = 0; - } - weaponactive = -1; - for (unsigned i = 0; i < Person::players.size(); i++) { - Person::players[i]->wentforweapon = 0; - } - } - - bled = 0; - - if (!dead && creature == wolftype) { - award_bonus(0, Wolfbonus); - } - - if (unconscioustime < .1 && (bonus != spinecrusher || bonustime > 1) && (bonus != FinishedBonus || bonustime > 1) && bloodloss < damagetolerance) - award_bonus(id, touchofdeath); - if (id != 0 && unconscioustime > .1) { - numafterkill++; - } - - dead = 2; - - skeleton.free = 1; - - emit_sound_at(breaksound, coords); - } - - if (skeleton.free == 1) { - if (id == 0) - pause_sound(whooshsound); - - if (!dead) { - //If knocked over, open hands and close mouth - if (righthandmorphend != 0) - righthandmorphness = 0; - righthandmorphend = 0; - targetrighthandmorphness = 1; - - if (lefthandmorphend != 0) - lefthandmorphness = 0; - lefthandmorphend = 0; - targetlefthandmorphness = 1; - - if (headmorphend != 3 && headmorphend != 5 && headmorphstart != 3 && headmorphstart != 5) { - if (headmorphend != 0) - headmorphness = 0; - headmorphend = 0; - targetheadmorphness = 1; - } - } - - skeleton.DoGravity(&scale); - float damageamount; - damageamount = skeleton.DoConstraints(&coords, &scale) * 5; - if (damage > damagetolerance - damageamount && !dead && (bonus != spinecrusher || bonustime > 1) && (bonus != style || bonustime > 1) && (bonus != cannon || bonustime > 1)) - award_bonus(id, deepimpact); - DoDamage(damageamount / ((protectionhigh + protectionhead + protectionlow) / 3)); - - average = 0; - howmany = 0; - for (j = 0; j < skeleton.joints.size(); j++) { - average += skeleton.joints[j].position; - howmany++; - } - average /= howmany; - coords += average * scale; - for (j = 0; j < skeleton.joints.size(); j++) { - skeleton.joints[j].position -= average; - } - average /= multiplier; - - velocity = 0; - for (int i = 0; i < skeleton.joints.size(); i++) { - velocity += skeleton.joints[i].velocity * scale; - } - velocity /= skeleton.joints.size(); - - if (!isnormal(velocity.x) && velocity.x) { - velocity = 0; - } - - if (findLength(&average) < 10 && dead && skeleton.free) { - skeleton.longdead += (2000 - findLength(&average)) * multiplier + multiplier; - if (skeleton.longdead > 2000) { - if (skeleton.longdead > 6000) { - if (id == 0) - pause_sound(whooshsound); - skeleton.free = 3; - DrawSkeleton(); - skeleton.free = 2; - } - if (dead == 2 && bloodloss < damagetolerance) { - XYZ headpoint; - headpoint = (jointPos(head) + jointPos(neck)) / 2 * scale + coords; - DoBlood(1, 255); - if (bloodtoggle && !bled) { - terrain.MakeDecal(blooddecal, headpoint, .2 * 1.2, .5, 0); - } - if (bloodtoggle && !bled) - for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { - j = terrain.patchobjects[whichpatchx][whichpatchz][l]; - XYZ point = DoRotation(headpoint - objects.position[j], 0, -objects.yaw[j], 0); - float size = .2 * 1.2; - float opacity = .6; - float yaw = 0; - objects.model[j].MakeDecal(blooddecal, &point, &size, &opacity, &yaw); - } - bled = 1; - } - if (dead == 2 && bloodloss >= damagetolerance) { - XYZ headpoint; - headpoint = (jointPos(abdomen) + jointPos(neck)) / 2 * scale + coords; - if (bleeding <= 0) - DoBlood(1, 255); - if (bloodtoggle && !bled) { - terrain.MakeDecal(blooddecalslow, headpoint, .8, .5, 0); - } - if (bloodtoggle && !bled) - for (l = 0; l < terrain.patchobjectnum[whichpatchx][whichpatchz]; l++) { - j = terrain.patchobjects[whichpatchx][whichpatchz][l]; - XYZ point = DoRotation(headpoint - objects.position[j], 0, -objects.yaw[j], 0); - float size = .8; - float opacity = .6; - float yaw = 0; - objects.model[j].MakeDecal(blooddecalslow, &point, &size, &opacity, &yaw); - } - bled = 1; - } - } - } - - if (!dead && crouchkeydown && skeleton.freetime > .5 && id == 0 && skeleton.free) { - bool canrecover = 1; - XYZ startpoint, endpoint, colpoint, colviewer, coltarget; - startpoint = coords; - endpoint = coords; - endpoint.y -= .7; - if (terrain.lineTerrain(startpoint, endpoint, &colpoint) != -1) - canrecover = 0; - if (velocity.y < -30) - canrecover = 0; - for (i = 0; i < objects.numobjects; i++) { - if (objects.type[i] != treeleavestype && objects.type[i] != bushtype && objects.type[i] != firetype) { - colviewer = startpoint; - coltarget = endpoint; - if (objects.model[i].LineCheck(&colviewer, &coltarget, &colpoint, &objects.position[i], &objects.yaw[i]) != -1) - canrecover = 0; - } - } - if (canrecover) { - skeleton.free = 0; - XYZ middle; - middle = 0; - - terrainnormal = jointPos(groin) - jointPos(abdomen); - if (joint(groin).locked && joint(abdomen).locked) { - terrainnormal = jointPos(groin) - jointPos(abdomen); - middle = (jointPos(groin) + jointPos(abdomen)) / 2; - } - if (joint(abdomen).locked && joint(neck).locked) { - terrainnormal = jointPos(abdomen) - jointPos(neck); - middle = (jointPos(neck) + jointPos(abdomen)) / 2; - } - if (joint(groin).locked && joint(neck).locked) { - terrainnormal = jointPos(groin) - jointPos(neck); - middle = (jointPos(groin) + jointPos(neck)) / 2; - } - Normalise(&terrainnormal); - - targetyaw = -asin(0 - terrainnormal.x); - targetyaw *= 360 / 6.28; - if (terrainnormal.z < 0) - targetyaw = 180 - targetyaw; - yaw = targetyaw; - - frameTarget = 0; - animTarget = flipanim; - crouchtogglekeydown = 1; - target = 0; - tilt2 = 0; - targettilt2 = 0; - - animCurrent = tempanim; - frameCurrent = 0; - target = 0; - - for (int i = 0; i < skeleton.joints.size(); i++) { - tempanimation.frames[0].joints[i].position = skeleton.joints[i].position; - tempanimation.frames[0].joints[i].position = DoRotation(tempanimation.frames[0].joints[i].position, 0, -yaw, 0); - } - } - } - - if (findLength(&average) < 10 && !dead && skeleton.free) { - skeleton.longdead += (2000 - findLength(&average)) * multiplier + multiplier; - if (skeleton.longdead > (damage + 500) * 1.5) { - if (id == 0) - pause_sound(whooshsound); - skeleton.free = 0; - velocity = 0; - XYZ middle; - middle = 0; - - terrainnormal = jointPos(groin) - jointPos(abdomen); - if (joint(groin).locked && joint(abdomen).locked) { - terrainnormal = jointPos(groin) - jointPos(abdomen); - middle = (jointPos(groin) + jointPos(abdomen)) / 2; - } - if (joint(abdomen).locked && joint(neck).locked) { - terrainnormal = jointPos(abdomen) - jointPos(neck); - middle = (jointPos(neck) + jointPos(abdomen)) / 2; - } - if (joint(groin).locked && joint(neck).locked) { - terrainnormal = jointPos(groin) - jointPos(neck); - middle = (jointPos(groin) + jointPos(neck)) / 2; - } - Normalise(&terrainnormal); - - targetyaw = -asin(0 - terrainnormal.x); - targetyaw *= 360 / 6.28; - if (terrainnormal.z < 0) - targetyaw = 180 - targetyaw; - yaw = targetyaw; - - targettilt2 = asin(terrainnormal.y) * 180 / 3.14 * -1; - - - if (skeleton.forward.y < 0) { - animTarget = getupfrombackanim; - frameTarget = 0; - targettilt2 = 0; - } - if (skeleton.forward.y > -.3) { - animTarget = getupfromfrontanim; - yaw += 180; - targetyaw += 180; - targettilt2 *= -1; - frameTarget = 0; - targettilt2 = 0; - } - - if ((Random() % 8 == 0 && id != 0 && creature == rabbittype) || (Random() % 2 == 0 && id != 0 && creature == wolftype) || (id == 0 && crouchkeydown && (forwardkeydown || backkeydown || leftkeydown || rightkeydown))) { - animTarget = rollanim; - targetyaw = lookyaw; - if (id == 0) { - if (rightkeydown) { - targetyaw -= 90; - if (forwardkeydown) - targetyaw += 45; - if (backkeydown) - targetyaw -= 45; - } - if (leftkeydown) { - targetyaw += 90; - if (forwardkeydown) - targetyaw -= 45; - if (backkeydown) - targetyaw += 45; - } - if (backkeydown) { - if ( !leftkeydown && !rightkeydown) - targetyaw += 180; - } - targetyaw += 180; - } - } - - if (abs(targettilt2) > 50) - targettilt2 = 0; - animCurrent = tempanim; - frameCurrent = 0; - target = 0; - tilt2 = targettilt2; - - if (middle.y > 0 && animTarget != rollanim) - targetoffset.y = middle.y + 1; - - for (int i = 0; i < skeleton.joints.size(); i++) { - tempanimation.frames[0].joints[i].position = skeleton.joints[i].position; - tempanimation.frames[0].joints[i].position = DoRotation(tempanimation.frames[0].joints[i].position, 0, -yaw, 0); - } - } - } - - bool hasstaff; - hasstaff = 0; - if (num_weapons > 0) - if (weapons[0].getType() == staff) - hasstaff = 1; - if (!skeleton.freefall && freefall && ((jumpkeydown && jumpkeydowntime < .2) || (hasstaff && rabbitkickragdoll)) && !dead) { - if (velocity.y > -30) { - XYZ tempvelocity; - tempvelocity = velocity; - Normalise(&tempvelocity); - targetyaw = -asin(0 - tempvelocity.x); - targetyaw *= 360 / 6.28; - if (velocity.z < 0) - targetyaw = 180 - targetyaw; - //targetyaw+=180; - - skeleton.free = 0; - if (dotproduct(&skeleton.forward, &tempvelocity) < 0) { - animTarget = rollanim; - frameTarget = 2; - } else { - animTarget = backhandspringanim; - targetyaw += 180; - frameTarget = 6; - } - target = 0; - - emit_sound_at(movewhooshsound, coords, 128.); - - animCurrent = animTarget; - frameCurrent = frameTarget - 1; - target = 0; - - velocity = 0; - - yaw = targetyaw; - tilt = 0; - targettilt = 0; - tilt2 = 0; - targettilt2 = 0; - } - } - if (skeleton.freefall == 0) - freefall = 0; - - } - - if (aitype != passivetype || skeleton.free == 1) - if (findLengthfast(&velocity) > .1) - for (i = 0; i < objects.numobjects; i++) { - if (objects.type[i] == firetype) - if (distsqflat(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 12 && distsq(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 49) { - if (onfire) { - if (!objects.onfire[i]) { - emit_sound_at(firestartsound, objects.position[i]); - } - objects.onfire[i] = 1; - } - if (!onfire) { - if (objects.onfire[i]) { - CatchFire(); - } - } - } - if (objects.type[i] == bushtype) - if (distsqflat(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 12 && distsq(&coords, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 49) { - if (onfire) { - if (!objects.onfire[i]) { - emit_sound_at(firestartsound, objects.position[i]); - } - objects.onfire[i] = 1; - } - - if (!onfire) { - if (objects.onfire[i]) { - CatchFire(); - } - } - if (objects.messedwith[i] <= 0) { - XYZ tempvel; - XYZ pos; - - emit_sound_at(bushrustle, coords, 40 * findLength(&velocity)); - - if (id == 0) { - addEnvSound(coords, 4 * findLength(&velocity)); - } - - int howmany; - if (environment == grassyenvironment) - howmany = findLength(&velocity) * 4; - if (environment == snowyenvironment) - howmany = findLength(&velocity) * 2; - if (detail == 2) - if (environment != desertenvironment) - for (j = 0; j < howmany; j++) { - tempvel.x = float(abs(Random() % 100) - 50) / 20; - tempvel.y = float(abs(Random() % 100) - 50) / 20; - tempvel.z = float(abs(Random() % 100) - 50) / 20; - pos = coords; - pos.y += 1; - pos.x += float(abs(Random() % 100) - 50) / 200; - pos.y += float(abs(Random() % 100) - 50) / 200; - pos.z += float(abs(Random() % 100) - 50) / 200; - Sprite::MakeSprite(splintersprite, pos, tempvel * .5 + velocity * float(abs(Random() % 100)) / 100, 165 / 255 + float(abs(Random() % 100) - 50) / 400, 0, 0, .2 + float(abs(Random() % 100) - 50) / 1300, 1); - Sprite::setLastSpriteSpecial(1); - } - howmany = findLength(&velocity) * 4; - if (detail == 2) - if (environment == snowyenvironment) - for (j = 0; j < howmany; j++) { - tempvel.x = float(abs(Random() % 100) - 50) / 20; - tempvel.y = float(abs(Random() % 100) - 50) / 20; - tempvel.z = float(abs(Random() % 100) - 50) / 20; - pos = coords; - pos.y += 1; - pos.x += float(abs(Random() % 100) - 50) / 200; - pos.y += float(abs(Random() % 100) - 50) / 200; - pos.z += float(abs(Random() % 100) - 50) / 200; - Sprite::MakeSprite(splintersprite, pos, tempvel * .3 + velocity * float(abs(Random() % 100)) / 100 / 2, 1, 1, 1, .1, 1); - Sprite::setLastSpriteSpecial(2); - } - } - objects.rotx[i] += velocity.x * multiplier * 6; - objects.roty[i] += velocity.z * multiplier * 6; - objects.messedwith[i] = .5; - } - XYZ tempcoord; - if (objects.type[i] == treeleavestype && environment != desertenvironment) { - if (objects.pitch[i] == 0) - tempcoord = coords; - else { - tempcoord = coords - objects.position[i]; - tempcoord = DoRotation(tempcoord, 0, -objects.yaw[i], 0); - tempcoord = DoRotation(tempcoord, -objects.pitch[i], 0, 0); - tempcoord += objects.position[i]; - } - if (distsqflat(&tempcoord, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 8 && distsq(&tempcoord, &objects.position[i]) < objects.scale[i]*objects.scale[i] * 300 && tempcoord.y > objects.position[i].y + 3 * objects.scale[i]) { - if (objects.messedwith[i] <= 0) { - XYZ tempvel; - XYZ pos; - - emit_sound_at(bushrustle, coords, 40 * findLength(&velocity)); - - if (id == 0) { - addEnvSound(coords, 4 * findLength(&velocity)); - } - - int howmany; - if (environment == grassyenvironment) - howmany = findLength(&velocity) * 4; - if (environment == snowyenvironment) - howmany = findLength(&velocity) * 2; - if (detail == 2) - if (environment != desertenvironment) - for (j = 0; j < howmany; j++) { - tempvel.x = float(abs(Random() % 100) - 50) / 20; - tempvel.y = float(abs(Random() % 100) - 50) / 20; - tempvel.z = float(abs(Random() % 100) - 50) / 20; - pos = coords; - pos += velocity * .1; - pos.y += 1; - pos.x += float(abs(Random() % 100) - 50) / 150; - pos.y += float(abs(Random() % 100) - 50) / 150; - pos.z += float(abs(Random() % 100) - 50) / 150; - Sprite::MakeSprite(splintersprite, pos, tempvel * .5 + velocity * float(abs(Random() % 100)) / 100, 165 / 255 + float(abs(Random() % 100) - 50) / 400, 0, 0, .2 + float(abs(Random() % 100) - 50) / 1300, 1); - Sprite::setLastSpriteSpecial(1); - } - howmany = findLength(&velocity) * 4; - if (detail == 2) - if (environment == snowyenvironment) - for (j = 0; j < howmany; j++) { - tempvel.x = float(abs(Random() % 100) - 50) / 20; - tempvel.y = float(abs(Random() % 100) - 50) / 20; - tempvel.z = float(abs(Random() % 100) - 50) / 20; - pos = coords; - pos += velocity * .1; - pos.y += 1; - pos.x += float(abs(Random() % 100) - 50) / 150; - pos.y += float(abs(Random() % 100) - 50) / 150; - pos.z += float(abs(Random() % 100) - 50) / 150; - Sprite::MakeSprite(splintersprite, pos, tempvel * .3 + velocity * float(abs(Random() % 100)) / 100 / 2, 1, 1, 1, .1, 1); - Sprite::setLastSpriteSpecial(2); - } - } - objects.messedwith[i] = .5; - } - } - } - - if (!skeleton.free) { - bool play; - play = 0; - if ((stunned > 0 || surprised > 0) && Person::players.size() > 2 && aitype != passivetype) - play = 1; - if (hasvictim) - if (aitype != passivetype && victim->skeleton.free && !victim->dead) - play = 1; - if (tutoriallevel == 1 && id != 0) - play = 0; - if (play && aitype != playercontrolled) { - int whichsound = -1; - i = abs(Random() % 4); - if (speechdelay <= 0) { - if (creature == rabbittype) { - if (i == 0) - whichsound = rabbitchitter; - if (i == 1) - whichsound = rabbitchitter2; - } - if (creature == wolftype) { - if (i == 0) - whichsound = growlsound; - if (i == 1) - whichsound = growl2sound; - } - } - speechdelay = .3; - - if (whichsound != -1) { - emit_sound_at(whichsound, coords); - } - } - - if (animTarget == staggerbackhighanim) - staggerdelay = 1; - if (animTarget == staggerbackhardanim) - staggerdelay = 1; - staggerdelay -= multiplier; - if (animTarget != crouchstabanim && animTarget != swordgroundstabanim && animTarget != staffgroundsmashanim) - hasvictim = 1; - if (velocity.y < -30 && animTarget == jumpdownanim) - RagDoll(0); - if (animCurrent != getIdle() && wasIdle() && animTarget != getIdle() && isIdle()) { - animTarget = getIdle(); - frameTarget = 0; - target = 0; - } - weaponmissdelay -= multiplier; - highreversaldelay -= multiplier; - lowreversaldelay -= multiplier; - lastcollide -= multiplier; - skiddelay -= multiplier; - if (!isnormal(velocity.x) && velocity.x) { - velocity = 0; - } - if (!isnormal(targettilt) && targettilt) { - targettilt = 0; - } - if (!isnormal(targettilt2) && targettilt2) { - targettilt2 = 0; - } - if (!isnormal(targetyaw) && targetyaw) { - targetyaw = 0; - } - - if (animTarget == bounceidleanim || animTarget == wolfidle || animTarget == walkanim || animTarget == drawrightanim || animTarget == crouchdrawrightanim || animTarget == drawleftanim || animTarget == fightidleanim || animTarget == fightsidestep || animTarget == hanganim || isCrouch() || animTarget == backhandspringanim) { - //open hands and close mouth - if (righthandmorphend != 0 && righthandmorphness == targetrighthandmorphness) { - righthandmorphness = 0; - righthandmorphend = 0; - targetrighthandmorphness = 1; - } - - if (lefthandmorphend != 0 && lefthandmorphness == targetlefthandmorphness) { - lefthandmorphness = 0; - lefthandmorphend = 0; - targetlefthandmorphness = 1; - } - - if (headmorphend != 3 && headmorphend != 5 && headmorphstart != 3 && headmorphstart != 5 && headmorphend != 0 && headmorphness == targetheadmorphness) { - headmorphness = 0; - headmorphend = 0; - targetheadmorphness = 1; - } - } - - if (animTarget == rollanim || animTarget == dodgebackanim || animTarget == removeknifeanim || animTarget == knifefightidleanim || animTarget == swordfightidleanim || animTarget == blockhighleftstrikeanim || animTarget == crouchremoveknifeanim || animTarget == sneakanim || animTarget == sweepanim || animTarget == spinkickreversedanim || animTarget == jumpdownanim || isWallJump() || isFlip() || animTarget == climbanim || isRun() || animTarget == getupfrombackanim || animTarget == getupfromfrontanim) { - //open hands and mouth - if (righthandmorphend != 0 && righthandmorphness == targetrighthandmorphness) { - righthandmorphness = 0; - righthandmorphend = 0; - targetrighthandmorphness = 1; - } - - if (lefthandmorphend != 0 && lefthandmorphness == targetlefthandmorphness) { - lefthandmorphness = 0; - lefthandmorphend = 0; - targetlefthandmorphness = 1; - } - - if (headmorphend != 1 && headmorphness == targetheadmorphness) { - headmorphness = 0; - headmorphend = 1; - targetheadmorphness = 1; - } - } - - if (animTarget == jumpupanim || animTarget == crouchstabanim || animTarget == swordgroundstabanim || animTarget == swordfightidlebothanim || animTarget == blockhighleftanim) { - //close hands and mouth - if (righthandmorphend != 1 && righthandmorphness == targetrighthandmorphness) { - righthandmorphness = 0; - righthandmorphend = 1; - targetrighthandmorphness = 1; - } - - if (lefthandmorphend != 1 && lefthandmorphness == targetlefthandmorphness) { - lefthandmorphness = 0; - lefthandmorphend = 1; - targetlefthandmorphness = 1; - } - - if (headmorphend != 0 && headmorphness == targetheadmorphness) { - headmorphness = 0; - headmorphend = 0; - targetheadmorphness = 1; - } - } - - if (animTarget == spinkickanim || animTarget == staffspinhitreversalanim || animTarget == staffspinhitreversedanim || animTarget == staffhitreversalanim || animTarget == staffhitreversedanim || animTarget == hurtidleanim || animTarget == winduppunchanim || animTarget == swordslashreversalanim || animTarget == swordslashreversedanim || animTarget == knifeslashreversalanim || animTarget == knifeslashreversedanim || animTarget == knifethrowanim || animTarget == knifefollowanim || animTarget == knifefollowedanim || animTarget == killanim || animTarget == dropkickanim || animTarget == upunchanim || animTarget == knifeslashstartanim || animTarget == swordslashanim || animTarget == staffhitanim || animTarget == staffspinhitanim || animTarget == staffgroundsmashanim || animTarget == spinkickreversalanim || animTarget == sweepreversalanim || animTarget == lowkickanim || animTarget == sweepreversedanim || animTarget == rabbitkickreversalanim || animTarget == rabbitkickreversedanim || animTarget == jumpreversalanim || animTarget == jumpreversedanim) { - //close hands and yell - if (righthandmorphend != 1 && righthandmorphness == targetrighthandmorphness) { - righthandmorphness = 0; - righthandmorphend = 1; - targetrighthandmorphness = 1; - } - - if (lefthandmorphend != 1 && lefthandmorphness == targetlefthandmorphness) { - lefthandmorphness = 0; - lefthandmorphend = 1; - targetlefthandmorphness = 1; - } - - if (headmorphend != 2 && headmorphness == targetheadmorphness) { - headmorphness = 1; - headmorphend = 2; - targetheadmorphness = 1; - } - } - - bool behind; - behind = 0; - if (hasvictim) { - if ((victim != this->shared_from_this()) && !victim->dead && (victim->aitype != passivetype) && - (victim->aitype != searchtype) && (aitype != passivetype) && - (aitype != searchtype) && (victim->id < Person::players.size())) { - behind = (normaldotproduct(facing, coords - victim->coords) > 0); - } - } - - if (!dead && animTarget != hurtidleanim) - if (behind || animTarget == killanim || animTarget == knifethrowanim || animTarget == knifefollowanim || animTarget == spinkickreversalanim || animTarget == rabbitkickreversedanim || animTarget == jumpreversedanim) { - if (headmorphend != 4 || headmorphness == targetheadmorphness) { - headmorphend = 4; - //headmorphness=1; - targetheadmorphness = 1; - } - } - - if (weaponactive != -1) { - if (weapons[weaponids[weaponactive]].getType() != staff) { - righthandmorphstart = 1; - righthandmorphend = 1; - } - if (weapons[weaponids[weaponactive]].getType() == staff) { - righthandmorphstart = 2; - righthandmorphend = 2; - } - targetrighthandmorphness = 1; - } - - terrainnormal = terrain.getNormal(coords.x, coords.z); - - if (Animation::animations[animTarget].attack != reversal) { - if (!isnormal(coords.x)) - coords = oldcoords; - oldcoords = coords; - } - - flatfacing = 0; - flatfacing.z = 1; - - flatfacing = DoRotation(flatfacing, 0, yaw, 0); - facing = flatfacing; - ReflectVector(&facing, terrainnormal); - Normalise(&facing); - - if (isRun() || animTarget == sneakanim || animTarget == rollanim || animTarget == walkanim) { - if (onterrain) - targettilt2 = -facing.y * 20; - else - targettilt2 = 0; - } - onterrain = 0; - if (!isRun() && !Animation::animations[animTarget].attack && animTarget != getupfromfrontanim && animTarget != getupfrombackanim && animTarget != sneakanim) - targettilt2 = 0; - if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { - flatvelocity = velocity; - flatvelocity.y = 0; - flatvelspeed = findLength(&flatvelocity); - targettilt = flatvelspeed * fast_sqrt(abs(velocity.y) * .7) * normaldotproduct(DoRotation(flatfacing, 0, -90, 0), flatvelocity); - targettilt2 = flatvelspeed * fast_sqrt(abs(velocity.y) * .7) * normaldotproduct(flatfacing, flatvelocity); - if (velocity.y < 0) - targettilt2 *= -1; - if (velocity.y < 0) - targettilt *= -1; - if (targettilt > 25) - targettilt = 25; - if (targettilt < -25) - targettilt = -25; - } - - if (targettilt2 > 45) - targettilt2 = 45; - if (targettilt2 < -45) - targettilt2 = -45; - if (abs(tilt2 - targettilt2) < multiplier * 400) - tilt2 = targettilt2; - else if (tilt2 > targettilt2) { - tilt2 -= multiplier * 400; - } else if (tilt2 < targettilt2) { - tilt2 += multiplier * 400; - } - if (!Animation::animations[animTarget].attack && animTarget != getupfrombackanim && animTarget != getupfromfrontanim) { - if (tilt2 > 25) - tilt2 = 25; - if (tilt2 < -25) - tilt2 = -25; - } - - if (!isnormal(targettilt) && targettilt) { - targettilt = 0; - } - if (!isnormal(targettilt2) && targettilt2) { - targettilt2 = 0; - } - - //Running velocity - if (animTarget == rabbittackleanim) { - velocity += facing * multiplier * speed * 700 * scale; - velspeed = findLength(&velocity); - if (velspeed > speed * 65 * scale) { - velocity /= velspeed; - velspeed = speed * 65 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed; - } - if (animTarget != rabbitrunninganim && animTarget != wolfrunninganim) { - if (isRun() || animTarget == rabbitkickanim) { - velocity += facing * multiplier * speed * 700 * scale; - velspeed = findLength(&velocity); - if (velspeed > speed * 45 * scale) { - velocity /= velspeed; - velspeed = speed * 45 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - if (velspeed < speed * 30 * scale) - velspeed = speed * 30 * scale; - velocity = flatfacing * velspeed; - } - } else if (isRun()) { - velocity += facing * multiplier * speed * 700 * scale; - velspeed = findLength(&velocity); - if (creature == rabbittype) { - if (velspeed > speed * 55 * scale) { - velocity /= velspeed; - velspeed = speed * 55 * scale; - velocity *= velspeed; - } - } - if (creature == wolftype) { - if (velspeed > speed * 75 * scale) { - velocity /= velspeed; - velspeed = speed * 75 * scale; - velocity *= velspeed; - } - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed; - } - - if (animTarget == rollanim && targetFrame().label != 6) { - velocity += facing * multiplier * speed * 700 * scale; - velspeed = findLength(&velocity); - if (velspeed > speed * 45 * scale) { - velocity /= velspeed; - velspeed = speed * 45 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed; - } - - if (animTarget == sneakanim || animTarget == walkanim) { - velocity += facing * multiplier * speed * 700 * scale; - velspeed = findLength(&velocity); - if (velspeed > speed * 12 * scale) { - velocity /= velspeed; - velspeed = speed * 12 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed; - } - - if ((animTarget == fightidleanim || animTarget == knifefightidleanim) && (animCurrent == bounceidleanim || animCurrent == hurtidleanim)) { - velocity += facing * multiplier * speed * 700 * scale; - velspeed = findLength(&velocity); - if (velspeed > speed * 2 * scale) { - velocity /= velspeed; - velspeed = speed * 2 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed; - } - - - if ((animTarget == bounceidleanim || animCurrent == hurtidleanim) && (animCurrent == fightidleanim || animCurrent == knifefightidleanim)) { - velocity -= facing * multiplier * speed * 700 * scale; - velspeed = findLength(&velocity); - if (velspeed > speed * 2 * scale) { - velocity /= velspeed; - velspeed = speed * 2 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed * -1; - } - - if (animTarget == fightsidestep) { - velocity += DoRotation(facing * multiplier * speed * 700 * scale, 0, -90, 0); - velspeed = findLength(&velocity); - if (velspeed > speed * 12 * scale) { - velocity /= velspeed; - velspeed = speed * 12 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = DoRotation(flatfacing * velspeed, 0, -90, 0); - } - - if (animTarget == staggerbackhighanim) { - coords -= facing * multiplier * speed * 16 * scale; - velocity = 0; - } - if (animTarget == staggerbackhardanim && Animation::animations[staggerbackhardanim].frames[frameTarget].label != 6) { - coords -= facing * multiplier * speed * 20 * scale; - velocity = 0; - } - - if (animTarget == backhandspringanim) { - //coords-=facing*multiplier*50*scale; - velocity += facing * multiplier * speed * 700 * scale * -1; - velspeed = findLength(&velocity); - if (velspeed > speed * 50 * scale) { - velocity /= velspeed; - velspeed = speed * 50 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed * -1; - } - if (animTarget == dodgebackanim) { - //coords-=facing*multiplier*50*scale; - velocity += facing * multiplier * speed * 700 * scale * -1; - velspeed = findLength(&velocity); - if (velspeed > speed * 60 * scale) { - velocity /= velspeed; - velspeed = speed * 60 * scale; - velocity *= velspeed; - } - velocity.y += gravity * multiplier * 20; - ReflectVector(&velocity, terrain.getNormal(coords.x, coords.z)); - velspeed = findLength(&velocity); - velocity = flatfacing * velspeed * -1; - } - - if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { - velspeed = findLength(&velocity); - } - - - if (animTarget == jumpupanim || animTarget == jumpdownanim || isFlip()) { - velocity.y += gravity * multiplier; - } - - if (animTarget != climbanim && animTarget != hanganim && !isWallJump()) - coords += velocity * multiplier; - - if (coords.y < terrain.getHeight(coords.x, coords.z) && (animTarget == jumpdownanim || animTarget == jumpupanim || isFlip())) { - if (isFlip() && targetFrame().label == 7) - RagDoll(0); - - if (animTarget == jumpupanim) { - jumppower = -4; - animTarget = getIdle(); - } - target = 0; - frameTarget = 0; - onterrain = 1; - - if (id == 0) { - pause_sound(whooshsound); - OPENAL_SetVolume(channels[whooshsound], 0); - } - - if (animTarget == jumpdownanim || isFlip()) { - if (isFlip())jumppower = -4; - animTarget = getLanding(); - emit_sound_at(landsound, coords, 128.); - - if (id == 0) { - addEnvSound(coords); - } - } - } - - if (animTarget != jumpupanim && animTarget != jumpdownanim && !isFlip() && animTarget != climbanim && animTarget != hanganim && !isWallJump()) - coords.y += gravity * multiplier * 2; - if (animTarget != jumpupanim && animTarget != jumpdownanim && !isFlip() && coords.y < terrain.getHeight(coords.x, coords.z)) { - coords.y = terrain.getHeight(coords.x, coords.z); - onterrain = 1; - } - - - if (isIdle() || animTarget == drawrightanim || animTarget == drawleftanim || animTarget == crouchdrawrightanim || animTarget == crouchstabanim || animTarget == swordgroundstabanim || isStop() || animTarget == removeknifeanim || animTarget == crouchremoveknifeanim || isLanding() || isCrouch() || Animation::animations[animTarget].attack || (animTarget == rollanim && targetFrame().label == 6)) { - velspeed = findLength(&velocity); - velocity.y = 0; - if (velspeed < multiplier * 300 * scale) { - velocity = 0; - } else - velocity -= velocity / velspeed * multiplier * 300 * scale; - if (velspeed > 5 && (isLanding() || isLandhard())) { - skiddingdelay += multiplier; - if (skiddelay <= 0) { - FootLand(leftfoot, .5); - FootLand(rightfoot, .5); - skiddelay = .02; - } - } else - skiddingdelay = 0; - } - - if (isLandhard()) { - velspeed = findLength(&velocity); - velocity.y = 0; - if (velspeed < multiplier * 600 * scale) { - velocity = 0; - } else - velocity -= velocity / velspeed * multiplier * 600 * scale; - velocity = 0; - if (velspeed > 5 && (isLanding() || isLandhard())) { - skiddingdelay += multiplier; - if (skiddelay <= 0) { - FootLand(leftfoot, .5); - FootLand(rightfoot, .5); - skiddelay = .02; - } - } else - skiddingdelay = 0; - } - - if (skiddingdelay < 0) - skiddingdelay += multiplier; - if (skiddingdelay > .02 && !forwardkeydown && !backkeydown && !leftkeydown && !rightkeydown && !jumpkeydown && isLanding() && !landhard) { - skiddingdelay = -1; - if (!onterrain || environment == grassyenvironment) { - emit_sound_at(skidsound, coords, 128 * velspeed / 10); - } else { - emit_sound_at(snowskidsound, coords, 128 * velspeed / 10); - } - } - - if (Animation::animations[animTarget].attack == normalattack && animTarget != rabbitkickanim && !victim->skeleton.free) { - terrainnormal = victim->coords - coords; - Normalise(&terrainnormal); - targetyaw = -asin(0 - terrainnormal.x); - targetyaw *= 360 / 6.28; - if (terrainnormal.z < 0) - targetyaw = 180 - targetyaw; - targettilt2 = -asin(terrainnormal.y) * 360 / 6.28; //*-70; - } - - if (Animation::animations[animTarget].attack == reversal && animTarget != rabbittacklinganim) { - targetyaw = victim->targetyaw; - } - if (animTarget == rabbittacklinganim) { - coords = victim->coords; - } - } - skeleton.oldfree = skeleton.free; - - XYZ midterrain; - midterrain = 0; - midterrain.x = terrain.size * terrain.scale / 2; - midterrain.z = terrain.size * terrain.scale / 2; - if (distsqflat(&coords, &midterrain) > (terrain.size * terrain.scale / 2 - viewdistance) * (terrain.size * terrain.scale / 2 - viewdistance)) { - XYZ tempposit; - tempposit = coords - midterrain; - tempposit.y = 0; - Normalise(&tempposit); - tempposit *= (terrain.size * terrain.scale / 2 - viewdistance); - coords.x = tempposit.x + midterrain.x; - coords.z = tempposit.z + midterrain.z; - } -} - - -/* EFFECT - * inverse kinematics helper function - */ -void IKHelper(Person *p, float interp) -{ - XYZ point, change, change2; - float heightleft, heightright; - - // TODO: implement localToWorld and worldToLocal - // but keep in mind it won't be the same math if player is ragdolled or something - // - localToWorldStanding / worldToLocalStanding (or crouching or...?) - // then comb through code for places where to use it - - // point = localToWorld(jointPos(leftfoot)) - point = DoRotation(p->jointPos(leftfoot), 0, p->yaw, 0) * p->scale + p->coords; - // adjust height of foot - heightleft = terrain.getHeight(point.x, point.z) + .04; - point.y = heightleft; - change = p->jointPos(leftankle) - p->jointPos(leftfoot); - change2 = p->jointPos(leftknee) - p->jointPos(leftfoot); - // jointPos(leftfoot) = interpolate(worldToLocal(point), jointPos(leftfoot), interp) - p->jointPos(leftfoot) = DoRotation((point - p->coords) / p->scale, 0, -p->yaw, 0) * interp + p->jointPos(leftfoot) * (1 - interp); - // move ankle along with foot - p->jointPos(leftankle) = p->jointPos(leftfoot) + change; - // average knee pos between old and new pos - p->jointPos(leftknee) = (p->jointPos(leftfoot) + change2) / 2 + (p->jointPos(leftknee)) / 2; - - // do same as above for right leg - point = DoRotation(p->jointPos(rightfoot), 0, p->yaw, 0) * p->scale + p->coords; - heightright = terrain.getHeight(point.x, point.z) + .04; - point.y = heightright; - change = p->jointPos(rightankle) - p->jointPos(rightfoot); - change2 = p->jointPos(rightknee) - p->jointPos(rightfoot); - p->jointPos(rightfoot) = DoRotation((point - p->coords) / p->scale, 0, -p->yaw, 0) * interp + p->jointPos(rightfoot) * (1 - interp); - p->jointPos(rightankle) = p->jointPos(rightfoot) + change; - p->jointPos(rightknee) = (p->jointPos(rightfoot) + change2) / 2 + (p->jointPos(rightknee)) / 2; - - // fix up skeleton now that we've moved body parts? - p->skeleton.DoConstraints(&p->coords, &p->scale); -} - -/* EFFECT - * MONSTER - * TODO: ??? - */ -int Person::DrawSkeleton() -{ - int oldplayerdetail; - if ((frustum.SphereInFrustum(coords.x, coords.y + scale * 3, coords.z, scale * 8) && distsq(&viewer, &coords) < viewdistance * viewdistance) || skeleton.free == 3) { - if (onterrain && (isIdle() || isCrouch() || wasIdle() || wasCrouch()) && !skeleton.free) { - calcrot = 1; - } - - if (headless) { - headmorphness = 0; - headmorphstart = 6; - headmorphend = 6; - } - - glAlphaFunc(GL_GREATER, 0.0001); - XYZ terrainlight; - float terrainheight; - float distance; - if (!isnormal(yaw)) - yaw = 0; - if (!isnormal(tilt)) - tilt = 0; - if (!isnormal(tilt2)) - tilt2 = 0; - oldplayerdetail = playerdetail; - playerdetail = 0; - if (distsq(&viewer, &coords) < viewdistance * viewdistance / 32 && detail == 2) { - playerdetail = 1; - } - if (distsq(&viewer, &coords) < viewdistance * viewdistance / 128 && detail == 1) { - playerdetail = 1; - } - if (distsq(&viewer, &coords) < viewdistance * viewdistance / 256 && (detail != 1 && detail != 2)) { - playerdetail = 1; - } - if (id == 0) - playerdetail = 1; - if (playerdetail != oldplayerdetail) { - updatedelay = 0; - normalsupdatedelay = 0; - } - static float updatedelaychange; - static float morphness; - static float framemult; - if (calcrot) { - skeleton.FindForwards(); - if (howactive == typesittingwall) { - skeleton.specialforward[1] = 0; - skeleton.specialforward[1].z = 1; - } - } - static XYZ mid; - static float M[16]; - static int i, j, k; - static int weaponattachmuscle; - static int weaponrotatemuscle; - static XYZ weaponpoint; - static int start, endthing; - if ((dead != 2 || skeleton.free != 2) && updatedelay <= 0) { - if (!isSleeping() && !isSitting()) { - // TODO: give these meaningful names - const bool cond1 = (isIdle() || isCrouch() || isLanding() || isLandhard() - || animTarget == drawrightanim || animTarget == drawleftanim || animTarget == crouchdrawrightanim); - const bool cond2 = (wasIdle() || wasCrouch() || wasLanding() || wasLandhard() - || animCurrent == drawrightanim || animCurrent == drawleftanim || animCurrent == crouchdrawrightanim); - - if (onterrain && (cond1 && cond2) && !skeleton.free) { - IKHelper(this, 1); - if (creature == wolftype) - IKHelper(this, 1); - } - - if (onterrain && (cond1 && !cond2) && !skeleton.free) { - IKHelper(this, target); - if (creature == wolftype) - IKHelper(this, target); - } - - if (onterrain && (!cond1 && cond2) && !skeleton.free) { - IKHelper(this, 1 - target); - if (creature == wolftype) - IKHelper(this, 1 - target); - } - } - - if (!skeleton.free && (!Animation::animations[animTarget].attack && animTarget != getupfrombackanim && ((animTarget != rollanim && !isFlip()) || targetFrame().label == 6) && animTarget != getupfromfrontanim && animTarget != wolfrunninganim && animTarget != rabbitrunninganim && animTarget != backhandspringanim && animTarget != walljumpfrontanim && animTarget != hurtidleanim && !isLandhard() && !isSleeping())) - DoHead(); - else { - targetheadyaw = -targetyaw; - targetheadpitch = 0; - if (Animation::animations[animTarget].attack == 3) - targetheadyaw += 180; - } - for (i = 0; i < skeleton.drawmodel.vertexNum; i++) { - skeleton.drawmodel.vertex[i] = 0; - skeleton.drawmodel.vertex[i].y = 999; - } - for (i = 0; i < skeleton.drawmodellow.vertexNum; i++) { - skeleton.drawmodellow.vertex[i] = 0; - skeleton.drawmodellow.vertex[i].y = 999; - } - for (i = 0; i < skeleton.drawmodelclothes.vertexNum; i++) { - skeleton.drawmodelclothes.vertex[i] = 0; - skeleton.drawmodelclothes.vertex[i].y = 999; - } - for (int i = 0; i < skeleton.muscles.size(); i++) { - // convenience renames - const int p1 = skeleton.muscles[i].parent1->label; - const int p2 = skeleton.muscles[i].parent2->label; - - if ((skeleton.muscles[i].vertices.size() > 0 && playerdetail) || (skeleton.muscles[i].verticeslow.size() > 0 && !playerdetail)) { - morphness = 0; - start = 0; - endthing = 0; - - if (p1 == righthand || p2 == righthand) { - morphness = righthandmorphness; - start = righthandmorphstart; - endthing = righthandmorphend; - } - if (p1 == lefthand || p2 == lefthand) { - morphness = lefthandmorphness; - start = lefthandmorphstart; - endthing = lefthandmorphend; - } - if (p1 == head || p2 == head) { - morphness = headmorphness; - start = headmorphstart; - endthing = headmorphend; - } - if ((p1 == neck && p2 == abdomen) || (p2 == neck && p1 == abdomen)) { - morphness = chestmorphness; - start = chestmorphstart; - endthing = chestmorphend; - } - if ((p1 == groin && p2 == abdomen) || (p2 == groin && p1 == abdomen)) { - morphness = tailmorphness; - start = tailmorphstart; - endthing = tailmorphend; - } - if (calcrot) - skeleton.FindRotationMuscle(i, animTarget); - mid = (skeleton.muscles[i].parent1->position + skeleton.muscles[i].parent2->position) / 2; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - if (!skeleton.free) - glRotatef(tilt2, 1, 0, 0); - if (!skeleton.free) - glRotatef(tilt, 0, 0, 1); - - - glTranslatef(mid.x, mid.y, mid.z); - - skeleton.muscles[i].lastrotate1 = skeleton.muscles[i].rotate1; - glRotatef(-skeleton.muscles[i].lastrotate1 + 90, 0, 1, 0); - - skeleton.muscles[i].lastrotate2 = skeleton.muscles[i].rotate2; - glRotatef(-skeleton.muscles[i].lastrotate2 + 90, 0, 0, 1); - - skeleton.muscles[i].lastrotate3 = skeleton.muscles[i].rotate3; - glRotatef(-skeleton.muscles[i].lastrotate3, 0, 1, 0); - - if (playerdetail || skeleton.free == 3) { - for (j = 0; j < skeleton.muscles[i].vertices.size(); j++) { - XYZ &v0 = skeleton.model[start].vertex[skeleton.muscles[i].vertices[j]]; - XYZ &v1 = skeleton.model[endthing].vertex[skeleton.muscles[i].vertices[j]]; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - if (p1 == abdomen || p2 == abdomen) - glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionbody.x, - (v0.y * (1 - morphness) + v1.y * morphness) * proportionbody.y, - (v0.z * (1 - morphness) + v1.z * morphness) * proportionbody.z); - if (p1 == lefthand || p1 == righthand || p1 == leftwrist || p1 == rightwrist || p1 == leftelbow || p1 == rightelbow || p2 == leftelbow || p2 == rightelbow) - glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionarms.x, - (v0.y * (1 - morphness) + v1.y * morphness) * proportionarms.y, - (v0.z * (1 - morphness) + v1.z * morphness) * proportionarms.z); - if (p1 == leftfoot || p1 == rightfoot || p1 == leftankle || p1 == rightankle || p1 == leftknee || p1 == rightknee || p2 == leftknee || p2 == rightknee) - glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionlegs.x, - (v0.y * (1 - morphness) + v1.y * morphness) * proportionlegs.y, - (v0.z * (1 - morphness) + v1.z * morphness) * proportionlegs.z); - if (p1 == head || p2 == head) - glTranslatef((v0.x * (1 - morphness) + v1.x * morphness) * proportionhead.x, - (v0.y * (1 - morphness) + v1.y * morphness) * proportionhead.y, - (v0.z * (1 - morphness) + v1.z * morphness) * proportionhead.z); - glGetFloatv(GL_MODELVIEW_MATRIX, M); - skeleton.drawmodel.vertex[skeleton.muscles[i].vertices[j]].x = M[12] * scale; - skeleton.drawmodel.vertex[skeleton.muscles[i].vertices[j]].y = M[13] * scale; - skeleton.drawmodel.vertex[skeleton.muscles[i].vertices[j]].z = M[14] * scale; - glPopMatrix(); - } - } - if (!playerdetail || skeleton.free == 3) { - for (j = 0; j < skeleton.muscles[i].verticeslow.size(); j++) { - XYZ &v0 = skeleton.modellow.vertex[skeleton.muscles[i].verticeslow[j]]; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - if (p1 == abdomen || p2 == abdomen) - glTranslatef(v0.x * proportionbody.x, - v0.y * proportionbody.y, - v0.z * proportionbody.z); - if (p1 == lefthand || p1 == righthand || p1 == leftwrist || p1 == rightwrist || p1 == leftelbow || p1 == rightelbow || p2 == leftelbow || p2 == rightelbow) - glTranslatef(v0.x * proportionarms.x, - v0.y * proportionarms.y, - v0.z * proportionarms.z); - if (p1 == leftfoot || p1 == rightfoot || p1 == leftankle || p1 == rightankle || p1 == leftknee || p1 == rightknee || p2 == leftknee || p2 == rightknee) - glTranslatef(v0.x * proportionlegs.x, - v0.y * proportionlegs.y, - v0.z * proportionlegs.z); - if (p1 == head || p2 == head) - glTranslatef(v0.x * proportionhead.x, - v0.y * proportionhead.y, - v0.z * proportionhead.z); - - glGetFloatv(GL_MODELVIEW_MATRIX, M); - skeleton.drawmodellow.vertex[skeleton.muscles[i].verticeslow[j]].x = M[12] * scale; - skeleton.drawmodellow.vertex[skeleton.muscles[i].verticeslow[j]].y = M[13] * scale; - skeleton.drawmodellow.vertex[skeleton.muscles[i].verticeslow[j]].z = M[14] * scale; - glPopMatrix(); - } - } - glPopMatrix(); - } - if (skeleton.clothes && skeleton.muscles[i].verticesclothes.size() > 0) { - mid = (skeleton.muscles[i].parent1->position + skeleton.muscles[i].parent2->position) / 2; - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - if (!skeleton.free) - glRotatef(tilt2, 1, 0, 0); - if (!skeleton.free) - glRotatef(tilt, 0, 0, 1); - glTranslatef(mid.x, mid.y, mid.z); - skeleton.muscles[i].lastrotate1 = skeleton.muscles[i].rotate1; - glRotatef(-skeleton.muscles[i].lastrotate1 + 90, 0, 1, 0); - - skeleton.muscles[i].lastrotate2 = skeleton.muscles[i].rotate2; - glRotatef(-skeleton.muscles[i].lastrotate2 + 90, 0, 0, 1); - - skeleton.muscles[i].lastrotate3 = skeleton.muscles[i].rotate3; - glRotatef(-skeleton.muscles[i].lastrotate3, 0, 1, 0); - - for (j = 0; j < skeleton.muscles[i].verticesclothes.size(); j++) { - XYZ &v0 = skeleton.modelclothes.vertex[skeleton.muscles[i].verticesclothes[j]]; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - if (p1 == abdomen || p2 == abdomen) - glTranslatef(v0.x * proportionbody.x, - v0.y * proportionbody.y, - v0.z * proportionbody.z); - if (p1 == lefthand || p1 == righthand || p1 == leftwrist || p1 == rightwrist || p1 == leftelbow || p1 == rightelbow || p2 == leftelbow || p2 == rightelbow) - glTranslatef(v0.x * proportionarms.x, - v0.y * proportionarms.y, - v0.z * proportionarms.z); - if (p1 == leftfoot || p1 == rightfoot || p1 == leftankle || p1 == rightankle || p1 == leftknee || p1 == rightknee || p2 == leftknee || p2 == rightknee) - glTranslatef(v0.x * proportionlegs.x, - v0.y * proportionlegs.y, - v0.z * proportionlegs.z); - if (p1 == head || p2 == head) - glTranslatef(v0.x * proportionhead.x, - v0.y * proportionhead.y, - v0.z * proportionhead.z); - glGetFloatv(GL_MODELVIEW_MATRIX, M); - skeleton.drawmodelclothes.vertex[skeleton.muscles[i].verticesclothes[j]].x = M[12] * scale; - skeleton.drawmodelclothes.vertex[skeleton.muscles[i].verticesclothes[j]].y = M[13] * scale; - skeleton.drawmodelclothes.vertex[skeleton.muscles[i].verticesclothes[j]].z = M[14] * scale; - glPopMatrix(); - } - glPopMatrix(); - } - updatedelay = 1 + (float)(Random() % 100) / 1000; - } - if (skeleton.free != 2 && (skeleton.free == 1 || skeleton.free == 3 || id == 0 || (normalsupdatedelay <= 0) || animTarget == getupfromfrontanim || animTarget == getupfrombackanim || animCurrent == getupfromfrontanim || animCurrent == getupfrombackanim)) { - normalsupdatedelay = 1; - if (playerdetail || skeleton.free == 3) - skeleton.drawmodel.CalculateNormals(0); - if (!playerdetail || skeleton.free == 3) - skeleton.drawmodellow.CalculateNormals(0); - if (skeleton.clothes) - skeleton.drawmodelclothes.CalculateNormals(0); - } else { - if (playerdetail || skeleton.free == 3) - skeleton.drawmodel.UpdateVertexArrayNoTexNoNorm(); - if (!playerdetail || skeleton.free == 3) - skeleton.drawmodellow.UpdateVertexArrayNoTexNoNorm(); - if (skeleton.clothes) { - skeleton.drawmodelclothes.UpdateVertexArrayNoTexNoNorm(); - } - } - } - framemult = .01; - updatedelaychange = -framemult * 4 * (45 - findDistance(&viewer, &coords) * 1); - if (updatedelaychange > -realmultiplier * 30) - updatedelaychange = -realmultiplier * 30; - if (updatedelaychange > -framemult * 4) - updatedelaychange = -framemult * 4; - if (skeleton.free == 1) - updatedelaychange *= 6; - if (id == 0) - updatedelaychange *= 8; - updatedelay += updatedelaychange; - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glTranslatef(coords.x, coords.y - .02, coords.z); - if (!skeleton.free) { - glTranslatef(offset.x * scale, offset.y * scale, offset.z * scale); - glRotatef(yaw, 0, 1, 0); - } - if (showpoints) { - glPointSize(5); - glColor4f(.4, 1, .4, 1); - glDisable(GL_LIGHTING); - glDisable(GL_TEXTURE_2D); - glBegin(GL_POINTS); - if (playerdetail) - for (i = 0; i < skeleton.drawmodel.vertexNum; i++) { - XYZ &v0 = skeleton.drawmodel.vertex[i]; - glVertex3f(v0.x, v0.y, v0.z); - } - glEnd(); - glBegin(GL_LINES); - - if (playerdetail) - for (i = 0; i < skeleton.drawmodel.TriangleNum; i++) { - XYZ &v0 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[0]]; - XYZ &v1 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[1]]; - XYZ &v2 = skeleton.drawmodel.vertex[skeleton.drawmodel.Triangles[i].vertex[2]]; - glVertex3f(v0.x, v0.y, v0.z); - glVertex3f(v1.x, v1.y, v1.z); - glVertex3f(v1.x, v1.y, v1.z); - glVertex3f(v2.x, v2.y, v2.z); - glVertex3f(v2.x, v2.y, v2.z); - glVertex3f(v0.x, v0.y, v0.z); - } - - glEnd(); - } - - terrainlight = terrain.getLighting(coords.x, coords.z); - distance = distsq(&viewer, &coords); - distance = (viewdistance * viewdistance - (distance - (viewdistance * viewdistance * fadestart)) * (1 / (1 - fadestart))) / viewdistance / viewdistance; - if (distance > 1) - distance = 1; - if (distance > 0) { - terrainheight = (coords.y - terrain.getHeight(coords.x, coords.z)) / 3 + 1; - if (terrainheight < 1) - terrainheight = 1; - if (terrainheight > 1.7) - terrainheight = 1.7; - - glColor4f((1 - (1 - terrainlight.x) / terrainheight) - burnt, (1 - (1 - terrainlight.y) / terrainheight) - burnt, (1 - (1 - terrainlight.z) / terrainheight) - burnt, distance); - glDisable(GL_BLEND); - glAlphaFunc(GL_GREATER, 0.0001); - glEnable(GL_TEXTURE_2D); - if (cellophane) { - glDisable(GL_TEXTURE_2D); - glColor4f(.7, .35, 0, .5); - glDepthMask(0); - glEnable(GL_LIGHTING); - glEnable(GL_BLEND); - } - if (tutoriallevel && id != 0) { - glColor4f(.7, .7, .7, 0.6); - glDepthMask(0); - glEnable(GL_LIGHTING); - glEnable(GL_BLEND); - if (canattack && cananger) - if (Animation::animations[animTarget].attack == normalattack || Animation::animations[animTarget].attack == reversed) { - glDisable(GL_TEXTURE_2D); - glColor4f(1, 0, 0, 0.8); - } - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glTranslatef(0, -smoketex, 0); - glTranslatef(-smoketex, 0, 0); - } - if (playerdetail) { - if (!showpoints) { - if ((tutoriallevel && id != 0)) - skeleton.drawmodel.drawdifftex(Sprite::cloudimpacttexture); - else - skeleton.drawmodel.draw(); - } - } - if (!playerdetail) { - if ((tutoriallevel && id != 0)) - skeleton.drawmodellow.drawdifftex(Sprite::cloudimpacttexture); - else - skeleton.drawmodellow.drawdifftex(skeleton.drawmodel.textureptr); - } - - if (!(Animation::animations[animTarget].attack == normalattack || Animation::animations[animTarget].attack == reversed)) - if (tutoriallevel && id != 0) { - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glEnable(GL_TEXTURE_2D); - glColor4f(.7, .7, .7, 0.6); - glDepthMask(0); - glEnable(GL_LIGHTING); - glEnable(GL_BLEND); - if (canattack && cananger) - if (Animation::animations[animTarget].attack == normalattack || Animation::animations[animTarget].attack == reversed) { - glDisable(GL_TEXTURE_2D); - glColor4f(1, 0, 0, 0.8); - } - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glTranslatef(0, -smoketex * .6, 0); - glTranslatef(smoketex * .6, 0, 0); - if (playerdetail) { - if (!showpoints) { - if ((tutoriallevel && id != 0)) - skeleton.drawmodel.drawdifftex(Sprite::cloudimpacttexture); - else - skeleton.drawmodel.draw(); - } - } - if (!playerdetail) { - if ((tutoriallevel && id != 0)) - skeleton.drawmodellow.drawdifftex(Sprite::cloudimpacttexture); - else - skeleton.drawmodellow.drawdifftex(skeleton.drawmodel.textureptr); - } - } - - - if (tutoriallevel && id != 0) { - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glEnable(GL_TEXTURE_2D); - } - if (skeleton.clothes) { - glDepthMask(0); - glEnable(GL_BLEND); - if (!immediate) - skeleton.drawmodelclothes.draw(); - if (immediate) - skeleton.drawmodelclothes.drawimmediate(); - glDepthMask(1); - } - } - glPopMatrix(); - - if (num_weapons > 0) { - for (k = 0; k < num_weapons; k++) { - i = weaponids[k]; - if (weaponactive == k) { - if (weapons[i].getType() != staff) { - for (j = 0; j < skeleton.muscles.size(); j++) { - if ((skeleton.muscles[j].parent1->label == righthand || skeleton.muscles[j].parent2->label == righthand) && skeleton.muscles[j].vertices.size() > 0) { - weaponattachmuscle = j; - } - } - for (j = 0; j < skeleton.muscles.size(); j++) { - if ((skeleton.muscles[j].parent1->label == rightwrist || skeleton.muscles[j].parent2->label == rightwrist) && (skeleton.muscles[j].parent1->label != righthand && skeleton.muscles[j].parent2->label != righthand) && skeleton.muscles[j].vertices.size() > 0) { - weaponrotatemuscle = j; - } - } - weaponpoint = (skeleton.muscles[weaponattachmuscle].parent1->position + skeleton.muscles[weaponattachmuscle].parent2->position) / 2; - if (creature == wolftype) - weaponpoint = (jointPos(rightwrist) * .7 + jointPos(righthand) * .3); - } - if (weapons[i].getType() == staff) { - for (j = 0; j < skeleton.muscles.size(); j++) { - if ((skeleton.muscles[j].parent1->label == righthand || skeleton.muscles[j].parent2->label == righthand) && skeleton.muscles[j].vertices.size() > 0) { - weaponattachmuscle = j; - } - } - for (j = 0; j < skeleton.muscles.size(); j++) { - if ((skeleton.muscles[j].parent1->label == rightelbow || skeleton.muscles[j].parent2->label == rightelbow) && (skeleton.muscles[j].parent1->label != rightshoulder && skeleton.muscles[j].parent2->label != rightshoulder) && skeleton.muscles[j].vertices.size() > 0) { - weaponrotatemuscle = j; - } - } - //weaponpoint=jointPos(rightwrist); - weaponpoint = (skeleton.muscles[weaponattachmuscle].parent1->position + skeleton.muscles[weaponattachmuscle].parent2->position) / 2; - //weaponpoint+=skeleton.specialforward[1]*.1+(jointPos(rightwrist)-jointPos(rightelbow)); - XYZ tempnormthing, vec1, vec2; - vec1 = (jointPos(rightwrist) - jointPos(rightelbow)); - vec2 = (jointPos(rightwrist) - jointPos(rightshoulder)); - CrossProduct(&vec1, &vec2, &tempnormthing); - Normalise(&tempnormthing); - if (animTarget != staffhitanim && animCurrent != staffhitanim && animTarget != staffgroundsmashanim && animCurrent != staffgroundsmashanim && animTarget != staffspinhitanim && animCurrent != staffspinhitanim) - weaponpoint += tempnormthing * .1 - skeleton.specialforward[1] * .3 + (jointPos(rightwrist) - jointPos(rightelbow)); - } - } - if (weaponactive != k && weaponstuck != k) { - if (weapons[i].getType() == knife) - weaponpoint = jointPos(abdomen) + (jointPos(righthip) - jointPos(lefthip)) * .1 + (jointPos(rightshoulder) - jointPos(leftshoulder)) * .35; - if (weapons[i].getType() == sword) - weaponpoint = jointPos(abdomen) + (jointPos(lefthip) - jointPos(righthip)) * .09 + (jointPos(leftshoulder) - jointPos(rightshoulder)) * .33; - if (weapons[i].getType() == staff) - weaponpoint = jointPos(abdomen) + (jointPos(lefthip) - jointPos(righthip)) * .09 + (jointPos(leftshoulder) - jointPos(rightshoulder)) * .33; - for (j = 0; j < skeleton.muscles.size(); j++) { - if ((skeleton.muscles[j].parent1->label == abdomen || skeleton.muscles[j].parent2->label == abdomen) && (skeleton.muscles[j].parent1->label == neck || skeleton.muscles[j].parent2->label == neck) && skeleton.muscles[j].vertices.size() > 0) { - weaponrotatemuscle = j; - } - } - } - if (weaponstuck == k) { - if (weaponstuckwhere == 0) - weaponpoint = jointPos(abdomen) * .5 + jointPos(neck) * .5 - skeleton.forward * .8; - else - weaponpoint = jointPos(abdomen) * .5 + jointPos(neck) * .5 + skeleton.forward * .8; - for (j = 0; j < skeleton.muscles.size(); j++) { - if ((skeleton.muscles[j].parent1->label == abdomen || skeleton.muscles[j].parent2->label == abdomen) && (skeleton.muscles[j].parent1->label == neck || skeleton.muscles[j].parent2->label == neck) && skeleton.muscles[j].vertices.size() > 0) { - weaponrotatemuscle = j; - } - } - } - if (skeleton.free) { - weapons[i].position = weaponpoint * scale + coords; - weapons[i].bigrotation = 0; - weapons[i].bigtilt = 0; - weapons[i].bigtilt2 = 0; - } else { - weapons[i].position = DoRotation(DoRotation(DoRotation(weaponpoint, 0, 0, tilt), tilt2, 0, 0), 0, yaw, 0) * scale + coords + currentoffset * (1 - target) * scale + targetoffset * target * scale; - weapons[i].bigrotation = yaw; - weapons[i].bigtilt = tilt; - weapons[i].bigtilt2 = tilt2; - } - weapons[i].rotation1 = skeleton.muscles[weaponrotatemuscle].lastrotate1; - weapons[i].rotation2 = skeleton.muscles[weaponrotatemuscle].lastrotate2; - weapons[i].rotation3 = skeleton.muscles[weaponrotatemuscle].lastrotate3; - if (weaponactive == k) { - if (weapons[i].getType() == knife) { - weapons[i].smallrotation = 180; - weapons[i].smallrotation2 = 0; - if (isCrouch() || wasCrouch()) { - weapons[i].smallrotation2 = 20; - } - if (animTarget == hurtidleanim) { - weapons[i].smallrotation2 = 50; - } - if ((animCurrent == crouchstabanim && animTarget == crouchstabanim) || (animCurrent == backhandspringanim && animTarget == backhandspringanim)) { - XYZ temppoint1, temppoint2; - float distance; - - temppoint1 = jointPos(righthand); - temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); - distance = findDistance(&temppoint1, &temppoint2); - weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); - weapons[i].rotation2 *= 360 / 6.28; - temppoint1.y = 0; - temppoint2.y = 0; - weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); - weapons[i].rotation1 *= 360 / 6.28; - weapons[i].rotation3 = 0; - weapons[i].smallrotation = -90; - weapons[i].smallrotation2 = 0; - if (temppoint1.x > temppoint2.x) - weapons[i].rotation1 = 360 - weapons[i].rotation1; - } - if ((animCurrent == knifeslashreversalanim && animTarget == knifeslashreversalanim) || (animCurrent == knifeslashreversedanim && animTarget == knifeslashreversedanim)) { - XYZ temppoint1, temppoint2; - float distance; - - temppoint1 = jointPos(righthand); - temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); - distance = findDistance(&temppoint1, &temppoint2); - weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); - weapons[i].rotation2 *= 360 / 6.28; - temppoint1.y = 0; - temppoint2.y = 0; - weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); - weapons[i].rotation1 *= 360 / 6.28; - weapons[i].rotation3 = 0; - weapons[i].smallrotation = 90; - weapons[i].smallrotation2 = 0; - if (temppoint1.x > temppoint2.x) - weapons[i].rotation1 = 360 - weapons[i].rotation1; - } - if (animTarget == knifethrowanim) { - weapons[i].smallrotation = 90; - //weapons[i].smallrotation2=-90; - weapons[i].smallrotation2 = 0; - weapons[i].rotation1 = 0; - weapons[i].rotation2 = 0; - weapons[i].rotation3 = 0; - } - if (animTarget == knifesneakattackanim && frameTarget < 5) { - weapons[i].smallrotation = -90; - weapons[i].rotation1 = 0; - weapons[i].rotation2 = 0; - weapons[i].rotation3 = 0; - } - } - if (weapons[i].getType() == sword) { - weapons[i].smallrotation = 0; - weapons[i].smallrotation2 = 0; - if (animTarget == knifethrowanim) { - weapons[i].smallrotation = -90; - weapons[i].smallrotation2 = 0; - weapons[i].rotation1 = 0; - weapons[i].rotation2 = 0; - weapons[i].rotation3 = 0; - } - if ((animTarget == swordgroundstabanim && animCurrent == swordgroundstabanim) || (animTarget == swordsneakattackanim && animCurrent == swordsneakattackanim) || (animTarget == swordslashparryanim && animCurrent == swordslashparryanim) || (animTarget == swordslashparriedanim && animCurrent == swordslashparriedanim) || (animTarget == swordslashreversalanim && animCurrent == swordslashreversalanim) || (animTarget == swordslashreversedanim && animCurrent == swordslashreversedanim) || (animTarget == knifeslashreversalanim && animCurrent == knifeslashreversalanim) || (animTarget == knifeslashreversedanim && animCurrent == knifeslashreversedanim) || (animTarget == swordslashanim && animCurrent == swordslashanim) || (animTarget == drawleftanim && animCurrent == drawleftanim) || (animCurrent == backhandspringanim && animTarget == backhandspringanim)) { - XYZ temppoint1, temppoint2; - float distance; - - temppoint1 = currentFrame().joints[skeleton.jointlabels[righthand]].position * (1 - target) + targetFrame().joints[skeleton.jointlabels[righthand]].position * (target); //jointPos(righthand); - temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); - distance = findDistance(&temppoint1, &temppoint2); - weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); - weapons[i].rotation2 *= 360 / 6.28; - temppoint1.y = 0; - temppoint2.y = 0; - weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); - weapons[i].rotation1 *= 360 / 6.28; - weapons[i].rotation3 = 0; - weapons[i].smallrotation = 90; - weapons[i].smallrotation2 = 0; - if (temppoint1.x > temppoint2.x) - weapons[i].rotation1 = 360 - weapons[i].rotation1; - } - } - if (weapons[i].getType() == staff) { - weapons[i].smallrotation = 100; - weapons[i].smallrotation2 = 0; - if ((animTarget == staffhitanim && animCurrent == staffhitanim) || (animTarget == staffhitreversedanim && animCurrent == staffhitreversedanim) || (animTarget == staffspinhitreversedanim && animCurrent == staffspinhitreversedanim) || (animTarget == staffgroundsmashanim && animCurrent == staffgroundsmashanim) || (animTarget == staffspinhitanim && animCurrent == staffspinhitanim)) { - XYZ temppoint1, temppoint2; - float distance; - - temppoint1 = currentFrame().joints[skeleton.jointlabels[righthand]].position * (1 - target) + targetFrame().joints[skeleton.jointlabels[righthand]].position * (target); //jointPos(righthand); - temppoint2 = currentFrame().weapontarget * (1 - target) + targetFrame().weapontarget * (target); - distance = findDistance(&temppoint1, &temppoint2); - weapons[i].rotation2 = asin((temppoint1.y - temppoint2.y) / distance); - weapons[i].rotation2 *= 360 / 6.28; - temppoint1.y = 0; - temppoint2.y = 0; - weapons[i].rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); - weapons[i].rotation1 *= 360 / 6.28; - weapons[i].rotation3 = 0; - weapons[i].smallrotation = 90; - weapons[i].smallrotation2 = 0; - if (temppoint1.x > temppoint2.x) - weapons[i].rotation1 = 360 - weapons[i].rotation1; - } - } - } - if (weaponactive != k && weaponstuck != k) { - if (weapons[i].getType() == knife) { - weapons[i].smallrotation = -70; - weapons[i].smallrotation2 = 10; - } - if (weapons[i].getType() == sword) { - weapons[i].smallrotation = -100; - weapons[i].smallrotation2 = -8; - } - if (weapons[i].getType() == staff) { - weapons[i].smallrotation = -100; - weapons[i].smallrotation2 = -8; - } - } - if (weaponstuck == k) { - if (weaponstuckwhere == 0) - weapons[i].smallrotation = 180; - else - weapons[i].smallrotation = 0; - weapons[i].smallrotation2 = 10; - } - } - } - } - - calcrot = 0; - if (skeleton.free) - calcrot = 1; - if (Animation::animations[animTarget].attack || isRun() || animTarget == staggerbackhardanim || isFlip() || animTarget == climbanim || animTarget == sneakanim || animTarget == rollanim || animTarget == walkanim || animTarget == backhandspringanim || isWallJump()) - calcrot = 1; - if (animCurrent != animTarget) - calcrot = 1; - if (skeleton.free == 2) - calcrot = 0; - - return 0; -} - - -/* FUNCTION? - */ -int Person::SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate, Model *model) -{ - static int i, j; - static float distance; - static float olddistance; - static int intersecting; - static int firstintersecting; - static XYZ point; - static XYZ oldp1; - static XYZ start, end; - static float slopethreshold = -.4; - - firstintersecting = -1; - - oldp1 = *p1; - *p1 = *p1 - *move; - if (distsq(p1, &model->boundingspherecenter) > radius * radius + model->boundingsphereradius * model->boundingsphereradius) - return -1; - if (*rotate) - *p1 = DoRotation(*p1, 0, -*rotate, 0); - for (i = 0; i < 4; i++) { - for (j = 0; j < model->TriangleNum; j++) { - if (model->facenormals[j].y <= slopethreshold) { - intersecting = 0; - distance = abs((model->facenormals[j].x * p1->x) + (model->facenormals[j].y * p1->y) + (model->facenormals[j].z * p1->z) - ((model->facenormals[j].x * model->vertex[model->Triangles[j].vertex[0]].x) + (model->facenormals[j].y * model->vertex[model->Triangles[j].vertex[0]].y) + (model->facenormals[j].z * model->vertex[model->Triangles[j].vertex[0]].z))); - if (distance < radius) { - point = *p1 - model->facenormals[j] * distance; - if (PointInTriangle( &point, model->facenormals[j], &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]])) - intersecting = 1; - if (!intersecting) - intersecting = sphere_line_intersection(&model->vertex[model->Triangles[j].vertex[0]], - &model->vertex[model->Triangles[j].vertex[1]], - p1, &radius); - if (!intersecting) - intersecting = sphere_line_intersection(&model->vertex[model->Triangles[j].vertex[1]], - &model->vertex[model->Triangles[j].vertex[2]], - p1, &radius); - if (!intersecting) - intersecting = sphere_line_intersection(&model->vertex[model->Triangles[j].vertex[0]], - &model->vertex[model->Triangles[j].vertex[2]], - p1, &radius); - end = *p1 - point; - if (dotproduct(&model->facenormals[j], &end) > 0 && intersecting) { - start = *p1; - end = *p1; - end.y -= radius; - if (LineFacetd(&start, &end, &model->vertex[model->Triangles[j].vertex[0]], &model->vertex[model->Triangles[j].vertex[1]], &model->vertex[model->Triangles[j].vertex[2]], &model->facenormals[j], &point)) { - p1->y = point.y + radius; - if ((animTarget == jumpdownanim || isFlip())) { - if (isFlip() && (frameTarget < 5 || targetFrame().label == 7 || targetFrame().label == 4)) - RagDoll(0); - - if (animTarget == jumpupanim) { - jumppower = -4; - animTarget = getIdle(); - } - target = 0; - frameTarget = 0; - onterrain = 1; - - if (id == 0) { - pause_sound(whooshsound); - OPENAL_SetVolume(channels[whooshsound], 0); - } - - if ((animTarget == jumpdownanim || isFlip()) && !wasLanding() && !wasLandhard()) { - if (isFlip()) - jumppower = -4; - animTarget = getLanding(); - emit_sound_at(landsound, coords, 128.); - - if (id == 0) { - addEnvSound(coords); - } - } - } - } - } - } - if ((distance < olddistance || firstintersecting == -1) && intersecting) { - olddistance = distance; - firstintersecting = j; - *p = point; - } - } - } - for (j = 0; j < model->TriangleNum; j++) { - if (model->facenormals[j].y > slopethreshold) { - intersecting = 0; - start = *p1; - start.y -= radius / 4; - XYZ &v0 = model->vertex[model->Triangles[j].vertex[0]]; - XYZ &v1 = model->vertex[model->Triangles[j].vertex[1]]; - XYZ &v2 = model->vertex[model->Triangles[j].vertex[2]]; - distance = abs((model->facenormals[j].x * start.x) - + (model->facenormals[j].y * start.y) - + (model->facenormals[j].z * start.z) - - ((model->facenormals[j].x * v0.x) - + (model->facenormals[j].y * v0.y) - + (model->facenormals[j].z * v0.z))); - if (distance < radius * .5) { - point = start - model->facenormals[j] * distance; - if (PointInTriangle( &point, model->facenormals[j], &v0, &v1, &v2)) - intersecting = 1; - if (!intersecting) - intersecting = sphere_line_intersection(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, p1->x, p1->y, p1->z, radius / 2); - if (!intersecting) - intersecting = sphere_line_intersection(v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, p1->x, p1->y, p1->z, radius / 2); - if (!intersecting) - intersecting = sphere_line_intersection(v0.x, v0.y, v0.z, v2.x, v2.y, v2.z, p1->x, p1->y, p1->z, radius / 2); - end = *p1 - point; - if (dotproduct(&model->facenormals[j], &end) > 0 && intersecting) { - if ((animTarget == jumpdownanim || animTarget == jumpupanim || isFlip())) { - start = velocity; - velocity -= DoRotation(model->facenormals[j], 0, *rotate, 0) * findLength(&velocity) * abs(normaldotproduct(velocity, DoRotation(model->facenormals[j], 0, *rotate, 0))); //(distance-radius*.5)/multiplier; - if (findLengthfast(&start) < findLengthfast(&velocity)) - velocity = start; - } - *p1 += model->facenormals[j] * (distance - radius * .5); - } - } - if ((distance < olddistance || firstintersecting == -1) && intersecting) { - olddistance = distance; - firstintersecting = j; - *p = point; - } - } - } - } - if (*rotate) - *p = DoRotation(*p, 0, *rotate, 0); - *p = *p + *move; - if (*rotate) - *p1 = DoRotation(*p1, 0, *rotate, 0); - *p1 += *move; - return firstintersecting; -} - -void Person::takeWeapon(int weaponId) -{ - weaponactive = 0; - weapons[weaponId].owner = id; - if (num_weapons > 0) { - weaponids[num_weapons] = weaponids[0]; - } - num_weapons++; - weaponids[0] = weaponId; -} - -void Person::addClothes() -{ - if (numclothes > 0) { - for (int i = 0; i < numclothes; i++) { - addClothes(i); - } - DoMipmaps(); - } -} - -bool Person::addClothes(const int& clothesId) -{ - LOGFUNC; - const std::string fileName = clothes[clothesId]; - - GLubyte* array = &skeleton.skinText[0]; - - //Load Image - ImageRec texture; - bool opened = load_image(Folders::getResourcePath(fileName).c_str(), texture); - - float alphanum; - //Is it valid? - if (opened) { - float tintr = clothestintr[clothesId]; - float tintg = clothestintg[clothesId]; - float tintb = clothestintb[clothesId]; - - if (tintr > 1) tintr = 1; - if (tintg > 1) tintg = 1; - if (tintb > 1) tintb = 1; - - if (tintr < 0) tintr = 0; - if (tintg < 0) tintg = 0; - if (tintb < 0) tintb = 0; - - int bytesPerPixel = texture.bpp / 8; - - int tempnum = 0; - alphanum = 255; - for (int i = 0; i < (int)(texture.sizeY * texture.sizeX * bytesPerPixel); i++) { - if (bytesPerPixel == 3) - alphanum = 255; - else if ((i + 1) % 4 == 0) - alphanum = texture.data[i]; - if ((i + 1) % 4 || bytesPerPixel == 3) { - if ((i % 4) == 0) - texture.data[i] *= tintr; - if ((i % 4) == 1) - texture.data[i] *= tintg; - if ((i % 4) == 2) - texture.data[i] *= tintb; - array[tempnum] = (float)array[tempnum] * (1 - alphanum / 255) + (float)texture.data[i] * (alphanum / 255); - tempnum++; - } - } - return 1; - } else { - return 0; - } -} diff --git a/Source/Person.h b/Source/Person.h deleted file mode 100644 index cb62622..0000000 --- a/Source/Person.h +++ /dev/null @@ -1,403 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _PERSON_H_ -#define _PERSON_H_ - -/**> HEADER FILES <**/ - -#include "gamegl.h" -#include "Quaternions.h" -#include "Animation/Skeleton.h" -#include "Models.h" -#include "Terrain.h" -#include "Sprite.h" -#include -#include -#include "Weapons.h" -#include "Animation/Animation.h" - -#define passivetype 0 -#define guardtype 1 -#define searchtype 2 -#define attacktype 3 -#define attacktypecutoff 4 -#define playercontrolled 5 -#define gethelptype 6 -#define getweapontype 7 -#define pathfindtype 8 - -#define rabbittype 0 -#define wolftype 1 - -struct InvalidPersonException : public exception { - const char * what () const throw () { - return "Invalid weapon number"; - } -}; - -class Person : public enable_shared_from_this -{ -public: - static std::vector> players; - - int whichpatchx; - int whichpatchz; - - // animCurrent and animTarget are used to interpolate between different animations - // (and for a bunch of other things). - // animations interpolate with one another at various speeds. - // animTarget seems to determine the combat state? - int animCurrent; - int animTarget; - - // frameCurrent and frameTarget are used to interpolate between the frames of an animation - // (e.g. the crouched animation has only two frames, lerped back and forth slowly). - // animations advance at various speeds. - int frameCurrent; - int frameTarget; - - int oldanimCurrent; - int oldanimTarget; - int oldframeCurrent; - int oldframeTarget; - - int howactive; - - float parriedrecently; - - bool superruntoggle; - - int lastattack, lastattack2, lastattack3; - - XYZ currentoffset, targetoffset, offset; - float target; - float transspeed; - - XYZ realoldcoords; - XYZ oldcoords; - XYZ coords; - XYZ velocity; - - XYZ proportionhead; - XYZ proportionlegs; - XYZ proportionarms; - XYZ proportionbody; - - float unconscioustime; - - bool immobile; - - float velspeed; - float targetyaw; - float targetrot; - float rot; - float oldrot; - float lookyaw; - float lookpitch; - float yaw; - float pitch; - float lowyaw; - float tilt; - float targettilt; - float tilt2; - float targettilt2; - bool rabbitkickenabled; - - float bloodloss; - float bleeddelay; - float skiddelay; - float skiddingdelay; - float deathbleeding; - float tempdeltav; - - float damagetolerance; - float damage; - float permanentdamage; - float superpermanentdamage; - float lastcollide; - /* Seems to be 0 = alive, 1 = unconscious, 2 = dead */ - int dead; - - float jumppower; - bool onground; - - int wentforweapon; - - bool calcrot; - - XYZ facing; - - float bleeding; - float bleedx, bleedy; - int direction; - float texupdatedelay; - - float headyaw, headpitch; - float targetheadyaw, targetheadpitch; - - bool onterrain; - bool pause; - - float grabdelay; - - std::shared_ptr victim; - bool hasvictim; - - float updatedelay; - float normalsupdatedelay; - - bool jumpstart; - - bool forwardkeydown; - bool forwardstogglekeydown; - bool rightkeydown; - bool leftkeydown; - bool backkeydown; - bool jumpkeydown; - bool jumptogglekeydown; - bool crouchkeydown; - bool crouchtogglekeydown; - bool drawkeydown; - bool drawtogglekeydown; - bool throwkeydown; - bool throwtogglekeydown; - bool attackkeydown; - bool feint; - bool lastfeint; - bool headless; - - float crouchkeydowntime; - float jumpkeydowntime; - bool freefall; - - - float turnspeed; - - int aitype; - float aiupdatedelay; - float losupdatedelay; - int ally; - float collide; - float collided; - float avoidcollided; - bool loaded; - bool whichdirection; - float whichdirectiondelay; - bool avoidsomething; - XYZ avoidwhere; - float blooddimamount; - - float staggerdelay; - float blinkdelay; - float twitchdelay; - float twitchdelay2; - float twitchdelay3; - float lefthandmorphness; - float righthandmorphness; - float headmorphness; - float chestmorphness; - float tailmorphness; - float targetlefthandmorphness; - float targetrighthandmorphness; - float targetheadmorphness; - float targetchestmorphness; - float targettailmorphness; - int lefthandmorphstart, lefthandmorphend; - int righthandmorphstart, righthandmorphend; - int headmorphstart, headmorphend; - int chestmorphstart, chestmorphend; - int tailmorphstart, tailmorphend; - - float weaponmissdelay; - float highreversaldelay; - float lowreversaldelay; - - int creature; - - unsigned id; - - Skeleton skeleton; - - float speed; - float scale; - float power; - float speedmult; - - float protectionhead; - float protectionhigh; - float protectionlow; - float armorhead; - float armorhigh; - float armorlow; - bool metalhead; - bool metalhigh; - bool metallow; - - int numclothes; - char clothes[10][256]; - float clothestintr[10]; - float clothestintg[10]; - float clothestintb[10]; - - bool landhard; - bool bled; - bool spurt; - bool onfire; - float onfiredelay; - float burnt; - - float flamedelay; - - int playerdetail; - - int num_weapons; - int weaponids[4]; - /* Key of weaponids which is the weapon in hand, if any. -1 otherwise. - * Always 0 or -1 as activeweapon is moved to position 0 when taken */ - int weaponactive; - int weaponstuck; - /* 0 or 1 to say if weapon is stuck in the front or the back */ - int weaponstuckwhere; - - int numwaypoints; - XYZ waypoints[90]; - int waypointtype[90]; - float pausetime; - - XYZ headtarget; - float interestdelay; - - XYZ finalfinaltarget; - XYZ finaltarget; - int finalpathfindpoint; - int targetpathfindpoint; - int lastpathfindpoint; - int lastpathfindpoint2; - int lastpathfindpoint3; - int lastpathfindpoint4; - - int waypoint; - - XYZ lastseen; - float lastseentime; - float lastchecktime; - float stunned; - float surprised; - float runninghowlong; - int occluded; - int lastoccluded; - int laststanding; - int escapednum; - - float speechdelay; - float neckspurtdelay; - float neckspurtparticledelay; - float neckspurtamount; - - int whichskin; - bool rabbitkickragdoll; - - Animation tempanimation; - - bool jumpclimb; - - Person(); - Person(FILE*, int, unsigned); - - void skeletonLoad(bool clothes = false); - - // convenience functions - inline Joint& joint(int bodypart) { return skeleton.joints[skeleton.jointlabels[bodypart]]; } - inline XYZ& jointPos(int bodypart) { return joint(bodypart).position; } - inline XYZ& jointVel(int bodypart) { return joint(bodypart).velocity; } - inline AnimationFrame& currentFrame() { return Animation::animations.at(animCurrent).frames.at(frameCurrent); } - inline AnimationFrame& targetFrame() { return Animation::animations.at(animTarget).frames.at(frameTarget); } - - - void CheckKick(); - void CatchFire(); - void DoBlood(float howmuch, int which); - void DoBloodBig(float howmuch, int which); - bool DoBloodBigWhere(float howmuch, int which, XYZ where); - - bool wasIdle() { return animation_bits[animCurrent] & ab_idle; } - bool isIdle() { return animation_bits[animTarget] & ab_idle; } - int getIdle(); - - bool isSitting() { return animation_bits[animTarget] & ab_sit; } - - bool isSleeping() { return animation_bits[animTarget] & ab_sleep; } - - bool wasCrouch() { return animation_bits[animCurrent] & ab_crouch; } - bool isCrouch() { return animation_bits[animTarget] & ab_crouch; } - int getCrouch(); - - bool wasStop() { return animation_bits[animCurrent] & ab_stop; } - bool isStop() { return animation_bits[animTarget] & ab_stop; } - int getStop(); - - bool wasSneak(); - bool isSneak(); - int getSneak(); - - bool wasRun() { return animation_bits[animCurrent] & ab_run; } - bool isRun() { return animation_bits[animTarget] & ab_run; } - int getRun(); - - bool wasLanding() { return animation_bits[animCurrent] & ab_land; } - bool isLanding() { return animation_bits[animTarget] & ab_land; } - int getLanding(); - - bool wasLandhard() { return animation_bits[animCurrent] & ab_landhard; } - bool isLandhard() { return animation_bits[animTarget] & ab_landhard; } - int getLandhard(); - - bool wasFlip() { return animation_bits[animCurrent] & ab_flip; } - bool isFlip() { return animation_bits[animTarget] & ab_flip; } - - bool isWallJump() { return animation_bits[animTarget] & ab_walljump; } - void Reverse(); - void DoDamage(float howmuch); - void DoHead(); - void DoMipmaps() { - skeleton.drawmodel.textureptr.bind(); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, skeleton.skinsize, skeleton.skinsize, 0, GL_RGB, GL_UNSIGNED_BYTE, &skeleton.skinText[0]); - } - - int SphereCheck(XYZ *p1, float radius, XYZ *p, XYZ *move, float *rotate, Model *model); - int DrawSkeleton(); - void Puff(int whichlabel); - void FootLand(bodypart whichfoot, float opacity); - void DoStuff(); - void setAnimation(int); - void DoAnimations(); - void RagDoll(bool checkcollision); - - void takeWeapon (int weaponId); - - bool addClothes(const int& clothesId); - void addClothes(); -}; - -const int maxplayers = 10; - -#endif diff --git a/Source/PhysicsMath.h b/Source/PhysicsMath.h deleted file mode 100644 index 7dc9461..0000000 --- a/Source/PhysicsMath.h +++ /dev/null @@ -1,765 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _PHYSICSMATH_H_ -#define _PHYSICSMATH_H_ - -//#include - -#include "MacCompatibility.h" - -//------------------------------------------------------------------------// -// Misc. Constants -//------------------------------------------------------------------------// - -float const pi = 3.14159265f; -float const g = -32.174f; // acceleration due to gravity, ft/s^2 -float const rho = 0.0023769f; // desity of air at sea level, slugs/ft^3 -float const tol = 0.0000000001f; // float type tolerance - - -//------------------------------------------------------------------------// -// Misc. Functions -//------------------------------------------------------------------------// -inline float DegreesToRadians(float deg); -inline float RadiansToDegrees(float rad); - -inline float DegreesToRadians(float deg) -{ - return deg * pi / 180.0f; -} - -inline float RadiansToDegrees(float rad) -{ - return rad * 180.0f / pi; -} - -//------------------------------------------------------------------------// -// Vector Class and vector functions -//------------------------------------------------------------------------// -class Vector -{ -public: - float x; - float y; - float z; - - Vector(void); - Vector(float xi, float yi, float zi); - - float Magnitude(void); - void Normalize(void); - void Reverse(void); - - Vector& operator+=(Vector u); // vector addition - Vector& operator-=(Vector u); // vector subtraction - Vector& operator*=(float s); // scalar multiply - Vector& operator/=(float s); // scalar divide - - Vector operator-(void); - -}; - -inline Vector operator+(Vector u, Vector v); -inline Vector operator-(Vector u, Vector v); -inline Vector operator^(Vector u, Vector v); -inline float operator*(Vector u, Vector v); -inline Vector operator*(float s, Vector u); -inline Vector operator*(Vector u, float s); -inline Vector operator/(Vector u, float s); -inline float TripleScalarProduct(Vector u, Vector v, Vector w); -/* -float fast_sqrt2 (register float arg); -float fast_sqrt2 (register float arg) -{ -// Can replace with slower return std::sqrt(arg); -register float result; - -if (arg == 0.0) return 0.0; - -asm { -frsqrte result,arg // Calculate Square root -} - -// Newton Rhapson iterations. -result = result + 0.5 * result * (1.0 - arg * result * result); -result = result + 0.5 * result * (1.0 - arg * result * result); - -return result * arg; -} -*/ -inline Vector::Vector(void) -{ - x = 0; - y = 0; - z = 0; -} - -inline Vector::Vector(float xi, float yi, float zi) -{ - x = xi; - y = yi; - z = zi; -} - -inline float Vector::Magnitude(void) -{ - return (float) sqrt(x * x + y * y + z * z); -} - -inline void Vector::Normalize(void) -{ - float m = (float) sqrt(x * x + y * y + z * z); - if (m <= tol) - m = 1; - x /= m; - y /= m; - z /= m; - - if (fabs(x) < tol) - x = 0.0f; - if (fabs(y) < tol) - y = 0.0f; - if (fabs(z) < tol) - z = 0.0f; -} - -inline void Vector::Reverse(void) -{ - x = -x; - y = -y; - z = -z; -} - -inline Vector& Vector::operator+=(Vector u) -{ - x += u.x; - y += u.y; - z += u.z; - return *this; -} - -inline Vector& Vector::operator-=(Vector u) -{ - x -= u.x; - y -= u.y; - z -= u.z; - return *this; -} - -inline Vector& Vector::operator*=(float s) -{ - x *= s; - y *= s; - z *= s; - return *this; -} - -inline Vector& Vector::operator/=(float s) -{ - x /= s; - y /= s; - z /= s; - return *this; -} - -inline Vector Vector::operator-(void) -{ - return Vector(-x, -y, -z); -} - - -inline Vector operator+(Vector u, Vector v) -{ - return Vector(u.x + v.x, u.y + v.y, u.z + v.z); -} - -inline Vector operator-(Vector u, Vector v) -{ - return Vector(u.x - v.x, u.y - v.y, u.z - v.z); -} - -// Vector cross product (u cross v) -inline Vector operator^(Vector u, Vector v) -{ - return Vector( u.y * v.z - u.z * v.y, - -u.x * v.z + u.z * v.x, - u.x * v.y - u.y * v.x ); -} - -// Vector dot product -inline float operator*(Vector u, Vector v) -{ - return (u.x * v.x + u.y * v.y + u.z * v.z); -} - -inline Vector operator*(float s, Vector u) -{ - return Vector(u.x * s, u.y * s, u.z * s); -} - -inline Vector operator*(Vector u, float s) -{ - return Vector(u.x * s, u.y * s, u.z * s); -} - -inline Vector operator/(Vector u, float s) -{ - return Vector(u.x / s, u.y / s, u.z / s); -} - -// triple scalar product (u dot (v cross w)) -inline float TripleScalarProduct(Vector u, Vector v, Vector w) -{ - return float( (u.x * (v.y * w.z - v.z * w.y)) + - (u.y * (-v.x * w.z + v.z * w.x)) + - (u.z * (v.x * w.y - v.y * w.x)) ); - //return u*(v^w); - -} - - - -//------------------------------------------------------------------------// -// Matrix Class and matrix functions -//------------------------------------------------------------------------// - -class Matrix3x3 -{ -public: - // elements eij: i -> row, j -> column - float e11, e12, e13, e21, e22, e23, e31, e32, e33; - - Matrix3x3(void); - Matrix3x3( float r1c1, float r1c2, float r1c3, - float r2c1, float r2c2, float r2c3, - float r3c1, float r3c2, float r3c3 ); - - float det(void); - Matrix3x3 Transpose(void); - Matrix3x3 Inverse(void); - - Matrix3x3& operator+=(Matrix3x3 m); - Matrix3x3& operator-=(Matrix3x3 m); - Matrix3x3& operator*=(float s); - Matrix3x3& operator/=(float s); -}; - -inline Matrix3x3 operator+(Matrix3x3 m1, Matrix3x3 m2); -inline Matrix3x3 operator-(Matrix3x3 m1, Matrix3x3 m2); -inline Matrix3x3 operator/(Matrix3x3 m, float s); -inline Matrix3x3 operator*(Matrix3x3 m1, Matrix3x3 m2); -inline Matrix3x3 operator*(Matrix3x3 m, float s); -inline Matrix3x3 operator*(float s, Matrix3x3 m); -inline Vector operator*(Matrix3x3 m, Vector u); -inline Vector operator*(Vector u, Matrix3x3 m); - - - - - -inline Matrix3x3::Matrix3x3(void) -{ - e11 = 0; - e12 = 0; - e13 = 0; - e21 = 0; - e22 = 0; - e23 = 0; - e31 = 0; - e32 = 0; - e33 = 0; -} - -inline Matrix3x3::Matrix3x3( float r1c1, float r1c2, float r1c3, - float r2c1, float r2c2, float r2c3, - float r3c1, float r3c2, float r3c3 ) -{ - e11 = r1c1; - e12 = r1c2; - e13 = r1c3; - e21 = r2c1; - e22 = r2c2; - e23 = r2c3; - e31 = r3c1; - e32 = r3c2; - e33 = r3c3; -} - -inline float Matrix3x3::det(void) -{ - return e11 * e22 * e33 - - e11 * e32 * e23 + - e21 * e32 * e13 - - e21 * e12 * e33 + - e31 * e12 * e23 - - e31 * e22 * e13; -} - -inline Matrix3x3 Matrix3x3::Transpose(void) -{ - return Matrix3x3(e11, e21, e31, e12, e22, e32, e13, e23, e33); -} - -inline Matrix3x3 Matrix3x3::Inverse(void) -{ - float d = e11 * e22 * e33 - - e11 * e32 * e23 + - e21 * e32 * e13 - - e21 * e12 * e33 + - e31 * e12 * e23 - - e31 * e22 * e13; - - if (d == 0) - d = 1; - - return Matrix3x3( (e22 * e33 - e23 * e32) / d, - -(e12 * e33 - e13 * e32) / d, - (e12 * e23 - e13 * e22) / d, - -(e21 * e33 - e23 * e31) / d, - (e11 * e33 - e13 * e31) / d, - -(e11 * e23 - e13 * e21) / d, - (e21 * e32 - e22 * e31) / d, - -(e11 * e32 - e12 * e31) / d, - (e11 * e22 - e12 * e21) / d ); -} - -inline Matrix3x3& Matrix3x3::operator+=(Matrix3x3 m) -{ - e11 += m.e11; - e12 += m.e12; - e13 += m.e13; - e21 += m.e21; - e22 += m.e22; - e23 += m.e23; - e31 += m.e31; - e32 += m.e32; - e33 += m.e33; - return *this; -} - -inline Matrix3x3& Matrix3x3::operator-=(Matrix3x3 m) -{ - e11 -= m.e11; - e12 -= m.e12; - e13 -= m.e13; - e21 -= m.e21; - e22 -= m.e22; - e23 -= m.e23; - e31 -= m.e31; - e32 -= m.e32; - e33 -= m.e33; - return *this; -} - -inline Matrix3x3& Matrix3x3::operator*=(float s) -{ - e11 *= s; - e12 *= s; - e13 *= s; - e21 *= s; - e22 *= s; - e23 *= s; - e31 *= s; - e32 *= s; - e33 *= s; - return *this; -} - -inline Matrix3x3& Matrix3x3::operator/=(float s) -{ - e11 /= s; - e12 /= s; - e13 /= s; - e21 /= s; - e22 /= s; - e23 /= s; - e31 /= s; - e32 /= s; - e33 /= s; - return *this; -} - -inline Matrix3x3 operator+(Matrix3x3 m1, Matrix3x3 m2) -{ - return Matrix3x3( m1.e11 + m2.e11, - m1.e12 + m2.e12, - m1.e13 + m2.e13, - m1.e21 + m2.e21, - m1.e22 + m2.e22, - m1.e23 + m2.e23, - m1.e31 + m2.e31, - m1.e32 + m2.e32, - m1.e33 + m2.e33); -} - -inline Matrix3x3 operator-(Matrix3x3 m1, Matrix3x3 m2) -{ - return Matrix3x3( m1.e11 - m2.e11, - m1.e12 - m2.e12, - m1.e13 - m2.e13, - m1.e21 - m2.e21, - m1.e22 - m2.e22, - m1.e23 - m2.e23, - m1.e31 - m2.e31, - m1.e32 - m2.e32, - m1.e33 - m2.e33); -} - -inline Matrix3x3 operator/(Matrix3x3 m, float s) -{ - return Matrix3x3( m.e11 / s, - m.e12 / s, - m.e13 / s, - m.e21 / s, - m.e22 / s, - m.e23 / s, - m.e31 / s, - m.e32 / s, - m.e33 / s); -} - -inline Matrix3x3 operator*(Matrix3x3 m1, Matrix3x3 m2) -{ - return Matrix3x3( m1.e11 * m2.e11 + m1.e12 * m2.e21 + m1.e13 * m2.e31, - m1.e11 * m2.e12 + m1.e12 * m2.e22 + m1.e13 * m2.e32, - m1.e11 * m2.e13 + m1.e12 * m2.e23 + m1.e13 * m2.e33, - m1.e21 * m2.e11 + m1.e22 * m2.e21 + m1.e23 * m2.e31, - m1.e21 * m2.e12 + m1.e22 * m2.e22 + m1.e23 * m2.e32, - m1.e21 * m2.e13 + m1.e22 * m2.e23 + m1.e23 * m2.e33, - m1.e31 * m2.e11 + m1.e32 * m2.e21 + m1.e33 * m2.e31, - m1.e31 * m2.e12 + m1.e32 * m2.e22 + m1.e33 * m2.e32, - m1.e31 * m2.e13 + m1.e32 * m2.e23 + m1.e33 * m2.e33 ); -} - -inline Matrix3x3 operator*(Matrix3x3 m, float s) -{ - return Matrix3x3( m.e11 * s, - m.e12 * s, - m.e13 * s, - m.e21 * s, - m.e22 * s, - m.e23 * s, - m.e31 * s, - m.e32 * s, - m.e33 * s); -} - -inline Matrix3x3 operator*(float s, Matrix3x3 m) -{ - return Matrix3x3( m.e11 * s, - m.e12 * s, - m.e13 * s, - m.e21 * s, - m.e22 * s, - m.e23 * s, - m.e31 * s, - m.e32 * s, - m.e33 * s); -} - -inline Vector operator*(Matrix3x3 m, Vector u) -{ - return Vector( m.e11 * u.x + m.e12 * u.y + m.e13 * u.z, - m.e21 * u.x + m.e22 * u.y + m.e23 * u.z, - m.e31 * u.x + m.e32 * u.y + m.e33 * u.z); -} - -inline Vector operator*(Vector u, Matrix3x3 m) -{ - return Vector( u.x * m.e11 + u.y * m.e21 + u.z * m.e31, - u.x * m.e12 + u.y * m.e22 + u.z * m.e32, - u.x * m.e13 + u.y * m.e23 + u.z * m.e33); -} - -//------------------------------------------------------------------------// -// Quaternion Class and Quaternion functions -//------------------------------------------------------------------------// - -class Quaternion -{ -public: - float n; // number (scalar) part - Vector v; // vector part: v.x, v.y, v.z - - Quaternion(void); - Quaternion(float e0, float e1, float e2, float e3); - - float Magnitude(void); - Vector GetVector(void); - float GetScalar(void); - Quaternion operator+=(Quaternion q); - Quaternion operator-=(Quaternion q); - Quaternion operator*=(float s); - Quaternion operator/=(float s); - Quaternion operator~(void) const { - return Quaternion(n, -v.x, -v.y, -v.z); - } -}; - -inline Quaternion operator+(Quaternion q1, Quaternion q2); -inline Quaternion operator-(Quaternion q1, Quaternion q2); -inline Quaternion operator*(Quaternion q1, Quaternion q2); -inline Quaternion operator*(Quaternion q, float s); -inline Quaternion operator*(float s, Quaternion q); -inline Quaternion operator*(Quaternion q, Vector v); -inline Quaternion operator*(Vector v, Quaternion q); -inline Quaternion operator/(Quaternion q, float s); -inline float QGetAngle(Quaternion q); -inline Vector QGetAxis(Quaternion q); -inline Quaternion QRotate(Quaternion q1, Quaternion q2); -inline Vector QVRotate(Quaternion q, Vector v); -inline Quaternion MakeQFromEulerAngles(float x, float y, float z); -inline Vector MakeEulerAnglesFromQ(Quaternion q); - - -inline Quaternion::Quaternion(void) -{ - n = 0; - v.x = 0; - v.y = 0; - v.z = 0; -} - -inline Quaternion::Quaternion(float e0, float e1, float e2, float e3) -{ - n = e0; - v.x = e1; - v.y = e2; - v.z = e3; -} - -inline float Quaternion::Magnitude(void) -{ - return (float) sqrt(n * n + v.x * v.x + v.y * v.y + v.z * v.z); -} - -inline Vector Quaternion::GetVector(void) -{ - return Vector(v.x, v.y, v.z); -} - -inline float Quaternion::GetScalar(void) -{ - return n; -} - -inline Quaternion Quaternion::operator+=(Quaternion q) -{ - n += q.n; - v.x += q.v.x; - v.y += q.v.y; - v.z += q.v.z; - return *this; -} - -inline Quaternion Quaternion::operator-=(Quaternion q) -{ - n -= q.n; - v.x -= q.v.x; - v.y -= q.v.y; - v.z -= q.v.z; - return *this; -} - -inline Quaternion Quaternion::operator*=(float s) -{ - n *= s; - v.x *= s; - v.y *= s; - v.z *= s; - return *this; -} - -inline Quaternion Quaternion::operator/=(float s) -{ - n /= s; - v.x /= s; - v.y /= s; - v.z /= s; - return *this; -} - -/*inline Quaternion Quaternion::operator~() -{ -return Quaternion(n, -v.x, -v.y, -v.z); -}*/ - -inline Quaternion operator+(Quaternion q1, Quaternion q2) -{ - return Quaternion( q1.n + q2.n, - q1.v.x + q2.v.x, - q1.v.y + q2.v.y, - q1.v.z + q2.v.z); -} - -inline Quaternion operator-(Quaternion q1, Quaternion q2) -{ - return Quaternion( q1.n - q2.n, - q1.v.x - q2.v.x, - q1.v.y - q2.v.y, - q1.v.z - q2.v.z); -} - -inline Quaternion operator*(Quaternion q1, Quaternion q2) -{ - return Quaternion( q1.n * q2.n - q1.v.x * q2.v.x - q1.v.y * q2.v.y - q1.v.z * q2.v.z, - q1.n * q2.v.x + q1.v.x * q2.n + q1.v.y * q2.v.z - q1.v.z * q2.v.y, - q1.n * q2.v.y + q1.v.y * q2.n + q1.v.z * q2.v.x - q1.v.x * q2.v.z, - q1.n * q2.v.z + q1.v.z * q2.n + q1.v.x * q2.v.y - q1.v.y * q2.v.x); -} - -inline Quaternion operator*(Quaternion q, float s) -{ - return Quaternion(q.n * s, q.v.x * s, q.v.y * s, q.v.z * s); -} - -inline Quaternion operator*(float s, Quaternion q) -{ - return Quaternion(q.n * s, q.v.x * s, q.v.y * s, q.v.z * s); -} - -inline Quaternion operator*(Quaternion q, Vector v) -{ - return Quaternion( -(q.v.x * v.x + q.v.y * v.y + q.v.z * v.z), - q.n * v.x + q.v.y * v.z - q.v.z * v.y, - q.n * v.y + q.v.z * v.x - q.v.x * v.z, - q.n * v.z + q.v.x * v.y - q.v.y * v.x); -} - -inline Quaternion operator*(Vector v, Quaternion q) -{ - return Quaternion( -(q.v.x * v.x + q.v.y * v.y + q.v.z * v.z), - q.n * v.x + q.v.z * v.y - q.v.y * v.z, - q.n * v.y + q.v.x * v.z - q.v.z * v.x, - q.n * v.z + q.v.y * v.x - q.v.x * v.y); -} - -inline Quaternion operator/(Quaternion q, float s) -{ - return Quaternion(q.n / s, q.v.x / s, q.v.y / s, q.v.z / s); -} - -inline float QGetAngle(Quaternion q) -{ - return (float) (2 * acosf(q.n)); -} - -inline Vector QGetAxis(Quaternion q) -{ - Vector v; - float m; - - v = q.GetVector(); - m = v.Magnitude(); - - if (m <= tol) - return Vector(); - else - return v / m; -} - -inline Quaternion QRotate(Quaternion q1, Quaternion q2) -{ - return q1 * q2 * (~q1); -} - -inline Vector QVRotate(Quaternion q, Vector v) -{ - Quaternion t; - - - t = q * v * (~q); - - return t.GetVector(); -} - -inline Quaternion MakeQFromEulerAngles(float x, float y, float z) -{ - Quaternion q; - double roll = DegreesToRadians(x); - double pitch = DegreesToRadians(y); - double yaw = DegreesToRadians(z); - - double cyaw, cpitch, croll, syaw, spitch, sroll; - double cyawcpitch, syawspitch, cyawspitch, syawcpitch; - - cyaw = cos(0.5f * yaw); - cpitch = cos(0.5f * pitch); - croll = cos(0.5f * roll); - syaw = sin(0.5f * yaw); - spitch = sin(0.5f * pitch); - sroll = sin(0.5f * roll); - - cyawcpitch = cyaw * cpitch; - syawspitch = syaw * spitch; - cyawspitch = cyaw * spitch; - syawcpitch = syaw * cpitch; - - q.n = (float) (cyawcpitch * croll + syawspitch * sroll); - q.v.x = (float) (cyawcpitch * sroll - syawspitch * croll); - q.v.y = (float) (cyawspitch * croll + syawcpitch * sroll); - q.v.z = (float) (syawcpitch * croll - cyawspitch * sroll); - - return q; -} - -inline Vector MakeEulerAnglesFromQ(Quaternion q) -{ - double r11, r21, r31, r32, r33; - double q00, q11, q22, q33; - double tmp; - Vector u; - - q00 = q.n * q.n; - q11 = q.v.x * q.v.x; - q22 = q.v.y * q.v.y; - q33 = q.v.z * q.v.z; - - r11 = q00 + q11 - q22 - q33; - r21 = 2 * (q.v.x * q.v.y + q.n * q.v.z); - r31 = 2 * (q.v.x * q.v.z - q.n * q.v.y); - r32 = 2 * (q.v.y * q.v.z + q.n * q.v.x); - r33 = q00 - q11 - q22 + q33; - - tmp = fabs(r31); - if (tmp > 0.999999) { - double r12 = 2 * (q.v.x * q.v.y - q.n * q.v.z); - double r13 = 2 * (q.v.x * q.v.z + q.n * q.v.y); - - u.x = RadiansToDegrees(0.0f); //roll - u.y = RadiansToDegrees((float) (-(pi / 2) * r31 / tmp)); // pitch - u.z = RadiansToDegrees((float) atan2(-r12, -r31 * r13)); // yaw - return u; - } - - u.x = RadiansToDegrees((float) atan2(r32, r33)); // roll - u.y = RadiansToDegrees((float) asinf(-r31)); // pitch - u.z = RadiansToDegrees((float) atan2(r21, r11)); // yaw - return u; - - -} - - - - - -#endif diff --git a/Source/Quaternions.cpp b/Source/Quaternions.cpp deleted file mode 100644 index ff1ad71..0000000 --- a/Source/Quaternions.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Quaternions.h" - -// Functions -quaternion Quat_Mult(quaternion q1, quaternion q2) -{ - quaternion QResult; - float a, b, c, d, e, f, g, h; - a = (q1.w + q1.x) * (q2.w + q2.x); - b = (q1.z - q1.y) * (q2.y - q2.z); - c = (q1.w - q1.x) * (q2.y + q2.z); - d = (q1.y + q1.z) * (q2.w - q2.x); - e = (q1.x + q1.z) * (q2.x + q2.y); - f = (q1.x - q1.z) * (q2.x - q2.y); - g = (q1.w + q1.y) * (q2.w - q2.z); - h = (q1.w - q1.y) * (q2.w + q2.z); - QResult.w = b + (-e - f + g + h) / 2; - QResult.x = a - (e + f + g + h) / 2; - QResult.y = c + (e - f + g - h) / 2; - QResult.z = d + (e - f - g + h) / 2; - return QResult; -} - - - -quaternion To_Quat(Matrix_t m) -{ - // From Jason Shankel, (C) 2000. - static quaternion Quat; - - static double Tr = m[0][0] + m[1][1] + m[2][2] + 1.0, fourD; - static double q[4]; - - static int i, j, k; - if (Tr >= 1.0) { - fourD = 2.0 * fast_sqrt(Tr); - q[3] = fourD / 4.0; - q[0] = (m[2][1] - m[1][2]) / fourD; - q[1] = (m[0][2] - m[2][0]) / fourD; - q[2] = (m[1][0] - m[0][1]) / fourD; - } else { - if (m[0][0] > m[1][1]) { - i = 0; - } else { - i = 1; - } - if (m[2][2] > m[i][i]) { - i = 2; - } - j = (i + 1) % 3; - k = (j + 1) % 3; - fourD = 2.0 * fast_sqrt(m[i][i] - m[j][j] - m[k][k] + 1.0); - q[i] = fourD / 4.0; - q[j] = (m[j][i] + m[i][j]) / fourD; - q[k] = (m[k][i] + m[i][k]) / fourD; - q[3] = (m[j][k] - m[k][j]) / fourD; - } - - Quat.x = q[0]; - Quat.y = q[1]; - Quat.z = q[2]; - Quat.w = q[3]; - return Quat; -} -void Quat_2_Matrix(quaternion Quat, Matrix_t m) -{ - // From the GLVelocity site (http://glvelocity.gamedev.net) - float fW = Quat.w; - float fX = Quat.x; - float fY = Quat.y; - float fZ = Quat.z; - float fXX = fX * fX; - float fYY = fY * fY; - float fZZ = fZ * fZ; - m[0][0] = 1.0f - 2.0f * (fYY + fZZ); - m[1][0] = 2.0f * (fX * fY + fW * fZ); - m[2][0] = 2.0f * (fX * fZ - fW * fY); - m[3][0] = 0.0f; - m[0][1] = 2.0f * (fX * fY - fW * fZ); - m[1][1] = 1.0f - 2.0f * (fXX + fZZ); - m[2][1] = 2.0f * (fY * fZ + fW * fX); - m[3][1] = 0.0f; - m[0][2] = 2.0f * (fX * fZ + fW * fY); - m[1][2] = 2.0f * (fX * fZ - fW * fX); - m[2][2] = 1.0f - 2.0f * (fXX + fYY); - m[3][2] = 0.0f; - m[0][3] = 0.0f; - m[1][3] = 0.0f; - m[2][3] = 0.0f; - m[3][3] = 1.0f; -} -quaternion To_Quat(angle_axis Ang_Ax) -{ - // From the Quaternion Powers article on gamedev.net - static quaternion Quat; - - Quat.x = Ang_Ax.x * sin(Ang_Ax.angle / 2); - Quat.y = Ang_Ax.y * sin(Ang_Ax.angle / 2); - Quat.z = Ang_Ax.z * sin(Ang_Ax.angle / 2); - Quat.w = cos(Ang_Ax.angle / 2); - return Quat; -} -angle_axis Quat_2_AA(quaternion Quat) -{ - static angle_axis Ang_Ax; - static float scale, tw; - tw = (float)acosf(Quat.w) * 2; - scale = (float)sin(tw / 2.0); - Ang_Ax.x = Quat.x / scale; - Ang_Ax.y = Quat.y / scale; - Ang_Ax.z = Quat.z / scale; - - Ang_Ax.angle = 2.0 * acosf(Quat.w) / (float)PI * 180; - return Ang_Ax; -} - -quaternion To_Quat(int In_Degrees, euler Euler) -{ - // From the gamasutra quaternion article - static quaternion Quat; - static float cr, cp, cy, sr, sp, sy, cpcy, spsy; - //If we are in Degree mode, convert to Radians - if (In_Degrees) { - Euler.x = Euler.x * (float)PI / 180; - Euler.y = Euler.y * (float)PI / 180; - Euler.z = Euler.z * (float)PI / 180; - } - //Calculate trig identities - //Formerly roll, pitch, yaw - cr = float(cos(Euler.x / 2)); - cp = float(cos(Euler.y / 2)); - cy = float(cos(Euler.z / 2)); - sr = float(sin(Euler.x / 2)); - sp = float(sin(Euler.y / 2)); - sy = float(sin(Euler.z / 2)); - - cpcy = cp * cy; - spsy = sp * sy; - Quat.w = cr * cpcy + sr * spsy; - Quat.x = sr * cpcy - cr * spsy; - Quat.y = cr * sp * cy + sr * cp * sy; - Quat.z = cr * cp * sy - sr * sp * cy; - - return Quat; -} - -quaternion QNormalize(quaternion Quat) -{ - static float norm; - norm = Quat.x * Quat.x + - Quat.y * Quat.y + - Quat.z * Quat.z + - Quat.w * Quat.w; - Quat.x = float(Quat.x / norm); - Quat.y = float(Quat.y / norm); - Quat.z = float(Quat.z / norm); - Quat.w = float(Quat.w / norm); - return Quat; -} - -XYZ Quat2Vector(quaternion Quat) -{ - QNormalize(Quat); - - float fW = Quat.w; - float fX = Quat.x; - float fY = Quat.y; - float fZ = Quat.z; - - XYZ tempvec; - - tempvec.x = 2.0f * (fX * fZ - fW * fY); - tempvec.y = 2.0f * (fY * fZ + fW * fX); - tempvec.z = 1.0f - 2.0f * (fX * fX + fY * fY); - - return tempvec; -} - -bool PointInTriangle(Vector *p, Vector normal, float p11, float p12, float p13, float p21, float p22, float p23, float p31, float p32, float p33) -{ - static float u0, u1, u2; - static float v0, v1, v2; - static float a, b; - static float max; - static int i, j; - static bool bInter; - static float pointv[3]; - static float p1v[3]; - static float p2v[3]; - static float p3v[3]; - static float normalv[3]; - - bInter = 0; - - pointv[0] = p->x; - pointv[1] = p->y; - pointv[2] = p->z; - - - p1v[0] = p11; - p1v[1] = p12; - p1v[2] = p13; - - p2v[0] = p21; - p2v[1] = p22; - p2v[2] = p23; - - p3v[0] = p31; - p3v[1] = p32; - p3v[2] = p33; - - normalv[0] = normal.x; - normalv[1] = normal.y; - normalv[2] = normal.z; - -#define ABS(X) (((X)<0.f)?-(X):(X) ) -#define MAX(A, B) (((A)<(B))?(B):(A)) - max = MAX(MAX(ABS(normalv[0]), ABS(normalv[1])), ABS(normalv[2])); -#undef MAX - if (max == ABS(normalv[0])) { - i = 1; // y, z - j = 2; - } - if (max == ABS(normalv[1])) { - i = 0; // x, z - j = 2; - } - if (max == ABS(normalv[2])) { - i = 0; // x, y - j = 1; - } -#undef ABS - - u0 = pointv[i] - p1v[i]; - v0 = pointv[j] - p1v[j]; - u1 = p2v[i] - p1v[i]; - v1 = p2v[j] - p1v[j]; - u2 = p3v[i] - p1v[i]; - v2 = p3v[j] - p1v[j]; - - if (u1 > -1.0e-05f && u1 < 1.0e-05f) { // == 0.0f) - b = u0 / u2; - if (0.0f <= b && b <= 1.0f) { - a = (v0 - b * v2) / v1; - if ((a >= 0.0f) && (( a + b ) <= 1.0f)) - bInter = 1; - } - } else { - b = (v0 * u1 - u0 * v1) / (v2 * u1 - u2 * v1); - if (0.0f <= b && b <= 1.0f) { - a = (u0 - b * u2) / u1; - if ((a >= 0.0f) && (( a + b ) <= 1.0f )) - bInter = 1; - } - } - - return bInter; -} - -bool LineFacet(Vector p1, Vector p2, Vector pa, Vector pb, Vector pc, Vector *p) -{ - static float d; - static float denom, mu; - static Vector n; - - //Calculate the parameters for the plane - n.x = (pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y); - n.y = (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z); - n.z = (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x); - n.Normalize(); - d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; - - //Calculate the position on the line that intersects the plane - denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); - if (fabs(denom) < 0.0000001) // Line and plane don't intersect - return 0; - mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; - p->x = p1.x + mu * (p2.x - p1.x); - p->y = p1.y + mu * (p2.y - p1.y); - p->z = p1.z + mu * (p2.z - p1.z); - if (mu < 0 || mu > 1) // Intersection not along line segment - return 0; - - if (!PointInTriangle( p, n, pa.x, pa.y, pa.z, pb.x, pb.y, pb.z, pc.x, pc.y, pc.z)) { - return 0; - } - - return 1; -} - -bool PointInTriangle(XYZ *p, XYZ normal, XYZ *p1, XYZ *p2, XYZ *p3) -{ - static float u0, u1, u2; - static float v0, v1, v2; - static float a, b; - static float max; - static int i, j; - static bool bInter = 0; - static float pointv[3]; - static float p1v[3]; - static float p2v[3]; - static float p3v[3]; - static float normalv[3]; - - bInter = 0; - - pointv[0] = p->x; - pointv[1] = p->y; - pointv[2] = p->z; - - - p1v[0] = p1->x; - p1v[1] = p1->y; - p1v[2] = p1->z; - - p2v[0] = p2->x; - p2v[1] = p2->y; - p2v[2] = p2->z; - - p3v[0] = p3->x; - p3v[1] = p3->y; - p3v[2] = p3->z; - - normalv[0] = normal.x; - normalv[1] = normal.y; - normalv[2] = normal.z; - -#define ABS(X) (((X)<0.f)?-(X):(X) ) -#define MAX(A, B) (((A)<(B))?(B):(A)) - max = MAX(MAX(ABS(normalv[0]), ABS(normalv[1])), ABS(normalv[2])); -#undef MAX - if (max == ABS(normalv[0])) { - i = 1; // y, z - j = 2; - } - if (max == ABS(normalv[1])) { - i = 0; // x, z - j = 2; - } - if (max == ABS(normalv[2])) { - i = 0; // x, y - j = 1; - } -#undef ABS - - u0 = pointv[i] - p1v[i]; - v0 = pointv[j] - p1v[j]; - u1 = p2v[i] - p1v[i]; - v1 = p2v[j] - p1v[j]; - u2 = p3v[i] - p1v[i]; - v2 = p3v[j] - p1v[j]; - - if (u1 > -1.0e-05f && u1 < 1.0e-05f) { // == 0.0f) - b = u0 / u2; - if (0.0f <= b && b <= 1.0f) { - a = (v0 - b * v2) / v1; - if ((a >= 0.0f) && (( a + b ) <= 1.0f)) - bInter = 1; - } - } else { - b = (v0 * u1 - u0 * v1) / (v2 * u1 - u2 * v1); - if (0.0f <= b && b <= 1.0f) { - a = (u0 - b * u2) / u1; - if ((a >= 0.0f) && (( a + b ) <= 1.0f )) - bInter = 1; - } - } - - return bInter; -} - -bool LineFacet(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p) -{ - static float d; - static float denom, mu; - static XYZ n; - - //Calculate the parameters for the plane - n.x = (pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y); - n.y = (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z); - n.z = (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x); - Normalise(&n); - d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; - - //Calculate the position on the line that intersects the plane - denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); - if (fabs(denom) < 0.0000001) // Line and plane don't intersect - return 0; - mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; - p->x = p1.x + mu * (p2.x - p1.x); - p->y = p1.y + mu * (p2.y - p1.y); - p->z = p1.z + mu * (p2.z - p1.z); - if (mu < 0 || mu > 1) // Intersection not along line segment - return 0; - - if (!PointInTriangle( p, n, &pa, &pb, &pc)) { - return 0; - } - - return 1; -} - -float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p) -{ - static float d; - static float denom, mu; - static XYZ n; - - //Calculate the parameters for the plane - n.x = (pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y); - n.y = (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z); - n.z = (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x); - Normalise(&n); - d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; - - //Calculate the position on the line that intersects the plane - denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); - if (fabs(denom) < 0.0000001) // Line and plane don't intersect - return 0; - mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; - p->x = p1.x + mu * (p2.x - p1.x); - p->y = p1.y + mu * (p2.y - p1.y); - p->z = p1.z + mu * (p2.z - p1.z); - if (mu < 0 || mu > 1) // Intersection not along line segment - return 0; - - if (!PointInTriangle( p, n, &pa, &pb, &pc)) { - return 0; - } - - return 1; -} - -float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ n, XYZ *p) -{ - static float d; - static float denom, mu; - - //Calculate the parameters for the plane - d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; - - //Calculate the position on the line that intersects the plane - denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); - if (fabs(denom) < 0.0000001) // Line and plane don't intersect - return 0; - mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; - p->x = p1.x + mu * (p2.x - p1.x); - p->y = p1.y + mu * (p2.y - p1.y); - p->z = p1.z + mu * (p2.z - p1.z); - if (mu < 0 || mu > 1) // Intersection not along line segment - return 0; - - if (!PointInTriangle( p, n, &pa, &pb, &pc)) { - return 0; - } - return 1; -} - -float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *p) -{ - static float d; - static float denom, mu; - static XYZ n; - - //Calculate the parameters for the plane - n.x = (pb->y - pa->y) * (pc->z - pa->z) - (pb->z - pa->z) * (pc->y - pa->y); - n.y = (pb->z - pa->z) * (pc->x - pa->x) - (pb->x - pa->x) * (pc->z - pa->z); - n.z = (pb->x - pa->x) * (pc->y - pa->y) - (pb->y - pa->y) * (pc->x - pa->x); - Normalise(&n); - d = - n.x * pa->x - n.y * pa->y - n.z * pa->z; - - - //Calculate the position on the line that intersects the plane - denom = n.x * (p2->x - p1->x) + n.y * (p2->y - p1->y) + n.z * (p2->z - p1->z); - if (fabs(denom) < 0.0000001) // Line and plane don't intersect - return 0; - mu = - (d + n.x * p1->x + n.y * p1->y + n.z * p1->z) / denom; - p->x = p1->x + mu * (p2->x - p1->x); - p->y = p1->y + mu * (p2->y - p1->y); - p->z = p1->z + mu * (p2->z - p1->z); - if (mu < 0 || mu > 1) // Intersection not along line segment - return 0; - - if (!PointInTriangle( p, n, pa, pb, pc)) { - return 0; - } - return 1; -} - -float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *n, XYZ *p) -{ - static float d; - static float denom, mu; - - //Calculate the parameters for the plane - d = - n->x * pa->x - n->y * pa->y - n->z * pa->z; - - //Calculate the position on the line that intersects the plane - denom = n->x * (p2->x - p1->x) + n->y * (p2->y - p1->y) + n->z * (p2->z - p1->z); - if (fabs(denom) < 0.0000001) // Line and plane don't intersect - return 0; - mu = - (d + n->x * p1->x + n->y * p1->y + n->z * p1->z) / denom; - p->x = p1->x + mu * (p2->x - p1->x); - p->y = p1->y + mu * (p2->y - p1->y); - p->z = p1->z + mu * (p2->z - p1->z); - if (mu < 0 || mu > 1) // Intersection not along line segment - return 0; - - if (!PointInTriangle( p, *n, pa, pb, pc)) { - return 0; - } - return 1; -} - - diff --git a/Source/Quaternions.h b/Source/Quaternions.h deleted file mode 100644 index f090ef9..0000000 --- a/Source/Quaternions.h +++ /dev/null @@ -1,499 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - - -#ifndef _QUATERNIONS_H_ -#define _QUATERNIONS_H_ - -#include "math.h" -#include "PhysicsMath.h" -#include "gamegl.h" - -/**> Quaternion Structures <**/ -#define PI 3.14159265355555897932384626 -#define RADIANS 0 -#define DEGREES 1 -#define deg2rad .0174532925 - -//using namespace std; -typedef float Matrix_t [4][4]; -struct euler { - float x, y, z; -}; -struct angle_axis { - float x, y, z, angle; -}; -struct quaternion { - float x, y, z, w; -}; - -class XYZ -{ -public: - float x; - float y; - float z; - XYZ() : x(0.0f), y(0.0f), z(0.0f) {} - inline XYZ operator+(XYZ add); - inline XYZ operator-(XYZ add); - inline XYZ operator*(float add); - inline XYZ operator*(XYZ add); - inline XYZ operator/(float add); - inline void operator+=(XYZ add); - inline void operator-=(XYZ add); - inline void operator*=(float add); - inline void operator*=(XYZ add); - inline void operator/=(float add); - inline void operator=(float add); - inline void vec(Vector add); - inline bool operator==(XYZ add); -}; - -/*********************> Quaternion Function definition <********/ -quaternion To_Quat(int Degree_Flag, euler Euler); -quaternion To_Quat(angle_axis Ang_Ax); -quaternion To_Quat(Matrix_t m); -angle_axis Quat_2_AA(quaternion Quat); -void Quat_2_Matrix(quaternion Quat, Matrix_t m); -quaternion Normalize(quaternion Quat); -quaternion Quat_Mult(quaternion q1, quaternion q2); -quaternion QNormalize(quaternion Quat); -XYZ Quat2Vector(quaternion Quat); - -inline void CrossProduct(XYZ *P, XYZ *Q, XYZ *V); -inline void CrossProduct(XYZ P, XYZ Q, XYZ *V); -inline void Normalise(XYZ *vectory); -inline float normaldotproduct(XYZ point1, XYZ point2); -inline float fast_sqrt (register float arg); -bool PointInTriangle(XYZ *p, XYZ normal, XYZ *p1, XYZ *p2, XYZ *p3); -bool LineFacet(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p); -float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ *p); -float LineFacetd(XYZ p1, XYZ p2, XYZ pa, XYZ pb, XYZ pc, XYZ n, XYZ *p); -float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *n, XYZ *p); -float LineFacetd(XYZ *p1, XYZ *p2, XYZ *pa, XYZ *pb, XYZ *pc, XYZ *p); -bool PointInTriangle(Vector *p, Vector normal, float p11, float p12, float p13, float p21, float p22, float p23, float p31, float p32, float p33); -bool LineFacet(Vector p1, Vector p2, Vector pa, Vector pb, Vector pc, Vector *p); -inline void ReflectVector(XYZ *vel, const XYZ *n); -inline void ReflectVector(XYZ *vel, const XYZ &n); -inline XYZ DoRotation(XYZ thePoint, float xang, float yang, float zang); -inline XYZ DoRotationRadian(XYZ thePoint, float xang, float yang, float zang); -inline float findDistance(XYZ *point1, XYZ *point2); -inline float findLength(XYZ *point1); -inline float findLengthfast(XYZ *point1); -inline float distsq(XYZ *point1, XYZ *point2); -inline float distsq(XYZ point1, XYZ point2); -inline float distsqflat(XYZ *point1, XYZ *point2); -inline float dotproduct(const XYZ *point1, const XYZ *point2); -bool sphere_line_intersection ( - float x1, float y1 , float z1, - float x2, float y2 , float z2, - float x3, float y3 , float z3, float r ); -bool sphere_line_intersection ( - XYZ *p1, XYZ *p2, XYZ *p3, float *r ); -inline bool DistancePointLine( XYZ *Point, XYZ *LineStart, XYZ *LineEnd, float *Distance, XYZ *Intersection ); - - -inline void Normalise(XYZ *vectory) -{ - static float d; - d = fast_sqrt(vectory->x * vectory->x + vectory->y * vectory->y + vectory->z * vectory->z); - if (d == 0) { - return; - } - vectory->x /= d; - vectory->y /= d; - vectory->z /= d; -} - -inline XYZ XYZ::operator+(XYZ add) -{ - static XYZ ne; - ne = add; - ne.x += x; - ne.y += y; - ne.z += z; - return ne; -} - -inline XYZ XYZ::operator-(XYZ add) -{ - static XYZ ne; - ne = add; - ne.x = x - ne.x; - ne.y = y - ne.y; - ne.z = z - ne.z; - return ne; -} - -inline XYZ XYZ::operator*(float add) -{ - static XYZ ne; - ne.x = x * add; - ne.y = y * add; - ne.z = z * add; - return ne; -} - -inline XYZ XYZ::operator*(XYZ add) -{ - static XYZ ne; - ne.x = x * add.x; - ne.y = y * add.y; - ne.z = z * add.z; - return ne; -} - -inline XYZ XYZ::operator/(float add) -{ - static XYZ ne; - ne.x = x / add; - ne.y = y / add; - ne.z = z / add; - return ne; -} - -inline void XYZ::operator+=(XYZ add) -{ - x += add.x; - y += add.y; - z += add.z; -} - -inline void XYZ::operator-=(XYZ add) -{ - x = x - add.x; - y = y - add.y; - z = z - add.z; -} - -inline void XYZ::operator*=(float add) -{ - x = x * add; - y = y * add; - z = z * add; -} - -inline void XYZ::operator*=(XYZ add) -{ - x = x * add.x; - y = y * add.y; - z = z * add.z; -} - -inline void XYZ::operator/=(float add) -{ - x = x / add; - y = y / add; - z = z / add; -} - -inline void XYZ::operator=(float add) -{ - x = add; - y = add; - z = add; -} - -inline void XYZ::vec(Vector add) -{ - x = add.x; - y = add.y; - z = add.z; -} - -inline bool XYZ::operator==(XYZ add) -{ - if (x == add.x && y == add.y && z == add.z) - return 1; - return 0; -} - -inline void CrossProduct(XYZ *P, XYZ *Q, XYZ *V) -{ - V->x = P->y * Q->z - P->z * Q->y; - V->y = P->z * Q->x - P->x * Q->z; - V->z = P->x * Q->y - P->y * Q->x; -} - -inline void CrossProduct(XYZ P, XYZ Q, XYZ *V) -{ - V->x = P.y * Q.z - P.z * Q.y; - V->y = P.z * Q.x - P.x * Q.z; - V->z = P.x * Q.y - P.y * Q.x; -} - -inline float fast_sqrt (register float arg) -{ - return sqrtf(arg); -} - -inline float normaldotproduct(XYZ point1, XYZ point2) -{ - static GLfloat returnvalue; - Normalise(&point1); - Normalise(&point2); - returnvalue = (point1.x * point2.x + point1.y * point2.y + point1.z * point2.z); - return returnvalue; -} - -inline void ReflectVector(XYZ *vel, const XYZ *n) -{ - ReflectVector(vel, *n); -} - -inline void ReflectVector(XYZ *vel, const XYZ &n) -{ - static XYZ vn; - static XYZ vt; - static float dotprod; - - dotprod = dotproduct(&n, vel); - vn.x = n.x * dotprod; - vn.y = n.y * dotprod; - vn.z = n.z * dotprod; - - vt.x = vel->x - vn.x; - vt.y = vel->y - vn.y; - vt.z = vel->z - vn.z; - - vel->x = vt.x - vn.x; - vel->y = vt.y - vn.y; - vel->z = vt.z - vn.z; -} - -inline float dotproduct(const XYZ *point1, const XYZ *point2) -{ - static GLfloat returnvalue; - returnvalue = (point1->x * point2->x + point1->y * point2->y + point1->z * point2->z); - return returnvalue; -} - -inline float findDistance(XYZ *point1, XYZ *point2) -{ - return(fast_sqrt((point1->x - point2->x) * (point1->x - point2->x) + (point1->y - point2->y) * (point1->y - point2->y) + (point1->z - point2->z) * (point1->z - point2->z))); -} - -inline float findLength(XYZ *point1) -{ - return(fast_sqrt((point1->x) * (point1->x) + (point1->y) * (point1->y) + (point1->z) * (point1->z))); -} - - -inline float findLengthfast(XYZ *point1) -{ - return((point1->x) * (point1->x) + (point1->y) * (point1->y) + (point1->z) * (point1->z)); -} - -inline float distsq(XYZ *point1, XYZ *point2) -{ - return((point1->x - point2->x) * (point1->x - point2->x) + (point1->y - point2->y) * (point1->y - point2->y) + (point1->z - point2->z) * (point1->z - point2->z)); -} - -inline float distsq(XYZ point1, XYZ point2) -{ - return((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y) + (point1.z - point2.z) * (point1.z - point2.z)); -} - -inline float distsqflat(XYZ *point1, XYZ *point2) -{ - return((point1->x - point2->x) * (point1->x - point2->x) + (point1->z - point2->z) * (point1->z - point2->z)); -} - -inline XYZ DoRotation(XYZ thePoint, float xang, float yang, float zang) -{ - static XYZ newpoint; - if (xang) { - xang *= 6.283185f; - xang /= 360; - } - if (yang) { - yang *= 6.283185f; - yang /= 360; - } - if (zang) { - zang *= 6.283185f; - zang /= 360; - } - - - if (yang) { - newpoint.z = thePoint.z * cosf(yang) - thePoint.x * sinf(yang); - newpoint.x = thePoint.z * sinf(yang) + thePoint.x * cosf(yang); - thePoint.z = newpoint.z; - thePoint.x = newpoint.x; - } - - if (zang) { - newpoint.x = thePoint.x * cosf(zang) - thePoint.y * sinf(zang); - newpoint.y = thePoint.y * cosf(zang) + thePoint.x * sinf(zang); - thePoint.x = newpoint.x; - thePoint.y = newpoint.y; - } - - if (xang) { - newpoint.y = thePoint.y * cosf(xang) - thePoint.z * sinf(xang); - newpoint.z = thePoint.y * sinf(xang) + thePoint.z * cosf(xang); - thePoint.z = newpoint.z; - thePoint.y = newpoint.y; - } - - return thePoint; -} - -inline float square( float f ) -{ - return (f * f) ; -} - -inline bool sphere_line_intersection ( - float x1, float y1 , float z1, - float x2, float y2 , float z2, - float x3, float y3 , float z3, float r ) -{ - - // x1,y1,z1 P1 coordinates (point of line) - // x2,y2,z2 P2 coordinates (point of line) - // x3,y3,z3, r P3 coordinates and radius (sphere) - // x,y,z intersection coordinates - // - // This function returns a pointer array which first index indicates - // the number of intersection point, followed by coordinate pairs. - - //~ static float x , y , z; - static float a, b, c, /*mu,*/ i ; - - if (x1 > x3 + r && x2 > x3 + r) return(0); - if (x1 < x3 - r && x2 < x3 - r) return(0); - if (y1 > y3 + r && y2 > y3 + r) return(0); - if (y1 < y3 - r && y2 < y3 - r) return(0); - if (z1 > z3 + r && z2 > z3 + r) return(0); - if (z1 < z3 - r && z2 < z3 - r) return(0); - a = square(x2 - x1) + square(y2 - y1) + square(z2 - z1); - b = 2 * ( (x2 - x1) * (x1 - x3) - + (y2 - y1) * (y1 - y3) - + (z2 - z1) * (z1 - z3) ) ; - c = square(x3) + square(y3) + - square(z3) + square(x1) + - square(y1) + square(z1) - - 2 * ( x3 * x1 + y3 * y1 + z3 * z1 ) - square(r) ; - i = b * b - 4 * a * c ; - - if ( i < 0.0 ) { - // no intersection - return(0); - } - return(1); -} - -inline bool sphere_line_intersection ( - XYZ *p1, XYZ *p2, XYZ *p3, float *r ) -{ - - // x1,p1->y,p1->z P1 coordinates (point of line) - // p2->x,p2->y,p2->z P2 coordinates (point of line) - // p3->x,p3->y,p3->z, r P3 coordinates and radius (sphere) - // x,y,z intersection coordinates - // - // This function returns a pointer array which first index indicates - // the number of intersection point, followed by coordinate pairs. - - //~ static float x , y , z; - static float a, b, c, /*mu,*/ i ; - - if (p1->x > p3->x + *r && p2->x > p3->x + *r) return(0); - if (p1->x < p3->x - *r && p2->x < p3->x - *r) return(0); - if (p1->y > p3->y + *r && p2->y > p3->y + *r) return(0); - if (p1->y < p3->y - *r && p2->y < p3->y - *r) return(0); - if (p1->z > p3->z + *r && p2->z > p3->z + *r) return(0); - if (p1->z < p3->z - *r && p2->z < p3->z - *r) return(0); - a = square(p2->x - p1->x) + square(p2->y - p1->y) + square(p2->z - p1->z); - b = 2 * ( (p2->x - p1->x) * (p1->x - p3->x) - + (p2->y - p1->y) * (p1->y - p3->y) - + (p2->z - p1->z) * (p1->z - p3->z) ) ; - c = square(p3->x) + square(p3->y) + - square(p3->z) + square(p1->x) + - square(p1->y) + square(p1->z) - - 2 * ( p3->x * p1->x + p3->y * p1->y + p3->z * p1->z ) - square(*r) ; - i = b * b - 4 * a * c ; - - if ( i < 0.0 ) { - // no intersection - return(0); - } - return(1); -} - -inline XYZ DoRotationRadian(XYZ thePoint, float xang, float yang, float zang) -{ - static XYZ newpoint; - static XYZ oldpoint; - - oldpoint = thePoint; - - if (yang != 0) { - newpoint.z = oldpoint.z * cosf(yang) - oldpoint.x * sinf(yang); - newpoint.x = oldpoint.z * sinf(yang) + oldpoint.x * cosf(yang); - oldpoint.z = newpoint.z; - oldpoint.x = newpoint.x; - } - - if (zang != 0) { - newpoint.x = oldpoint.x * cosf(zang) - oldpoint.y * sinf(zang); - newpoint.y = oldpoint.y * cosf(zang) + oldpoint.x * sinf(zang); - oldpoint.x = newpoint.x; - oldpoint.y = newpoint.y; - } - - if (xang != 0) { - newpoint.y = oldpoint.y * cosf(xang) - oldpoint.z * sinf(xang); - newpoint.z = oldpoint.y * sinf(xang) + oldpoint.z * cosf(xang); - oldpoint.z = newpoint.z; - oldpoint.y = newpoint.y; - } - - return oldpoint; - -} - -inline bool DistancePointLine( XYZ *Point, XYZ *LineStart, XYZ *LineEnd, float *Distance, XYZ *Intersection ) -{ - float LineMag; - float U; - - LineMag = findDistance( LineEnd, LineStart ); - - U = ( ( ( Point->x - LineStart->x ) * ( LineEnd->x - LineStart->x ) ) + - ( ( Point->y - LineStart->y ) * ( LineEnd->y - LineStart->y ) ) + - ( ( Point->z - LineStart->z ) * ( LineEnd->z - LineStart->z ) ) ) / - ( LineMag * LineMag ); - - if ( U < 0.0f || U > 1.0f ) - return 0; // closest point does not fall within the line segment - - Intersection->x = LineStart->x + U * ( LineEnd->x - LineStart->x ); - Intersection->y = LineStart->y + U * ( LineEnd->y - LineStart->y ); - Intersection->z = LineStart->z + U * ( LineEnd->z - LineStart->z ); - - *Distance = findDistance( Point, Intersection ); - - return 1; -} - -#endif diff --git a/Source/Random.h b/Source/Random.h deleted file mode 100644 index 096cedc..0000000 --- a/Source/Random.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _RANDOM_H_ -#define _RANDOM_H_ - -#include - -static inline short Random() -{ - return rand(); -} - -#endif diff --git a/Source/Settings.cpp b/Source/Settings.cpp deleted file mode 100644 index eca9efd..0000000 --- a/Source/Settings.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Settings.h" -#include "Game.h" -#include "Input.h" -#include "Utils/Folders.h" - -using namespace Game; - -void DefaultSettings() -{ - detail = 2; - ismotionblur = 1; - usermousesensitivity = 1; - newscreenwidth = kContextWidth = 1024; - newscreenheight = kContextHeight = 768; - fullscreen = 0; - floatjump = 0; - autoslomo = 1; - decals = 1; - invertmouse = 0; - bloodtoggle = 0; - foliage = 1; - musictoggle = 1; - trilinear = 1; - gamespeed = 1; - damageeffects = 0; - texttoggle = 1; - alwaysblur = 0; - showpoints = 0; - showdamagebar = 0; - immediate = 0; - velocityblur = 0; - volume = 0.8f; - ambientsound = 1; - devtools = 0; - - crouchkey = SDL_SCANCODE_LSHIFT; - jumpkey = SDL_SCANCODE_SPACE; - leftkey = SDL_SCANCODE_A; - forwardkey = SDL_SCANCODE_W; - backkey = SDL_SCANCODE_S; - rightkey = SDL_SCANCODE_D; - drawkey = SDL_SCANCODE_E; - throwkey = SDL_SCANCODE_Q; - attackkey = MOUSEBUTTON1; - consolekey = SDL_SCANCODE_GRAVE; - - newdetail = detail; -} - -void SaveSettings() -{ - if (newdetail < 0) - newdetail = 0; - if (newdetail > 2) - newdetail = 2; - if (newscreenwidth > 3000) - newscreenwidth = screenwidth; - if (newscreenwidth < 0) - newscreenwidth = screenwidth; - if (newscreenheight > 3000) - newscreenheight = screenheight; - if (newscreenheight < 0) - newscreenheight = screenheight; - errno = 0; - ofstream opstream(Folders::getConfigFilePath()); - if (opstream.fail()) { - perror(("Couldn't save config file " + Folders::getConfigFilePath()).c_str()); - return; - } - opstream << "Screenwidth:\n"; - opstream << newscreenwidth; - opstream << "\nScreenheight:\n"; - opstream << newscreenheight; - opstream << "\nFullscreen:\n"; - opstream << fullscreen; - opstream << "\nMouse sensitivity:\n"; - opstream << usermousesensitivity; - opstream << "\nBlur(0,1):\n"; - opstream << ismotionblur; - opstream << "\nOverall Detail(0,1,2) higher=better:\n"; - opstream << newdetail; - opstream << "\nFloating jump:\n"; - opstream << floatjump; - opstream << "\nMouse jump:\n"; - opstream << mousejump; - opstream << "\nAmbient sound:\n"; - opstream << ambientsound; - opstream << "\nBlood (0,1,2):\n"; - opstream << bloodtoggle; - opstream << "\nAuto slomo:\n"; - opstream << autoslomo; - opstream << "\nFoliage:\n"; - opstream << foliage; - opstream << "\nMusic:\n"; - opstream << musictoggle; - opstream << "\nTrilinear:\n"; - opstream << trilinear; - opstream << "\nDecals(shadows,blood puddles,etc):\n"; - opstream << decals; - opstream << "\nInvert mouse:\n"; - opstream << invertmouse; - opstream << "\nGamespeed:\n"; - if (oldgamespeed == 0) - oldgamespeed = 1; - opstream << oldgamespeed; - opstream << "\nDamage effects(blackout, doublevision):\n"; - opstream << damageeffects; - opstream << "\nText:\n"; - opstream << texttoggle; - opstream << "\nShow Points:\n"; - opstream << showpoints; - opstream << "\nAlways Blur:\n"; - opstream << alwaysblur; - opstream << "\nImmediate mode (turn on on G5):\n"; - opstream << immediate; - opstream << "\nVelocity blur:\n"; - opstream << velocityblur; - opstream << "\nVolume:\n"; - opstream << volume; - opstream << "\nForward key:\n"; - opstream << forwardkey; - opstream << "\nBack key:\n"; - opstream << backkey; - opstream << "\nLeft key:\n"; - opstream << leftkey; - opstream << "\nRight key:\n"; - opstream << rightkey; - opstream << "\nJump key:\n"; - opstream << jumpkey; - opstream << "\nCrouch key:\n"; - opstream << crouchkey; - opstream << "\nDraw key:\n"; - opstream << drawkey; - opstream << "\nThrow key:\n"; - opstream << throwkey; - opstream << "\nAttack key:\n"; - opstream << attackkey; - opstream << "\nConsole key:\n"; - opstream << consolekey; - opstream << "\nDamage bar:\n"; - opstream << showdamagebar; - opstream << "\nStereoMode:\n"; - opstream << stereomode; - opstream << "\nStereoSeparation:\n"; - opstream << stereoseparation; - opstream << "\nStereoReverse:\n"; - opstream << stereoreverse; - opstream << "\n"; - opstream.close(); -} - -bool LoadSettings() -{ - errno = 0; - ifstream ipstream(Folders::getConfigFilePath(), std::ios::in); - if ( ipstream.fail() ) { - perror(("Couldn't read config file " + Folders::getConfigFilePath()).c_str()); - return false; - } - char setting[256]; - char string[256]; - - printf("Loading config\n"); - while (!ipstream.eof()) { - ipstream.getline( setting, sizeof(setting) ); - - // skip blank lines - // assume lines starting with spaces are all blank - if ( strlen(setting) == 0 || setting[0] == ' ' || setting[0] == '\t') - continue; - //~ printf("setting : %s\n",setting); - - if ( ipstream.eof() || ipstream.fail() ) { - fprintf(stderr, "Error reading config file: Got setting name '%s', but value can't be read\n", setting); - ipstream.close(); - return false; - } - - - if ( !strncmp(setting, "Screenwidth", 11) ) { - ipstream >> kContextWidth; - } else if ( !strncmp(setting, "Screenheight", 12) ) { - ipstream >> kContextHeight; - } else if ( !strncmp(setting, "Fullscreen", 10) ) { - ipstream >> fullscreen; - } else if ( !strncmp(setting, "Mouse sensitivity", 17) ) { - ipstream >> usermousesensitivity; - } else if ( !strncmp(setting, "Blur", 4) ) { - ipstream >> ismotionblur; - } else if ( !strncmp(setting, "Overall Detail", 14) ) { - ipstream >> detail; - } else if ( !strncmp(setting, "Floating jump", 13) ) { - ipstream >> floatjump; - } else if ( !strncmp(setting, "Mouse jump", 10) ) { - ipstream >> mousejump; - } else if ( !strncmp(setting, "Ambient sound", 13) ) { - ipstream >> ambientsound; - } else if ( !strncmp(setting, "Blood ", 6) ) { - ipstream >> bloodtoggle; - } else if ( !strncmp(setting, "Auto slomo", 10) ) { - ipstream >> autoslomo; - } else if ( !strncmp(setting, "Foliage", 7) ) { - ipstream >> foliage; - } else if ( !strncmp(setting, "Music", 5) ) { - ipstream >> musictoggle; - } else if ( !strncmp(setting, "Trilinear", 9) ) { - ipstream >> trilinear; - } else if ( !strncmp(setting, "Decals", 6) ) { - ipstream >> decals; - } else if ( !strncmp(setting, "Invert mouse", 12) ) { - ipstream >> invertmouse; - } else if ( !strncmp(setting, "Gamespeed", 9) ) { - ipstream >> gamespeed; - oldgamespeed = gamespeed; - if (oldgamespeed == 0) { - gamespeed = 1; - oldgamespeed = 1; - } - } else if ( !strncmp(setting, "Damage effects", 14) ) { - ipstream >> damageeffects; - } else if ( !strncmp(setting, "Text", 4) ) { - ipstream >> texttoggle; - } else if ( !strncmp(setting, "Devtools", 5) ) { - ipstream >> devtools; - } else if ( !strncmp(setting, "Show Points", 11) ) { - ipstream >> showpoints; - } else if ( !strncmp(setting, "Always Blur", 11) ) { - ipstream >> alwaysblur; - } else if ( !strncmp(setting, "Immediate mode ", 15) ) { - ipstream >> immediate; - } else if ( !strncmp(setting, "Velocity blur", 13) ) { - ipstream >> velocityblur; - } else if ( !strncmp(setting, "Volume", 6) ) { - ipstream >> volume; - } else if ( !strncmp(setting, "Forward key", 11) ) { - ipstream >> forwardkey; - } else if ( !strncmp(setting, "Back key", 8) ) { - ipstream >> backkey; - } else if ( !strncmp(setting, "Left key", 8) ) { - ipstream >> leftkey; - } else if ( !strncmp(setting, "Right key", 9) ) { - ipstream >> rightkey; - } else if ( !strncmp(setting, "Jump key", 8) ) { - ipstream >> jumpkey; - } else if ( !strncmp(setting, "Crouch key", 10) ) { - ipstream >> crouchkey; - } else if ( !strncmp(setting, "Draw key", 8) ) { - ipstream >> drawkey; - } else if ( !strncmp(setting, "Throw key", 9) ) { - ipstream >> throwkey; - } else if ( !strncmp(setting, "Attack key", 10) ) { - ipstream >> attackkey; - } else if ( !strncmp(setting, "Console key", 11) ) { - ipstream >> consolekey; - } else if ( !strncmp(setting, "Damage bar", 10) ) { - ipstream >> showdamagebar; - } else if ( !strncmp(setting, "StereoMode", 10) ) { - int i; - ipstream >> i; - stereomode = (StereoMode)i; - } else if ( !strncmp(setting, "StereoSeparation", 16) ) { - ipstream >> stereoseparation; - } else if ( !strncmp(setting, "StereoReverse", 13) ) { - ipstream >> stereoreverse; - } else { - ipstream >> string; - fprintf(stderr, "Unknown config option '%s' with value '%s'. Ignoring.\n", setting, string); - } - - if ( ipstream.fail() ) { - fprintf(stderr, "Error reading config file: EOF reached when trying to read value for setting '%s'.\n", setting); - ipstream.close(); - return false; - } - - if ( ipstream.bad() ) { - fprintf(stderr, "Error reading config file: Failed to read value for setting '%s'.\n", setting); - ipstream.close(); - return false; - } - } - - ipstream.close(); - - if (detail > 2) - detail = 2; - if (detail < 0) - detail = 0; - if (screenwidth < 0) - screenwidth = 1024; - if (screenheight < 0) - screenheight = 768; - - newdetail = detail; - return true; -} diff --git a/Source/Settings.h b/Source/Settings.h deleted file mode 100644 index e5f983a..0000000 --- a/Source/Settings.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef SETTINGS_H_ -#define SETTINGS_H_ - -#include "Game.h" - -extern float usermousesensitivity; -extern bool ismotionblur; -extern bool floatjump; -extern bool mousejump; -extern bool ambientsound; -extern int bloodtoggle; -extern bool autoslomo; -extern bool foliage; -extern bool musictoggle; -extern bool trilinear; -extern bool decals; -extern bool invertmouse; -extern float gamespeed; -extern float oldgamespeed; -extern bool damageeffects; -extern bool texttoggle; -extern bool devtools; -extern bool showpoints; -extern bool showdamagebar; -extern bool alwaysblur; -extern bool immediate; -extern bool velocityblur; -extern float volume; -extern int detail; -extern int kContextWidth; -extern int kContextHeight; -extern float screenwidth, screenheight; -extern bool fullscreen; - -void DefaultSettings(); -void SaveSettings(); -bool LoadSettings(); - - -#endif diff --git a/Source/Skybox.cpp b/Source/Skybox.cpp deleted file mode 100644 index 8fcc967..0000000 --- a/Source/Skybox.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Skybox.h" -#include "Game.h" - -extern float viewdistance; -extern float blurness; -extern int environment; -extern bool skyboxtexture; -extern float skyboxr; -extern float skyboxg; -extern float skyboxb; - -void SkyBox::load (const std::string& ffront, const std::string& fleft, const std::string& fback, - const std::string& fright, const std::string& fup, const std::string& fdown) -{ - front.load(ffront, true); - left.load(fleft, true); - back.load(fback, true); - right.load(fright, true); - up.load(fup, true); - down.load(fdown, true); -} - -void SkyBox::draw() -{ - static float size = viewdistance / 4; - glPushMatrix(); - static GLfloat M[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, M); - M[12] = 0; - M[13] = 0; - M[14] = 0; - glLoadMatrixf(M); - if (environment == desertenvironment) { - glScalef(1 + blurness / 1000, 1, 1 + blurness / 1000); - glColor3f(1 * skyboxr, .95 * skyboxg, .95 * skyboxb); - } else { - glColor3f(.85 * skyboxr, .85 * skyboxg, .95 * skyboxb); - } - - if (!skyboxtexture) { - glDisable(GL_TEXTURE_2D); - glColor3f(skyboxr * .8, skyboxg * .8, skyboxb * .8); - } - glDepthMask(0); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - glDisable(GL_LIGHTING); - if (skyboxtexture) - glEnable(GL_TEXTURE_2D); - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - front.bind(); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glBegin(GL_QUADS); - glNormal3f( 0.0f, 0.0f, -1); - glTexCoord2f(0, 0); - glVertex3f(-size, -size, size); - glTexCoord2f(1, 0); - glVertex3f( size, -size, size); - glTexCoord2f(1, 1); - glVertex3f( size, size, size); - glTexCoord2f(0, 1); - glVertex3f(-size, size, size); - glEnd(); - back.bind(); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glBegin(GL_QUADS); - glNormal3f( 0.0f, 0.0f, 1); - glTexCoord2f(1, 0); - glVertex3f(-size, -size, -size); - glTexCoord2f(1, 1); - glVertex3f(-size, size, -size); - glTexCoord2f(0, 1); - glVertex3f( size, size, -size); - glTexCoord2f(0, 0); - glVertex3f( size, -size, -size); - glEnd(); - up.bind(); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glBegin(GL_QUADS); - glNormal3f( 0.0f, -1.0f, 0); - glTexCoord2f(0, 1); - glVertex3f(-size, size, -size); - glTexCoord2f(0, 0); - glVertex3f(-size, size, size); - glTexCoord2f(1, 0); - glVertex3f( size, size, size); - glTexCoord2f(1, 1); - glVertex3f( size, size, -size); - glEnd(); - down.bind(); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glBegin(GL_QUADS); - glNormal3f( 0.0f, 1.0f, 0); - - glTexCoord2f(0, 0); - glVertex3f(-size, -size, -size); - glTexCoord2f(1, 0); - glVertex3f( size, -size, -size); - glTexCoord2f(1, 1); - glVertex3f( size, -size, size); - glTexCoord2f(0, 1); - glVertex3f(-size, -size, size); - glEnd(); - right.bind(); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glBegin(GL_QUADS); - glNormal3f( -1.0f, 0.0f, 0); - glTexCoord2f(1, 0); - glVertex3f( size, -size, -size); - glTexCoord2f(1, 1); - glVertex3f( size, size, -size); - glTexCoord2f(0, 1); - glVertex3f( size, size, size); - glTexCoord2f(0, 0); - glVertex3f( size, -size, size); - glEnd(); - left.bind(); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glBegin(GL_QUADS); - glNormal3f( 1.0f, 0.0f, 0); - glTexCoord2f(0, 0); - glVertex3f(-size, -size, -size); - glTexCoord2f(1, 0); - glVertex3f(-size, -size, size); - glTexCoord2f(1, 1); - glVertex3f(-size, size, size); - glTexCoord2f(0, 1); - glVertex3f(-size, size, -size); - glEnd(); - glEnable(GL_CULL_FACE); - glDepthMask(1); - glPopMatrix(); -} - -SkyBox::~SkyBox() -{ - front.destroy(); - left.destroy(); - back.destroy(); - right.destroy(); - up.destroy(); - down.destroy(); -}; - diff --git a/Source/Skybox.h b/Source/Skybox.h deleted file mode 100644 index 5619a2a..0000000 --- a/Source/Skybox.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _SKYBOX_H_ -#define _SKYBOX_H_ - -#include "Quaternions.h" -#include "ImageIO.h" -#include "Quaternions.h" -#include "gamegl.h" -#include "Texture.h" - -class SkyBox -{ -public: - Texture front, left, back, right, up, down; - - void load(const std::string& ffront, const std::string& fleft, const std::string& fback, - const std::string& fright, const std::string& fup, const std::string& fdown); - void draw(); - - SkyBox() {} - ~SkyBox(); -}; - -#endif diff --git a/Source/Sounds.cpp b/Source/Sounds.cpp deleted file mode 100644 index 735d939..0000000 --- a/Source/Sounds.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Quaternions.h" -#include "Sounds.h" -#include "openal_wrapper.h" -#include "Utils/Folders.h" - -struct OPENAL_SAMPLE *samp[sounds_count]; - -extern XYZ envsound[30]; -extern float envsoundvol[30]; -extern int numenvsounds; -extern float envsoundlife[30]; - -int footstepsound, footstepsound2, footstepsound3, footstepsound4; - -int channels[100]; - -static const char *sound_data[sounds_count] = { -#define DECLARE_SOUND(id, filename) filename, -#include "Sounds.def" -#undef DECLARE_SOUND -}; - -// FIXME: dimensionality is not a property of the sound sample. -// This should be decided at the time of playback -static int snd_mode(int snd) -{ - switch (snd) { - case alarmsound: - case consolefailsound: - case consolesuccesssound: - case firestartsound: - case fireendsound: - return OPENAL_2D; - default: - return OPENAL_HW3D; - } -} - -void loadAllSounds() -{ - for (int i = 0; i < sounds_count; i++) { - std::string buf = std::string("Sounds/") + sound_data[i]; - samp[i] = OPENAL_Sample_Load(OPENAL_FREE, - Folders::getResourcePath(buf).c_str(), - snd_mode(i), - 0, 0); - } - footstepsound = footstepsn1; - footstepsound2 = footstepsn2; - footstepsound3 = footstepst1; - footstepsound4 = footstepst2; - // Huh? - // OPENAL_Sample_SetMode(samp[whooshsound], OPENAL_LOOP_NORMAL); - for (int i = stream_firesound; i <= stream_menutheme; i++) - OPENAL_Stream_SetMode(samp[i], OPENAL_LOOP_NORMAL); -} - -void addEnvSound(XYZ coords, float vol, float life) -{ - envsound[numenvsounds] = coords; - envsoundvol[numenvsounds] = vol; - envsoundlife[numenvsounds] = life; - numenvsounds++; -} - -void emit_sound_at(int soundid, const XYZ &pos, float vol) -{ - PlaySoundEx (soundid, samp[soundid], NULL, true); - OPENAL_3D_SetAttributes_ (channels[soundid], pos, NULL); - OPENAL_SetVolume (channels[soundid], vol); - OPENAL_SetPaused (channels[soundid], false); -} - -void emit_sound_np(int soundid, float vol) -{ - PlaySoundEx (soundid, samp[soundid], NULL, true); - OPENAL_SetVolume (channels[soundid], vol); - OPENAL_SetPaused (channels[soundid], false); -} - -void emit_stream_at(int soundid, const XYZ &pos, float vol) -{ - PlayStreamEx (soundid, samp[soundid], NULL, true); - OPENAL_3D_SetAttributes_ (channels[soundid], pos, NULL); - OPENAL_SetVolume (channels[soundid], vol); - OPENAL_SetPaused (channels[soundid], false); -} - -void emit_stream_np(int soundid, float vol) -{ - PlayStreamEx (soundid, samp[soundid], NULL, true); - OPENAL_SetVolume (channels[soundid], vol); - OPENAL_SetPaused (channels[soundid], false); -} - -void resume_stream(int soundid) -{ - OPENAL_SetPaused (channels[soundid], false); -} - -void pause_sound(int soundid) -{ - OPENAL_SetPaused (channels[soundid], true); -} diff --git a/Source/Sounds.def b/Source/Sounds.def deleted file mode 100644 index 6fde586..0000000 --- a/Source/Sounds.def +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -DECLARE_SOUND(footstepsn1, "FootStepSnow1.ogg") -DECLARE_SOUND(footstepsn2, "FootStepSnow2.ogg") -DECLARE_SOUND(footstepst1, "FootStepStone1.ogg") -DECLARE_SOUND(footstepst2, "FootStepStone2.ogg") -DECLARE_SOUND(footstepgr1, "FootStepGrass1.ogg") -DECLARE_SOUND(footstepgr2, "FootStepGrass2.ogg") -DECLARE_SOUND(landsound, "Land.ogg") -DECLARE_SOUND(jumpsound, "Jump.ogg") -DECLARE_SOUND(hawksound, "Hawk.ogg") -DECLARE_SOUND(whooshsound, "Whoosh.ogg") -DECLARE_SOUND(landsound1, "Land1.ogg") -DECLARE_SOUND(landsound2, "Land2.ogg") -DECLARE_SOUND(breaksound, "Broken.ogg") -DECLARE_SOUND(lowwhooshsound, "LowWhoosh.ogg") -DECLARE_SOUND(midwhooshsound, "MidWhoosh.ogg") -DECLARE_SOUND(highwhooshsound, "HighWhoosh.ogg") -DECLARE_SOUND(movewhooshsound, "MoveWhoosh.ogg") -DECLARE_SOUND(heavyimpactsound, "HeavyImpact.ogg") -DECLARE_SOUND(whooshhitsound, "WhooshHit.ogg") -DECLARE_SOUND(thudsound, "Thud.ogg") -DECLARE_SOUND(alarmsound, "Alarm.ogg") -DECLARE_SOUND(breaksound2, "Break.ogg") -DECLARE_SOUND(knifedrawsound, "KnifeDraw.ogg") -DECLARE_SOUND(knifesheathesound, "KnifeSheathe.ogg") -DECLARE_SOUND(fleshstabsound, "FleshStab.ogg") -DECLARE_SOUND(fleshstabremovesound, "FleshStabRemove.ogg") -DECLARE_SOUND(knifeswishsound, "KnifeSwish.ogg") -DECLARE_SOUND(knifeslicesound, "KnifeSlice.ogg") -DECLARE_SOUND(swordslicesound, "SwordSlice.ogg") -DECLARE_SOUND(skidsound, "Skid.ogg") -DECLARE_SOUND(snowskidsound, "SnowSkid.ogg") -DECLARE_SOUND(bushrustle, "BushRustle.ogg") -DECLARE_SOUND(clank1sound, "Clank1.ogg") -DECLARE_SOUND(clank2sound, "Clank2.ogg") -DECLARE_SOUND(clank3sound, "Clank3.ogg") -DECLARE_SOUND(clank4sound, "Clank4.ogg") -DECLARE_SOUND(consolesuccesssound, "ConsoleSuccess.ogg") -DECLARE_SOUND(consolefailsound, "ConsoleFail.ogg") -DECLARE_SOUND(metalhitsound, "MetalHit.ogg") -DECLARE_SOUND(clawslicesound, "ClawSlice.ogg") -DECLARE_SOUND(splattersound, "Splatter.ogg") -DECLARE_SOUND(growlsound, "Growl.ogg") -DECLARE_SOUND(growl2sound, "Growl2.ogg") -DECLARE_SOUND(barksound, "Bark.ogg") -DECLARE_SOUND(bark2sound, "Bark2.ogg") -DECLARE_SOUND(bark3sound, "Bark3.ogg") -DECLARE_SOUND(snarlsound, "Snarl.ogg") -DECLARE_SOUND(snarl2sound, "Snarl2.ogg") -DECLARE_SOUND(barkgrowlsound, "BarkGrowl.ogg") -DECLARE_SOUND(rabbitattacksound, "RabbitAttack.ogg") -DECLARE_SOUND(rabbitattack2sound, "RabbitAttack2.ogg") -DECLARE_SOUND(rabbitattack3sound, "RabbitAttack3.ogg") -DECLARE_SOUND(rabbitattack4sound, "RabbitAttack4.ogg") -DECLARE_SOUND(rabbitpainsound, "RabbitPain.ogg") -DECLARE_SOUND(rabbitpain1sound, "RabbitPain2.ogg") -DECLARE_SOUND(rabbitchitter, "RabbitChitter.ogg") -DECLARE_SOUND(rabbitchitter2, "RabbitChitter2.ogg") -DECLARE_SOUND(swordstaffsound, "SwordStaff.ogg") -DECLARE_SOUND(staffbodysound, "StaffBody.ogg") -DECLARE_SOUND(staffheadsound, "StaffHead.ogg") -DECLARE_SOUND(staffbreaksound, "StaffBreak.ogg") -DECLARE_SOUND(firestartsound, "FireStart.ogg") -DECLARE_SOUND(fireendsound, "FireEnd.ogg") -DECLARE_SOUND(stream_firesound, "Fire.ogg") -DECLARE_SOUND(stream_grasstheme, "Music1Grass.ogg") -DECLARE_SOUND(stream_snowtheme, "Music1Snow.ogg") -DECLARE_SOUND(stream_deserttheme, "Music1Desert.ogg") -DECLARE_SOUND(stream_wind, "Wind.ogg") -DECLARE_SOUND(stream_desertambient, "DesertAmbient.ogg") -DECLARE_SOUND(stream_fighttheme, "Music2.ogg") -DECLARE_SOUND(stream_menutheme, "Music3.ogg") diff --git a/Source/Sounds.h b/Source/Sounds.h deleted file mode 100644 index 7557993..0000000 --- a/Source/Sounds.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef SOUNDS_H -#define SOUNDS_H - -enum sound_types { -#define DECLARE_SOUND(id, filename) id, -#include "Sounds.def" -#undef DECLARE_SOUND - sounds_count -}; - -extern struct OPENAL_SAMPLE *samp[sounds_count]; -extern int channels[]; - -extern void loadAllSounds(); - -extern void addEnvSound(XYZ coords, float vol = 16, float life = .4); - -extern void emit_sound_at(int soundid, const XYZ &pos = XYZ(), float vol = 256.f); -extern void emit_sound_np(int soundid, float vol = 256.f); -extern void emit_stream_at(int soundid, const XYZ &pos = XYZ(), float vol = 256.f); -extern void emit_stream_np(int soundid, float vol = 256.f); -extern void resume_stream(int soundid); -extern void pause_sound(int soundid); - -extern int footstepsound, footstepsound2, footstepsound3, footstepsound4; -#endif diff --git a/Source/Sprite.cpp b/Source/Sprite.cpp deleted file mode 100644 index a0502d2..0000000 --- a/Source/Sprite.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Sprite.h" -#include "Person.h" -#include "Game.h" -extern XYZ viewer; -extern float viewdistance; -extern float fadestart; -extern int environment; -extern float texscale; -extern Light light; -extern float multiplier; -extern float gravity; -extern Terrain terrain; -extern Objects objects; -extern int detail; -extern XYZ viewerfacing; -extern int bloodtoggle; -extern XYZ windvector; - -// init statics -Texture Sprite::cloudtexture; -Texture Sprite::cloudimpacttexture; -Texture Sprite::bloodtexture; -Texture Sprite::flametexture; -Texture Sprite::bloodflametexture; -Texture Sprite::smoketexture; -Texture Sprite::snowflaketexture; -Texture Sprite::shinetexture; -Texture Sprite::splintertexture; -Texture Sprite::leaftexture; -Texture Sprite::toothtexture; - -float Sprite::checkdelay = 0; - -vector Sprite::sprites = vector(); - -//Functions -void Sprite::Draw() -{ - int k; - static float M[16]; - static XYZ point; - static float distancemult; - static int lasttype; - static int lastspecial; - static int whichpatchx, whichpatchz; - static XYZ start, end, colpoint; - static bool check; - static bool blend; - static float tempmult; - static XYZ difference; - static float lightcolor[3]; - static float viewdistsquared = viewdistance * viewdistance; - static XYZ tempviewer; - - tempviewer = viewer + viewerfacing * 6; - check = 0; - - lightcolor[0] = light.color[0] * .5 + light.ambient[0]; - lightcolor[1] = light.color[1] * .5 + light.ambient[1]; - lightcolor[2] = light.color[2] * .5 + light.ambient[2]; - - checkdelay -= multiplier * 10; - - if (checkdelay <= 0) { - check = 1; - checkdelay = 1; - } - - lasttype = -1; - lastspecial = -1; - glEnable(GL_BLEND); - glDisable(GL_LIGHTING); - glDisable(GL_CULL_FACE); - glEnable(GL_TEXTURE_2D); - blend = 1; - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(0); - glAlphaFunc(GL_GREATER, 0.0001); - for (unsigned i = 0; i < sprites.size(); i++) { - if (lasttype != sprites[i]->type) { - switch (sprites[i]->type) { - case cloudsprite: - cloudtexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - break; - case breathsprite: - case cloudimpactsprite: - cloudimpacttexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - break; - case smoketype: - smoketexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - break; - case bloodsprite: - bloodtexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - break; - case splintersprite : - if (lastspecial != sprites[i]->special) { - if (sprites[i]->special == 0) - splintertexture.bind(); - if (sprites[i]->special == 1) - leaftexture.bind(); - if (sprites[i]->special == 2) - snowflaketexture.bind(); - if (sprites[i]->special == 3) - toothtexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - } - break; - case snowsprite: - snowflaketexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - break; - case weaponshinesprite: - shinetexture.bind(); - if (blend) { - blend = 0; - glAlphaFunc(GL_GREATER, 0.001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } - break; - case flamesprite: - case weaponflamesprite: - flametexture.bind(); - if (blend || lasttype == bloodflamesprite) { - blend = 0; - glAlphaFunc(GL_GREATER, 0.3); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } - break; - case bloodflamesprite: - bloodflametexture.bind(); - if (blend) { - blend = 0; - glAlphaFunc(GL_GREATER, 0.3); - glBlendFunc(GL_ONE, GL_ZERO); - } - break; - } - } - if (sprites[i]->type == snowsprite) - distancemult = (144 - (distsq(&tempviewer, &sprites[i]->position) - (144 * fadestart)) * (1 / (1 - fadestart))) / 144; - else - distancemult = (viewdistsquared - (distsq(&viewer, &sprites[i]->position) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; - if (sprites[i]->type == flamesprite) { - if (distancemult >= 1) - glColor4f(sprites[i]->color[0], sprites[i]->color[1], sprites[i]->color[2], sprites[i]->opacity); - else - glColor4f(sprites[i]->color[0], sprites[i]->color[1], sprites[i]->color[2], sprites[i]->opacity * distancemult); - } else { - if (distancemult >= 1) - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity); - else - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity * distancemult); - } - lasttype = sprites[i]->type; - lastspecial = sprites[i]->special; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glTranslatef(sprites[i]->position.x, sprites[i]->position.y, sprites[i]->position.z); - if ((sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite)) { - difference = viewer - sprites[i]->position; - Normalise(&difference); - glTranslatef(difference.x * sprites[i]->size / 4, difference.y * sprites[i]->size / 4, difference.z * sprites[i]->size / 4); - } - if (sprites[i]->type == snowsprite) { - glRotatef(sprites[i]->rotation * .2, 0, .3, 1); - glTranslatef(1, 0, 0); - } - glGetFloatv(GL_MODELVIEW_MATRIX, M); - point.x = M[12]; - point.y = M[13]; - point.z = M[14]; - glLoadIdentity(); - glTranslatef(point.x, point.y, point.z); - - glRotatef(sprites[i]->rotation, 0, 0, 1); - - if ((sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == bloodflamesprite)) { - if (sprites[i]->alivetime < .14) - glScalef(sprites[i]->alivetime / .14, sprites[i]->alivetime / .14, sprites[i]->alivetime / .14); - } - if (sprites[i]->type == smoketype || sprites[i]->type == snowsprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == breathsprite) { - if (sprites[i]->alivetime < .3) { - if (distancemult >= 1) - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity * sprites[i]->alivetime / .3); - if (distancemult < 1) - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->opacity * distancemult * sprites[i]->alivetime / .3); - } - } - if (sprites[i]->type == splintersprite && sprites[i]->special > 0 && sprites[i]->special != 3) { - if (sprites[i]->alivetime < .2) { - if (distancemult >= 1) - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], sprites[i]->alivetime / .2); - else - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], distancemult * sprites[i]->alivetime / .2); - } else { - if (distancemult >= 1) - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); - else - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); - } - } - if (sprites[i]->type == splintersprite && (sprites[i]->special == 0 || sprites[i]->special == 3)) { - if (distancemult >= 1) - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); - else - glColor4f(sprites[i]->color[0]*lightcolor[0], sprites[i]->color[1]*lightcolor[1], sprites[i]->color[2]*lightcolor[2], 1); - } - - glBegin(GL_TRIANGLES); - glTexCoord2f(1.0f, 1.0f); - glVertex3f( .5 * sprites[i]->size, .5 * sprites[i]->size, 0.0f); - glTexCoord2f(0.0f, 1.0f); - glVertex3f(-.5 * sprites[i]->size, .5 * sprites[i]->size, 0.0f); - glTexCoord2f(1.0f, 0.0f); - glVertex3f( .5 * sprites[i]->size, -.5 * sprites[i]->size, 0.0f); - glTexCoord2f(0.0f, 0.0f); - glVertex3f(-.5 * sprites[i]->size, -.5 * sprites[i]->size, 0.0f); - glTexCoord2f(1.0f, 0.0f); - glVertex3f( .5 * sprites[i]->size, -.5 * sprites[i]->size, 0.0f); - glTexCoord2f(0.0f, 1.0f); - glVertex3f(-.5 * sprites[i]->size, .5 * sprites[i]->size, 0.0f); - glEnd(); - glPopMatrix(); - } - tempmult = multiplier; - for (int i = sprites.size() - 1; i >= 0; i--) { - multiplier = tempmult; - if (sprites[i]->type != snowsprite) { - sprites[i]->position += sprites[i]->velocity * multiplier; - sprites[i]->velocity += windvector * multiplier; - } - if (sprites[i]->type == flamesprite || sprites[i]->type == smoketype) - sprites[i]->position += windvector * multiplier / 2; - if ((sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == bloodflamesprite)) - multiplier *= sprites[i]->speed * .7; - sprites[i]->alivetime += multiplier; - - if (sprites[i]->type == cloudsprite || sprites[i]->type == cloudimpactsprite) { - sprites[i]->opacity -= multiplier / 2; - sprites[i]->size += multiplier / 2; - sprites[i]->velocity.y += gravity * multiplier * .25; - } - if (sprites[i]->type == breathsprite) { - sprites[i]->opacity -= multiplier / 2; - sprites[i]->size += multiplier / 2; - if (findLength(&sprites[i]->velocity) <= multiplier) { - sprites[i]->velocity = 0; - } else { - XYZ slowdown; - slowdown = sprites[i]->velocity * -1; - Normalise(&slowdown); - slowdown *= multiplier; - sprites[i]->velocity += slowdown; - } - } - if (sprites[i]->type == snowsprite) { - sprites[i]->size -= multiplier / 120; - sprites[i]->rotation += multiplier * 360; - sprites[i]->position.y -= multiplier; - sprites[i]->position += windvector * multiplier; - if (sprites[i]->position.y < tempviewer.y - 6) sprites[i]->position.y += 12; - if (sprites[i]->position.y > tempviewer.y + 6) sprites[i]->position.y -= 12; - if (sprites[i]->position.z < tempviewer.z - 6) sprites[i]->position.z += 12; - if (sprites[i]->position.z > tempviewer.z + 6) sprites[i]->position.z -= 12; - if (sprites[i]->position.x < tempviewer.x - 6) sprites[i]->position.x += 12; - if (sprites[i]->position.x > tempviewer.x + 6) sprites[i]->position.x -= 12; - } - if (sprites[i]->type == bloodsprite) { - bool spritehit = 0; - sprites[i]->rotation += multiplier * 100; - sprites[i]->velocity.y += gravity * multiplier; - if (check) { - XYZ where, startpoint, endpoint, movepoint, footpoint; - float rotationpoint; - int whichtri; - - for (unsigned j = 0; j < Person::players.size(); j++) { - if (!spritehit && Person::players[j]->dead && sprites[i]->alivetime > .1) { - where = sprites[i]->oldposition; - where -= Person::players[j]->coords; - if (!Person::players[j]->skeleton.free) - where = DoRotation(where, 0, -Person::players[j]->yaw, 0); - startpoint = where; - where = sprites[i]->position; - where -= Person::players[j]->coords; - if (!Person::players[j]->skeleton.free) - where = DoRotation(where, 0, -Person::players[j]->yaw, 0); - endpoint = where; - - movepoint = 0; - rotationpoint = 0; - whichtri = Person::players[j]->skeleton.drawmodel.LineCheck(&startpoint, &endpoint, &footpoint, &movepoint, &rotationpoint); - if (whichtri != -1) { - spritehit = 1; - Person::players[j]->DoBloodBigWhere(0, 160, sprites[i]->oldposition); - DeleteSprite(i); - } - } - } - - whichpatchx = sprites[i]->position.x / (terrain.size / subdivision * terrain.scale); - whichpatchz = sprites[i]->position.z / (terrain.size / subdivision * terrain.scale); - if (whichpatchx > 0 && whichpatchz > 0 && whichpatchx < subdivision && whichpatchz < subdivision) - if (terrain.patchobjectnum[whichpatchx][whichpatchz]) { - if (!spritehit) - for (int j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { - k = terrain.patchobjects[whichpatchx][whichpatchz][j]; - start = sprites[i]->oldposition; - end = sprites[i]->position; - if (!spritehit) - if (objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]) != -1) { - if (detail == 2 || (detail == 1 && abs(Random() % 4) == 0) || (detail == 0 && abs(Random() % 8) == 0)) - objects.model[k].MakeDecal(blooddecalfast, DoRotation(colpoint - objects.position[k], 0, -objects.yaw[k], 0), sprites[i]->size * 1.6, .5, Random() % 360); - DeleteSprite(i); - spritehit = 1; - } - } - } - if (!spritehit) - if (sprites[i]->position.y < terrain.getHeight(sprites[i]->position.x, sprites[i]->position.z)) { - terrain.MakeDecal(blooddecalfast, sprites[i]->position, sprites[i]->size * 1.6, .6, Random() % 360); - DeleteSprite(i); - } - } - } - if (sprites[i]->type == splintersprite) { - sprites[i]->rotation += sprites[i]->rotatespeed * multiplier; - sprites[i]->opacity -= multiplier / 2; - if (sprites[i]->special == 0 || sprites[i]->special == 2 || sprites[i]->special == 3) - sprites[i]->velocity.y += gravity * multiplier; - if (sprites[i]->special == 1) - sprites[i]->velocity.y += gravity * multiplier * .5; - } - if (sprites[i]->type == flamesprite || sprites[i]->type == weaponflamesprite || sprites[i]->type == weaponshinesprite || sprites[i]->type == bloodflamesprite) { - sprites[i]->rotation += multiplier * sprites[i]->rotatespeed; - sprites[i]->opacity -= multiplier * 5 / 4; - if (sprites[i]->type != weaponshinesprite && sprites[i]->type != bloodflamesprite) - if (sprites[i]->opacity < .5 && sprites[i]->opacity + multiplier * 5 / 4 >= .5 && (abs(Random() % 4) == 0 || (sprites[i]->initialsize > 2 && Random() % 2 == 0))) - MakeSprite(smoketype, sprites[i]->position, sprites[i]->velocity, .9, .9, .6, sprites[i]->size * 1.2, .4); - if (sprites[i]->alivetime > .14 && (sprites[i]->type == flamesprite)) { - sprites[i]->velocity = 0; - sprites[i]->velocity.y = 1.5; - } - } - if (sprites[i]->type == smoketype) { - sprites[i]->opacity -= multiplier / 3 / sprites[i]->initialsize; - sprites[i]->color[0] -= multiplier; - sprites[i]->color[1] -= multiplier; - sprites[i]->color[2] -= multiplier; - if (sprites[i]->color[0] < .6) - sprites[i]->color[0] = .6; - if (sprites[i]->color[1] < .6) - sprites[i]->color[1] = .6; - if (sprites[i]->color[2] < .6) - sprites[i]->color[2] = .6; - sprites[i]->size += multiplier; - sprites[i]->velocity = 0; - sprites[i]->velocity.y = 1.5; - sprites[i]->rotation += multiplier * sprites[i]->rotatespeed / 5; - } - if (sprites[i]->opacity <= 0 || sprites[i]->size <= 0) - DeleteSprite(i); - } - if (check) - for (int i = sprites.size() - 1; i >= 0; i--) { - sprites[i]->oldposition = sprites[i]->position; - } - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void Sprite::DeleteSprite(int i) -{ - sprites.erase(sprites.begin() + i); -} - -void Sprite::MakeSprite(int atype, XYZ where, XYZ avelocity, float red, float green, float blue, float asize, float aopacity) -{ - if (sprites.size() < max_sprites - 1) { - sprites.push_back(new Sprite()); - if ((atype != bloodsprite && atype != bloodflamesprite) || bloodtoggle) { - sprites.back()->special = 0; - sprites.back()->type = atype; - sprites.back()->position = where; - sprites.back()->oldposition = where; - sprites.back()->velocity = avelocity; - sprites.back()->alivetime = 0; - sprites.back()->opacity = aopacity; - sprites.back()->size = asize; - sprites.back()->initialsize = asize; - sprites.back()->color[0] = red; - sprites.back()->color[1] = green; - sprites.back()->color[2] = blue; - sprites.back()->rotatespeed = abs(Random() % 720) - 360; - sprites.back()->speed = float(abs(Random() % 100)) / 200 + 1.5; - } - } -} - -Sprite::Sprite() -{ - oldposition = 0; - position = 0; - velocity = 0; - size = 0; - initialsize = 0; - type = 0; - special = 0; - memset(color, 0, sizeof(color)); - opacity = 0; - rotation = 0; - alivetime = 0; - speed = 0; - rotatespeed = 0; -} - -void Sprite::clearTextures() -{ - toothtexture.destroy(); - cloudtexture.destroy(); - cloudimpacttexture.destroy(); - bloodtexture.destroy(); - flametexture.destroy(); - bloodflametexture.destroy(); - smoketexture.destroy(); - snowflaketexture.destroy(); - shinetexture.destroy(); - splintertexture.destroy(); - leaftexture.destroy(); -} - diff --git a/Source/Sprite.h b/Source/Sprite.h deleted file mode 100644 index 6f679ce..0000000 --- a/Source/Sprite.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _SPRITE_H_ -#define _SPRITE_H_ - -#include "Quaternions.h" -#include "gamegl.h" -#include "ImageIO.h" -#include "Quaternions.h" -#include "Frustum.h" -#include "Lights.h" -#include "Terrain.h" -#include "Objects.h" -#include "Texture.h" - -#include - -#define max_sprites 20000 - -enum { - cloudsprite = 0, - bloodsprite, - flamesprite, - smoketype, - weaponflamesprite, - cloudimpactsprite, - snowsprite, - weaponshinesprite, - bloodflamesprite, - breathsprite, - splintersprite, - spritenumber -}; - -class Sprite -{ -private: - XYZ oldposition; - XYZ position; - XYZ velocity; - float size; - float initialsize; - int type; - int special; - float color[3]; - float opacity; - float rotation; - float alivetime; - float speed; - float rotatespeed; - - static float checkdelay; - - static vector sprites; - -public: - static void DeleteSprite(int which); - static void MakeSprite(int atype, XYZ where, XYZ avelocity, float red, float green, float blue, float asize, float aopacity); - static void Draw(); - static void deleteSprites() { - sprites.clear(); - } - static void setLastSpriteSpecial(int s) { - sprites.back()->special = s; - } - static void setLastSpriteSpeed(int s) { - sprites.back()->speed = s; - } - static void setLastSpriteAlivetime(float al) { - sprites.back()->alivetime = al; - } - static void clearTextures(); - - static Texture cloudtexture; - static Texture bloodtexture; - static Texture flametexture; - static Texture smoketexture; - - static Texture cloudimpacttexture; - static Texture snowflaketexture; - static Texture shinetexture; - static Texture bloodflametexture; - - static Texture splintertexture; - - static Texture leaftexture; - static Texture toothtexture; - - Sprite(); - ~Sprite(); -}; - -#endif diff --git a/Source/Stereo.cpp b/Source/Stereo.cpp deleted file mode 100644 index 8887a52..0000000 --- a/Source/Stereo.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Game.h" -#include "Stereo.h" - - -extern int kContextWidth; -extern int kContextHeight; - -bool CanInitStereo(StereoMode mode) -{ - GLint stencilbits = 0; - - switch (mode) { - case stereoNone: - case stereoAnaglyph: - return true; - break; - case stereoHorizontalInterlaced: - case stereoVerticalInterlaced: - glGetIntegerv(GL_STENCIL_BITS, &stencilbits); - if ( stencilbits < 1 ) { - fprintf(stderr, "Failed to get a stencil buffer, interlaced stereo not available.\n"); - return false; - } else { - fprintf(stderr, "Stencil buffer has %i bits, good.\n", stencilbits); - } - return true; - break; - default: - return false; - } - -} - -void InitStereo(StereoMode mode) -{ - switch (mode) { - default: - case stereoNone: - case stereoAnaglyph: - glDisable(GL_STENCIL_TEST); - return; - case stereoHorizontalInterlaced: - case stereoVerticalInterlaced: - fprintf(stderr, "Screen width is %i, height is %i\n", kContextWidth, kContextHeight); - - // Setup stencil buffer - glDisable( GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glDisable(GL_LIGHTING); - glDisable(GL_TEXTURE_2D); - - glEnable( GL_STENCIL_TEST); - glClearStencil(0); - glClear( GL_STENCIL_BUFFER_BIT ); - glStencilFunc(GL_ALWAYS, 0x1, 0x1); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); - - // Setup viewport - glViewport(0, 0, kContextWidth, kContextHeight); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho((GLdouble)0, (GLdouble)kContextWidth, (GLdouble)kContextHeight, 0, -1, 1); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); - glDisable(GL_LINE_SMOOTH); - - // Add 0.5 to the coordinates, because OpenGL considers a pixel should be - // turned on when a line passes through the center of it. - if ( mode == stereoHorizontalInterlaced ) { - for (int y = 0; y < kContextHeight; y += 2) { - glBegin(GL_LINES); - glVertex3f(0.5, y + 0.5, 0); - glVertex3f(kContextWidth + 0.5, y + 0.5, 0); - glEnd(); - } - } else { - for (int x = 0; x < kContextWidth; x += 2) { - glBegin(GL_LINES); - glVertex3f(x + 0.5, 0.5, 0); - glVertex3f(x + 0.5, kContextHeight + 0.5, 0); - glEnd(); - } - } - - glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); - - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - - glStencilFunc(GL_NOTEQUAL, 0x01, 0x01); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glEnable( GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glEnable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - } - -} - -const std::string StereoModeName(StereoMode mode) -{ - switch (mode) { - case stereoNone: - return "None"; - break; - case stereoAnaglyph: - return "Anaglyph"; - break; - case stereoHorizontalInterlaced: - return "Horizontal interlacing"; - break; - case stereoVerticalInterlaced: - return "Vertical interlacing"; - break; - case stereoHorizontalSplit: - return "Horizontal split"; - break; - case stereoVerticalSplit: - return "Vertical split"; - break; - case stereoOpenGL: - return "OpenGL"; - break; - default: - return "(error)"; - break; - } -} diff --git a/Source/Stereo.h b/Source/Stereo.h deleted file mode 100644 index 8e70e5e..0000000 --- a/Source/Stereo.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef STEREO_H_ -#define STEREO_H_ - -enum StereoMode { - stereoNone, - stereoAnaglyph, /* red/cyan */ - stereoHorizontalInterlaced, /* some 3D monitors */ - stereoVerticalInterlaced, - stereoHorizontalSplit, /* cross-eyed view */ - stereoVerticalSplit, - stereoOpenGL, /* Whatever OpenGL does, if supported */ - stereoCount /* must be last element */ -}; - - -enum StereoSide { - // Code multiplies by StereoSide to calculate camera offsets - stereoLeft = -1, - stereoCenter = 0, - stereoRight = 1 -}; - -extern StereoMode stereomode; -extern StereoMode newstereomode; -extern float stereoseparation; -extern bool stereoreverse; - -bool CanInitStereo(StereoMode mode); -void InitStereo(StereoMode mode); -const std::string StereoModeName(StereoMode mode); - -#endif diff --git a/Source/Terrain.cpp b/Source/Terrain.cpp deleted file mode 100644 index 34da00e..0000000 --- a/Source/Terrain.cpp +++ /dev/null @@ -1,1528 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "Game.h" -#include "Terrain.h" -#include "Objects.h" -#include "Utils/Folders.h" - -extern XYZ viewer; -extern float viewdistance; -extern float fadestart; -extern int environment; -extern float texscale; -extern Light light; -extern float multiplier; -extern FRUSTUM frustum; -extern float texdetail; -extern int detail; -extern bool decals; -extern float blurness; -extern float targetblurness; -extern Objects objects; -extern bool visibleloading; -extern bool skyboxtexture; -extern int tutoriallevel; - -//Functions - -int Terrain::lineTerrain(XYZ p1, XYZ p2, XYZ *p) -{ - static int i, j, k; - static float distance; - static float olddistance; - static int intersecting; - static int firstintersecting; - static XYZ point; - static int startx, starty; - static int endx, endy; - static float highest, lowest; - - firstintersecting = -1; - olddistance = 10000; - distance = 1; - - XYZ triangles[3]; - - p1 /= scale; - p2 /= scale; - - startx = p1.x; - starty = p1.z; - endx = p2.x; - endy = p2.z; - - if (startx > endx) { - i = endx; - endx = startx; - startx = i; - } - if (starty > endy) { - i = endy; - endy = starty; - starty = i; - } - - if (startx < 0) - startx = 0; - if (starty < 0) - starty = 0; - if (endx > size - 1) - endx = size - 1; - if (endy > size - 1) - endy = size - 1; - - for (i = startx; i <= endx; i++) { - for (j = starty; j <= endy; j++) { - highest = -1000; - lowest = 1000; - for (k = 0; k < 2; k++) { - if (heightmap[i + k][j] > highest) - highest = heightmap[i + k][j]; - if (heightmap[i + k][j] < lowest) - lowest = heightmap[i + k][j]; - if (heightmap[i + k][j + 1] > highest) - highest = heightmap[i + k][j + 1]; - if (heightmap[i + k][j + 1] < lowest) - lowest = heightmap[i + k][j + 1]; - } - if ((p1.y <= highest || p2.y <= highest) && (p1.y >= lowest || p2.y >= lowest)) { - triangles[0].x = i; - triangles[0].y = heightmap[i][j]; - triangles[0].z = j; - - triangles[1].x = i; - triangles[1].y = heightmap[i][j + 1]; - triangles[1].z = j + 1; - - triangles[2].x = i + 1; - triangles[2].y = heightmap[i + 1][j]; - triangles[2].z = j; - - intersecting = LineFacet(p1, p2, triangles[0], triangles[1], triangles[2], &point); - distance = distsq(&p1, &point); - if ((distance < olddistance || firstintersecting == -1) && intersecting == 1) { - olddistance = distance; - firstintersecting = 1; - *p = point; - } - - triangles[0].x = i + 1; - triangles[0].y = heightmap[i + 1][j]; - triangles[0].z = j; - - triangles[1].x = i; - triangles[1].y = heightmap[i][j + 1]; - triangles[1].z = j + 1; - - triangles[2].x = i + 1; - triangles[2].y = heightmap[i + 1][j + 1]; - triangles[2].z = j + 1; - - intersecting = LineFacet(p1, p2, triangles[0], triangles[1], triangles[2], &point); - distance = distsq(&p1, &point); - if ((distance < olddistance || firstintersecting == -1) && intersecting == 1) { - olddistance = distance; - firstintersecting = 1; - *p = point; - } - } - } - } - return firstintersecting; -} - -void Terrain::UpdateTransparency(int whichx, int whichy) -{ - static XYZ vertex; - static int i, j, a, b, c, d, patch_size, stepsize; - static float distance; - - static float viewdistsquared; - - viewdistsquared = viewdistance * viewdistance; - patch_size = size / subdivision; - - stepsize = 1; - c = whichx * patch_elements + whichy * patch_elements * subdivision; - - for (i = patch_size * whichx; i < patch_size * (whichx + 1) + 1; i += stepsize) { - for (j = patch_size * whichy; j < patch_size * (whichy + 1) + 1; j += stepsize) { - if (i < size && j < size) { - vertex.x = i * scale; - vertex.z = j * scale; - vertex.y = heightmap[i][j] * scale; - distance = distsq(&viewer, &vertex); - if (distance > viewdistsquared) - distance = viewdistsquared; - colors[i][j][3] = (viewdistsquared - (distance - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; - } - } - } - - for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { - for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { - a = (i - (patch_size * whichx)) / stepsize; - b = (j - (patch_size * whichy)) / stepsize; - d = (a * 54) + (b * 54 * patch_size / stepsize); - vArray[d + c + 6] = colors[i][j][3]; - - vArray[d + c + 15] = colors[i][j + stepsize][3]; - - vArray[d + c + 24] = colors[i + stepsize][j][3]; - - vArray[d + c + 33] = colors[i + stepsize][j][3]; - - vArray[d + c + 42] = colors[i][j + stepsize][3]; - - vArray[d + c + 51] = colors[i + stepsize][j + stepsize][3]; - } - } -} - -void Terrain::UpdateTransparencyother(int whichx, int whichy) -{ - static int i, j, a, b, c, d, patch_size, stepsize; - - patch_size = size / subdivision; - - stepsize = 1; - c = whichx * patch_elements + whichy * patch_elements * subdivision; - - for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { - for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { - a = (i - (patch_size * whichx)) / stepsize; - b = (j - (patch_size * whichy)) / stepsize; - d = (a * 54) + (b * 54 * patch_size / stepsize); - vArray[d + c + 6] = colors[i][j][3] * opacityother[i][j]; - - vArray[d + c + 15] = colors[i][j + stepsize][3] * opacityother[i][j + stepsize]; - - vArray[d + c + 24] = colors[i + stepsize][j][3] * opacityother[i + stepsize][j]; - - vArray[d + c + 33] = colors[i + stepsize][j][3] * opacityother[i + stepsize][j]; - - vArray[d + c + 42] = colors[i][j + stepsize][3] * opacityother[i][j + stepsize]; - - vArray[d + c + 51] = colors[i + stepsize][j + stepsize][3] * opacityother[i + stepsize][j + stepsize]; - } - } -} - -void Terrain::UpdateTransparencyotherother(int whichx, int whichy) -{ - static XYZ vertex; - static int i, j, a, b, c, d, patch_size, stepsize; - static float distance; - - static float viewdistsquared; - - viewdistsquared = viewdistance * viewdistance; - patch_size = size / subdivision; - - stepsize = 1; - c = whichx * patch_elements + whichy * patch_elements * subdivision; - - for (i = patch_size * whichx; i < patch_size * (whichx + 1) + 1; i += stepsize) { - for (j = patch_size * whichy; j < patch_size * (whichy + 1) + 1; j += stepsize) { - if (i < size && j < size) { - vertex.x = i * scale; - vertex.z = j * scale; - vertex.y = heightmap[i][j] * scale; - distance = distsq(&viewer, &vertex); - if (distance > viewdistsquared) - distance = viewdistsquared; - colors[i][j][3] = (viewdistsquared - (distance - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; - } - } - } - - for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { - for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { - a = (i - (patch_size * whichx)) / stepsize; - b = (j - (patch_size * whichy)) / stepsize; - d = (a * 54) + (b * 54 * patch_size / stepsize); - vArray[d + c + 6] = colors[i][j][3]; - - vArray[d + c + 15] = colors[i][j + stepsize][3]; - - vArray[d + c + 24] = colors[i + stepsize][j][3]; - - vArray[d + c + 33] = colors[i + stepsize][j][3]; - - vArray[d + c + 42] = colors[i][j + stepsize][3]; - - vArray[d + c + 51] = colors[i + stepsize][j + stepsize][3]; - } - } -} - -void Terrain::UpdateVertexArray(int whichx, int whichy) -{ - static int i, j, a, b, c, patch_size, stepsize; - - - numtris[whichx][whichy] = 0; - - patch_size = size / subdivision; - - stepsize = 1; - c = whichx * patch_elements + whichy * patch_elements * subdivision; - for (i = patch_size * whichx; i < patch_size * (whichx + 1); i += stepsize) { - for (j = patch_size * whichy; j < patch_size * (whichy + 1); j += stepsize) { - a = (i - ((float)size / subdivision * (float)whichx)) / stepsize; - b = (j - ((float)size / subdivision * (float)whichy)) / stepsize; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 0] = i * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 1] = heightmap[i][j] * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 2] = j * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 3] = colors[i][j][0]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 4] = colors[i][j][1]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 5] = colors[i][j][2]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 6] = colors[i][j][3]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 7] = i * scale * texscale + texoffsetx[i][j]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 8] = j * scale * texscale + texoffsety[i][j]; - - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 9] = i * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 10] = heightmap[i][j + stepsize] * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 11] = j * scale + stepsize * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 12] = colors[i][j + stepsize][0]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 13] = colors[i][j + stepsize][1]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 14] = colors[i][j + stepsize][2]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 15] = colors[i][j + stepsize][3]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 16] = i * scale * texscale + texoffsetx[i][j + stepsize]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 17] = j * scale * texscale + stepsize * scale * texscale + texoffsety[i][j + stepsize]; - - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 18] = i * scale + stepsize * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 19] = heightmap[i + stepsize][j] * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 20] = j * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 21] = colors[i + stepsize][j][0]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 22] = colors[i + stepsize][j][1]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 23] = colors[i + stepsize][j][2]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 24] = colors[i + stepsize][j][3]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 25] = i * scale * texscale + stepsize * scale * texscale + texoffsetx[i + stepsize][j]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 26] = j * scale * texscale + texoffsety[i + stepsize][j]; - - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 27] = i * scale + stepsize * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 28] = heightmap[i + stepsize][j] * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 29] = j * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 30] = colors[i + stepsize][j][0]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 31] = colors[i + stepsize][j][1]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 32] = colors[i + stepsize][j][2]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 33] = colors[i + stepsize][j][3]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 34] = i * scale * texscale + stepsize * scale * texscale + texoffsetx[i + stepsize][j]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 35] = j * scale * texscale + texoffsety[i + stepsize][j]; - - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 36] = i * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 37] = heightmap[i][j + stepsize] * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 38] = j * scale + stepsize * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 39] = colors[i][j + stepsize][0]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 40] = colors[i][j + stepsize][1]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 41] = colors[i][j + stepsize][2]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 42] = colors[i][j + stepsize][3]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 43] = i * scale * texscale + texoffsetx[i][j + stepsize]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 44] = j * scale * texscale + stepsize * scale * texscale + texoffsety[i][j + stepsize]; - - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 45] = i * scale + stepsize * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 46] = heightmap[i + stepsize][j + stepsize] * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 47] = j * scale + stepsize * scale; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 48] = colors[i + stepsize][j + stepsize][0]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 49] = colors[i + stepsize][j + stepsize][1]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 50] = colors[i + stepsize][j + stepsize][2]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 51] = colors[i + stepsize][j + stepsize][3]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 52] = i * scale * texscale + stepsize * scale * texscale + texoffsetx[i + stepsize][j + stepsize]; - vArray[(a * 54) + (b * 54 * patch_size / stepsize) + c + 53] = j * scale * texscale + stepsize * scale * texscale + texoffsety[i + stepsize][j + stepsize]; - numtris[whichx][whichy] += 2; - } - } - - maxypatch[whichx][whichy] = -10000; - minypatch[whichx][whichy] = 10000; - for (a = 0; a < size / subdivision; a++) { - for (b = 0; b < size / subdivision; b++) { - if (heightmap[(size / subdivision)*whichx + a][(size / subdivision)*whichy + b]*scale > maxypatch[whichx][whichy]) - maxypatch[whichx][whichy] = heightmap[(size / subdivision) * whichx + a][(size / subdivision) * whichy + b] * scale; - if (heightmap[(size / subdivision)*whichx + a][(size / subdivision)*whichy + b]*scale < minypatch[whichx][whichy]) - minypatch[whichx][whichy] = heightmap[(size / subdivision) * whichx + a][(size / subdivision) * whichy + b] * scale; - } - } - heightypatch[whichx][whichy] = (maxypatch[whichx][whichy] - minypatch[whichx][whichy]); - if (heightypatch[whichx][whichy] < size / subdivision * scale) - heightypatch[whichx][whichy] = size / subdivision * scale; - avgypatch[whichx][whichy] = (minypatch[whichx][whichy] + maxypatch[whichx][whichy]) / 2; - - for (i = whichx * size / subdivision; i < (whichx + 1)*size / subdivision - 1; i++) { - for (j = whichy * size / subdivision; j < (whichy + 1)*size / subdivision - 1; j++) { - triangles[(i * (size - 1) * 2) + (j * 2)][0].x = i * scale; - triangles[(i * (size - 1) * 2) + (j * 2)][0].y = heightmap[i][j] * scale; - triangles[(i * (size - 1) * 2) + (j * 2)][0].z = j * scale; - - triangles[(i * (size - 1) * 2) + (j * 2)][1].x = i * scale; - triangles[(i * (size - 1) * 2) + (j * 2)][1].y = heightmap[i][j + 1] * scale; - triangles[(i * (size - 1) * 2) + (j * 2)][1].z = j * scale + scale; - - triangles[(i * (size - 1) * 2) + (j * 2)][2].x = i * scale + 1 * scale; - triangles[(i * (size - 1) * 2) + (j * 2)][2].y = heightmap[i + 1][j] * scale; - triangles[(i * (size - 1) * 2) + (j * 2)][2].z = j * scale; - - triangles[(i * (size - 1) * 2) + (j * 2) + 1][0].x = i * scale + 1 * scale; - triangles[(i * (size - 1) * 2) + (j * 2) + 1][0].y = heightmap[i + 1][j] * scale; - triangles[(i * (size - 1) * 2) + (j * 2) + 1][0].z = j * scale; - - triangles[(i * (size - 1) * 2) + (j * 2) + 1][1].x = i * scale; - triangles[(i * (size - 1) * 2) + (j * 2) + 1][1].y = heightmap[i][j + 1] * scale; - triangles[(i * (size - 1) * 2) + (j * 2) + 1][1].z = j * scale + 1 * scale; - - triangles[(i * (size - 1) * 2) + (j * 2) + 1][2].x = i * scale + 1 * scale; - triangles[(i * (size - 1) * 2) + (j * 2) + 1][2].y = heightmap[i + 1][j + 1] * scale; - triangles[(i * (size - 1) * 2) + (j * 2) + 1][2].z = j * scale + 1 * scale; - } - } - -} - - -bool Terrain::load(const std::string& fileName) -{ - static long i, j; - static long x, y; - static float patch_size; - - float temptexdetail = texdetail; - - ImageRec texture; - - //Load Image - if (!load_image(Folders::getResourcePath(fileName).c_str(), texture)) { - return false; - } - - //Is it valid? - if (texture.bpp > 24) { - int bytesPerPixel = texture.bpp / 8; - - int tempnum = 0; - for (i = 0; i < (long)(texture.sizeY * texture.sizeX * bytesPerPixel); i++) { - if ((i + 1) % 4) { - texture.data[tempnum] = texture.data[i]; - tempnum++; - } - } - } - texture.bpp = 24; - if (visibleloading) - Game::LoadingScreen(); - - texdetail = temptexdetail; - - size = texture.sizeX; - - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - heightmap[size - 1 - i][j] = (float)((texture.data[(i + (j * size)) * texture.bpp / 8])) / 5; - } - } - - if (visibleloading) - Game::LoadingScreen(); - - float slopeness; - - for (i = 0; i < subdivision; i++) { - for (j = 0; j < subdivision; j++) { - textureness[i][j] = -1; - } - } - if (visibleloading) - Game::LoadingScreen(); - - - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - heightmap[i][j] *= .5; - - texoffsetx[i][j] = (float)abs(Random() % 100) / 1200 / scale * 3; - texoffsety[i][j] = (float)abs(Random() % 100) / 1200 / scale * 3; - - slopeness = 0; - if (environment == snowyenvironment) { - if (j != 0 && heightmap[i][j] - heightmap[i][j - 1] > slopeness) { - slopeness = heightmap[i][j] - heightmap[i][j - 1]; - } - opacityother[i][j] = slopeness * slopeness * 2; - if (opacityother[i][j] > 1) - opacityother[i][j] = 1; - opacityother[i][j] -= (float)abs(Random() % 100) / 300; - } - if (environment == desertenvironment) { - if (j != 0 && heightmap[i][j] - heightmap[i][j - 1] > slopeness) { - slopeness = heightmap[i][j] - heightmap[i][j - 1]; - } - opacityother[i][j] = slopeness * slopeness * 2; - if (opacityother[i][j] > 1) - opacityother[i][j] = 1; - opacityother[i][j] -= (float)abs(Random() % 100) / 300; - } - if (environment == grassyenvironment) { - if (i != 0 && heightmap[i][j] - heightmap[i - 1][j] > slopeness) { - slopeness = heightmap[i][j] - heightmap[i - 1][j]; - } - if (j != 0 && heightmap[i][j] - heightmap[i][j - 1] > slopeness) { - slopeness = heightmap[i][j] - heightmap[i][j - 1]; - } - if (i < size - 1 && heightmap[i][j] - heightmap[i + 1][j] > slopeness) { - slopeness = heightmap[i][j] - heightmap[i + 1][j]; - } - if (j < size - 1 && heightmap[i][j] - heightmap[i][j + 1] > slopeness) { - slopeness = heightmap[i][j] - heightmap[i][j + 1]; - } - opacityother[i][j] = slopeness * slopeness * 10; - if (opacityother[i][j] > 1) - opacityother[i][j] = 1; - opacityother[i][j] -= (float)abs(Random() % 100) / 100; - } - } - } - if (visibleloading) - Game::LoadingScreen(); - - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - if (environment == snowyenvironment) { - heightmap[i][j] -= opacityother[i][j]; - } - if (environment == desertenvironment) { - heightmap[i][j] -= opacityother[i][j]; - } - } - } - if (visibleloading) - Game::LoadingScreen(); - - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - if (opacityother[i][j] < .1) - opacityother[i][j] = 0; - if (textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == -1) { - if (!opacityother[i][j]) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = allfirst; - if (opacityother[i][j] == 1) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = allsecond; - } - if (opacityother[i][j] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - - x = i; - y = j; - if (i > 0) { - i--; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - - if (j > 0) { - j--; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - j++; - } - - if (j < size - 1) { - j++; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - j--; - } - i++; - } - - if (i < size - 1) { - i++; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - - if (j > 0) { - j--; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - j++; - } - - if (j < size - 1) { - j++; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - j--; - } - i--; - } - - if (j > 0) { - j--; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - j++; - } - - if (j < size - 1) { - j++; - if (opacityother[x][y] && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allfirst) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[x][y] != 1 && textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] == allsecond) - textureness[(int)(i * subdivision / size)][(int)(j * subdivision / size)] = mixed; - if (opacityother[i][j] && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allfirst) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - if (opacityother[i][j] != 1 && textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] == allsecond) - textureness[(int)(x * subdivision / size)][(int)(y * subdivision / size)] = mixed; - j--; - - } - } - } - if (visibleloading) - Game::LoadingScreen(); - - patch_size = size / subdivision; - patch_elements = (patch_size) * (patch_size) * 54; - CalculateNormals(); - - return true; -} - -void Terrain::CalculateNormals() -{ - static int i, j; - static XYZ facenormal; - static XYZ p, q, a, b, c; - - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - normals[i][j].x = 0; - normals[i][j].y = 0; - normals[i][j].z = 0; - } - } - - for (i = 0; i < size - 1; i++) { - for (j = 0; j < size - 1; j++) { - a.x = i; - a.y = heightmap[i][j]; - a.z = j; - b.x = i; - b.y = heightmap[i][j + 1]; - b.z = j + 1; - c.x = i + 1; - c.y = heightmap[i + 1][j]; - c.z = j; - - p.x = b.x - a.x; - p.y = b.y - a.y; - p.z = b.z - a.z; - q.x = c.x - a.x; - q.y = c.y - a.y; - q.z = c.z - a.z; - - CrossProduct(&p, &q, &facenormal); - - facenormals[i][j] = facenormal; - - normals[i][j] = normals[i][j] + facenormal; - normals[i][j + 1] = normals[i][j + 1] + facenormal; - normals[i + 1][j] = normals[i + 1][j] + facenormal; - - - a.x = i + 1; - a.y = heightmap[i + 1][j]; - a.z = j; - b.x = i; - b.y = heightmap[i][j + 1]; - b.z = j + 1; - c.x = i + 1; - c.y = heightmap[i + 1][j + 1]; - c.z = j + 1; - - p.x = b.x - a.x; - p.y = b.y - a.y; - p.z = b.z - a.z; - q.x = c.x - a.x; - q.y = c.y - a.y; - q.z = c.z - a.z; - - CrossProduct(&p, &q, &facenormal); - - normals[i + 1][j + 1] = normals[i + 1][j + 1] + facenormal; - normals[i][j + 1] = normals[i][j + 1] + facenormal; - normals[i + 1][j] = normals[i + 1][j] + facenormal; - - Normalise(&facenormals[i][j]); - } - } - - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - Normalise(&normals[i][j]); - } - } -} - -void Terrain::drawpatch(int whichx, int whichy, float opacity) -{ - if (opacity >= 1) - glDisable(GL_BLEND); - if (opacity < 1) { - glEnable(GL_BLEND); - UpdateTransparency(whichx, whichy); - } - glColor4f(1, 1, 1, 1); - //Set up vertex array - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[0 + whichx * patch_elements + whichy * patch_elements * subdivision]); - glColorPointer(4, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[3 + whichx * patch_elements + whichy * patch_elements * subdivision]); - glTexCoordPointer(2, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[7 + whichx * patch_elements + whichy * patch_elements * subdivision]); - - //Draw - glDrawArrays(GL_TRIANGLES, 0, numtris[whichx][whichy] * 3); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -void Terrain::drawpatchother(int whichx, int whichy, float opacity) -{ - glEnable(GL_BLEND); - if (opacity < 1) { - UpdateTransparency(whichx, whichy); - } - UpdateTransparencyother(whichx, whichy); - glColor4f(1, 1, 1, 1); - //Set up vertex array - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[0 + whichx * patch_elements + whichy * patch_elements * subdivision]); - glColorPointer(4, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[3 + whichx * patch_elements + whichy * patch_elements * subdivision]); - glTexCoordPointer(2, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[7 + whichx * patch_elements + whichy * patch_elements * subdivision]); - - //Draw - glDrawArrays(GL_TRIANGLES, 0, numtris[whichx][whichy] * 3); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -void Terrain::drawpatchotherother(int whichx, int whichy, float opacity) -{ - glEnable(GL_BLEND); - UpdateTransparencyotherother(whichx, whichy); - - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glScalef(6, 6, 6); - - glColor4f(1, 1, 1, 1); - - //Set up vertex array - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[0 + whichx * patch_elements + whichy * patch_elements * subdivision]); - glColorPointer(4, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[3 + whichx * patch_elements + whichy * patch_elements * subdivision]); - glTexCoordPointer(2, GL_FLOAT, 9 * sizeof(GLfloat), &vArray[7 + whichx * patch_elements + whichy * patch_elements * subdivision]); - - //Draw - glDrawArrays(GL_TRIANGLES, 0, numtris[whichx][whichy] * 3); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); -} - - -float Terrain::getHeight(float pointx, float pointz) -{ - static int tilex, tiley; - static XYZ startpoint, endpoint, intersect, triangle[3]; - - pointx /= scale; - pointz /= scale; - - if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) - return 0; - - startpoint.x = pointx; - startpoint.y = -1000; - startpoint.z = pointz; - - endpoint = startpoint; - endpoint.y = 1000; - - tilex = pointx; - tiley = pointz; - - triangle[0].x = tilex; - triangle[0].z = tiley; - triangle[0].y = heightmap[tilex][tiley]; - - triangle[1].x = tilex + 1; - triangle[1].z = tiley; - triangle[1].y = heightmap[tilex + 1][tiley]; - - triangle[2].x = tilex; - triangle[2].z = tiley + 1; - triangle[2].y = heightmap[tilex][tiley + 1]; - - if (!LineFacetd(&startpoint, &endpoint, &triangle[0], &triangle[1], &triangle[2], &intersect)) { - triangle[0].x = tilex + 1; - triangle[0].z = tiley; - triangle[0].y = heightmap[tilex + 1][tiley]; - - triangle[1].x = tilex + 1; - triangle[1].z = tiley + 1; - triangle[1].y = heightmap[tilex + 1][tiley + 1]; - - triangle[2].x = tilex; - triangle[2].z = tiley + 1; - triangle[2].y = heightmap[tilex][tiley + 1]; - LineFacetd(&startpoint, &endpoint, &triangle[0], &triangle[1], &triangle[2], &intersect); - } - return intersect.y * scale + getOpacity(pointx * scale, pointz * scale) / 8; -} - -float Terrain::getOpacity(float pointx, float pointz) -{ - static float height1, height2; - static int tilex, tiley; - - pointx /= scale; - pointz /= scale; - - if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) - return 0; - - tilex = pointx; - tiley = pointz; - - height1 = opacityother[tilex][tiley] * (1 - (pointx - tilex)) + opacityother[tilex + 1][tiley] * (pointx - tilex); - height2 = opacityother[tilex][tiley + 1] * (1 - (pointx - tilex)) + opacityother[tilex + 1][tiley + 1] * (pointx - tilex); - - return height1 * (1 - (pointz - tiley)) + height2 * (pointz - tiley); -} - -XYZ Terrain::getNormal(float pointx, float pointz) -{ - static XYZ height1, height2, total; - static int tilex, tiley; - - pointx /= scale; - pointz /= scale; - - height1 = 0; - if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) - return height1; - tilex = pointx; - tiley = pointz; - - height1 = normals[tilex][tiley] * (1 - (pointx - tilex)) + normals[tilex + 1][tiley] * (pointx - tilex); - height2 = normals[tilex][tiley + 1] * (1 - (pointx - tilex)) + normals[tilex + 1][tiley + 1] * (pointx - tilex); - total = height1 * (1 - (pointz - tiley)) + height2 * (pointz - tiley); - Normalise(&total); - return total; -} - -XYZ Terrain::getLighting(float pointx, float pointz) -{ - static XYZ height1, height2; - static int tilex, tiley; - - pointx /= scale; - pointz /= scale; - - height1 = 0; - if (pointx >= size - 1 || pointz >= size - 1 || pointx <= 0 || pointz <= 0) - return height1; - tilex = pointx; - tiley = pointz; - - height1.x = colors[tilex][tiley][0] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley][0] * (pointx - tilex); - height1.y = colors[tilex][tiley][1] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley][1] * (pointx - tilex); - height1.z = colors[tilex][tiley][2] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley][2] * (pointx - tilex); - height2.x = colors[tilex][tiley + 1][0] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley + 1][0] * (pointx - tilex); - height2.y = colors[tilex][tiley + 1][1] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley + 1][1] * (pointx - tilex); - height2.z = colors[tilex][tiley + 1][2] * (1 - (pointx - tilex)) + colors[tilex + 1][tiley + 1][2] * (pointx - tilex); - - return height1 * (1 - (pointz - tiley)) + height2 * (pointz - tiley); -} - -void Terrain::draw(int layer) -{ - static int i, j; - static float opacity; - static XYZ terrainpoint; - static float distance[subdivision][subdivision]; - - static int beginx, endx; - static int beginz, endz; - - static float patch_size = size / subdivision * scale; - static float viewdistsquared; - - viewdistsquared = viewdistance * viewdistance; - - //Only nearby blocks - beginx = (viewer.x - viewdistance) / (patch_size) - 1; - if (beginx < 0) - beginx = 0; - beginz = (viewer.z - viewdistance) / (patch_size) - 1; - if (beginz < 0) - beginz = 0; - - endx = (viewer.x + viewdistance) / (patch_size) + 1; - if (endx > subdivision) - endx = subdivision; - endz = (viewer.z + viewdistance) / (patch_size) + 1; - if (endz > subdivision) - endz = subdivision; - - if (!layer) { - for (i = beginx; i < endx; i++) { - for (j = beginz; j < endz; j++) { - terrainpoint.x = i * patch_size + (patch_size) / 2; - terrainpoint.y = viewer.y; //heightmap[i][j]*scale; - terrainpoint.z = j * patch_size + (patch_size) / 2; - distance[i][j] = distsq(&viewer, &terrainpoint); - } - } - } - for (i = beginx; i < endx; i++) { - for (j = beginz; j < endz; j++) { - if (distance[i][j] < (viewdistance + patch_size) * (viewdistance + patch_size)) { - opacity = 1; - if (distance[i][j] > viewdistsquared * fadestart - viewdistsquared) - opacity = 0; - if (opacity == 1 && i != subdivision) - if (distance[i + 1][j] > viewdistsquared * fadestart - viewdistsquared) - opacity = 0; - if (opacity == 1 && j != subdivision) - if (distance[i][j + 1] > viewdistsquared * fadestart - viewdistsquared) - opacity = 0; - if (opacity == 1 && j != subdivision && i != subdivision) - if (distance[i + 1][j + 1] > viewdistsquared * fadestart - viewdistsquared) - opacity = 0; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - if (frustum.CubeInFrustum(i * patch_size + patch_size * .5, avgypatch[i][j], j * patch_size + patch_size * .5, heightypatch[i][j] / 2)) { - if (environment == desertenvironment && distance[i][j] > viewdistsquared / 4) - glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, blurness); - else if (environment == desertenvironment) - glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); - if (!layer && textureness[i][j] != allsecond) - drawpatch(i, j, opacity); - if (layer == 1 && textureness[i][j] != allfirst) - drawpatchother(i, j, opacity); - if (layer == 2 && textureness[i][j] != allfirst) - drawpatchotherother(i, j, opacity); - } - glPopMatrix(); - } - } - } - if (environment == desertenvironment) - glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0 ); -} - -void Terrain::drawdecals() -{ - if (decals) { - static int i; - static float distancemult; - static int lasttype; - - static float viewdistsquared; - static bool blend; - - viewdistsquared = viewdistance * viewdistance; - blend = 1; - - lasttype = -1; - glEnable(GL_BLEND); - glDisable(GL_LIGHTING); - glDisable(GL_CULL_FACE); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(0); - for (i = 0; i < numdecals; i++) { - if (decaltype[i] == blooddecalfast && decalalivetime[i] < 2) - decalalivetime[i] = 2; - if ((decaltype[i] == shadowdecal || decaltype[i] == shadowdecalpermanent) && decaltype[i] != lasttype) { - shadowtexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - } - if (decaltype[i] == footprintdecal && decaltype[i] != lasttype) { - footprinttexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - } - if (decaltype[i] == bodyprintdecal && decaltype[i] != lasttype) { - bodyprinttexture.bind(); - if (!blend) { - blend = 1; - glAlphaFunc(GL_GREATER, 0.0001); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - } - if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalslow) && decaltype[i] != lasttype) { - bloodtexture.bind(); - if (blend) { - blend = 0; - glAlphaFunc(GL_GREATER, 0.15); - glBlendFunc(GL_ONE, GL_ZERO); - } - } - if ((decaltype[i] == blooddecalfast) && decaltype[i] != lasttype) { - bloodtexture2.bind(); - if (blend) { - blend = 0; - glAlphaFunc(GL_GREATER, 0.15); - glBlendFunc(GL_ONE, GL_ZERO); - } - } - if (decaltype[i] == shadowdecal || decaltype[i] == shadowdecalpermanent) { - distancemult = (viewdistsquared - (distsq(&viewer, &decalposition[i]) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; - if (distancemult >= 1) - glColor4f(1, 1, 1, decalopacity[i]); - if (distancemult < 1) - glColor4f(1, 1, 1, decalopacity[i]*distancemult); - } - if (decaltype[i] == footprintdecal || decaltype[i] == bodyprintdecal) { - distancemult = (viewdistsquared - (distsq(&viewer, &decalposition[i]) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; - if (distancemult >= 1) { - glColor4f(1, 1, 1, decalopacity[i]); - if (decalalivetime[i] > 3) - glColor4f(1, 1, 1, decalopacity[i] * (5 - decalalivetime[i]) / 2); - } - if (distancemult < 1) { - glColor4f(1, 1, 1, decalopacity[i]*distancemult); - if (decalalivetime[i] > 3) - glColor4f(1, 1, 1, decalopacity[i] * (5 - decalalivetime[i]) / 2 * distancemult); - } - } - if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow)) { - distancemult = (viewdistsquared - (distsq(&viewer, &decalposition[i]) - (viewdistsquared * fadestart)) * (1 / (1 - fadestart))) / viewdistsquared; - if (distancemult >= 1) { - glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]); - if (decalalivetime[i] < 4) - glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]*decalalivetime[i]*.25); - if (decalalivetime[i] > 58) - glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i] * (60 - decalalivetime[i]) / 2); - } - if (distancemult < 1) { - glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]*distancemult); - if (decalalivetime[i] < 4) - glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i]*decalalivetime[i]*distancemult * .25); - if (decalalivetime[i] > 58) - glColor4f(decalbrightness[i], decalbrightness[i], decalbrightness[i], decalopacity[i] * (60 - decalalivetime[i]) / 2 * distancemult); - } - } - lasttype = decaltype[i]; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glBegin(GL_TRIANGLES); - for (int j = 0; j < 3; j++) { - glTexCoord2f(decaltexcoords[i][j][0], decaltexcoords[i][j][1]); - glVertex3f(decalvertex[i][j].x, decalvertex[i][j].y, decalvertex[i][j].z); - } - glEnd(); - glPopMatrix(); - } - for (i = numdecals - 1; i >= 0; i--) { - decalalivetime[i] += multiplier; - if (decaltype[i] == blooddecalslow) - decalalivetime[i] -= multiplier * 2 / 3; - if (decaltype[i] == blooddecalfast) - decalalivetime[i] += multiplier * 4; - if (decaltype[i] == shadowdecal) - DeleteDecal(i); - if (decaltype[i] == footprintdecal && decalalivetime[i] >= 5) - DeleteDecal(i); - if (decaltype[i] == bodyprintdecal && decalalivetime[i] >= 5) - DeleteDecal(i); - if ((decaltype[i] == blooddecal || decaltype[i] == blooddecalfast || decaltype[i] == blooddecalslow) && decalalivetime[i] >= 60) - DeleteDecal(i); - } - glAlphaFunc(GL_GREATER, 0.0001); - } -} - -void Terrain::AddObject(XYZ where, float radius, int id) -{ - bool done; - int i, j; - XYZ points[4]; - if (id >= 0 && id < 10000) - for (i = 0; i < subdivision; i++) { - for (j = 0; j < subdivision; j++) { - if (patchobjectnum[i][j] < 300 - 1) { - done = 0; - points[0].x = (size / subdivision) * i; - points[0].z = (size / subdivision) * j; - points[0].y = heightmap[(int)points[0].x][(int)points[0].z]; - points[1].x = (size / subdivision) * (i + 1); - points[1].z = (size / subdivision) * j; - points[1].y = heightmap[(int)points[1].x][(int)points[1].z]; - points[2].x = (size / subdivision) * (i + 1); - points[2].z = (size / subdivision) * (j + 1); - points[2].y = heightmap[(int)points[2].x][(int)points[2].z]; - points[3].x = (size / subdivision) * i; - points[3].z = (size / subdivision) * (j + 1); - points[3].y = heightmap[(int)points[3].x][(int)points[3].z]; - points[0] *= scale; - points[1] *= scale; - points[2] *= scale; - points[3] *= scale; - if (!done && where.x + radius > points[0].x && where.x - radius < points[2].x && where.z + radius > points[0].z && where.z - radius < points[2].z) { - patchobjects[i][j][patchobjectnum[i][j]] = id; - patchobjectnum[i][j]++; - done = 1; - } - } - } - } -} - -void Terrain::DeleteDecal(int which) -{ - if (decals) { - decaltype[which] = decaltype[numdecals - 1]; - decalposition[which] = decalposition[numdecals - 1]; - for (int i = 0; i < 3; i++) { - decalvertex[which][i] = decalvertex[numdecals - 1][i]; - decaltexcoords[which][i][0] = decaltexcoords[numdecals - 1][i][0]; - decaltexcoords[which][i][1] = decaltexcoords[numdecals - 1][i][1]; - } - decalrotation[which] = decalrotation[numdecals - 1]; - decalalivetime[which] = decalalivetime[numdecals - 1]; - decalopacity[which] = decalopacity[numdecals - 1]; - decalbrightness[which] = decalbrightness[numdecals - 1]; - numdecals--; - } -} - -void Terrain::MakeDecal(int type, XYZ where, float size, float opacity, float rotation) -{ - if (decals) { - if (opacity > 0 && size > 0) { - static int patchx[4]; - static int patchy[4]; - - decaltexcoords[numdecals][0][0] = 1; - decaltexcoords[numdecals][0][1] = 0; - - patchx[0] = (where.x + size) / scale; - patchx[1] = (where.x - size) / scale; - patchx[2] = (where.x - size) / scale; - patchx[3] = (where.x + size) / scale; - - patchy[0] = (where.z - size) / scale; - patchy[1] = (where.z - size) / scale; - patchy[2] = (where.z + size) / scale; - patchy[3] = (where.z + size) / scale; - - if ((patchx[0] != patchx[1] || patchy[0] != patchy[1]) && (patchx[0] != patchx[2] || patchy[0] != patchy[2]) && (patchx[0] != patchx[3] || patchy[0] != patchy[3])) { - MakeDecalLock(type, where, patchx[0], patchy[0], size, opacity, rotation); - } - - if ((patchx[1] != patchx[2] || patchy[1] != patchy[2]) && (patchx[1] != patchx[3] || patchy[1] != patchy[3])) { - MakeDecalLock(type, where, patchx[1], patchy[1], size, opacity, rotation); - } - - if ((patchx[2] != patchx[3] || patchy[2] != patchy[3])) { - MakeDecalLock(type, where, patchx[2], patchy[2], size, opacity, rotation); - } - MakeDecalLock(type, where, patchx[3], patchy[3], size, opacity, rotation); - } - } - //} -} - -void Terrain::MakeDecalLock(int type, XYZ where, int whichx, int whichy, float size, float opacity, float rotation) -{ - if (decals) { - static float placex, placez; - static XYZ rot; - - float decalbright; - - rot = getLighting(where.x, where.z); - decalbrightness[numdecals] = (rot.x + rot.y + rot.z) / 3; - if (decalbrightness[numdecals] < .4) - decalbrightness[numdecals] = .4; - - if (environment == grassyenvironment) { - decalbrightness[numdecals] *= .6; - } - - if (decalbrightness[numdecals] > 1) - decalbrightness[numdecals] = 1; - decalbright = decalbrightness[numdecals]; - - decalposition[numdecals] = where; - decaltype[numdecals] = type; - decalopacity[numdecals] = opacity; - decalrotation[numdecals] = rotation; - decalalivetime[numdecals] = 0; - - placex = (float)whichx * scale + scale; - placez = (float)whichy * scale; - - decaltexcoords[numdecals][0][0] = (placex - where.x) / size / 2 + .5; - decaltexcoords[numdecals][0][1] = (placez - where.z) / size / 2 + .5; - - decalvertex[numdecals][0].x = placex; - decalvertex[numdecals][0].z = placez; - decalvertex[numdecals][0].y = heightmap[whichx + 1][whichy] * scale + .01; - - - placex = (float)whichx * scale + scale; - placez = (float)whichy * scale + scale; - - decaltexcoords[numdecals][1][0] = (placex - where.x) / size / 2 + .5; - decaltexcoords[numdecals][1][1] = (placez - where.z) / size / 2 + .5; - - decalvertex[numdecals][1].x = placex; - decalvertex[numdecals][1].z = placez; - decalvertex[numdecals][1].y = heightmap[whichx + 1][whichy + 1] * scale + .01; - - - placex = (float)whichx * scale; - placez = (float)whichy * scale + scale; - - decaltexcoords[numdecals][2][0] = (placex - where.x) / size / 2 + .5; - decaltexcoords[numdecals][2][1] = (placez - where.z) / size / 2 + .5; - - decalvertex[numdecals][2].x = placex; - decalvertex[numdecals][2].z = placez; - decalvertex[numdecals][2].y = heightmap[whichx][whichy + 1] * scale + .01; - - if (decalrotation[numdecals]) { - for (int i = 0; i < 3; i++) { - rot.y = 0; - rot.x = decaltexcoords[numdecals][i][0] - .5; - rot.z = decaltexcoords[numdecals][i][1] - .5; - rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); - decaltexcoords[numdecals][i][0] = rot.x + .5; - decaltexcoords[numdecals][i][1] = rot.z + .5; - } - } - - if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) - if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) - if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) - if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) - if (numdecals < max_decals - 1) - numdecals++; - - decalbrightness[numdecals] = decalbright; - - decalposition[numdecals] = where; - decaltype[numdecals] = type; - decalopacity[numdecals] = opacity; - decalrotation[numdecals] = rotation; - decalalivetime[numdecals] = 0; - - placex = (float)whichx * scale + scale; - placez = (float)whichy * scale; - - decaltexcoords[numdecals][0][0] = (placex - where.x) / size / 2 + .5; - decaltexcoords[numdecals][0][1] = (placez - where.z) / size / 2 + .5; - - decalvertex[numdecals][0].x = placex; - decalvertex[numdecals][0].z = placez; - decalvertex[numdecals][0].y = heightmap[whichx + 1][whichy] * scale + .01; - - - placex = (float)whichx * scale; - placez = (float)whichy * scale; - - decaltexcoords[numdecals][1][0] = (placex - where.x) / size / 2 + .5; - decaltexcoords[numdecals][1][1] = (placez - where.z) / size / 2 + .5; - - decalvertex[numdecals][1].x = placex; - decalvertex[numdecals][1].z = placez; - decalvertex[numdecals][1].y = heightmap[whichx][whichy] * scale + .01; - - - placex = (float)whichx * scale; - placez = (float)whichy * scale + scale; - - decaltexcoords[numdecals][2][0] = (placex - where.x) / size / 2 + .5; - decaltexcoords[numdecals][2][1] = (placez - where.z) / size / 2 + .5; - - decalvertex[numdecals][2].x = placex; - decalvertex[numdecals][2].z = placez; - decalvertex[numdecals][2].y = heightmap[whichx][whichy + 1] * scale + .01; - - if (decalrotation[numdecals]) { - for (int i = 0; i < 3; i++) { - rot.y = 0; - rot.x = decaltexcoords[numdecals][i][0] - .5; - rot.z = decaltexcoords[numdecals][i][1] - .5; - rot = DoRotation(rot, 0, -decalrotation[numdecals], 0); - decaltexcoords[numdecals][i][0] = rot.x + .5; - decaltexcoords[numdecals][i][1] = rot.z + .5; - } - } - - if (!(decaltexcoords[numdecals][0][0] < 0 && decaltexcoords[numdecals][1][0] < 0 && decaltexcoords[numdecals][2][0] < 0)) - if (!(decaltexcoords[numdecals][0][1] < 0 && decaltexcoords[numdecals][1][1] < 0 && decaltexcoords[numdecals][2][1] < 0)) - if (!(decaltexcoords[numdecals][0][0] > 1 && decaltexcoords[numdecals][1][0] > 1 && decaltexcoords[numdecals][2][0] > 1)) - if (!(decaltexcoords[numdecals][0][1] > 1 && decaltexcoords[numdecals][1][1] > 1 && decaltexcoords[numdecals][2][1] > 1)) - if (numdecals < max_decals - 1) - numdecals++; - } -} - -void Terrain::DoShadows() -{ - static int i, j, k, l, todivide; - static float brightness, total; - static XYZ testpoint, testpoint2, terrainpoint, lightloc, col; - lightloc = light.location; - if (!skyboxtexture) { - lightloc.x = 0; - lightloc.z = 0; - } - if (skyboxtexture && tutoriallevel) { - lightloc.x *= .4; - lightloc.z *= .4; - } - int patchx, patchz; - float shadowed; - Normalise(&lightloc); - //Calculate shadows - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - terrainpoint.x = (float)(i) * scale; - terrainpoint.z = (float)(j) * scale; - terrainpoint.y = heightmap[i][j] * scale; - - shadowed = 0; - patchx = (float)(i) * subdivision / size; - patchz = (float)(j) * subdivision / size; - if (patchobjectnum[patchx][patchz]) { - for (k = 0; k < patchobjectnum[patchx][patchz]; k++) { - l = patchobjects[patchx][patchz][k]; - if (objects.type[l] != treetrunktype) { - testpoint = terrainpoint; - testpoint2 = terrainpoint + lightloc * 50 * (1 - shadowed); - if (objects.model[l].LineCheck(&testpoint, &testpoint2, &col, &objects.position[l], &objects.yaw[l]) != -1) { - shadowed = 1 - (findDistance(&terrainpoint, &col) / 50); - } - } - } - if (visibleloading) - Game::LoadingScreen(); - } - brightness = dotproduct(&lightloc, &normals[i][j]); - if (shadowed) - brightness *= 1 - shadowed; - - if (brightness > 1) - brightness = 1; - if (brightness < 0) - brightness = 0; - - colors[i][j][0] = light.color[0] * brightness + light.ambient[0]; - colors[i][j][1] = light.color[1] * brightness + light.ambient[1]; - colors[i][j][2] = light.color[2] * brightness + light.ambient[2]; - - if (colors[i][j][0] > 1) colors[i][j][0] = 1; - if (colors[i][j][1] > 1) colors[i][j][1] = 1; - if (colors[i][j][2] > 1) colors[i][j][2] = 1; - if (colors[i][j][0] < 0) colors[i][j][0] = 0; - if (colors[i][j][1] < 0) colors[i][j][1] = 0; - if (colors[i][j][2] < 0) colors[i][j][2] = 0; - } - } - - if (visibleloading) - Game::LoadingScreen(); - - //Smooth shadows - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - for (k = 0; k < 3; k++) { - total = 0; - todivide = 0; - if (i != 0) { - total += colors[j][i - 1][k]; - todivide++; - } - if (i != size - 1) { - total += colors[j][i + 1][k]; - todivide++; - } - if (j != 0) { - total += colors[j - 1][i][k]; - todivide++; - } - if (j != size - 1) { - total += colors[j + 1][i][k]; - todivide++; - } - if (i != 0 && j != 0) { - total += colors[j - 1][i - 1][k]; - todivide++; - } - if (i != size - 1 && j != 0) { - total += colors[j - 1][i + 1][k]; - todivide++; - } - if (j != size - 1 && i != size - 1) { - total += colors[j + 1][i + 1][k]; - todivide++; - } - if (j != size - 1 && i != 0) { - total += colors[j + 1][i - 1][k]; - todivide++; - } - total += colors[j][i][k]; - todivide++; - - colors[j][i][k] = total / todivide; - } - } - } - - for (i = 0; i < subdivision; i++) { - for (j = 0; j < subdivision; j++) { - UpdateVertexArray(i, j); - } - } -} - -Terrain::Terrain() -{ - size = 0; - - memset(patchobjectnum, 0, sizeof(patchobjectnum)); - memset(patchobjects, 0, sizeof(patchobjects)); - - scale = 1.0f; - type = 0; - memset(heightmap, 0, sizeof(heightmap)); - memset(normals, 0, sizeof(normals)); - memset(facenormals, 0, sizeof(facenormals)); - memset(triangles, 0, sizeof(triangles)); - memset(colors, 0, sizeof(colors)); - memset(opacityother, 0, sizeof(opacityother)); - memset(texoffsetx, 0, sizeof(texoffsetx)); - memset(texoffsety, 0, sizeof(texoffsety)); - memset(numtris, 0, sizeof(numtris)); - memset(textureness, 0, sizeof(textureness)); - - memset(vArray, 0, sizeof(vArray)); - - memset(visible, 0, sizeof(visible)); - memset(avgypatch, 0, sizeof(avgypatch)); - memset(maxypatch, 0, sizeof(maxypatch)); - memset(minypatch, 0, sizeof(minypatch)); - memset(heightypatch, 0, sizeof(heightypatch)); - - patch_elements = 0; - - memset(decaltexcoords, 0, sizeof(decaltexcoords)); - memset(decalvertex, 0, sizeof(decalvertex)); - memset(decaltype, 0, sizeof(decaltype)); - memset(decalopacity, 0, sizeof(decalopacity)); - memset(decalrotation, 0, sizeof(decalrotation)); - memset(decalalivetime, 0, sizeof(decalalivetime)); - memset(decalbrightness, 0, sizeof(decalbrightness)); - memset(decalposition, 0, sizeof(decalposition)); - numdecals = 0; -} -Terrain::~Terrain() -{ - terraintexture.destroy(); - shadowtexture.destroy(); - bodyprinttexture.destroy(); - footprinttexture.destroy(); - bloodtexture.destroy(); - bloodtexture2.destroy(); - breaktexture.destroy(); -} - diff --git a/Source/Terrain.h b/Source/Terrain.h deleted file mode 100644 index c375687..0000000 --- a/Source/Terrain.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _TERRAIN_H_ -#define _TERRAIN_H_ - -#include "gamegl.h" -#include "Frustum.h" -#include "Lights.h" -#include "ImageIO.h" -#include "Quaternions.h" -#include "Quaternions.h" -#include "Texture.h" - -#define max_terrain_size 256 -#define curr_terrain_size size -#define subdivision 64 -#define max_patch_elements (max_terrain_size/subdivision)*(max_terrain_size/subdivision)*54 - -#define allfirst 0 -#define mixed 1 -#define allsecond 2 - -#define max_decals 1000 - -#define shadowdecal 0 -#define footprintdecal 1 -#define blooddecal 2 -#define blooddecalfast 3 -#define shadowdecalpermanent 4 -#define breakdecal 5 -#define blooddecalslow 6 -#define bodyprintdecal 7 - -#define snowyenvironment 0 -#define grassyenvironment 1 -#define desertenvironment 2 -// -// Model Structures -// - -class Terrain -{ -public: - Texture bloodtexture; - Texture bloodtexture2; - Texture shadowtexture; - Texture footprinttexture; - Texture bodyprinttexture; - Texture breaktexture; - Texture terraintexture; - short size; - - int patchobjectnum[subdivision][subdivision]; - int patchobjects[subdivision][subdivision][300]; - - float scale; - int type; - float heightmap[max_terrain_size + 1][max_terrain_size + 1]; - XYZ normals[max_terrain_size][max_terrain_size]; - XYZ facenormals[max_terrain_size][max_terrain_size]; - XYZ triangles[(max_terrain_size - 1) * (max_terrain_size - 1) * 2][3]; - float colors[max_terrain_size][max_terrain_size][4]; - float opacityother[max_terrain_size][max_terrain_size]; - float texoffsetx[max_terrain_size][max_terrain_size]; - float texoffsety[max_terrain_size][max_terrain_size]; - int numtris[subdivision][subdivision]; - int textureness[subdivision][subdivision]; - - GLfloat vArray[(max_patch_elements)*subdivision*subdivision]; - - bool visible[subdivision][subdivision]; - float avgypatch[subdivision][subdivision]; - float maxypatch[subdivision][subdivision]; - float minypatch[subdivision][subdivision]; - float heightypatch[subdivision][subdivision]; - - int patch_elements; - - float decaltexcoords[max_decals][3][2]; - XYZ decalvertex[max_decals][3]; - int decaltype[max_decals]; - float decalopacity[max_decals]; - float decalrotation[max_decals]; - float decalalivetime[max_decals]; - float decalbrightness[max_decals]; - XYZ decalposition[max_decals]; - int numdecals; - - void AddObject(XYZ where, float radius, int id); - void DeleteDecal(int which); - void MakeDecal(int type, XYZ where, float size, float opacity, float rotation); - void MakeDecalLock(int type, XYZ where, int whichx, int whichy, float size, float opacity, float rotation); - int lineTerrain(XYZ p1, XYZ p2, XYZ *p); - float getHeight(float pointx, float pointz); - float getOpacity(float pointx, float pointz); - XYZ getLighting(float pointx, float pointz); - XYZ getNormal(float pointx, float pointz); - void UpdateVertexArray(int whichx, int whichy); - void UpdateTransparency(int whichx, int whichy); - void UpdateTransparencyother(int whichx, int whichy); - void UpdateTransparencyotherother(int whichx, int whichy); - bool load(const std::string& fileName); - void CalculateNormals(); - void drawdecals(); - void draw(int layer); - void drawpatch(int whichx, int whichy, float opacity); - void drawpatchother(int whichx, int whichy, float opacity); - void drawpatchotherother(int whichx, int whichy, float opacity); - void DoShadows(); - - Terrain(); - ~Terrain(); -}; - -#endif diff --git a/Source/Text.cpp b/Source/Text.cpp deleted file mode 100644 index 0a31c3e..0000000 --- a/Source/Text.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -/**> HEADER FILES <**/ -#include "Text.h" -#include "Game.h" - -void Text::LoadFontTexture(const std::string& fileName) -{ - LOGFUNC; - - LOG(std::string("Loading font texture...") + fileName); - - FontTexture.load(fileName, false); - if (base) { - glDeleteLists(base, 512); - base = 0; - } -} - -void Text::BuildFont() // Build Our Font Display List -{ - float cx; // Holds Our X Character Coord - float cy; // Holds Our Y Character Coord - int loop; - - LOGFUNC; - - if (base) { - glDeleteLists(base, 512); - base = 0; - } - - base = glGenLists(512); // Creating 256 Display Lists - FontTexture.bind(); - for (loop = 0; loop < 512; loop++) { // Loop Through All 256 Lists - if (loop < 256) { - cx = float(loop % 16) / 16.0f; // X Position Of Current Character - cy = float(loop / 16) / 16.0f; // Y Position Of Current Character - } else { - cx = float((loop - 256) % 16) / 16.0f; // X Position Of Current Character - cy = float((loop - 256) / 16) / 16.0f; // Y Position Of Current Character - } - glNewList(base + loop, GL_COMPILE); // Start Building A List - glBegin(GL_QUADS); // Use A Quad For Each Character - glTexCoord2f(cx, 1 - cy - 0.0625f + .001); // Texture Coord (Bottom Left) - glVertex2i(0, 0); // Vertex Coord (Bottom Left) - glTexCoord2f(cx + 0.0625f, 1 - cy - 0.0625f + .001); // Texture Coord (Bottom Right) - glVertex2i(16, 0); // Vertex Coord (Bottom Right) - glTexCoord2f(cx + 0.0625f, 1 - cy - .001); // Texture Coord (Top Right) - glVertex2i(16, 16); // Vertex Coord (Top Right) - glTexCoord2f(cx, 1 - cy - +.001); // Texture Coord (Top Left) - glVertex2i(0, 16); // Vertex Coord (Top Left) - glEnd(); // Done Building Our Quad (Character) - if (loop < 256) - glTranslated(10, 0, 0); // Move To The Right Of The Character - else - glTranslated(8, 0, 0); // Move To The Right Of The Character - glEndList(); // Done Building The Display List - } // Loop Until All 256 Are Built -} - -void Text::glPrint(float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens -{ - glPrint(x, y, string, set, size, width, height, 0, strlen(string)); -} - -void Text::_glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end, int offset) // Where The Printing Happens -{ - if (set > 1) { - set = 1; - } - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - FontTexture.bind(); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, width, 0, height, -100, 100); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glTranslated(x, y, 0); - glScalef(size, size, 1); - glListBase(base - 32 + (128 * set) + offset); // Choose The Font Set (0 or 1) - glCallLists(end - start, GL_BYTE, &string[start]); // Write The Text To The Screen - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glEnable(GL_DEPTH_TEST); - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); -} - -void Text::glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end) // Where The Printing Happens -{ - _glPrint(x, y, string, set, size, width, height, start, end, 0); -} - -void Text::glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens -{ - glPrintOutline(x, y, string, set, size, width, height, 0, strlen(string)); -} - -void Text::glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height, int start, int end) // Where The Printing Happens -{ - _glPrint(x, y, string, set, size, width, height, start, end, 256); -} -void Text::glPrintOutlined(float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens -{ - glPrintOutlined(1, 1, 1, x, y, string, set, size, width, height); -} - -void Text::glPrintOutlined(float r, float g, float b, float x, float y, const char *string, int set, float size, float width, float height) // Where The Printing Happens -{ - glColor4f(0, 0, 0, 1); - glPrintOutline( x - 2 * size, y - 2 * size, string, set, size * 2.5 / 2, width, height); - glColor4f(r, g, b, 1); - glPrint( x, y, string, set, size, width, height); -} - -Text::Text() -{ - base = 0; -} -Text::~Text() -{ - if (base) { - glDeleteLists(base, 512); - base = 0; - } - FontTexture.destroy(); -} - diff --git a/Source/Text.h b/Source/Text.h deleted file mode 100644 index c2a2ccc..0000000 --- a/Source/Text.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _TEXT_H_ -#define _TEXT_H_ - - -/**> HEADER FILES <**/ -#include "Quaternions.h" -//#include "Files.h" -#include "Quaternions.h" -#include "gamegl.h" -#include "ImageIO.h" -#include "Texture.h" - -class Text -{ -public: - Texture FontTexture; - GLuint base; - - void LoadFontTexture(const std::string& fileName); - void BuildFont(); - void glPrint(float x, float y, const char *string, int set, float size, float width, float height); - void glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height); - void glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end); - void glPrintOutline(float x, float y, const char *string, int set, float size, float width, float height, int start, int end); - void glPrintOutlined(float x, float y, const char *string, int set, float size, float width, float height); - void glPrintOutlined(float r, float g, float b, float x, float y, const char *string, int set, float size, float width, float height); - - Text(); - ~Text(); - -private: - void _glPrint(float x, float y, const char *string, int set, float size, float width, float height, int start, int end, int offset); -}; - -#endif diff --git a/Source/Texture.cpp b/Source/Texture.cpp deleted file mode 100644 index 3106c8f..0000000 --- a/Source/Texture.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include "gamegl.h" -#include "Texture.h" -#include "ImageIO.h" -#include "Utils/Folders.h" - -using namespace std; - -extern bool trilinear; - -vector TextureRes::list; - -void TextureRes::load() -{ - ImageRec texture; - - //load image into 'texture' - if (!load_image(filename.c_str(), texture)) { - cerr << "Texture " << filename << " loading failed" << endl; - return; - } - - skinsize = texture.sizeX; - GLuint type = GL_RGBA; - if (texture.bpp == 24) - type = GL_RGB; - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glDeleteTextures(1, &id); - glGenTextures(1, &id); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - glBindTexture(GL_TEXTURE_2D, id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (hasMipmap) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (trilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR_MIPMAP_NEAREST)); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - if (isSkin) { - free(data); - const int nb = texture.sizeY * texture.sizeX * (texture.bpp / 8); - data = (GLubyte*)malloc(nb * sizeof(GLubyte)); - datalen = 0; - for (int i = 0; i < nb; i++) - if ((i + 1) % 4 || type == GL_RGB) - data[datalen++] = texture.data[i]; - glTexImage2D(GL_TEXTURE_2D, 0, type, texture.sizeX, texture.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, data); - } else { - glTexImage2D(GL_TEXTURE_2D, 0, type, texture.sizeX, texture.sizeY, 0, type, GL_UNSIGNED_BYTE, texture.data); - } -} - -void TextureRes::bind() -{ - glBindTexture(GL_TEXTURE_2D, id); -} - -TextureRes::TextureRes(const string& _filename, bool _hasMipmap): - id(0), filename(_filename), hasMipmap(_hasMipmap), isSkin(false), - skinsize(0), data(NULL), datalen(0) -{ - load(); - list.push_back(this); -} - -TextureRes::TextureRes(const string& _filename, bool _hasMipmap, GLubyte* array, int* skinsizep): - id(0), filename(_filename), hasMipmap(_hasMipmap), isSkin(true), - skinsize(0), data(NULL), datalen(0) -{ - load(); - *skinsizep = skinsize; - for (int i = 0; i < datalen; i++) - array[i] = data[i]; - list.push_back(this); -} - -TextureRes::~TextureRes() -{ - free(data); - glDeleteTextures(1, &id); - for (vector::iterator it = list.begin(); it != list.end(); it++) - if (*it == this) { - list.erase(it); - break; - } -} - -void Texture::load(const string& filename, bool hasMipmap) -{ - destroy(); - tex = new TextureRes(Folders::getResourcePath(filename), hasMipmap); -} - -void Texture::load(const string& filename, bool hasMipmap, GLubyte* array, int* skinsizep) -{ - destroy(); - tex = new TextureRes(Folders::getResourcePath(filename), hasMipmap, array, skinsizep); -} - -void Texture::destroy() -{ - if (tex) { - delete tex; - tex = NULL; - } -} - -void Texture::bind() -{ - if (tex) - tex->bind(); - else - glBindTexture(GL_TEXTURE_2D, 0); -} diff --git a/Source/Texture.h b/Source/Texture.h deleted file mode 100644 index 95547bf..0000000 --- a/Source/Texture.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _TEXTURE_H_ -#define _TEXTURE_H_ - -#include -#include -#include -using namespace std; - -class TextureRes -{ -private: - static vector list; - - GLuint id; - string filename; - bool hasMipmap; - bool isSkin; - int skinsize; - GLubyte* data; - int datalen; - - void load(); - -public: - TextureRes(const string& filename, bool hasMipmap); - TextureRes(const string& filename, bool hasMipmap, GLubyte* array, int* skinsize); - ~TextureRes(); - void bind(); -}; - -class Texture -{ -private: - TextureRes* tex; -public: - inline Texture(): tex(NULL) {} - void load(const string& filename, bool hasMipmap); - void load(const string& filename, bool hasMipmap, GLubyte* array, int* skinsizep); - void destroy(); - void bind(); -}; - -#endif diff --git a/Source/Thirdparty/optionparser.h b/Source/Thirdparty/optionparser.h new file mode 100644 index 0000000..2e178ce --- /dev/null +++ b/Source/Thirdparty/optionparser.h @@ -0,0 +1,2821 @@ +/* + * The Lean Mean C++ Option Parser + * + * Copyright (C) 2012 Matthias S. Benkmann + * + * The "Software" in the following 2 paragraphs refers to this file containing + * the code to The Lean Mean C++ Option Parser. + * The "Software" does NOT refer to any other files which you + * may have received alongside this file (e.g. as part of a larger project that + * incorporates The Lean Mean C++ Option Parser). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software, to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following + * conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * NOTE: It is recommended that you read the processed HTML doxygen documentation + * rather than this source. If you don't know doxygen, it's like javadoc for C++. + * If you don't want to install doxygen you can find a copy of the processed + * documentation at + * + * http://optionparser.sourceforge.net/ + * + */ + +/** + * @file + * + * @brief This is the only file required to use The Lean Mean C++ Option Parser. + * Just \#include it and you're set. + * + * The Lean Mean C++ Option Parser handles the program's command line arguments + * (argc, argv). + * It supports the short and long option formats of getopt(), getopt_long() + * and getopt_long_only() but has a more convenient interface. + * The following features set it apart from other option parsers: + * + * @par Highlights: + *
    + *
  • It is a header-only library. Just \#include "Thirdparty/optionparser.h" and you're set. + *
  • It is freestanding. There are no dependencies whatsoever, not even the + * C or C++ standard library. + *
  • It has a usage message formatter that supports column alignment and + * line wrapping. This aids localization because it adapts to + * translated strings that are shorter or longer (even if they contain + * Asian wide characters). + *
  • Unlike getopt() and derivatives it doesn't force you to loop through + * options sequentially. Instead you can access options directly like this: + *
      + *
    • Test for presence of a switch in the argument vector: + * @code if ( options[QUIET] ) ... @endcode + *
    • Evaluate --enable-foo/--disable-foo pair where the last one used wins: + * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode + *
    • Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose): + * @code int verbosity = options[VERBOSE].count(); @endcode + *
    • Iterate over all --file=<fname> arguments: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + *
    • If you really want to, you can still process all arguments in order: + * @code + * for (int i = 0; i < p.optionsCount(); ++i) { + * Option& opt = buffer[i]; + * switch(opt.index()) { + * case HELP: ... + * case VERBOSE: ... + * case FILE: fname = opt.arg; ... + * case UNKNOWN: ... + * @endcode + *
    + *
@n + * Despite these features the code size remains tiny. + * It is smaller than uClibc's GNU getopt() and just a + * couple 100 bytes larger than uClibc's SUSv3 getopt(). @n + * (This does not include the usage formatter, of course. But you don't have to use that.) + * + * @par Download: + * Tarball with examples and test programs: + * optionparser-1.4.tar.gz @n + * Just the header (this is all you really need): + * optionparser.h + * + * @par Changelog: + * Version 1.4: Fixed 2 printUsage() bugs that messed up output with small COLUMNS values @n + * Version 1.3: Compatible with Microsoft Visual C++. @n + * Version 1.2: Added @ref option::Option::namelen "Option::namelen" and removed the extraction + * of short option characters into a special buffer. @n + * Changed @ref option::Arg::Optional "Arg::Optional" to accept arguments if they are attached + * rather than separate. This is what GNU getopt() does and how POSIX recommends + * utilities should interpret their arguments.@n + * Version 1.1: Optional mode with argument reordering as done by GNU getopt(), so that + * options and non-options can be mixed. See + * @ref option::Parser::parse() "Parser::parse()". + * + * @par Feedback: + * Send questions, bug reports, feature requests etc. to: optionparser-feedback (a) lists.sourceforge.net + * @htmlonly @endhtmlonly + * + * + * @par Example program: + * (Note: @c option::* identifiers are links that take you to their documentation.) + * @code + * #error EXAMPLE SHORTENED FOR READABILITY. BETTER EXAMPLES ARE IN THE .TAR.GZ! + * #include + * #include "Thirdparty/optionparser.h" + * + * enum optionIndex { UNKNOWN, HELP, PLUS }; + * const option::Descriptor usage[] = + * { + * {UNKNOWN, 0,"" , "" ,option::Arg::None, "USAGE: example [options]\n\n" + * "Options:" }, + * {HELP, 0,"" , "help",option::Arg::None, " --help \tPrint usage and exit." }, + * {PLUS, 0,"p", "plus",option::Arg::None, " --plus, -p \tIncrement count." }, + * {UNKNOWN, 0,"" , "" ,option::Arg::None, "\nExamples:\n" + * " example --unknown -- --this_is_no_option\n" + * " example -unk --plus -ppp file1 file2\n" }, + * {0,0,0,0,0,0} + * }; + * + * int main(int argc, char* argv[]) + * { + * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present + * option::Stats stats(usage, argc, argv); + * option::Option options[stats.options_max], buffer[stats.buffer_max]; + * option::Parser parse(usage, argc, argv, options, buffer); + * + * if (parse.error()) + * return 1; + * + * if (options[HELP] || argc == 0) { + * option::printUsage(std::cout, usage); + * return 0; + * } + * + * std::cout << "--plus count: " << + * options[PLUS].count() << "\n"; + * + * for (option::Option* opt = options[UNKNOWN]; opt; opt = opt->next()) + * std::cout << "Unknown option: " << opt->name << "\n"; + * + * for (int i = 0; i < parse.nonOptionsCount(); ++i) + * std::cout << "Non-option #" << i << ": " << parse.nonOption(i) << "\n"; + * } + * @endcode + * + * @par Option syntax: + * @li The Lean Mean C++ Option Parser follows POSIX getopt() conventions and supports + * GNU-style getopt_long() long options as well as Perl-style single-minus + * long options (getopt_long_only()). + * @li short options have the format @c -X where @c X is any character that fits in a char. + * @li short options can be grouped, i.e. -X -Y is equivalent to @c -XY. + * @li a short option may take an argument either separate (-X foo) or + * attached (@c -Xfoo). You can make the parser accept the additional format @c -X=foo by + * registering @c X as a long option (in addition to being a short option) and + * enabling single-minus long options. + * @li an argument-taking short option may be grouped if it is the last in the group, e.g. + * @c -ABCXfoo or -ABCX foo (@c foo is the argument to the @c -X option). + * @li a lone minus character @c '-' is not treated as an option. It is customarily used where + * a file name is expected to refer to stdin or stdout. + * @li long options have the format @c --option-name. + * @li the option-name of a long option can be anything and include any characters. + * Even @c = characters will work, but don't do that. + * @li [optional] long options may be abbreviated as long as the abbreviation is unambiguous. + * You can set a minimum length for abbreviations. + * @li [optional] long options may begin with a single minus. The double minus form is always + * accepted, too. + * @li a long option may take an argument either separate ( --option arg ) or + * attached ( --option=arg ). In the attached form the equals sign is mandatory. + * @li an empty string can be passed as an attached long option argument: --option-name= . + * Note the distinction between an empty string as argument and no argument at all. + * @li an empty string is permitted as separate argument to both long and short options. + * @li Arguments to both short and long options may start with a @c '-' character. E.g. + * -X-X , -X -X or --long-X=-X . If @c -X + * and @c --long-X take an argument, that argument will be @c "-X" in all 3 cases. + * @li If using the built-in @ref option::Arg::Optional "Arg::Optional", optional arguments must + * be attached. + * @li the special option @c -- (i.e. without a name) terminates the list of + * options. Everything that follows is a non-option argument, even if it starts with + * a @c '-' character. The @c -- itself will not appear in the parse results. + * @li the first argument that doesn't start with @c '-' or @c '--' and does not belong to + * a preceding argument-taking option, will terminate the option list and is the + * first non-option argument. All following command line arguments are treated as + * non-option arguments, even if they start with @c '-' . @n + * NOTE: This behaviour is mandated by POSIX, but GNU getopt() only honours this if it is + * explicitly requested (e.g. by setting POSIXLY_CORRECT). @n + * You can enable the GNU behaviour by passing @c true as first argument to + * e.g. @ref option::Parser::parse() "Parser::parse()". + * @li Arguments that look like options (i.e. @c '-' followed by at least 1 character) but + * aren't, are NOT treated as non-option arguments. They are treated as unknown options and + * are collected into a list of unknown options for error reporting. @n + * This means that in order to pass a first non-option + * argument beginning with the minus character it is required to use the + * @c -- special option, e.g. + * @code + * program -x -- --strange-filename + * @endcode + * In this example, @c --strange-filename is a non-option argument. If the @c -- + * were omitted, it would be treated as an unknown option. @n + * See @ref option::Descriptor::longopt for information on how to collect unknown options. + * + */ + +#ifndef OPTIONPARSER_H_ +#define OPTIONPARSER_H_ + +/** @brief The namespace of The Lean Mean C++ Option Parser. */ +namespace option +{ + +#ifdef _MSC_VER +#include +#pragma intrinsic(_BitScanReverse) +struct MSC_Builtin_CLZ +{ + static int builtin_clz(unsigned x) + { + unsigned long index; + _BitScanReverse(&index, x); + return 32-index; // int is always 32bit on Windows, even for target x64 + } +}; +#define __builtin_clz(x) MSC_Builtin_CLZ::builtin_clz(x) +#endif + +class Option; + +/** + * @brief Possible results when checking if an argument is valid for a certain option. + * + * In the case that no argument is provided for an option that takes an + * optional argument, return codes @c ARG_OK and @c ARG_IGNORE are equivalent. + */ +enum ArgStatus +{ + //! The option does not take an argument. + ARG_NONE, + //! The argument is acceptable for the option. + ARG_OK, + //! The argument is not acceptable but that's non-fatal because the option's argument is optional. + ARG_IGNORE, + //! The argument is not acceptable and that's fatal. + ARG_ILLEGAL +}; + +/** + * @brief Signature of functions that check if an argument is valid for a certain type of option. + * + * Every Option has such a function assigned in its Descriptor. + * @code + * Descriptor usage[] = { {UNKNOWN, 0, "", "", Arg::None, ""}, ... }; + * @endcode + * + * A CheckArg function has the following signature: + * @code ArgStatus CheckArg(const Option& option, bool msg); @endcode + * + * It is used to check if a potential argument would be acceptable for the option. + * It will even be called if there is no argument. In that case @c option.arg will be @c NULL. + * + * If @c msg is @c true and the function determines that an argument is not acceptable and + * that this is a fatal error, it should output a message to the user before + * returning @ref ARG_ILLEGAL. If @c msg is @c false the function should remain silent (or you + * will get duplicate messages). + * + * See @ref ArgStatus for the meaning of the return values. + * + * While you can provide your own functions, + * often the following pre-defined checks (which never return @ref ARG_ILLEGAL) will suffice: + * + * @li @c Arg::None @copybrief Arg::None + * @li @c Arg::Optional @copybrief Arg::Optional + * + */ +typedef ArgStatus (*CheckArg)(const Option& option, bool msg); + +/** + * @brief Describes an option, its help text (usage) and how it should be parsed. + * + * The main input when constructing an option::Parser is an array of Descriptors. + + * @par Example: + * @code + * enum OptionIndex {CREATE, ...}; + * enum OptionType {DISABLE, ENABLE, OTHER}; + * + * const option::Descriptor usage[] = { + * { CREATE, // index + * OTHER, // type + * "c", // shortopt + * "create", // longopt + * Arg::None, // check_arg + * "--create Tells the program to create something." // help + * } + * , ... + * }; + * @endcode + */ +struct Descriptor +{ + /** + * @brief Index of this option's linked list in the array filled in by the parser. + * + * Command line options whose Descriptors have the same index will end up in the same + * linked list in the order in which they appear on the command line. If you have + * multiple long option aliases that refer to the same option, give their descriptors + * the same @c index. + * + * If you have options that mean exactly opposite things + * (e.g. @c --enable-foo and @c --disable-foo ), you should also give them the same + * @c index, but distinguish them through different values for @ref type. + * That way they end up in the same list and you can just take the last element of the + * list and use its type. This way you get the usual behaviour where switches later + * on the command line override earlier ones without having to code it manually. + * + * @par Tip: + * Use an enum rather than plain ints for better readability, as shown in the example + * at Descriptor. + */ + const unsigned index; + + /** + * @brief Used to distinguish between options with the same @ref index. + * See @ref index for details. + * + * It is recommended that you use an enum rather than a plain int to make your + * code more readable. + */ + const int type; + + /** + * @brief Each char in this string will be accepted as a short option character. + * + * The string must not include the minus character @c '-' or you'll get undefined + * behaviour. + * + * If this Descriptor should not have short option characters, use the empty + * string "". NULL is not permitted here! + * + * See @ref longopt for more information. + */ + const char* const shortopt; + + /** + * @brief The long option name (without the leading @c -- ). + * + * If this Descriptor should not have a long option name, use the empty + * string "". NULL is not permitted here! + * + * While @ref shortopt allows multiple short option characters, each + * Descriptor can have only a single long option name. If you have multiple + * long option names referring to the same option use separate Descriptors + * that have the same @ref index and @ref type. You may repeat + * short option characters in such an alias Descriptor but there's no need to. + * + * @par Dummy Descriptors: + * You can use dummy Descriptors with an + * empty string for both @ref shortopt and @ref longopt to add text to + * the usage that is not related to a specific option. See @ref help. + * The first dummy Descriptor will be used for unknown options (see below). + * + * @par Unknown Option Descriptor: + * The first dummy Descriptor in the list of Descriptors, + * whose @ref shortopt and @ref longopt are both the empty string, will be used + * as the Descriptor for unknown options. An unknown option is a string in + * the argument vector that is not a lone minus @c '-' but starts with a minus + * character and does not match any Descriptor's @ref shortopt or @ref longopt. @n + * Note that the dummy descriptor's @ref check_arg function @e will be called and + * its return value will be evaluated as usual. I.e. if it returns @ref ARG_ILLEGAL + * the parsing will be aborted with Parser::error()==true. @n + * if @c check_arg does not return @ref ARG_ILLEGAL the descriptor's + * @ref index @e will be used to pick the linked list into which + * to put the unknown option. @n + * If there is no dummy descriptor, unknown options will be dropped silently. + * + */ + const char* const longopt; + + /** + * @brief For each option that matches @ref shortopt or @ref longopt this function + * will be called to check a potential argument to the option. + * + * This function will be called even if there is no potential argument. In that case + * it will be passed @c NULL as @c arg parameter. Do not confuse this with the empty + * string. + * + * See @ref CheckArg for more information. + */ + const CheckArg check_arg; + + /** + * @brief The usage text associated with the options in this Descriptor. + * + * You can use option::printUsage() to format your usage message based on + * the @c help texts. You can use dummy Descriptors where + * @ref shortopt and @ref longopt are both the empty string to add text to + * the usage that is not related to a specific option. + * + * See option::printUsage() for special formatting characters you can use in + * @c help to get a column layout. + * + * @attention + * Must be UTF-8-encoded. If your compiler supports C++11 you can use the "u8" + * prefix to make sure string literals are properly encoded. + */ + const char* help; +}; + +/** + * @brief A parsed option from the command line together with its argument if it has one. + * + * The Parser chains all parsed options with the same Descriptor::index together + * to form a linked list. This allows you to easily implement all of the common ways + * of handling repeated options and enable/disable pairs. + * + * @li Test for presence of a switch in the argument vector: + * @code if ( options[QUIET] ) ... @endcode + * @li Evaluate --enable-foo/--disable-foo pair where the last one used wins: + * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode + * @li Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose): + * @code int verbosity = options[VERBOSE].count(); @endcode + * @li Iterate over all --file=<fname> arguments: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + */ +class Option +{ + Option* next_; + Option* prev_; +public: + /** + * @brief Pointer to this Option's Descriptor. + * + * Remember that the first dummy descriptor (see @ref Descriptor::longopt) is used + * for unknown options. + * + * @attention + * @c desc==NULL signals that this Option is unused. This is the default state of + * elements in the result array. You don't need to test @c desc explicitly. You + * can simply write something like this: + * @code + * if (options[CREATE]) + * { + * ... + * } + * @endcode + * This works because of operator const Option*() . + */ + const Descriptor* desc; + + /** + * @brief The name of the option as used on the command line. + * + * The main purpose of this string is to be presented to the user in messages. + * + * In the case of a long option, this is the actual @c argv pointer, i.e. the first + * character is a '-'. In the case of a short option this points to the option + * character within the @c argv string. + * + * Note that in the case of a short option group or an attached option argument, this + * string will contain additional characters following the actual name. Use @ref namelen + * to filter out the actual option name only. + * + */ + const char* name; + + /** + * @brief Pointer to this Option's argument (if any). + * + * NULL if this option has no argument. Do not confuse this with the empty string which + * is a valid argument. + */ + const char* arg; + + /** + * @brief The length of the option @ref name. + * + * Because @ref name points into the actual @c argv string, the option name may be + * followed by more characters (e.g. other short options in the same short option group). + * This value is the number of bytes (not characters!) that are part of the actual name. + * + * For a short option, this length is always 1. For a long option this length is always + * at least 2 if single minus long options are permitted and at least 3 if they are disabled. + * + * @note + * In the pathological case of a minus within a short option group (e.g. @c -xf-z), this + * length is incorrect, because this case will be misinterpreted as a long option and the + * name will therefore extend to the string's 0-terminator or a following '=" character + * if there is one. This is irrelevant for most uses of @ref name and @c namelen. If you + * really need to distinguish the case of a long and a short option, compare @ref name to + * the @c argv pointers. A long option's @c name is always identical to one of them, + * whereas a short option's is never. + */ + int namelen; + + /** + * @brief Returns Descriptor::type of this Option's Descriptor, or 0 if this Option + * is invalid (unused). + * + * Because this method (and last(), too) can be used even on unused Options with desc==0, you can (provided + * you arrange your types properly) switch on type() without testing validity first. + * @code + * enum OptionType { UNUSED=0, DISABLED=0, ENABLED=1 }; + * enum OptionIndex { FOO }; + * const Descriptor usage[] = { + * { FOO, ENABLED, "", "enable-foo", Arg::None, 0 }, + * { FOO, DISABLED, "", "disable-foo", Arg::None, 0 }, + * { 0, 0, 0, 0, 0, 0 } }; + * ... + * switch(options[FOO].last()->type()) // no validity check required! + * { + * case ENABLED: ... + * case DISABLED: ... // UNUSED==DISABLED ! + * } + * @endcode + */ + int type() const + { + return desc == 0 ? 0 : desc->type; + } + + /** + * @brief Returns Descriptor::index of this Option's Descriptor, or -1 if this Option + * is invalid (unused). + */ + int index() const + { + return desc == 0 ? -1 : (int)desc->index; + } + + /** + * @brief Returns the number of times this Option (or others with the same Descriptor::index) + * occurs in the argument vector. + * + * This corresponds to the number of elements in the linked list this Option is part of. + * It doesn't matter on which element you call count(). The return value is always the same. + * + * Use this to implement cumulative options, such as -v, -vv, -vvv for + * different verbosity levels. + * + * Returns 0 when called for an unused/invalid option. + */ + int count() + { + int c = (desc == 0 ? 0 : 1); + Option* p = first(); + while (!p->isLast()) + { + ++c; + p = p->next_; + }; + return c; + } + + /** + * @brief Returns true iff this is the first element of the linked list. + * + * The first element in the linked list is the first option on the command line + * that has the respective Descriptor::index value. + * + * Returns true for an unused/invalid option. + */ + bool isFirst() const + { + return isTagged(prev_); + } + + /** + * @brief Returns true iff this is the last element of the linked list. + * + * The last element in the linked list is the last option on the command line + * that has the respective Descriptor::index value. + * + * Returns true for an unused/invalid option. + */ + bool isLast() const + { + return isTagged(next_); + } + + /** + * @brief Returns a pointer to the first element of the linked list. + * + * Use this when you want the first occurrence of an option on the command line to + * take precedence. Note that this is not the way most programs handle options. + * You should probably be using last() instead. + * + * @note + * This method may be called on an unused/invalid option and will return a pointer to the + * option itself. + */ + Option* first() + { + Option* p = this; + while (!p->isFirst()) + p = p->prev_; + return p; + } + + /** + * @brief Returns a pointer to the last element of the linked list. + * + * Use this when you want the last occurrence of an option on the command line to + * take precedence. This is the most common way of handling conflicting options. + * + * @note + * This method may be called on an unused/invalid option and will return a pointer to the + * option itself. + * + * @par Tip: + * If you have options with opposite meanings (e.g. @c --enable-foo and @c --disable-foo), you + * can assign them the same Descriptor::index to get them into the same list. Distinguish them by + * Descriptor::type and all you have to do is check last()->type() to get + * the state listed last on the command line. + */ + Option* last() + { + return first()->prevwrap(); + } + + /** + * @brief Returns a pointer to the previous element of the linked list or NULL if + * called on first(). + * + * If called on first() this method returns NULL. Otherwise it will return the + * option with the same Descriptor::index that precedes this option on the command + * line. + */ + Option* prev() + { + return isFirst() ? 0 : prev_; + } + + /** + * @brief Returns a pointer to the previous element of the linked list with wrap-around from + * first() to last(). + * + * If called on first() this method returns last(). Otherwise it will return the + * option with the same Descriptor::index that precedes this option on the command + * line. + */ + Option* prevwrap() + { + return untag(prev_); + } + + /** + * @brief Returns a pointer to the next element of the linked list or NULL if called + * on last(). + * + * If called on last() this method returns NULL. Otherwise it will return the + * option with the same Descriptor::index that follows this option on the command + * line. + */ + Option* next() + { + return isLast() ? 0 : next_; + } + + /** + * @brief Returns a pointer to the next element of the linked list with wrap-around from + * last() to first(). + * + * If called on last() this method returns first(). Otherwise it will return the + * option with the same Descriptor::index that follows this option on the command + * line. + */ + Option* nextwrap() + { + return untag(next_); + } + + /** + * @brief Makes @c new_last the new last() by chaining it into the list after last(). + * + * It doesn't matter which element you call append() on. The new element will always + * be appended to last(). + * + * @attention + * @c new_last must not yet be part of a list, or that list will become corrupted, because + * this method does not unchain @c new_last from an existing list. + */ + void append(Option* new_last) + { + Option* p = last(); + Option* f = first(); + p->next_ = new_last; + new_last->prev_ = p; + new_last->next_ = tag(f); + f->prev_ = tag(new_last); + } + + /** + * @brief Casts from Option to const Option* but only if this Option is valid. + * + * If this Option is valid (i.e. @c desc!=NULL), returns this. + * Otherwise returns NULL. This allows testing an Option directly + * in an if-clause to see if it is used: + * @code + * if (options[CREATE]) + * { + * ... + * } + * @endcode + * It also allows you to write loops like this: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + */ + operator const Option*() const + { + return desc ? this : 0; + } + + /** + * @brief Casts from Option to Option* but only if this Option is valid. + * + * If this Option is valid (i.e. @c desc!=NULL), returns this. + * Otherwise returns NULL. This allows testing an Option directly + * in an if-clause to see if it is used: + * @code + * if (options[CREATE]) + * { + * ... + * } + * @endcode + * It also allows you to write loops like this: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + */ + operator Option*() + { + return desc ? this : 0; + } + + /** + * @brief Creates a new Option that is a one-element linked list and has NULL + * @ref desc, @ref name, @ref arg and @ref namelen. + */ + Option() : + desc(0), name(0), arg(0), namelen(0) + { + prev_ = tag(this); + next_ = tag(this); + } + + /** + * @brief Creates a new Option that is a one-element linked list and has the given + * values for @ref desc, @ref name and @ref arg. + * + * If @c name_ points at a character other than '-' it will be assumed to refer to a + * short option and @ref namelen will be set to 1. Otherwise the length will extend to + * the first '=' character or the string's 0-terminator. + */ + Option(const Descriptor* desc_, const char* name_, const char* arg_) + { + init(desc_, name_, arg_); + } + + /** + * @brief Makes @c *this a copy of @c orig except for the linked list pointers. + * + * After this operation @c *this will be a one-element linked list. + */ + void operator=(const Option& orig) + { + init(orig.desc, orig.name, orig.arg); + } + + /** + * @brief Makes @c *this a copy of @c orig except for the linked list pointers. + * + * After this operation @c *this will be a one-element linked list. + */ + Option(const Option& orig) + { + init(orig.desc, orig.name, orig.arg); + } + +private: + /** + * @internal + * @brief Sets the fields of this Option to the given values (extracting @c name if necessary). + * + * If @c name_ points at a character other than '-' it will be assumed to refer to a + * short option and @ref namelen will be set to 1. Otherwise the length will extend to + * the first '=' character or the string's 0-terminator. + */ + void init(const Descriptor* desc_, const char* name_, const char* arg_) + { + desc = desc_; + name = name_; + arg = arg_; + prev_ = tag(this); + next_ = tag(this); + namelen = 0; + if (name == 0) + return; + namelen = 1; + if (name[0] != '-') + return; + while (name[namelen] != 0 && name[namelen] != '=') + ++namelen; + } + + static Option* tag(Option* ptr) + { + return (Option*) ((unsigned long long) ptr | 1); + } + + static Option* untag(Option* ptr) + { + return (Option*) ((unsigned long long) ptr & ~1ull); + } + + static bool isTagged(Option* ptr) + { + return ((unsigned long long) ptr & 1); + } +}; + +/** + * @brief Functions for checking the validity of option arguments. + * + * @copydetails CheckArg + * + * The following example code + * can serve as starting place for writing your own more complex CheckArg functions: + * @code + * struct Arg: public option::Arg + * { + * static void printError(const char* msg1, const option::Option& opt, const char* msg2) + * { + * fprintf(stderr, "ERROR: %s", msg1); + * fwrite(opt.name, opt.namelen, 1, stderr); + * fprintf(stderr, "%s", msg2); + * } + * + * static option::ArgStatus Unknown(const option::Option& option, bool msg) + * { + * if (msg) printError("Unknown option '", option, "'\n"); + * return option::ARG_ILLEGAL; + * } + * + * static option::ArgStatus Required(const option::Option& option, bool msg) + * { + * if (option.arg != 0) + * return option::ARG_OK; + * + * if (msg) printError("Option '", option, "' requires an argument\n"); + * return option::ARG_ILLEGAL; + * } + * + * static option::ArgStatus NonEmpty(const option::Option& option, bool msg) + * { + * if (option.arg != 0 && option.arg[0] != 0) + * return option::ARG_OK; + * + * if (msg) printError("Option '", option, "' requires a non-empty argument\n"); + * return option::ARG_ILLEGAL; + * } + * + * static option::ArgStatus Numeric(const option::Option& option, bool msg) + * { + * char* endptr = 0; + * if (option.arg != 0 && strtol(option.arg, &endptr, 10)){}; + * if (endptr != option.arg && *endptr == 0) + * return option::ARG_OK; + * + * if (msg) printError("Option '", option, "' requires a numeric argument\n"); + * return option::ARG_ILLEGAL; + * } + * }; + * @endcode + */ +struct Arg +{ + //! @brief For options that don't take an argument: Returns ARG_NONE. + static ArgStatus None(const Option&, bool) + { + return ARG_NONE; + } + + //! @brief Returns ARG_OK if the argument is attached and ARG_IGNORE otherwise. + static ArgStatus Optional(const Option& option, bool) + { + if (option.arg && option.name[option.namelen] != 0) + return ARG_OK; + else + return ARG_IGNORE; + } +}; + +/** + * @brief Determines the minimum lengths of the buffer and options arrays used for Parser. + * + * Because Parser doesn't use dynamic memory its output arrays have to be pre-allocated. + * If you don't want to use fixed size arrays (which may turn out too small, causing + * command line arguments to be dropped), you can use Stats to determine the correct sizes. + * Stats work cumulative. You can first pass in your default options and then the real + * options and afterwards the counts will reflect the union. + */ +struct Stats +{ + /** + * @brief Number of elements needed for a @c buffer[] array to be used for + * @ref Parser::parse() "parsing" the same argument vectors that were fed + * into this Stats object. + * + * @note + * This number is always 1 greater than the actual number needed, to give + * you a sentinel element. + */ + unsigned buffer_max; + + /** + * @brief Number of elements needed for an @c options[] array to be used for + * @ref Parser::parse() "parsing" the same argument vectors that were fed + * into this Stats object. + * + * @note + * @li This number is always 1 greater than the actual number needed, to give + * you a sentinel element. + * @li This number depends only on the @c usage, not the argument vectors, because + * the @c options array needs exactly one slot for each possible Descriptor::index. + */ + unsigned options_max; + + /** + * @brief Creates a Stats object with counts set to 1 (for the sentinel element). + */ + Stats() : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + } + + /** + * @brief Creates a new Stats object and immediately updates it for the + * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv, + * if you just want to update @ref options_max. + * + * @note + * The calls to Stats methods must match the later calls to Parser methods. + * See Parser::parse() for the meaning of the arguments. + */ + Stats(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt); + } + + //! @brief Stats(...) with non-const argv. + Stats(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX Stats(...) (gnu==false). + Stats(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX Stats(...) (gnu==false) with non-const argv. + Stats(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } + + /** + * @brief Updates this Stats object for the + * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv, + * if you just want to update @ref options_max. + * + * @note + * The calls to Stats methods must match the later calls to Parser methods. + * See Parser::parse() for the meaning of the arguments. + */ + void add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false); + + //! @brief add() with non-const argv. + void add(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) + { + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX add() (gnu==false). + void add(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) + { + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX add() (gnu==false) with non-const argv. + void add(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) + { + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } +private: + class CountOptionsAction; +}; + +/** + * @brief Checks argument vectors for validity and parses them into data + * structures that are easier to work with. + * + * @par Example: + * @code + * int main(int argc, char* argv[]) + * { + * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present + * option::Stats stats(usage, argc, argv); + * option::Option options[stats.options_max], buffer[stats.buffer_max]; + * option::Parser parse(usage, argc, argv, options, buffer); + * + * if (parse.error()) + * return 1; + * + * if (options[HELP]) + * ... + * @endcode + */ +class Parser +{ + int op_count; //!< @internal @brief see optionsCount() + int nonop_count; //!< @internal @brief see nonOptionsCount() + const char** nonop_args; //!< @internal @brief see nonOptions() + bool err; //!< @internal @brief see error() +public: + + /** + * @brief Creates a new Parser. + */ + Parser() : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + } + + /** + * @brief Creates a new Parser and immediately parses the given argument vector. + * @copydetails parse() + */ + Parser(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief Parser(...) with non-const argv. + Parser(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX Parser(...) (gnu==false). + Parser(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], int min_abbr_len = 0, + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX Parser(...) (gnu==false) with non-const argv. + Parser(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + /** + * @brief Parses the given argument vector. + * + * @param gnu if true, parse() will not stop at the first non-option argument. Instead it will + * reorder arguments so that all non-options are at the end. This is the default behaviour + * of GNU getopt() but is not conforming to POSIX. @n + * Note, that once the argument vector has been reordered, the @c gnu flag will have + * no further effect on this argument vector. So it is enough to pass @c gnu==true when + * creating Stats. + * @param usage Array of Descriptor objects that describe the options to support. The last entry + * of this array must have 0 in all fields. + * @param argc The number of elements from @c argv that are to be parsed. If you pass -1, the number + * will be determined automatically. In that case the @c argv list must end with a NULL + * pointer. + * @param argv The arguments to be parsed. If you pass -1 as @c argc the last pointer in the @c argv + * list must be NULL to mark the end. + * @param options Each entry is the first element of a linked list of Options. Each new option + * that is parsed will be appended to the list specified by that Option's + * Descriptor::index. If an entry is not yet used (i.e. the Option is invalid), + * it will be replaced rather than appended to. @n + * The minimum length of this array is the greatest Descriptor::index value that + * occurs in @c usage @e PLUS ONE. + * @param buffer Each argument that is successfully parsed (including unknown arguments, if they + * have a Descriptor whose CheckArg does not return @ref ARG_ILLEGAL) will be stored in this + * array. parse() scans the array for the first invalid entry and begins writing at that + * index. You can pass @c bufmax to limit the number of options stored. + * @param min_abbr_len Passing a value min_abbr_len > 0 enables abbreviated long + * options. The parser will match a prefix of a long option as if it was + * the full long option (e.g. @c --foob=10 will be interpreted as if it was + * @c --foobar=10 ), as long as the prefix has at least @c min_abbr_len characters + * (not counting the @c -- ) and is unambiguous. + * @n Be careful if combining @c min_abbr_len=1 with @c single_minus_longopt=true + * because the ambiguity check does not consider short options and abbreviated + * single minus long options will take precedence over short options. + * @param single_minus_longopt Passing @c true for this option allows long options to begin with + * a single minus. The double minus form will still be recognized. Note that + * single minus long options take precedence over short options and short option + * groups. E.g. @c -file would be interpreted as @c --file and not as + * -f -i -l -e (assuming a long option named @c "file" exists). + * @param bufmax The greatest index in the @c buffer[] array that parse() will write to is + * @c bufmax-1. If there are more options, they will be processed (in particular + * their CheckArg will be called) but not stored. @n + * If you used Stats::buffer_max to dimension this array, you can pass + * -1 (or not pass @c bufmax at all) which tells parse() that the buffer is + * "large enough". + * @attention + * Remember that @c options and @c buffer store Option @e objects, not pointers. Therefore it + * is not possible for the same object to be in both arrays. For those options that are found in + * both @c buffer[] and @c options[] the respective objects are independent copies. And only the + * objects in @c options[] are properly linked via Option::next() and Option::prev(). + * You can iterate over @c buffer[] to + * process all options in the order they appear in the argument vector, but if you want access to + * the other Options with the same Descriptor::index, then you @e must access the linked list via + * @c options[]. You can get the linked list in options from a buffer object via something like + * @c options[buffer[i].index()]. + */ + void parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1); + + //! @brief parse() with non-const argv. + void parse(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + { + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX parse() (gnu==false). + void parse(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + { + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX parse() (gnu==false) with non-const argv. + void parse(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, + bool single_minus_longopt = false, int bufmax = -1) + { + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + /** + * @brief Returns the number of valid Option objects in @c buffer[]. + * + * @note + * @li The returned value always reflects the number of Options in the buffer[] array used for + * the most recent call to parse(). + * @li The count (and the buffer[]) includes unknown options if they are collected + * (see Descriptor::longopt). + */ + int optionsCount() + { + return op_count; + } + + /** + * @brief Returns the number of non-option arguments that remained at the end of the + * most recent parse() that actually encountered non-option arguments. + * + * @note + * A parse() that does not encounter non-option arguments will leave this value + * as well as nonOptions() undisturbed. This means you can feed the Parser a + * default argument vector that contains non-option arguments (e.g. a default filename). + * Then you feed it the actual arguments from the user. If the user has supplied at + * least one non-option argument, all of the non-option arguments from the default + * disappear and are replaced by the user's non-option arguments. However, if the + * user does not supply any non-option arguments the defaults will still be in + * effect. + */ + int nonOptionsCount() + { + return nonop_count; + } + + /** + * @brief Returns a pointer to an array of non-option arguments (only valid + * if nonOptionsCount() >0 ). + * + * @note + * @li parse() does not copy arguments, so this pointer points into the actual argument + * vector as passed to parse(). + * @li As explained at nonOptionsCount() this pointer is only changed by parse() calls + * that actually encounter non-option arguments. A parse() call that encounters only + * options, will not change nonOptions(). + */ + const char** nonOptions() + { + return nonop_args; + } + + /** + * @brief Returns nonOptions()[i] (@e without checking if i is in range!). + */ + const char* nonOption(int i) + { + return nonOptions()[i]; + } + + /** + * @brief Returns @c true if an unrecoverable error occurred while parsing options. + * + * An illegal argument to an option (i.e. CheckArg returns @ref ARG_ILLEGAL) is an + * unrecoverable error that aborts the parse. Unknown options are only an error if + * their CheckArg function returns @ref ARG_ILLEGAL. Otherwise they are collected. + * In that case if you want to exit the program if either an illegal argument + * or an unknown option has been passed, use code like this + * + * @code + * if (parser.error() || options[UNKNOWN]) + * exit(1); + * @endcode + * + */ + bool error() + { + return err; + } + +private: + friend struct Stats; + class StoreOptionAction; + struct Action; + + /** + * @internal + * @brief This is the core function that does all the parsing. + * @retval false iff an unrecoverable error occurred. + */ + static bool workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, + bool single_minus_longopt, bool print_errors, int min_abbr_len); + + /** + * @internal + * @brief Returns true iff @c st1 is a prefix of @c st2 and + * in case @c st2 is longer than @c st1, then + * the first additional character is '='. + * + * @par Examples: + * @code + * streq("foo", "foo=bar") == true + * streq("foo", "foobar") == false + * streq("foo", "foo") == true + * streq("foo=bar", "foo") == false + * @endcode + */ + static bool streq(const char* st1, const char* st2) + { + while (*st1 != 0) + if (*st1++ != *st2++) + return false; + return (*st2 == 0 || *st2 == '='); + } + + /** + * @internal + * @brief Like streq() but handles abbreviations. + * + * Returns true iff @c st1 and @c st2 have a common + * prefix with the following properties: + * @li (if min > 0) its length is at least @c min characters or the same length as @c st1 (whichever is smaller). + * @li (if min <= 0) its length is the same as that of @c st1 + * @li within @c st2 the character following the common prefix is either '=' or end-of-string. + * + * Examples: + * @code + * streqabbr("foo", "foo=bar",) == true + * streqabbr("foo", "fo=bar" , 2) == true + * streqabbr("foo", "fo" , 2) == true + * streqabbr("foo", "fo" , 0) == false + * streqabbr("foo", "f=bar" , 2) == false + * streqabbr("foo", "f" , 2) == false + * streqabbr("fo" , "foo=bar",) == false + * streqabbr("foo", "foobar" ,) == false + * streqabbr("foo", "fobar" ,) == false + * streqabbr("foo", "foo" ,) == true + * @endcode + */ + static bool streqabbr(const char* st1, const char* st2, long long min) + { + const char* st1start = st1; + while (*st1 != 0 && (*st1 == *st2)) + { + ++st1; + ++st2; + } + + return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '='); + } + + /** + * @internal + * @brief Returns true iff character @c ch is contained in the string @c st. + * + * Returns @c true for @c ch==0 . + */ + static bool instr(char ch, const char* st) + { + while (*st != 0 && *st != ch) + ++st; + return *st == ch; + } + + /** + * @internal + * @brief Rotates args[-count],...,args[-1],args[0] to become + * args[0],args[-count],...,args[-1]. + */ + static void shift(const char** args, int count) + { + for (int i = 0; i > -count; --i) + { + const char* temp = args[i]; + args[i] = args[i - 1]; + args[i - 1] = temp; + } + } +}; + +/** + * @internal + * @brief Interface for actions Parser::workhorse() should perform for each Option it + * parses. + */ +struct Parser::Action +{ + /** + * @brief Called by Parser::workhorse() for each Option that has been successfully + * parsed (including unknown + * options if they have a Descriptor whose Descriptor::check_arg does not return + * @ref ARG_ILLEGAL. + * + * Returns @c false iff a fatal error has occured and the parse should be aborted. + */ + virtual bool perform(Option&) + { + return true; + } + + /** + * @brief Called by Parser::workhorse() after finishing the parse. + * @param numargs the number of non-option arguments remaining + * @param args pointer to the first remaining non-option argument (if numargs > 0). + * + * @return + * @c false iff a fatal error has occurred. + */ + virtual bool finished(int numargs, const char** args) + { + (void) numargs; + (void) args; + return true; + } +}; + +/** + * @internal + * @brief An Action to pass to Parser::workhorse() that will increment a counter for + * each parsed Option. + */ +class Stats::CountOptionsAction: public Parser::Action +{ + unsigned* buffer_max; +public: + /** + * Creates a new CountOptionsAction that will increase @c *buffer_max_ for each + * parsed Option. + */ + CountOptionsAction(unsigned* buffer_max_) : + buffer_max(buffer_max_) + { + } + + bool perform(Option&) + { + if (*buffer_max == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + ++*buffer_max; + return true; + } +}; + +/** + * @internal + * @brief An Action to pass to Parser::workhorse() that will store each parsed Option in + * appropriate arrays (see Parser::parse()). + */ +class Parser::StoreOptionAction: public Parser::Action +{ + Parser& parser; + Option* options; + Option* buffer; + int bufmax; //! Number of slots in @c buffer. @c -1 means "large enough". +public: + /** + * @brief Creates a new StoreOption action. + * @param parser_ the parser whose op_count should be updated. + * @param options_ each Option @c o is chained into the linked list @c options_[o.desc->index] + * @param buffer_ each Option is appended to this array as long as there's a free slot. + * @param bufmax_ number of slots in @c buffer_. @c -1 means "large enough". + */ + StoreOptionAction(Parser& parser_, Option options_[], Option buffer_[], int bufmax_) : + parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_) + { + // find first empty slot in buffer (if any) + int bufidx = 0; + while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx]) + ++bufidx; + + // set parser's optionCount + parser.op_count = bufidx; + } + + bool perform(Option& option) + { + if (bufmax < 0 || parser.op_count < bufmax) + { + if (parser.op_count == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + + buffer[parser.op_count] = option; + int idx = buffer[parser.op_count].desc->index; + if (options[idx]) + options[idx].append(buffer[parser.op_count]); + else + options[idx] = buffer[parser.op_count]; + ++parser.op_count; + } + return true; // NOTE: an option that is discarded because of a full buffer is not fatal + } + + bool finished(int numargs, const char** args) + { + // only overwrite non-option argument list if there's at least 1 + // new non-option argument. Otherwise we keep the old list. This + // makes it easy to use default non-option arguments. + if (numargs > 0) + { + parser.nonop_count = numargs; + parser.nonop_args = args; + } + + return true; + } +}; + +inline void Parser::parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], + Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax) +{ + StoreOptionAction action(*this, options, buffer, bufmax); + err = !workhorse(gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len); +} + +inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len, + bool single_minus_longopt) +{ + // determine size of options array. This is the greatest index used in the usage + 1 + int i = 0; + while (usage[i].shortopt != 0) + { + if (usage[i].index + 1 >= options_max) + options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel + + ++i; + } + + CountOptionsAction action(&buffer_max); + Parser::workhorse(gnu, usage, argc, argv, action, single_minus_longopt, false, min_abbr_len); +} + +inline bool Parser::workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, + bool single_minus_longopt, bool print_errors, int min_abbr_len) +{ + // protect against NULL pointer + if (args == 0) + numargs = 0; + + int nonops = 0; + + while (numargs != 0 && *args != 0) + { + const char* param = *args; // param can be --long-option, -srto or non-option argument + + // in POSIX mode the first non-option argument terminates the option list + // a lone minus character is a non-option argument + if (param[0] != '-' || param[1] == 0) + { + if (gnu) + { + ++nonops; + ++args; + if (numargs > 0) + --numargs; + continue; + } + else + break; + } + + // -- terminates the option list. The -- itself is skipped. + if (param[1] == '-' && param[2] == 0) + { + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; + break; + } + + bool handle_short_options; + const char* longopt_name; + if (param[1] == '-') // if --long-option + { + handle_short_options = false; + longopt_name = param + 2; + } + else + { + handle_short_options = true; + longopt_name = param + 1; //for testing a potential -long-option + } + + bool try_single_minus_longopt = single_minus_longopt; + bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid? + + do // loop over short options in group, for long options the body is executed only once + { + int idx; + + const char* optarg; + + /******************** long option **********************/ + if (handle_short_options == false || try_single_minus_longopt) + { + idx = 0; + while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name)) + ++idx; + + if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options + { + int i1 = 0; + while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len)) + ++i1; + if (usage[i1].longopt != 0) + { // now test if the match is unambiguous by checking for another match + int i2 = i1 + 1; + while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len)) + ++i2; + + if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx + idx = i1; + } + } + + // if we found something, disable handle_short_options (only relevant if single_minus_longopt) + if (usage[idx].longopt != 0) + handle_short_options = false; + + try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group + + optarg = longopt_name; + while (*optarg != 0 && *optarg != '=') + ++optarg; + if (*optarg == '=') // attached argument + ++optarg; + else + // possibly detached argument + optarg = (have_more_args ? args[1] : 0); + } + + /************************ short option ***********************************/ + if (handle_short_options) + { + if (*++param == 0) // point at the 1st/next option character + break; // end of short option group + + idx = 0; + while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt)) + ++idx; + + if (param[1] == 0) // if the potential argument is separate + optarg = (have_more_args ? args[1] : 0); + else + // if the potential argument is attached + optarg = param + 1; + } + + const Descriptor* descriptor = &usage[idx]; + + if (descriptor->shortopt == 0) /************** unknown option ********************/ + { + // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options + idx = 0; + while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0)) + ++idx; + descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]); + } + + if (descriptor != 0) + { + Option option(descriptor, param, optarg); + switch (descriptor->check_arg(option, print_errors)) + { + case ARG_ILLEGAL: + return false; // fatal + case ARG_OK: + // skip one element of the argument vector, if it's a separated argument + if (optarg != 0 && have_more_args && optarg == args[1]) + { + shift(args, nonops); + if (numargs > 0) + --numargs; + ++args; + } + + // No further short options are possible after an argument + handle_short_options = false; + + break; + case ARG_IGNORE: + case ARG_NONE: + option.arg = 0; + break; + } + + if (!action.perform(option)) + return false; + } + + } while (handle_short_options); + + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; + + } // while + + if (numargs > 0 && *args == 0) // It's a bug in the caller if numargs is greater than the actual number + numargs = 0; // of arguments, but as a service to the user we fix this if we spot it. + + if (numargs < 0) // if we don't know the number of remaining non-option arguments + { // we need to count them + numargs = 0; + while (args[numargs] != 0) + ++numargs; + } + + return action.finished(numargs + nonops, args - nonops); +} + +/** + * @internal + * @brief The implementation of option::printUsage(). + */ +struct PrintUsageImplementation +{ + /** + * @internal + * @brief Interface for Functors that write (part of) a string somewhere. + */ + struct IStringWriter + { + /** + * @brief Writes the given number of chars beginning at the given pointer somewhere. + */ + virtual void operator()(const char*, int) + { + } + }; + + /** + * @internal + * @brief Encapsulates a function with signature func(string, size) where + * string can be initialized with a const char* and size with an int. + */ + template + struct FunctionWriter: public IStringWriter + { + Function* write; + + virtual void operator()(const char* str, int size) + { + (*write)(str, size); + } + + FunctionWriter(Function* w) : + write(w) + { + } + }; + + /** + * @internal + * @brief Encapsulates a reference to an object with a write(string, size) + * method like that of @c std::ostream. + */ + template + struct OStreamWriter: public IStringWriter + { + OStream& ostream; + + virtual void operator()(const char* str, int size) + { + ostream.write(str, size); + } + + OStreamWriter(OStream& o) : + ostream(o) + { + } + }; + + /** + * @internal + * @brief Like OStreamWriter but encapsulates a @c const reference, which is + * typically a temporary object of a user class. + */ + template + struct TemporaryWriter: public IStringWriter + { + const Temporary& userstream; + + virtual void operator()(const char* str, int size) + { + userstream.write(str, size); + } + + TemporaryWriter(const Temporary& u) : + userstream(u) + { + } + }; + + /** + * @internal + * @brief Encapsulates a function with the signature func(fd, string, size) (the + * signature of the @c write() system call) + * where fd can be initialized from an int, string from a const char* and size from an int. + */ + template + struct SyscallWriter: public IStringWriter + { + Syscall* write; + int fd; + + virtual void operator()(const char* str, int size) + { + (*write)(fd, str, size); + } + + SyscallWriter(Syscall* w, int f) : + write(w), fd(f) + { + } + }; + + /** + * @internal + * @brief Encapsulates a function with the same signature as @c std::fwrite(). + */ + template + struct StreamWriter: public IStringWriter + { + Function* fwrite; + Stream* stream; + + virtual void operator()(const char* str, int size) + { + (*fwrite)(str, size, 1, stream); + } + + StreamWriter(Function* w, Stream* s) : + fwrite(w), stream(s) + { + } + }; + + /** + * @internal + * @brief Sets i1 = max(i1, i2) + */ + static void upmax(int& i1, int i2) + { + i1 = (i1 >= i2 ? i1 : i2); + } + + /** + * @internal + * @brief Moves the "cursor" to column @c want_x assuming it is currently at column @c x + * and sets @c x=want_x . + * If x > want_x , a line break is output before indenting. + * + * @param write Spaces and possibly a line break are written via this functor to get + * the desired indentation @c want_x . + * @param[in,out] x the current indentation. Set to @c want_x by this method. + * @param want_x the desired indentation. + */ + static void indent(IStringWriter& write, int& x, int want_x) + { + int indent = want_x - x; + if (indent < 0) + { + write("\n", 1); + indent = want_x; + } + + if (indent > 0) + { + char space = ' '; + for (int i = 0; i < indent; ++i) + write(&space, 1); + x = want_x; + } + } + + /** + * @brief Returns true if ch is the unicode code point of a wide character. + * + * @note + * The following character ranges are treated as wide + * @code + * 1100..115F + * 2329..232A (just 2 characters!) + * 2E80..A4C6 except for 303F + * A960..A97C + * AC00..D7FB + * F900..FAFF + * FE10..FE6B + * FF01..FF60 + * FFE0..FFE6 + * 1B000...... + * @endcode + */ + static bool isWideChar(unsigned ch) + { + if (ch == 0x303F) + return false; + + return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6) + || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF) + || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6) + || (0x1B000 <= ch)); + } + + /** + * @internal + * @brief Splits a @c Descriptor[] array into tables, rows, lines and columns and + * iterates over these components. + * + * The top-level organizational unit is the @e table. + * A table begins at a Descriptor with @c help!=NULL and extends up to + * a Descriptor with @c help==NULL. + * + * A table consists of @e rows. Due to line-wrapping and explicit breaks + * a row may take multiple lines on screen. Rows within the table are separated + * by \\n. They never cross Descriptor boundaries. This means a row ends either + * at \\n or the 0 at the end of the help string. + * + * A row consists of columns/cells. Columns/cells within a row are separated by \\t. + * Line breaks within a cell are marked by \\v. + * + * Rows in the same table need not have the same number of columns/cells. The + * extreme case are interjections, which are rows that contain neither \\t nor \\v. + * These are NOT treated specially by LinePartIterator, but they are treated + * specially by printUsage(). + * + * LinePartIterator iterates through the usage at 3 levels: table, row and part. + * Tables and rows are as described above. A @e part is a line within a cell. + * LinePartIterator iterates through 1st parts of all cells, then through the 2nd + * parts of all cells (if any),... @n + * Example: The row "1 \v 3 \t 2 \v 4" has 2 cells/columns and 4 parts. + * The parts will be returned in the order 1, 2, 3, 4. + * + * It is possible that some cells have fewer parts than others. In this case + * LinePartIterator will "fill up" these cells with 0-length parts. IOW, LinePartIterator + * always returns the same number of parts for each column. Note that this is different + * from the way rows and columns are handled. LinePartIterator does @e not guarantee that + * the same number of columns will be returned for each row. + * + */ + class LinePartIterator + { + const Descriptor* tablestart; //!< The 1st descriptor of the current table. + const Descriptor* rowdesc; //!< The Descriptor that contains the current row. + const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help. + const char* ptr; //!< Ptr to current part within the current row. + int col; //!< Index of current column. + int len; //!< Length of the current part (that ptr points at) in BYTES + int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account). + int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs. + int line_in_block; //!< Line index within the current cell of the current part. + int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration. + bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell. + + /** + * @brief Determines the byte and character lengths of the part at @ref ptr and + * stores them in @ref len and @ref screenlen respectively. + */ + void update_length() + { + screenlen = 0; + for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len) + { + ++screenlen; + unsigned ch = (unsigned char) ptr[len]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code + ++len; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + ++screenlen; + } + } + } + + public: + //! @brief Creates an iterator for @c usage. + LinePartIterator(const Descriptor usage[]) : + tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0), + target_line_in_block(0), hit_target_line(true) + { + } + + /** + * @brief Moves iteration to the next table (if any). Has to be called once on a new + * LinePartIterator to move to the 1st table. + * @retval false if moving to next table failed because no further table exists. + */ + bool nextTable() + { + // If this is NOT the first time nextTable() is called after the constructor, + // then skip to the next table break (i.e. a Descriptor with help == 0) + if (rowdesc != 0) + { + while (tablestart->help != 0 && tablestart->shortopt != 0) + ++tablestart; + } + + // Find the next table after the break (if any) + while (tablestart->help == 0 && tablestart->shortopt != 0) + ++tablestart; + + restartTable(); + return rowstart != 0; + } + + /** + * @brief Reset iteration to the beginning of the current table. + */ + void restartTable() + { + rowdesc = tablestart; + rowstart = tablestart->help; + ptr = 0; + } + + /** + * @brief Moves iteration to the next row (if any). Has to be called once after each call to + * @ref nextTable() to move to the 1st row of the table. + * @retval false if moving to next row failed because no further row exists. + */ + bool nextRow() + { + if (ptr == 0) + { + restartRow(); + return rowstart != 0; + } + + while (*ptr != 0 && *ptr != '\n') + ++ptr; + + if (*ptr == 0) + { + if ((rowdesc + 1)->help == 0) // table break + return false; + + ++rowdesc; + rowstart = rowdesc->help; + } + else // if (*ptr == '\n') + { + rowstart = ptr + 1; + } + + restartRow(); + return true; + } + + /** + * @brief Reset iteration to the beginning of the current row. + */ + void restartRow() + { + ptr = rowstart; + col = -1; + len = 0; + screenlen = 0; + max_line_in_block = 0; + line_in_block = 0; + target_line_in_block = 0; + hit_target_line = true; + } + + /** + * @brief Moves iteration to the next part (if any). Has to be called once after each call to + * @ref nextRow() to move to the 1st part of the row. + * @retval false if moving to next part failed because no further part exists. + * + * See @ref LinePartIterator for details about the iteration. + */ + bool next() + { + if (ptr == 0) + return false; + + if (col == -1) + { + col = 0; + update_length(); + return true; + } + + ptr += len; + while (true) + { + switch (*ptr) + { + case '\v': + upmax(max_line_in_block, ++line_in_block); + ++ptr; + break; + case '\t': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + hit_target_line = false; + line_in_block = 0; + ++col; + ++ptr; + break; + case 0: + case '\n': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + if (++target_line_in_block > max_line_in_block) + { + update_length(); + return false; + } + + hit_target_line = false; + line_in_block = 0; + col = 0; + ptr = rowstart; + continue; + default: + ++ptr; + continue; + } // switch + + if (line_in_block == target_line_in_block) + { + update_length(); + hit_target_line = true; + return true; + } + } // while + } + + /** + * @brief Returns the index (counting from 0) of the column in which + * the part pointed to by @ref data() is located. + */ + int column() + { + return col; + } + + /** + * @brief Returns the index (counting from 0) of the line within the current column + * this part belongs to. + */ + int line() + { + return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line + } + + /** + * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters). + */ + int length() + { + return len; + } + + /** + * @brief Returns the width in screen columns of the part pointed to by @ref data(). + * Takes multi-byte UTF-8 sequences and wide characters into account. + */ + int screenLength() + { + return screenlen; + } + + /** + * @brief Returns the current part of the iteration. + */ + const char* data() + { + return ptr; + } + }; + + /** + * @internal + * @brief Takes input and line wraps it, writing out one line at a time so that + * it can be interleaved with output from other columns. + * + * The LineWrapper is used to handle the last column of each table as well as interjections. + * The LineWrapper is called once for each line of output. If the data given to it fits + * into the designated width of the last column it is simply written out. If there + * is too much data, an appropriate split point is located and only the data up to this + * split point is written out. The rest of the data is queued for the next line. + * That way the last column can be line wrapped and interleaved with data from + * other columns. The following example makes this clearer: + * @code + * Column 1,1 Column 2,1 This is a long text + * Column 1,2 Column 2,2 that does not fit into + * a single line. + * @endcode + * + * The difficulty in producing this output is that the whole string + * "This is a long text that does not fit into a single line" is the + * 1st and only part of column 3. In order to produce the above + * output the string must be output piecemeal, interleaved with + * the data from the other columns. + */ + class LineWrapper + { + static const int bufmask = 15; //!< Must be a power of 2 minus 1. + /** + * @brief Ring buffer for length component of pair (data, length). + */ + int lenbuf[bufmask + 1]; + /** + * @brief Ring buffer for data component of pair (data, length). + */ + const char* datbuf[bufmask + 1]; + /** + * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer + * assumes that the indentation has already been written when @ref process() + * is called, so this value is only used when a buffer flush requires writing + * additional lines of output. + */ + int x; + /** + * @brief The width of the column to line wrap. + */ + int width; + int head; //!< @brief index for next write + int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read) + + /** + * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to + * free up space. The contract of process() says that only 1 line is output. So + * this variable is used to track whether something has output a line. It is + * reset at the beginning of process() and checked at the end to decide if + * output has already occurred or is still needed. + */ + bool wrote_something; + + bool buf_empty() + { + return ((tail + 1) & bufmask) == head; + } + + bool buf_full() + { + return tail == head; + } + + void buf_store(const char* data, int len) + { + lenbuf[head] = len; + datbuf[head] = data; + head = (head + 1) & bufmask; + } + + //! @brief Call BEFORE reading ...buf[tail]. + void buf_next() + { + tail = (tail + 1) & bufmask; + } + + /** + * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line + * is flushed out of the buffer into @c write. + */ + void output(IStringWriter& write, const char* data, int len) + { + if (buf_full()) + write_one_line(write); + + buf_store(data, len); + } + + /** + * @brief Writes a single line of output from the buffer to @c write. + */ + void write_one_line(IStringWriter& write) + { + if (wrote_something) // if we already wrote something, we need to start a new line + { + write("\n", 1); + int _ = 0; + indent(write, _, x); + } + + if (!buf_empty()) + { + buf_next(); + write(datbuf[tail], lenbuf[tail]); + } + + wrote_something = true; + } + public: + + /** + * @brief Writes out all remaining data from the LineWrapper using @c write. + * Unlike @ref process() this method indents all lines including the first and + * will output a \\n at the end (but only if something has been written). + */ + void flush(IStringWriter& write) + { + if (buf_empty()) + return; + int _ = 0; + indent(write, _, x); + wrote_something = false; + while (!buf_empty()) + write_one_line(write); + write("\n", 1); + } + + /** + * @brief Process, wrap and output the next piece of data. + * + * process() will output at least one line of output. This is not necessarily + * the @c data passed in. It may be data queued from a prior call to process(). + * If the internal buffer is full, more than 1 line will be output. + * + * process() assumes that the a proper amount of indentation has already been + * output. It won't write any further indentation before the 1st line. If + * more than 1 line is written due to buffer constraints, the lines following + * the first will be indented by this method, though. + * + * No \\n is written by this method after the last line that is written. + * + * @param write where to write the data. + * @param data the new chunk of data to write. + * @param len the length of the chunk of data to write. + */ + void process(IStringWriter& write, const char* data, int len) + { + wrote_something = false; + + while (len > 0) + { + if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes) + { + output(write, data, len); + len = 0; + } + else // if (len > width) it's possible (but not guaranteed) that utf8len > width + { + int utf8width = 0; + int maxi = 0; + while (maxi < len && utf8width < width) + { + int charbytes = 1; + unsigned ch = (unsigned char) data[maxi]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while ((maxi + charbytes < len) && // + (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code + ++charbytes; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + { + if (utf8width + 2 > width) + break; + ++utf8width; + } + } + ++utf8width; + maxi += charbytes; + } + + // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits + // onto the 1st line. If maxi == len, all characters fit on the line. + + if (maxi == len) + { + output(write, data, len); + len = 0; + } + else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line + { + int i; + for (i = maxi; i >= 0; --i) + if (data[i] == ' ') + break; + + if (i >= 0) + { + output(write, data, i); + data += i + 1; + len -= i + 1; + } + else // did not find a space to split at => split before data[maxi] + { // data[maxi] is always the beginning of a character, never a continuation byte + output(write, data, maxi); + data += maxi; + len -= maxi; + } + } + } + } + if (!wrote_something) // if we didn't already write something to make space in the buffer + write_one_line(write); // write at most one line of actual output + } + + /** + * @brief Constructs a LineWrapper that wraps its output to fit into + * screen columns @c x1 (incl.) to @c x2 (excl.). + * + * @c x1 gives the indentation LineWrapper uses if it needs to indent. + */ + LineWrapper(int x1, int x2) : + x(x1), width(x2 - x1), head(0), tail(bufmask) + { + if (width < 2) // because of wide characters we need at least width 2 or the code breaks + width = 2; + } + }; + + /** + * @internal + * @brief This is the implementation that is shared between all printUsage() templates. + * Because all printUsage() templates share this implementation, there is no template bloat. + */ + static void printUsage(IStringWriter& write, const Descriptor usage[], int width = 80, // + int last_column_min_percent = 50, int last_column_own_line_max_percent = 75) + { + if (width < 1) // protect against nonsense values + width = 80; + + if (width > 10000) // protect against overflow in the following computation + width = 10000; + + int last_column_min_width = ((width * last_column_min_percent) + 50) / 100; + int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100; + if (last_column_own_line_max_width == 0) + last_column_own_line_max_width = 1; + + LinePartIterator part(usage); + while (part.nextTable()) + { + + /***************** Determine column widths *******************************/ + + const int maxcolumns = 8; // 8 columns are enough for everyone + int col_width[maxcolumns]; + int lastcolumn; + int leftwidth; + int overlong_column_threshold = 10000; + do + { + lastcolumn = 0; + for (int i = 0; i < maxcolumns; ++i) + col_width[i] = 0; + + part.restartTable(); + while (part.nextRow()) + { + while (part.next()) + { + if (part.column() < maxcolumns) + { + upmax(lastcolumn, part.column()); + if (part.screenLength() < overlong_column_threshold) + // We don't let rows that don't use table separators (\t or \v) influence + // the width of column 0. This allows the user to interject section headers + // or explanatory paragraphs that do not participate in the table layout. + if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v') + upmax(col_width[part.column()], part.screenLength()); + } + } + } + + /* + * If the last column doesn't fit on the same + * line as the other columns, we can fix that by starting it on its own line. + * However we can't do this for any of the columns 0..lastcolumn-1. + * If their sum exceeds the maximum width we try to fix this by iteratively + * ignoring the widest line parts in the width determination until + * we arrive at a series of column widths that fit into one line. + * The result is a layout where everything is nicely formatted + * except for a few overlong fragments. + * */ + + leftwidth = 0; + overlong_column_threshold = 0; + for (int i = 0; i < lastcolumn; ++i) + { + leftwidth += col_width[i]; + upmax(overlong_column_threshold, col_width[i]); + } + + } while (leftwidth > width); + + /**************** Determine tab stops and last column handling **********************/ + + int tabstop[maxcolumns]; + tabstop[0] = 0; + for (int i = 1; i < maxcolumns; ++i) + tabstop[i] = tabstop[i - 1] + col_width[i - 1]; + + int rightwidth = width - tabstop[lastcolumn]; + bool print_last_column_on_own_line = false; + if (rightwidth < last_column_min_width && // if we don't have the minimum requested width for the last column + ( col_width[lastcolumn] == 0 || // and all last columns are > overlong_column_threshold + rightwidth < col_width[lastcolumn] // or there is at least one last column that requires more than the space available + ) + ) + { + print_last_column_on_own_line = true; + rightwidth = last_column_own_line_max_width; + } + + // If lastcolumn == 0 we must disable print_last_column_on_own_line because + // otherwise 2 copies of the last (and only) column would be output. + // Actually this is just defensive programming. It is currently not + // possible that lastcolumn==0 and print_last_column_on_own_line==true + // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 => + // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes + // a bullshit value >100 for last_column_min_percent) => the above if condition + // is false => print_last_column_on_own_line==false + if (lastcolumn == 0) + print_last_column_on_own_line = false; + + LineWrapper lastColumnLineWrapper(width - rightwidth, width); + LineWrapper interjectionLineWrapper(0, width); + + part.restartTable(); + + /***************** Print out all rows of the table *************************************/ + + while (part.nextRow()) + { + int x = -1; + while (part.next()) + { + if (part.column() > lastcolumn) + continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1) + + if (part.column() == 0) + { + if (x >= 0) + write("\n", 1); + x = 0; + } + + indent(write, x, tabstop[part.column()]); + + if ((part.column() < lastcolumn) + && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v')) + { + write(part.data(), part.length()); + x += part.screenLength(); + } + else // either part.column() == lastcolumn or we are in the special case of + // an interjection that doesn't contain \v or \t + { + // NOTE: This code block is not necessarily executed for + // each line, because some rows may have fewer columns. + + LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper; + + if (!print_last_column_on_own_line || part.column() != lastcolumn) + lineWrapper.process(write, part.data(), part.length()); + } + } // while + + if (print_last_column_on_own_line) + { + part.restartRow(); + while (part.next()) + { + if (part.column() == lastcolumn) + { + write("\n", 1); + int _ = 0; + indent(write, _, width - rightwidth); + lastColumnLineWrapper.process(write, part.data(), part.length()); + } + } + } + + write("\n", 1); + lastColumnLineWrapper.flush(write); + interjectionLineWrapper.flush(write); + } + } + } + +} +; + +/** + * @brief Outputs a nicely formatted usage string with support for multi-column formatting + * and line-wrapping. + * + * printUsage() takes the @c help texts of a Descriptor[] array and formats them into + * a usage message, wrapping lines to achieve the desired output width. + * + * Table formatting: + * + * Aside from plain strings which are simply line-wrapped, the usage may contain tables. Tables + * are used to align elements in the output. + * + * @code + * // Without a table. The explanatory texts are not aligned. + * -c, --create |Creates something. + * -k, --kill |Destroys something. + * + * // With table formatting. The explanatory texts are aligned. + * -c, --create |Creates something. + * -k, --kill |Destroys something. + * @endcode + * + * Table formatting removes the need to pad help texts manually with spaces to achieve + * alignment. To create a table, simply insert \\t (tab) characters to separate the cells + * within a row. + * + * @code + * const option::Descriptor usage[] = { + * {..., "-c, --create \tCreates something." }, + * {..., "-k, --kill \tDestroys something." }, ... + * @endcode + * + * Note that you must include the minimum amount of space desired between cells yourself. + * Table formatting will insert further spaces as needed to achieve alignment. + * + * You can insert line breaks within cells by using \\v (vertical tab). + * + * @code + * const option::Descriptor usage[] = { + * {..., "-c,\v--create \tCreates\vsomething." }, + * {..., "-k,\v--kill \tDestroys\vsomething." }, ... + * + * // results in + * + * -c, Creates + * --create something. + * -k, Destroys + * --kill something. + * @endcode + * + * You can mix lines that do not use \\t or \\v with those that do. The plain + * lines will not mess up the table layout. Alignment of the table columns will + * be maintained even across these interjections. + * + * @code + * const option::Descriptor usage[] = { + * {..., "-c, --create \tCreates something." }, + * {..., "----------------------------------" }, + * {..., "-k, --kill \tDestroys something." }, ... + * + * // results in + * + * -c, --create Creates something. + * ---------------------------------- + * -k, --kill Destroys something. + * @endcode + * + * You can have multiple tables within the same usage whose columns are + * aligned independently. Simply insert a dummy Descriptor with @c help==0. + * + * @code + * const option::Descriptor usage[] = { + * {..., "Long options:" }, + * {..., "--very-long-option \tDoes something long." }, + * {..., "--ultra-super-mega-long-option \tTakes forever to complete." }, + * {..., 0 }, // ---------- table break ----------- + * {..., "Short options:" }, + * {..., "-s \tShort." }, + * {..., "-q \tQuick." }, ... + * + * // results in + * + * Long options: + * --very-long-option Does something long. + * --ultra-super-mega-long-option Takes forever to complete. + * Short options: + * -s Short. + * -q Quick. + * + * // Without the table break it would be + * + * Long options: + * --very-long-option Does something long. + * --ultra-super-mega-long-option Takes forever to complete. + * Short options: + * -s Short. + * -q Quick. + * @endcode + * + * Output methods: + * + * Because TheLeanMeanC++Option parser is freestanding, you have to provide the means for + * output in the first argument(s) to printUsage(). Because printUsage() is implemented as + * a set of template functions, you have great flexibility in your choice of output + * method. The following example demonstrates typical uses. Anything that's similar enough + * will work. + * + * @code + * #include // write() + * #include // cout + * #include // ostringstream + * #include // fwrite() + * using namespace std; + * + * void my_write(const char* str, int size) { + * fwrite(str, size, 1, stdout); + * } + * + * struct MyWriter { + * void write(const char* buf, size_t size) const { + * fwrite(str, size, 1, stdout); + * } + * }; + * + * struct MyWriteFunctor { + * void operator()(const char* buf, size_t size) { + * fwrite(str, size, 1, stdout); + * } + * }; + * ... + * printUsage(my_write, usage); // custom write function + * printUsage(MyWriter(), usage); // temporary of a custom class + * MyWriter writer; + * printUsage(writer, usage); // custom class object + * MyWriteFunctor wfunctor; + * printUsage(&wfunctor, usage); // custom functor + * printUsage(write, 1, usage); // write() to file descriptor 1 + * printUsage(cout, usage); // an ostream& + * printUsage(fwrite, stdout, usage); // fwrite() to stdout + * ostringstream sstr; + * printUsage(sstr, usage); // an ostringstream& + * + * @endcode + * + * @par Notes: + * @li the @c write() method of a class that is to be passed as a temporary + * as @c MyWriter() is in the example, must be a @c const method, because + * temporary objects are passed as const reference. This only applies to + * temporary objects that are created and destroyed in the same statement. + * If you create an object like @c writer in the example, this restriction + * does not apply. + * @li a functor like @c MyWriteFunctor in the example must be passed as a pointer. + * This differs from the way functors are passed to e.g. the STL algorithms. + * @li All printUsage() templates are tiny wrappers around a shared non-template implementation. + * So there's no penalty for using different versions in the same program. + * @li printUsage() always interprets Descriptor::help as UTF-8 and always produces UTF-8-encoded + * output. If your system uses a different charset, you must do your own conversion. You + * may also need to change the font of the console to see non-ASCII characters properly. + * This is particularly true for Windows. + * @li @b Security @b warning: Do not insert untrusted strings (such as user-supplied arguments) + * into the usage. printUsage() has no protection against malicious UTF-8 sequences. + * + * @param prn The output method to use. See the examples above. + * @param usage the Descriptor[] array whose @c help texts will be formatted. + * @param width the maximum number of characters per output line. Note that this number is + * in actual characters, not bytes. printUsage() supports UTF-8 in @c help and will + * count multi-byte UTF-8 sequences properly. Asian wide characters are counted + * as 2 characters. + * @param last_column_min_percent (0-100) The minimum percentage of @c width that should be available + * for the last column (which typically contains the textual explanation of an option). + * If less space is available, the last column will be printed on its own line, indented + * according to @c last_column_own_line_max_percent. + * @param last_column_own_line_max_percent (0-100) If the last column is printed on its own line due to + * less than @c last_column_min_percent of the width being available, then only + * @c last_column_own_line_max_percent of the extra line(s) will be used for the + * last column's text. This ensures an indentation. See example below. + * + * @code + * // width=20, last_column_min_percent=50 (i.e. last col. min. width=10) + * --3456789 1234567890 + * 1234567890 + * + * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15) + * // last_column_own_line_max_percent=75 + * --3456789 + * 123456789012345 + * 67890 + * + * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15) + * // last_column_own_line_max_percent=33 (i.e. max. 5) + * --3456789 + * 12345 + * 67890 + * 12345 + * 67890 + * @endcode + */ +template +void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::OStreamWriter write(prn); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(Function* prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::FunctionWriter write(prn); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::TemporaryWriter write(prn); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::SyscallWriter write(prn, fd); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(Function* prn, Stream* stream, const Descriptor usage[], int width = 80, int last_column_min_percent = + 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::StreamWriter write(prn, stream); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +} +// namespace option + +#endif /* OPTIONPARSER_H_ */ diff --git a/Source/User/Account.cpp b/Source/User/Account.cpp new file mode 100644 index 0000000..ff4b04f --- /dev/null +++ b/Source/User/Account.cpp @@ -0,0 +1,276 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "User/Account.h" +#include "Utils/binio.h" +#include "MacCompatibility.h" +#include +#include "string.h" +#include + +using namespace std; + +extern bool devtools; + +vector Account::accounts; +int Account::i_active = -1; + +Account::Account(const string& name) : name(name), campaignProgress() +{ + difficulty = 0; + progress = 0; + points = 0; + memset(highscore, 0, sizeof(highscore)); + memset(fasttime, 0, sizeof(fasttime)); + memset(unlocked, 0, sizeof(unlocked)); + + setCurrentCampaign("main"); +} + +Account::Account(FILE* tfile) : Account("") +{ + funpackf(tfile, "Bi", &difficulty); + funpackf(tfile, "Bi", &progress); + int nbCampaigns; + funpackf(tfile, "Bi", &nbCampaigns); + + for (int k = 0; k < nbCampaigns; ++k) { + string campaignName = ""; + int t; + char c; + funpackf(tfile, "Bi", &t); + for (int j = 0; j < t; j++) { + funpackf(tfile, "Bb", &c); + campaignName.append(1, c); + } + funpackf(tfile, "Bf", &(campaignProgress[campaignName].time)); + funpackf(tfile, "Bf", &(campaignProgress[campaignName].score)); + funpackf(tfile, "Bf", &(campaignProgress[campaignName].fasttime)); + funpackf(tfile, "Bf", &(campaignProgress[campaignName].highscore)); + int campaignchoicesmade, campaignchoice; + funpackf(tfile, "Bi", &campaignchoicesmade); + for (int j = 0; j < campaignchoicesmade; j++) { + funpackf(tfile, "Bi", &campaignchoice); + if (campaignchoice >= 10) { // what is that for? + campaignchoice = 0; + } + campaignProgress[campaignName].choices.push_back(campaignchoice); + } + } + + currentCampaign = ""; + int t; + char c; + funpackf(tfile, "Bi", &t); + for (int i = 0; i < t; i++) { + funpackf(tfile, "Bb", &c); + currentCampaign.append(1, c); + } + + funpackf(tfile, "Bf", &points); + for (int i = 0; i < 50; i++) { + funpackf(tfile, "Bf", &(highscore[i])); + funpackf(tfile, "Bf", &(fasttime[i])); + } + for (int i = 0; i < 60; i++) { + funpackf(tfile, "Bb", &(unlocked[i])); + } + int temp; + char ctemp; + funpackf(tfile, "Bi", &temp); + for (int i = 0; i < temp; i++) { + funpackf(tfile, "Bb", &ctemp); + name.append(1, ctemp); + } + if (name.empty()) { + name = "Lugaru Player"; // no empty player name security. + } +} + +void Account::save(FILE* tfile) +{ + fpackf(tfile, "Bi", difficulty); + fpackf(tfile, "Bi", progress); + fpackf(tfile, "Bi", campaignProgress.size()); + + map::const_iterator it; + for (it = campaignProgress.begin(); it != campaignProgress.end(); ++it) { + fpackf(tfile, "Bi", it->first.size()); + for (unsigned j = 0; j < it->first.size(); j++) { + fpackf(tfile, "Bb", it->first[j]); + } + fpackf(tfile, "Bf", it->second.time); + fpackf(tfile, "Bf", it->second.score); + fpackf(tfile, "Bf", it->second.fasttime); + fpackf(tfile, "Bf", it->second.highscore); + fpackf(tfile, "Bi", it->second.choices.size()); + for (unsigned j = 0; j < it->second.choices.size(); j++) { + fpackf(tfile, "Bi", it->second.choices[j]); + } + } + + fpackf(tfile, "Bi", getCurrentCampaign().size()); + for (unsigned j = 0; j < getCurrentCampaign().size(); j++) { + fpackf(tfile, "Bb", getCurrentCampaign()[j]); + } + + fpackf(tfile, "Bf", points); + for (unsigned j = 0; j < 50; j++) { + fpackf(tfile, "Bf", highscore[j]); + fpackf(tfile, "Bf", fasttime[j]); + } + for (unsigned j = 0; j < 60; j++) { + fpackf(tfile, "Bb", unlocked[j]); + } + fpackf(tfile, "Bi", name.size()); + for (unsigned j = 0; j < name.size(); j++) { + fpackf(tfile, "Bb", name[j]); + } +} + +void Account::setCurrentCampaign(const string& name) +{ + currentCampaign = name; +} + +void Account::add(const string& name) +{ + accounts.emplace_back(name); + i_active = accounts.size() - 1; +} + +Account& Account::get(int i) +{ + return accounts.at(i); +} + +int Account::getNbAccounts() +{ + return accounts.size(); +} + +bool Account::hasActive() +{ + return (i_active >= 0); +} + +Account& Account::active() +{ + return accounts.at(i_active); +} + +void Account::setActive(int i) +{ + if ((i >= 0) && (i < int(accounts.size()))) { + i_active = i; + } else { + cerr << "Tried to set active account to " << i << " but there is not such account" << endl; + i_active = -1; + } +} + +void Account::destroyActive() +{ + if ((i_active >= 0) && (i_active < int(accounts.size()))) { + accounts.erase(accounts.begin() + i_active); + i_active = -1; + } else { + cerr << "Tried to destroy active account " << i_active << " but there is not such account" << endl; + i_active = -1; + } +} + +int Account::getDifficulty() +{ + return difficulty; +} + +void Account::endGame() +{ + campaignProgress[currentCampaign].choices.clear(); + campaignProgress[currentCampaign].score = 0; + campaignProgress[currentCampaign].time = 0; +} + +void Account::winCampaignLevel(int choice, float score, float time) +{ + campaignProgress[currentCampaign].choices.push_back(choice); + setCampaignScore(campaignProgress[currentCampaign].score + score); + campaignProgress[currentCampaign].time = time; +} + +void Account::winLevel(int level, float score, float time) +{ + if (!devtools) { + if (score > highscore[level]) + highscore[level] = score; + if (time < fasttime[level] || fasttime[level] == 0) + fasttime[level] = time; + } + if (progress < level + 1) + progress = level + 1; +} + +void Account::loadFile(string filename) +{ + FILE *tfile; + int numaccounts; + int iactive; + errno = 0; + + tfile = fopen(filename.c_str(), "rb" ); + + if (tfile) { + funpackf(tfile, "Bi", &numaccounts); + funpackf(tfile, "Bi", &iactive); + printf("Loading %d accounts\n", numaccounts); + for (int i = 0; i < numaccounts; i++) { + printf("Loading account %d/%d\n", i, numaccounts); + accounts.emplace_back(tfile); + } + + fclose(tfile); + setActive(iactive); + } else { + perror(("Couldn't load users from " + filename).c_str()); + i_active = -1; + } +} + +void Account::saveFile(string filename) +{ + FILE *tfile; + errno = 0; + + tfile = fopen(filename.c_str(), "wb" ); + if (tfile) { + fpackf(tfile, "Bi", getNbAccounts()); + fpackf(tfile, "Bi", i_active); + + for (int i = 0; i < getNbAccounts(); i++) { + printf("writing account %d/%d (%s)\n", i + 1, getNbAccounts(), accounts[i].getName().c_str()); + accounts[i].save(tfile); + } + + fclose(tfile); + } else { + perror(("Couldn't save users in " + filename).c_str()); + } +} diff --git a/Source/User/Account.h b/Source/User/Account.h new file mode 100644 index 0000000..385c6fa --- /dev/null +++ b/Source/User/Account.h @@ -0,0 +1,133 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _Account_H_ +#define _Account_H_ + +#include +#include +#include +#include + +struct CampaignProgress { + float highscore; + float fasttime; + float score; + float time; + std::vector choices; + CampaignProgress() { + highscore = 0; + fasttime = 0; + score = 0; + time = 0; + } +}; + +class Account +{ +public: + static void destroyActive(); + static void setActive(int i); + static void add(const std::string& name); + static Account& get(int i); + static void loadFile(std::string filename); + static void saveFile(std::string filename); + static int getNbAccounts(); + + static bool hasActive(); + static Account& active(); + + Account(const std::string& name = ""); + Account(FILE* tfile); + + void endGame(); + void winCampaignLevel(int choice, float score, float time); + void winLevel(int level, float score, float time); + + // getter and setters + int getDifficulty(); + void setDifficulty(int i) { + difficulty = i; + }; + const std::string& getName() { + return name; + }; + float getCampaignScore() { + return campaignProgress[currentCampaign].score; + }; + int getCampaignChoicesMade() { + return campaignProgress[currentCampaign].choices.size(); + }; + int getCampaignChoice(int i) { + return campaignProgress[currentCampaign].choices[i]; + }; + void setCampaignScore(int s) { + campaignProgress[currentCampaign].score = s; + if (s > campaignProgress[currentCampaign].highscore) + campaignProgress[currentCampaign].highscore = s; + }; + void setCampaignFinalTime(float t) { + campaignProgress[currentCampaign].time = t; + if ((t < campaignProgress[currentCampaign].fasttime) || ((campaignProgress[currentCampaign].fasttime == 0) && (t != 0))) + campaignProgress[currentCampaign].fasttime = t; + }; + float getCampaignFasttime() { + return campaignProgress[currentCampaign].fasttime; + }; + void resetFasttime() { + campaignProgress[currentCampaign].fasttime = 0; + }; + float getCampaignHighScore() { + return campaignProgress[currentCampaign].highscore; + }; + float getHighScore(int i) { + return highscore[i]; + }; + float getFastTime(int i) { + return fasttime[i]; + }; + int getProgress() { + return progress; + }; + std::string getCurrentCampaign() { + return currentCampaign; + }; + void setCurrentCampaign(const std::string& name); + +private: + //statics + static std::vector accounts; + static int i_active; + + void save(FILE* tfile); + + int difficulty; + int progress; // progress in challenge levels + float points; + float highscore[50]; + float fasttime[50]; + bool unlocked[60]; + std::string name; + + std::string currentCampaign; + std::map campaignProgress; +}; + +#endif diff --git a/Source/User/Settings.cpp b/Source/User/Settings.cpp new file mode 100644 index 0000000..9948e63 --- /dev/null +++ b/Source/User/Settings.cpp @@ -0,0 +1,316 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include "Game.h" +#include "User/Settings.h" +#include "Utils/Folders.h" +#include "Utils/Input.h" + +using namespace Game; + +void DefaultSettings() +{ + detail = 2; + ismotionblur = 1; + usermousesensitivity = 1; + newscreenwidth = kContextWidth = 1024; + newscreenheight = kContextHeight = 768; + fullscreen = 0; + floatjump = 0; + autoslomo = 1; + decals = 1; + invertmouse = 0; + bloodtoggle = 0; + foliage = 1; + musictoggle = 1; + trilinear = 1; + gamespeed = 1; + damageeffects = 0; + texttoggle = 1; + alwaysblur = 0; + showpoints = 0; + showdamagebar = 0; + immediate = 0; + velocityblur = 0; + volume = 0.8f; + ambientsound = 1; + devtools = 0; + + crouchkey = SDL_SCANCODE_LSHIFT; + jumpkey = SDL_SCANCODE_SPACE; + leftkey = SDL_SCANCODE_A; + forwardkey = SDL_SCANCODE_W; + backkey = SDL_SCANCODE_S; + rightkey = SDL_SCANCODE_D; + drawkey = SDL_SCANCODE_E; + throwkey = SDL_SCANCODE_Q; + attackkey = MOUSEBUTTON1; + consolekey = SDL_SCANCODE_GRAVE; + + newdetail = detail; +} + +void SaveSettings() +{ + if (newdetail < 0) + newdetail = 0; + if (newdetail > 2) + newdetail = 2; + if (newscreenwidth > 3000) + newscreenwidth = screenwidth; + if (newscreenwidth < 0) + newscreenwidth = screenwidth; + if (newscreenheight > 3000) + newscreenheight = screenheight; + if (newscreenheight < 0) + newscreenheight = screenheight; + errno = 0; + ofstream opstream(Folders::getConfigFilePath()); + if (opstream.fail()) { + perror(("Couldn't save config file " + Folders::getConfigFilePath()).c_str()); + return; + } + opstream << "Screenwidth:\n"; + opstream << newscreenwidth; + opstream << "\nScreenheight:\n"; + opstream << newscreenheight; + opstream << "\nFullscreen:\n"; + opstream << fullscreen; + opstream << "\nMouse sensitivity:\n"; + opstream << usermousesensitivity; + opstream << "\nBlur(0,1):\n"; + opstream << ismotionblur; + opstream << "\nOverall Detail(0,1,2) higher=better:\n"; + opstream << newdetail; + opstream << "\nFloating jump:\n"; + opstream << floatjump; + opstream << "\nMouse jump:\n"; + opstream << mousejump; + opstream << "\nAmbient sound:\n"; + opstream << ambientsound; + opstream << "\nBlood (0,1,2):\n"; + opstream << bloodtoggle; + opstream << "\nAuto slomo:\n"; + opstream << autoslomo; + opstream << "\nFoliage:\n"; + opstream << foliage; + opstream << "\nMusic:\n"; + opstream << musictoggle; + opstream << "\nTrilinear:\n"; + opstream << trilinear; + opstream << "\nDecals(shadows,blood puddles,etc):\n"; + opstream << decals; + opstream << "\nInvert mouse:\n"; + opstream << invertmouse; + opstream << "\nGamespeed:\n"; + if (oldgamespeed == 0) + oldgamespeed = 1; + opstream << oldgamespeed; + opstream << "\nDamage effects(blackout, doublevision):\n"; + opstream << damageeffects; + opstream << "\nText:\n"; + opstream << texttoggle; + opstream << "\nShow Points:\n"; + opstream << showpoints; + opstream << "\nAlways Blur:\n"; + opstream << alwaysblur; + opstream << "\nImmediate mode (turn on on G5):\n"; + opstream << immediate; + opstream << "\nVelocity blur:\n"; + opstream << velocityblur; + opstream << "\nVolume:\n"; + opstream << volume; + opstream << "\nForward key:\n"; + opstream << forwardkey; + opstream << "\nBack key:\n"; + opstream << backkey; + opstream << "\nLeft key:\n"; + opstream << leftkey; + opstream << "\nRight key:\n"; + opstream << rightkey; + opstream << "\nJump key:\n"; + opstream << jumpkey; + opstream << "\nCrouch key:\n"; + opstream << crouchkey; + opstream << "\nDraw key:\n"; + opstream << drawkey; + opstream << "\nThrow key:\n"; + opstream << throwkey; + opstream << "\nAttack key:\n"; + opstream << attackkey; + opstream << "\nConsole key:\n"; + opstream << consolekey; + opstream << "\nDamage bar:\n"; + opstream << showdamagebar; + opstream << "\nStereoMode:\n"; + opstream << stereomode; + opstream << "\nStereoSeparation:\n"; + opstream << stereoseparation; + opstream << "\nStereoReverse:\n"; + opstream << stereoreverse; + opstream << "\n"; + opstream.close(); +} + +bool LoadSettings() +{ + errno = 0; + ifstream ipstream(Folders::getConfigFilePath(), std::ios::in); + if ( ipstream.fail() ) { + perror(("Couldn't read config file " + Folders::getConfigFilePath()).c_str()); + return false; + } + char setting[256]; + char string[256]; + + printf("Loading config\n"); + while (!ipstream.eof()) { + ipstream.getline( setting, sizeof(setting) ); + + // skip blank lines + // assume lines starting with spaces are all blank + if ( strlen(setting) == 0 || setting[0] == ' ' || setting[0] == '\t') + continue; + //~ printf("setting : %s\n",setting); + + if ( ipstream.eof() || ipstream.fail() ) { + fprintf(stderr, "Error reading config file: Got setting name '%s', but value can't be read\n", setting); + ipstream.close(); + return false; + } + + + if ( !strncmp(setting, "Screenwidth", 11) ) { + ipstream >> kContextWidth; + } else if ( !strncmp(setting, "Screenheight", 12) ) { + ipstream >> kContextHeight; + } else if ( !strncmp(setting, "Fullscreen", 10) ) { + ipstream >> fullscreen; + } else if ( !strncmp(setting, "Mouse sensitivity", 17) ) { + ipstream >> usermousesensitivity; + } else if ( !strncmp(setting, "Blur", 4) ) { + ipstream >> ismotionblur; + } else if ( !strncmp(setting, "Overall Detail", 14) ) { + ipstream >> detail; + } else if ( !strncmp(setting, "Floating jump", 13) ) { + ipstream >> floatjump; + } else if ( !strncmp(setting, "Mouse jump", 10) ) { + ipstream >> mousejump; + } else if ( !strncmp(setting, "Ambient sound", 13) ) { + ipstream >> ambientsound; + } else if ( !strncmp(setting, "Blood ", 6) ) { + ipstream >> bloodtoggle; + } else if ( !strncmp(setting, "Auto slomo", 10) ) { + ipstream >> autoslomo; + } else if ( !strncmp(setting, "Foliage", 7) ) { + ipstream >> foliage; + } else if ( !strncmp(setting, "Music", 5) ) { + ipstream >> musictoggle; + } else if ( !strncmp(setting, "Trilinear", 9) ) { + ipstream >> trilinear; + } else if ( !strncmp(setting, "Decals", 6) ) { + ipstream >> decals; + } else if ( !strncmp(setting, "Invert mouse", 12) ) { + ipstream >> invertmouse; + } else if ( !strncmp(setting, "Gamespeed", 9) ) { + ipstream >> gamespeed; + oldgamespeed = gamespeed; + if (oldgamespeed == 0) { + gamespeed = 1; + oldgamespeed = 1; + } + } else if ( !strncmp(setting, "Damage effects", 14) ) { + ipstream >> damageeffects; + } else if ( !strncmp(setting, "Text", 4) ) { + ipstream >> texttoggle; + } else if ( !strncmp(setting, "Devtools", 5) ) { + ipstream >> devtools; + } else if ( !strncmp(setting, "Show Points", 11) ) { + ipstream >> showpoints; + } else if ( !strncmp(setting, "Always Blur", 11) ) { + ipstream >> alwaysblur; + } else if ( !strncmp(setting, "Immediate mode ", 15) ) { + ipstream >> immediate; + } else if ( !strncmp(setting, "Velocity blur", 13) ) { + ipstream >> velocityblur; + } else if ( !strncmp(setting, "Volume", 6) ) { + ipstream >> volume; + } else if ( !strncmp(setting, "Forward key", 11) ) { + ipstream >> forwardkey; + } else if ( !strncmp(setting, "Back key", 8) ) { + ipstream >> backkey; + } else if ( !strncmp(setting, "Left key", 8) ) { + ipstream >> leftkey; + } else if ( !strncmp(setting, "Right key", 9) ) { + ipstream >> rightkey; + } else if ( !strncmp(setting, "Jump key", 8) ) { + ipstream >> jumpkey; + } else if ( !strncmp(setting, "Crouch key", 10) ) { + ipstream >> crouchkey; + } else if ( !strncmp(setting, "Draw key", 8) ) { + ipstream >> drawkey; + } else if ( !strncmp(setting, "Throw key", 9) ) { + ipstream >> throwkey; + } else if ( !strncmp(setting, "Attack key", 10) ) { + ipstream >> attackkey; + } else if ( !strncmp(setting, "Console key", 11) ) { + ipstream >> consolekey; + } else if ( !strncmp(setting, "Damage bar", 10) ) { + ipstream >> showdamagebar; + } else if ( !strncmp(setting, "StereoMode", 10) ) { + int i; + ipstream >> i; + stereomode = (StereoMode)i; + } else if ( !strncmp(setting, "StereoSeparation", 16) ) { + ipstream >> stereoseparation; + } else if ( !strncmp(setting, "StereoReverse", 13) ) { + ipstream >> stereoreverse; + } else { + ipstream >> string; + fprintf(stderr, "Unknown config option '%s' with value '%s'. Ignoring.\n", setting, string); + } + + if ( ipstream.fail() ) { + fprintf(stderr, "Error reading config file: EOF reached when trying to read value for setting '%s'.\n", setting); + ipstream.close(); + return false; + } + + if ( ipstream.bad() ) { + fprintf(stderr, "Error reading config file: Failed to read value for setting '%s'.\n", setting); + ipstream.close(); + return false; + } + } + + ipstream.close(); + + if (detail > 2) + detail = 2; + if (detail < 0) + detail = 0; + if (screenwidth < 0) + screenwidth = 1024; + if (screenheight < 0) + screenheight = 768; + + newdetail = detail; + return true; +} diff --git a/Source/User/Settings.h b/Source/User/Settings.h new file mode 100644 index 0000000..e5f983a --- /dev/null +++ b/Source/User/Settings.h @@ -0,0 +1,60 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef SETTINGS_H_ +#define SETTINGS_H_ + +#include "Game.h" + +extern float usermousesensitivity; +extern bool ismotionblur; +extern bool floatjump; +extern bool mousejump; +extern bool ambientsound; +extern int bloodtoggle; +extern bool autoslomo; +extern bool foliage; +extern bool musictoggle; +extern bool trilinear; +extern bool decals; +extern bool invertmouse; +extern float gamespeed; +extern float oldgamespeed; +extern bool damageeffects; +extern bool texttoggle; +extern bool devtools; +extern bool showpoints; +extern bool showdamagebar; +extern bool alwaysblur; +extern bool immediate; +extern bool velocityblur; +extern float volume; +extern int detail; +extern int kContextWidth; +extern int kContextHeight; +extern float screenwidth, screenheight; +extern bool fullscreen; + +void DefaultSettings(); +void SaveSettings(); +bool LoadSettings(); + + +#endif diff --git a/Source/Utils/ImageIO.cpp b/Source/Utils/ImageIO.cpp new file mode 100644 index 0000000..7a405fc --- /dev/null +++ b/Source/Utils/ImageIO.cpp @@ -0,0 +1,297 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +/**> HEADER FILES <**/ + +#include +#include +#include +#include + +#include "Game.h" +#include "Utils/ImageIO.h" +#include "Utils/Folders.h" + +extern bool visibleloading; + +/* These two are needed for screenshot */ +extern int kContextWidth; +extern int kContextHeight; + +static bool load_png(const char * fname, ImageRec & tex); +static bool load_jpg(const char * fname, ImageRec & tex); +static bool save_screenshot_png(const char * fname); + +ImageRec::ImageRec() +{ + data = ( GLubyte* )malloc( 1024 * 1024 * 4 ); +} + +ImageRec::~ImageRec() +{ + free(data); + data = NULL; +} + +bool load_image(const char *file_name, ImageRec &tex) +{ + if (visibleloading) + Game::LoadingScreen(); + + if ( tex.data == NULL ) + return false; + + const char *ptr = strrchr((char *)file_name, '.'); + if (ptr) { + if (strcasecmp(ptr + 1, "png") == 0) + return load_png(file_name, tex); + else if (strcasecmp(ptr + 1, "jpg") == 0) + return load_jpg(file_name, tex); + } + + STUBBED("Unsupported image type"); + return false; +} + +bool save_screenshot(const char *file_name) +{ + const char *ptr = strrchr((char *)file_name, '.'); + if (ptr) { + if (strcasecmp(ptr + 1, "png") == 0) + return save_screenshot_png((Folders::getScreenshotDir() + '/' + file_name).c_str()); + } + + STUBBED("Unsupported image type"); + return false; +} + +struct my_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ +}; +typedef struct my_error_mgr * my_error_ptr; + +static void my_error_exit(j_common_ptr cinfo) +{ + struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err; + longjmp(err->setjmp_buffer, 1); +} + +/* stolen from public domain example.c code in libjpg distribution. */ +static bool load_jpg(const char *file_name, ImageRec &tex) +{ + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jerr; + JSAMPROW buffer[1]; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + FILE *infile = fopen(file_name, "rb"); + + if (infile == NULL) + return false; + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + if (setjmp(jerr.setjmp_buffer)) { + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return false; + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + (void) jpeg_read_header(&cinfo, TRUE); + + cinfo.out_color_space = JCS_RGB; + cinfo.quantize_colors = 0; + (void) jpeg_calc_output_dimensions(&cinfo); + (void) jpeg_start_decompress(&cinfo); + + row_stride = cinfo.output_width * cinfo.output_components; + tex.sizeX = cinfo.output_width; + tex.sizeY = cinfo.output_height; + tex.bpp = 24; + + while (cinfo.output_scanline < cinfo.output_height) { + buffer[0] = (JSAMPROW)(char *)tex.data + + ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride; + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + } + + (void) jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + + return true; +} + +/* stolen from public domain example.c code in libpng distribution. */ +static bool load_png(const char *file_name, ImageRec &tex) +{ + bool hasalpha = false; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + bool retval = false; + png_byte **row_pointers = NULL; + FILE *fp = fopen(file_name, "rb"); + + if (fp == NULL) { + cerr << file_name << " not found" << endl; + return false; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) + goto png_done; + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + goto png_done; + + if (setjmp(png_jmpbuf(png_ptr))) + goto png_done; + + png_init_io(png_ptr, fp); + png_read_png(png_ptr, info_ptr, + PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING, + NULL); + png_get_IHDR(png_ptr, info_ptr, &width, &height, + &bit_depth, &color_type, &interlace_type, NULL, NULL); + + if (bit_depth != 8) // transform SHOULD handle this... + goto png_done; + + if (color_type & PNG_COLOR_MASK_PALETTE) // !!! FIXME? + goto png_done; + + if ((color_type & PNG_COLOR_MASK_COLOR) == 0) // !!! FIXME? + goto png_done; + + hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0); + row_pointers = png_get_rows(png_ptr, info_ptr); + if (!row_pointers) + goto png_done; + + if (!hasalpha) { + png_byte *dst = tex.data; + for (int i = height - 1; i >= 0; i--) { + png_byte *src = row_pointers[i]; + for (unsigned j = 0; j < width; j++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = 0xFF; + src += 3; + dst += 4; + } + } + } + + else { + png_byte *dst = tex.data; + int pitch = width * 4; + for (int i = height - 1; i >= 0; i--, dst += pitch) + memcpy(dst, row_pointers[i], pitch); + } + + tex.sizeX = width; + tex.sizeY = height; + tex.bpp = 32; + retval = true; + +png_done: + if (!retval) { + cerr << "There was a problem loading " << file_name << endl; + } + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (fp) + fclose(fp); + return (retval); +} + +static bool save_screenshot_png(const char *file_name) +{ + FILE *fp = NULL; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + bool retval = false; + + fp = fopen(file_name, "wb"); + if (fp == NULL) + return false; + + png_bytep *row_pointers = new png_bytep[kContextHeight]; + png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3]; + if ((!screenshot) || (!row_pointers)) + goto save_png_done; + + glGetError(); + glReadPixels(0, 0, kContextWidth, kContextHeight, + GL_RGB, GL_UNSIGNED_BYTE, screenshot); + if (glGetError() != GL_NO_ERROR) + goto save_png_done; + + for (int i = 0; i < kContextHeight; i++) + row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3); + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) + goto save_png_done; + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + goto save_png_done; + + if (setjmp(png_jmpbuf(png_ptr))) + goto save_png_done; + + png_init_io(png_ptr, fp); + + if (setjmp(png_jmpbuf(png_ptr))) + goto save_png_done; + + png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight, + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + if (setjmp(png_jmpbuf(png_ptr))) + goto save_png_done; + + png_write_image(png_ptr, row_pointers); + + if (setjmp(png_jmpbuf(png_ptr))) + goto save_png_done; + + png_write_end(png_ptr, NULL); + retval = true; + +save_png_done: + png_destroy_write_struct(&png_ptr, &info_ptr); + delete[] screenshot; + delete[] row_pointers; + if (fp) + fclose(fp); + if (!retval) + unlink(file_name); + return retval; +} diff --git a/Source/Utils/ImageIO.h b/Source/Utils/ImageIO.h new file mode 100644 index 0000000..0c587bf --- /dev/null +++ b/Source/Utils/ImageIO.h @@ -0,0 +1,62 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _IMAGE_IO_H_ +#define _IMAGE_IO_H_ + +#ifdef _MSC_VER +#pragma once +#endif + + +/**> HEADER FILES <**/ +#include +#include +#include +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#define Polygon WinPolygon +#include +#undef Polygon +#include "GL/gl.h" +#else +#include "Graphic/gamegl.h" +#endif + +/**> DATA STRUCTURES <**/ +class ImageRec { +public: + GLubyte *data; // Image Data (Up To 32 Bits) + GLuint bpp; // Image Color Depth In Bits Per Pixel. + GLuint sizeX; + GLuint sizeY; + ImageRec(); + ~ImageRec(); +private: + /* Make sure this class cannot be copied to avoid memory problems */ + ImageRec(ImageRec const &); + ImageRec& operator=(ImageRec const &); +}; + +bool load_image(const char * fname, ImageRec & tex); +bool save_screenshot(const char * fname); + +#endif + diff --git a/Source/Utils/Input.cpp b/Source/Utils/Input.cpp new file mode 100644 index 0000000..9376522 --- /dev/null +++ b/Source/Utils/Input.cpp @@ -0,0 +1,92 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +/**> HEADER FILES <**/ +#include "Utils/Input.h" + +bool keyDown[SDL_NUM_SCANCODES + 6]; +bool keyPressed[SDL_NUM_SCANCODES + 6]; + +void Input::Tick() +{ + SDL_PumpEvents(); + int numkeys; + const Uint8 *keyState = SDL_GetKeyboardState(&numkeys); + for (int i = 0; i < numkeys; i++) { + keyPressed[i] = !keyDown[i] && keyState[i]; + keyDown[i] = keyState[i]; + } + Uint8 mb = SDL_GetMouseState(NULL, NULL); + for (int i = 1; i < 6; i++) { + keyPressed[SDL_NUM_SCANCODES + i] = !keyDown[SDL_NUM_SCANCODES + i] && (mb & SDL_BUTTON(i)); + keyDown[SDL_NUM_SCANCODES + i] = (mb & SDL_BUTTON(i)); + } +} + +bool Input::isKeyDown(int k) +{ + if (k >= SDL_NUM_SCANCODES + 6) // really useful? check that. + return false; + return keyDown[k]; +} + +bool Input::isKeyPressed(int k) +{ + if (k >= SDL_NUM_SCANCODES + 6) + return false; + return keyPressed[k]; +} + +const char* Input::keyToChar(unsigned short i) +{ + if (i < SDL_NUM_SCANCODES) + return SDL_GetScancodeName(SDL_Scancode(i)); + else if (i == MOUSEBUTTON1) + return "mouse1"; + else if (i == MOUSEBUTTON2) + return "mouse2"; + else if (i == MOUSEBUTTON3) + return "mouse3"; + else + return "unknown"; +} + +unsigned short Input::CharToKey(const char* which) +{ + for (unsigned short i = 0; i < SDL_NUM_SCANCODES; i++) { + if (!strcasecmp(which, SDL_GetScancodeName(SDL_Scancode(i)))) + return i; + } + if (!strcasecmp(which, "mouse1")) { + return MOUSEBUTTON1; + } + if (!strcasecmp(which, "mouse2")) { + return MOUSEBUTTON2; + } + if (!strcasecmp(which, "mouse3")) { + return MOUSEBUTTON3; + } + return SDL_NUM_SCANCODES; +} + +bool Input::MouseClicked() +{ + return isKeyPressed(SDL_NUM_SCANCODES + SDL_BUTTON_LEFT); +} diff --git a/Source/Utils/Input.h b/Source/Utils/Input.h new file mode 100644 index 0000000..3bf8d79 --- /dev/null +++ b/Source/Utils/Input.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef _Input_H_ +#define _Input_H_ + +/**> HEADER FILES <**/ +#include "SDL.h" +#include "Game.h" + +/**> CONSTANT DECLARATIONS <**/ +#define MOUSEBUTTON1 (SDL_NUM_SCANCODES + SDL_BUTTON_LEFT) +#define MOUSEBUTTON2 (SDL_NUM_SCANCODES + SDL_BUTTON_RIGHT) +#define MOUSEBUTTON3 (SDL_NUM_SCANCODES + SDL_BUTTON_MIDDLE) + +/**> FUNCTION PROTOTYPES <**/ +class Input +{ +public: + static void Tick(); + static bool isKeyDown(int k); + static bool isKeyPressed(int k); + static const char* keyToChar(unsigned short which); + static unsigned short CharToKey(const char* which); + static bool MouseClicked(); +}; + +#endif diff --git a/Source/Utils/binio.h b/Source/Utils/binio.h new file mode 100644 index 0000000..dddb214 --- /dev/null +++ b/Source/Utils/binio.h @@ -0,0 +1,121 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef binio_h +#define binio_h + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + + /* + Notes on format of format strings: + * whitespace is ignored + * each "group" consists of an optional count (defaults to 1), + an optional byte-order marker (defaults to H, "host-native"), + and a data-type specifier. + * when unpacking, each variable argument is a pointer to the + appropriate number of objects of the appropriate type. + * when packing, each variable argument is an object of the + appropriate type if the count is omitted, or a pointer to the + appropriate number of objects of the appropriate type if the + count is specified. + * the buffer supplied to pack/unpack must be of sufficient length + to hold all the data, or the behavior is unspecified. + * the file provided to the "f" versions of the functions must be + open in the appropriate mode, or the behavior is unspecified. + * the file supplied to funpackf must be of sufficient length to + hold all the data, or the behavior is unspecified. + * the behavior of all functions is unspecified if the format string + is incorrectly-formed. + + Data-type specifiers: + x skipped byte; no corresponding argument + b byte + s two-byte two's-complement integer + i four-byte two's-complement integer + l eight-byte two's-complement integer + f four-byte IEEE754 float + d eight-byte IEEE754 double + + Byte-order specifiers: + L little-endian + B big-endian + H host's native byte order + N network byte order + */ + +#ifndef ALREADY_DID_BINIO_STDINT +#define ALREADY_DID_BINIO_STDINT +#if defined(BinIO_STDINT_HEADER) +#include BinIO_STDINT_HEADER + typedef float float32_t; + typedef double float64_t; +#else + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned long uint32_t; +#ifdef WIN32 + typedef unsigned __int64 uint64_t; +#else + typedef unsigned long long uint64_t; +#endif + typedef float float32_t; + typedef double float64_t; +#endif +#endif + + typedef struct { + float64_t d; + uint64_t l; + int i; + float32_t f; + uint16_t s; + uint8_t b; + } + test_data; + + extern void packf ( const char *format, ...); + extern void spackf (void *buffer, const char *format, ...); + extern void fpackf (FILE *file, const char *format, ...); + extern void vspackf (void *buffer, const char *format, va_list args); + extern void vfpackf (FILE *file, const char *format, va_list args); + + extern void unpackf ( const char *format, ...); + extern void sunpackf (const void *buffer, const char *format, ...); + extern void funpackf (FILE *file, const char *format, ...); + extern void vsunpackf(const void *buffer, const char *format, va_list args); + extern void vfunpackf(FILE *file, const char *format, va_list args); + +#ifdef _MSC_VER +#ifndef va_copy +#define va_copy(dest,src) do { dest = src; } while (0) +#endif +#endif + +#if defined(__cplusplus) +} +#endif + +#endif + diff --git a/Source/Utils/pack.c b/Source/Utils/pack.c new file mode 100644 index 0000000..736085d --- /dev/null +++ b/Source/Utils/pack.c @@ -0,0 +1,158 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include + +#include "binio.h" +#include "private.h" + +struct BinIOPackContext { + uint8_t *buffer; + va_list args; +}; + +static void BinIOPack(void *context, int type, int byte_order, int count) +{ + struct BinIOPackContext *ctx = (struct BinIOPackContext*)context; + if (count == -1) { + switch (type) { + case BinIO_TYPE_IGNORE_BYTE: { + ctx->buffer += 1; + } + break; + case BinIO_TYPE_BYTE: { + uint8_t value = va_arg(ctx->args, int); + BinIOConvert1(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); + ctx->buffer += 1; + } + break; + case BinIO_TYPE_INT16: { + uint16_t value = va_arg(ctx->args, int); + BinIOConvert2(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); + ctx->buffer += 2; + } + break; + case BinIO_TYPE_INT32: { + int value = va_arg(ctx->args, int); + BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); + ctx->buffer += 4; + } + break; + case BinIO_TYPE_INT64: { + uint64_t value = va_arg(ctx->args, uint64_t); + BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); + ctx->buffer += 8; + } + break; + case BinIO_TYPE_FLOAT32: { + float32_t value = (float32_t)va_arg(ctx->args, double); + BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); + ctx->buffer += 4; + } + break; + case BinIO_TYPE_FLOAT64: { + float64_t value = va_arg(ctx->args, float64_t); + BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); + ctx->buffer += 8; + } + break; + } + } else { + switch (type) { + case BinIO_TYPE_IGNORE_BYTE: + ctx->buffer += 1 * count; + break; + case BinIO_TYPE_BYTE: + BinIOConvert1(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); + ctx->buffer += 1 * count; + break; + case BinIO_TYPE_INT16: + BinIOConvert2(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); + ctx->buffer += 2 * count; + break; + case BinIO_TYPE_INT32: + BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); + ctx->buffer += 4 * count; + break; + case BinIO_TYPE_INT64: + BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); + ctx->buffer += 8 * count; + break; + case BinIO_TYPE_FLOAT32: + BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); + ctx->buffer += 4 * count; + break; + case BinIO_TYPE_FLOAT64: + BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); + ctx->buffer += 8 * count; + break; + } + } +} + +extern void packf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfpackf(stdout, format, args); + va_end(args); +} + +extern void spackf(void *buffer, const char *format, ...) +{ + va_list args; + va_start(args, format); + vspackf(buffer, format, args); + va_end(args); +} + +extern void fpackf(FILE *file, const char *format, ...) +{ + va_list args; + va_start(args, format); + vfpackf(file, format, args); + va_end(args); +} + +extern void vspackf(void *buffer, const char *format, va_list args) +{ + struct BinIOFormatCursor cursor; + struct BinIOPackContext context; + + BinIOInitFormatCursor(&cursor, format); + + context.buffer = (unsigned char *)buffer; + va_copy(context.args, args); + + while (BinIONextChar(&context, &cursor, BinIOPack)) {} + + va_end(context.args); +} + +extern void vfpackf(FILE *file, const char *format, va_list args) +{ + size_t n_bytes = BinIOFormatByteCount(format); + void* buffer = malloc(n_bytes); + + vspackf(buffer, format, args); + + fwrite(buffer, n_bytes, 1, file); + free(buffer); +} diff --git a/Source/Utils/private.c b/Source/Utils/private.c new file mode 100644 index 0000000..6ff2b59 --- /dev/null +++ b/Source/Utils/private.c @@ -0,0 +1,204 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include + +#include "private.h" + +void BinIOConvert1(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count) +{ + if (BinIONormalizeByteOrder(from_byte_order) != + BinIONormalizeByteOrder(to_byte_order)) { + unsigned int i; + for (i = 0; i < count; ++i) { + BinIOSwap1(src, dst); + src += 1; + dst += 1; + } + } else { + memcpy(dst, src, 1 * count); + } +} + +void BinIOConvert2(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count) +{ + if (BinIONormalizeByteOrder(from_byte_order) != + BinIONormalizeByteOrder(to_byte_order)) { + unsigned int i; + for (i = 0; i < count; ++i) { + BinIOSwap2(src, dst); + src += 2; + dst += 2; + } + } else { + memcpy(dst, src, 2 * count); + } +} + +void BinIOConvert4(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count) +{ + if (BinIONormalizeByteOrder(from_byte_order) != + BinIONormalizeByteOrder(to_byte_order)) { + unsigned int i; + for (i = 0; i < count; ++i) { + BinIOSwap4(src, dst); + src += 4; + dst += 4; + } + } else { + memcpy(dst, src, 4 * count); + } +} + +void BinIOConvert8(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count) +{ + if (BinIONormalizeByteOrder(from_byte_order) != + BinIONormalizeByteOrder(to_byte_order)) { + unsigned int i; + for (i = 0; i < count; ++i) { + BinIOSwap8(src, dst); + src += 8; + dst += 8; + } + } else { + memcpy(dst, src, 8 * count); + } +} + +void BinIOInitFormatCursor(struct BinIOFormatCursor *cursor, + const char *format) +{ + cursor->cursor = format; + cursor->byte_order = BinIO_HOST_BYTE_ORDER; + cursor->count = -1; +} + +int BinIONextChar(void *context, + struct BinIOFormatCursor *cursor, + BinIOProcessFunction func) +{ + int count, value; + int c; + switch (c = *(cursor->cursor)++) { + case BinIO_LITTLE_ENDIAN_BYTE_ORDER: + case BinIO_BIG_ENDIAN_BYTE_ORDER: + case BinIO_HOST_BYTE_ORDER: + case BinIO_NETWORK_BYTE_ORDER: + cursor->byte_order = c; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + count = cursor->count; + value = c - '0'; + if (count == -1) { + cursor->count = value; + } else { + cursor->count = (count * 10) + value; + } + break; + + case BinIO_TYPE_IGNORE_BYTE: + case BinIO_TYPE_BYTE: + case BinIO_TYPE_INT16: + case BinIO_TYPE_INT32: + case BinIO_TYPE_INT64: + case BinIO_TYPE_FLOAT32: + case BinIO_TYPE_FLOAT64: + func(context, c, cursor->byte_order, cursor->count); + cursor->byte_order = BinIO_HOST_BYTE_ORDER; + cursor->count = -1; + break; + + case ' ': + case '\t': + case '\r': + case '\n': + break; + + default: + return 0; + } + + return 1; +} + +extern void BinIOCountBytes(void *context, int type, int byte_order, int count) +{ + size_t type_size = 0; + + if (count == -1) { + count = 1; + } + + switch (type) { + case BinIO_TYPE_IGNORE_BYTE: + type_size = 1; + break; + case BinIO_TYPE_BYTE: + type_size = 1; + break; + case BinIO_TYPE_INT16: + type_size = 2; + break; + case BinIO_TYPE_INT32: + type_size = 4; + break; + case BinIO_TYPE_INT64: + type_size = 8; + break; + case BinIO_TYPE_FLOAT32: + type_size = 4; + break; + case BinIO_TYPE_FLOAT64: + type_size = 8; + break; + } + + *(size_t*)context += type_size * count; +} + +extern size_t BinIOFormatByteCount(const char *format) +{ + struct BinIOFormatCursor cursor; + size_t n_bytes = 0; + + BinIOInitFormatCursor(&cursor, format); + + while (BinIONextChar(&n_bytes, &cursor, BinIOCountBytes)) {} + + return n_bytes; +} diff --git a/Source/Utils/private.h b/Source/Utils/private.h new file mode 100644 index 0000000..787a3ad --- /dev/null +++ b/Source/Utils/private.h @@ -0,0 +1,154 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#ifndef private_h +#define private_h + +#include +#include + +#define BinIO_TYPE_IGNORE_BYTE 'x' +#define BinIO_TYPE_BYTE 'b' +#define BinIO_TYPE_INT16 's' +#define BinIO_TYPE_INT32 'i' +#define BinIO_TYPE_INT64 'l' +#define BinIO_TYPE_FLOAT32 'f' +#define BinIO_TYPE_FLOAT64 'd' + +#define BinIO_LITTLE_ENDIAN_BYTE_ORDER 'L' +#define BinIO_BIG_ENDIAN_BYTE_ORDER 'B' +#define BinIO_HOST_BYTE_ORDER 'H' +#define BinIO_NETWORK_BYTE_ORDER 'N' + +#ifndef ALREADY_DID_BINIO_STDINT +#define ALREADY_DID_BINIO_STDINT +#if defined(BinIO_STDINT_HEADER) +#include BinIO_STDINT_HEADER +typedef float float32_t; +typedef double float64_t; +#else +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifdef WIN32 +typedef unsigned __int64 uint64_t; +#else +typedef unsigned long long uint64_t; +#endif +typedef float float32_t; +typedef double float64_t; +#endif +#endif + +#ifndef BinIO_INLINE +#if defined(__GNUC__) +#define BinIO_INLINE static inline +#else +#define BinIO_INLINE static +#endif +#endif + +#ifndef BinIO_BYTE_ORDER +#if defined(__ppc__) || defined(__POWERPC__) +#define BinIO_BYTE_ORDER BinIO_BIG_ENDIAN_BYTE_ORDER +#else +#define BinIO_BYTE_ORDER BinIO_LITTLE_ENDIAN_BYTE_ORDER +#endif +#endif + +BinIO_INLINE void BinIOSwap1(const uint8_t *src, uint8_t *dst) +{ + *dst = *src; +} + +BinIO_INLINE void BinIOSwap2(const uint8_t *src, uint8_t *dst) +{ + *(dst + 1) = *(src + 0); + *(dst + 0) = *(src + 1); +} + +BinIO_INLINE void BinIOSwap4(const uint8_t *src, uint8_t *dst) +{ + *(dst + 3) = *(src + 0); + *(dst + 2) = *(src + 1); + *(dst + 1) = *(src + 2); + *(dst + 0) = *(src + 3); +} + +BinIO_INLINE void BinIOSwap8(const uint8_t *src, uint8_t *dst) +{ + *(dst + 7) = *(src + 0); + *(dst + 6) = *(src + 1); + *(dst + 5) = *(src + 2); + *(dst + 4) = *(src + 3); + *(dst + 3) = *(src + 4); + *(dst + 2) = *(src + 5); + *(dst + 1) = *(src + 6); + *(dst + 0) = *(src + 7); +} + +BinIO_INLINE int BinIONormalizeByteOrder(int byte_order) +{ + if (byte_order == BinIO_HOST_BYTE_ORDER) { + byte_order = BinIO_BYTE_ORDER; + } else if (byte_order == BinIO_NETWORK_BYTE_ORDER) { + byte_order = BinIO_BIG_ENDIAN_BYTE_ORDER; + } + + return byte_order; +} + +extern void BinIOConvert1(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count); +extern void BinIOConvert2(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count); +extern void BinIOConvert4(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count); +extern void BinIOConvert8(int from_byte_order, int to_byte_order, + const uint8_t *src, uint8_t *dst, + unsigned int count); + +struct BinIOFormatCursor { + const char *cursor; + int byte_order; + int count; +}; + +typedef void (*BinIOProcessFunction)(void *context, + int type, + int byte_order, + int count); + +extern void BinIOInitFormatCursor(struct BinIOFormatCursor *cursor, + const char *format); + +extern int BinIONextChar(void *context, + struct BinIOFormatCursor *cursor, + BinIOProcessFunction func); + +extern void BinIOCountBytes(void *context, int type, int byte_order, int count); + +extern size_t BinIOFormatByteCount(const char *format); + +#endif + diff --git a/Source/Utils/unpack.c b/Source/Utils/unpack.c new file mode 100644 index 0000000..fbcce38 --- /dev/null +++ b/Source/Utils/unpack.c @@ -0,0 +1,117 @@ +/* +Copyright (C) 2003, 2010 - Wolfire Games +Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) + +This file is part of Lugaru. + +Lugaru is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Lugaru 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Lugaru. If not, see . +*/ + +#include + +#include "binio.h" +#include "private.h" + +struct BinIOUnpackContext { + const uint8_t *data; + va_list args; +}; + +static void BinIOUnpack(void *context, int type, int byte_order, int count) +{ + struct BinIOUnpackContext *ctx = (struct BinIOUnpackContext*)context; + if (count == -1) { + count = 1; + } + + switch (type) { + case BinIO_TYPE_IGNORE_BYTE: + ctx->data += 1 * count; + break; + case BinIO_TYPE_BYTE: + BinIOConvert1(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); + ctx->data += 1 * count; + break; + case BinIO_TYPE_INT16: + BinIOConvert2(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); + ctx->data += 2 * count; + break; + case BinIO_TYPE_INT32: + BinIOConvert4(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); + ctx->data += 4 * count; + break; + case BinIO_TYPE_INT64: + BinIOConvert8(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); + ctx->data += 8 * count; + break; + case BinIO_TYPE_FLOAT32: + BinIOConvert4(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); + ctx->data += 4 * count; + break; + case BinIO_TYPE_FLOAT64: + BinIOConvert8(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); + ctx->data += 8 * count; + break; + } +} + +void unpackf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfunpackf(stdin, format, args); + va_end(args); +} + +void sunpackf(const void *buffer, const char *format, ...) +{ + va_list args; + va_start(args, format); + vsunpackf(buffer, format, args); + va_end(args); +} + +void funpackf(FILE *file, const char *format, ...) +{ + va_list args; + va_start(args, format); + vfunpackf(file, format, args); + va_end(args); +} + +void vsunpackf(const void *buffer, const char *format, va_list args) +{ + struct BinIOFormatCursor cursor; + struct BinIOUnpackContext context; + + BinIOInitFormatCursor(&cursor, format); + + context.data = (const unsigned char*)buffer; + va_copy(context.args, args); + + while (BinIONextChar(&context, &cursor, BinIOUnpack)) {} + + va_end(context.args); +} + +void vfunpackf(FILE *file, const char *format, va_list args) +{ + size_t n_bytes = BinIOFormatByteCount(format); + void* buffer = malloc(n_bytes); + fread(buffer, n_bytes, 1, file); + + vsunpackf(buffer, format, args); + + free(buffer); +} diff --git a/Source/Weapons.cpp b/Source/Weapons.cpp deleted file mode 100644 index bc610d7..0000000 --- a/Source/Weapons.cpp +++ /dev/null @@ -1,1123 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -/**> HEADER FILES <**/ -#include "Weapons.h" -#include "openal_wrapper.h" -#include "Animation/Animation.h" -#include "Sounds.h" -#include "Game.h" -#include "Awards.h" - -extern float multiplier; -extern Terrain terrain; -extern float gravity; -extern int environment; -extern int detail; -extern FRUSTUM frustum; -extern XYZ viewer; -extern float realmultiplier; -extern int slomo; -extern float slomodelay; -extern bool cellophane; -extern float texdetail; -extern GLubyte bloodText[512 * 512 * 3]; -extern int bloodtoggle; -extern Objects objects; -extern bool autoslomo; -extern float camerashake; -extern float woozy; -extern float viewdistance; -extern float blackout; -extern bool freeze; -extern int tutoriallevel; -extern int numthrowkill; - -Model Weapon::throwingknifemodel; -Texture Weapon::knifetextureptr; -Texture Weapon::lightbloodknifetextureptr; -Texture Weapon::bloodknifetextureptr; - -Model Weapon::swordmodel; -Texture Weapon::swordtextureptr; -Texture Weapon::lightbloodswordtextureptr; -Texture Weapon::bloodswordtextureptr; - -Model Weapon::staffmodel; -Texture Weapon::stafftextureptr; - -Weapon::Weapon(int t, int o) : owner(o) -{ - setType(t); - bloody = 0; - blooddrip = 0; - blooddripdelay = 0; - onfire = 0; - flamedelay = 0; - damage = 0; - position = -1000; - tippoint = -1000; -} - -void Weapon::setType(int t) -{ - type = t; - if (type == sword) { - mass = 1.5; - tipmass = 1; - length = .8; - } - if (type == staff) { - mass = 2; - tipmass = 1; - length = 1.5; - } - if (type == knife) { - mass = 1; - tipmass = 1.2; - length = .25; - } -} - -void Weapon::DoStuff(int i) -{ - //~ cout << position.x << "," << position.y << "," << position.z << "|" << tippoint.x << "," << tippoint.y << "," << tippoint.z << endl; - static int whichpatchx, whichpatchz, whichhit; - static XYZ start, end, colpoint, normalrot, footvel, footpoint; - static XYZ terrainnormal; - static XYZ vel; - static XYZ midp; - static XYZ newpoint1, newpoint2; - static float friction = 3.5; - static float elasticity = .4; - static XYZ bounceness; - static float frictionness; - static float closestdistance; - static float distance; - static XYZ point[3]; - static XYZ closestpoint; - static XYZ closestswordpoint; - static float tempmult; - - if (owner != -1) { - oldowner = owner; - } - if (damage >= 2 && type == staff && owner != -1) { // the staff breaks - emit_sound_at(staffbreaksound, tippoint); - XYZ tempvel; - for (int j = 0; j < 40; j++) { - tempvel.x = float(abs(Random() % 100) - 50) / 20; - tempvel.y = float(abs(Random() % 100) - 50) / 20; - tempvel.z = float(abs(Random() % 100) - 50) / 20; - Sprite::MakeSprite(splintersprite, position + (tippoint - position) * ((float)j - 8) / 32, tempvel * .5, 115 / 255, 73 / 255, 12 / 255, .1, 1); - } - if (owner != -1) { - Person::players[owner]->weaponactive = -1; - Person::players[owner]->num_weapons--; - if (Person::players[owner]->num_weapons) { - Person::players[owner]->weaponids[0] = Person::players[owner]->weaponids[Person::players[owner]->num_weapons]; - if (Person::players[owner]->weaponstuck == Person::players[owner]->num_weapons) - Person::players[owner]->weaponstuck = 0; - } - } - owner = -1; - hitsomething = 0; - missed = 1; - freetime = 0; - firstfree = 1; - position = 0; - physics = 0; - } - oldposition = position; - oldtippoint = tippoint; - if (owner == -1 && (velocity.x || velocity.y || velocity.z) && !physics) { // if the weapon is flying - position += velocity * multiplier; - tippoint += velocity * multiplier; - whichpatchx = position.x / (terrain.size / subdivision * terrain.scale); - whichpatchz = position.z / (terrain.size / subdivision * terrain.scale); - if (whichpatchx > 0 && whichpatchz > 0 && whichpatchx < subdivision && whichpatchz < subdivision) { - if (terrain.patchobjectnum[whichpatchx][whichpatchz]) { // if there are objects where the weapon is - for (int j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { // check for collision - int k = terrain.patchobjects[whichpatchx][whichpatchz][j]; - start = oldtippoint; - end = tippoint; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - if (objects.type[k] == treetrunktype) { - objects.model[k].MakeDecal(breakdecal, DoRotation(colpoint - objects.position[k], 0, -objects.yaw[k], 0), .1, 1, Random() % 360); - normalrot = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0); - velocity = 0; - if (type == knife) - position = colpoint - normalrot * .1; - else if (type == sword) - position = colpoint - normalrot * .2; - else if (type == staff) - position = colpoint - normalrot * .2; - XYZ temppoint1, temppoint2; - float distance; - - temppoint1 = 0; - temppoint2 = normalrot; - distance = findDistance(&temppoint1, &temppoint2); - rotation2 = asin((temppoint1.y - temppoint2.y) / distance); - rotation2 *= 360 / 6.28; - temppoint1.y = 0; - temppoint2.y = 0; - rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); - rotation1 *= 360 / 6.28; - if (temppoint1.x > temppoint2.x) - rotation1 = 360 - rotation1; - - rotation3 = 0; - smallrotation = 90; - smallrotation2 = 0; - bigtilt = 0; - bigtilt2 = 0; - bigrotation = 0; - - emit_sound_at(knifesheathesound, position, 128.); - - bloody = 0; - - Sprite::MakeSprite(cloudimpactsprite, position, velocity, 1, 1, 1, .8, .3); - } else { - physics = 1; - firstfree = 1; - position -= velocity * multiplier; - tippoint -= velocity * multiplier; - tipvelocity = velocity; - } - } - } - } - } - - if (velocity.x || velocity.y || velocity.z) { - for (unsigned j = 0; j < Person::players.size(); j++) { - footvel = 0; - footpoint = DoRotation((Person::players[j]->jointPos(abdomen) + Person::players[j]->jointPos(neck)) / 2, 0, Person::players[j]->yaw, 0) * Person::players[j]->scale + Person::players[j]->coords; - if (owner == -1 && distsqflat(&position, &Person::players[j]->coords) < 1.5 && - distsq(&position, &Person::players[j]->coords) < 4 && Person::players[j]->weaponstuck == -1 && - !Person::players[j]->skeleton.free && (int(j) != oldowner)) { - if ((Person::players[j]->aitype != attacktypecutoff || abs(Random() % 6) == 0 || (Person::players[j]->animTarget != backhandspringanim && Person::players[j]->animTarget != rollanim && Person::players[j]->animTarget != flipanim && Random() % 2 == 0)) && !missed) { - if ( (Person::players[j]->creature == wolftype && Random() % 3 != 0 && Person::players[j]->weaponactive == -1 && (Person::players[j]->isIdle() || Person::players[j]->isRun() || Person::players[j]->animTarget == walkanim)) || - (Person::players[j]->creature == rabbittype && Random() % 2 == 0 && Person::players[j]->aitype == attacktypecutoff && Person::players[j]->weaponactive == -1)) { - emit_sound_at(knifedrawsound, Person::players[j]->coords, 128.); - - Person::players[j]->animTarget = removeknifeanim; - Person::players[j]->frameTarget = 1; - Person::players[j]->target = 1; - Person::players[j]->takeWeapon(i); - - Person::players[j]->aitype = attacktypecutoff; - } else { - if (j != 0) - numthrowkill++; - Person::players[j]->num_weapons++; - Person::players[j]->weaponstuck = Person::players[j]->num_weapons - 1; - if (normaldotproduct(Person::players[j]->facing, velocity) > 0) - Person::players[j]->weaponstuckwhere = 1; - else - Person::players[j]->weaponstuckwhere = 0; - - Person::players[j]->weaponids[Person::players[j]->num_weapons - 1] = i; - - Person::players[j]->RagDoll(0); - Person::players[j]->jointVel(abdomen) += velocity * 2; - Person::players[j]->jointVel(neck) += velocity * 2; - Person::players[j]->jointVel(rightshoulder) += velocity * 2; - Person::players[j]->jointVel(leftshoulder) += velocity * 2; - if (bloodtoggle && tutoriallevel != 1) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .8, .3); - if (tutoriallevel == 1) - Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 1, 1, .8, .3); - footvel = tippoint - position; - Normalise(&footvel); - if (bloodtoggle && tutoriallevel != 1) - Sprite::MakeSprite(bloodflamesprite, footpoint, footvel * -1, 1, 0, 0, .6, 1); - - if (tutoriallevel != 1) { - if (Person::players[j]->weaponstuckwhere == 0) - Person::players[j]->DoBloodBig(2, 205); - if (Person::players[j]->weaponstuckwhere == 1) - Person::players[j]->DoBloodBig(2, 200); - Person::players[j]->damage += 200 / Person::players[j]->armorhigh; - Person::players[j]->deathbleeding = 1; - Person::players[j]->bloodloss += (200 + abs((float)(Random() % 40)) - 20) / Person::players[j]->armorhigh; - owner = j; - bloody = 2; - blooddrip = 5; - } - - emit_sound_at(fleshstabsound, position, 128.); - - if (Animation::animations[Person::players[0]->animTarget].height == highheight) - award_bonus(0, ninja); - else - award_bonus(0, Bullseyebonus); - } - } else { - missed = 1; - } - } - } - } - if (position.y < terrain.getHeight(position.x, position.z)) { - if (terrain.getOpacity(position.x, position.z) < .2) { - velocity = 0; - if (terrain.lineTerrain(oldposition, position, &colpoint) != -1) { - position = colpoint * terrain.scale; - } else { - position.y = terrain.getHeight(position.x, position.z); - } - - terrain.MakeDecal(shadowdecalpermanent, position, .06, .5, 0); - normalrot = terrain.getNormal(position.x, position.z) * -1; - velocity = 0; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - GLfloat M[16]; - glLoadIdentity(); - glRotatef(bigrotation, 0, 1, 0); - glRotatef(bigtilt2, 1, 0, 0); - glRotatef(bigtilt, 0, 0, 1); - glRotatef(-rotation1 + 90, 0, 1, 0); - glRotatef(-rotation2 + 90, 0, 0, 1); - glRotatef(-rotation3, 0, 1, 0); - glRotatef(smallrotation, 1, 0, 0); - glRotatef(smallrotation2, 0, 1, 0); - glTranslatef(0, 0, 1); - glGetFloatv(GL_MODELVIEW_MATRIX, M); - tippoint.x = M[12]; - tippoint.y = M[13]; - tippoint.z = M[14]; - glPopMatrix(); - position -= tippoint * .15; - XYZ temppoint1, temppoint2; - - rotation3 = 0; - smallrotation = 90; - smallrotation2 = 0; - bigtilt = 0; - bigtilt2 = 0; - bigrotation = 0; - - emit_sound_at(knifesheathesound, position, 128.); - - XYZ terrainlight; - terrainlight = terrain.getLighting(position.x, position.z); - if (environment == snowyenvironment) { - if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7); - } else if (environment == grassyenvironment) { - if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5); - } else if (environment == desertenvironment) { - if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7); - } - - bloody = 0; - } else { - physics = 1; - firstfree = 1; - position -= velocity * multiplier; - tippoint -= velocity * multiplier; - tipvelocity = velocity; - } - } - if (velocity.x != 0 || velocity.z != 0 || velocity.y != 0) { - velocity.y += gravity * multiplier; - - XYZ temppoint1, temppoint2; - float distance; - - temppoint1 = 0; - temppoint2 = velocity; - distance = findDistance(&temppoint1, &temppoint2); - rotation2 = asin((temppoint1.y - temppoint2.y) / distance); - rotation2 *= 360 / 6.28; - temppoint1.y = 0; - temppoint2.y = 0; - rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); - rotation1 *= 360 / 6.28; - rotation3 = 0; - smallrotation = 90; - smallrotation2 = 0; - bigtilt = 0; - bigtilt2 = 0; - bigrotation = 0; - if (temppoint1.x > temppoint2.x) - rotation1 = 360 - rotation1; - } - - } - - //Sword physics - XYZ mid; - XYZ oldmid; - XYZ oldmid2; - - tempmult = multiplier; - multiplier /= 10; - for (int l = 0; l < 10; l++) { - if (owner == -1 && (velocity.x || velocity.y || velocity.z) && physics) { - //move - position += velocity * multiplier; - tippoint += tipvelocity * multiplier; - - //Length constrain - midp = (position * mass + tippoint * tipmass) / (mass + tipmass); - vel = tippoint - midp; - Normalise(&vel); - newpoint1 = midp - vel * length * (tipmass / (mass + tipmass)); - newpoint2 = midp + vel * length * (mass / (mass + tipmass)); - if (!freeze) { - if (freetime > .04) { - velocity = velocity + (newpoint1 - position) / multiplier; - tipvelocity = tipvelocity + (newpoint2 - tippoint) / multiplier; - } - } - position = newpoint1; - tippoint = newpoint2; - - - //Object collisions - whichpatchx = (position.x) / (terrain.size / subdivision * terrain.scale); - whichpatchz = (position.z) / (terrain.size / subdivision * terrain.scale); - if (whichpatchx > 0 && whichpatchz > 0 && whichpatchx < subdivision && whichpatchz < subdivision) - if (terrain.patchobjectnum[whichpatchx][whichpatchz]) { - for (int j = 0; j < terrain.patchobjectnum[whichpatchx][whichpatchz]; j++) { - int k = terrain.patchobjects[whichpatchx][whichpatchz][j]; - - if (firstfree) { - if (type == staff) { - start = tippoint - (position - tippoint) / 5; - end = position + (position - tippoint) / 30; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - XYZ diff; - diff = (colpoint - position); - Normalise(&diff); - hitsomething = 1; - - tippoint += (colpoint - position) + diff * .05; - position = colpoint + diff * .05; - oldtippoint = tippoint; - oldposition = tippoint; - } - } else { - start = position - (tippoint - position) / 5; - end = tippoint + (tippoint - position) / 30; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - XYZ diff; - diff = (colpoint - tippoint); - Normalise(&diff); - hitsomething = 1; - - position += (colpoint - tippoint) + diff * .05; - tippoint = colpoint + diff * .05; - oldposition = position; - oldtippoint = tippoint; - } - } - } - - start = oldposition; - end = position; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - hitsomething = 1; - position = colpoint; - terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; - ReflectVector(&velocity, &terrainnormal); - position += terrainnormal * .002; - - bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); - if (findLengthfast(&velocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(velocity, terrainnormal)); - velocity -= bounceness; - if (1 - friction * frictionness > 0) - velocity *= 1 - friction * frictionness; - else - velocity = 0; - velocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - else - whichsound = clank1sound + abs(Random() % 4); - emit_sound_at(whichsound, position, 128 * findLengthfast(&bounceness)); - } - } - start = oldtippoint; - end = tippoint; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - hitsomething = 1; - tippoint = colpoint; - terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; - ReflectVector(&tipvelocity, &terrainnormal); - tippoint += terrainnormal * .002; - - bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); - if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); - tipvelocity -= bounceness; - if (1 - friction * frictionness > 0) - tipvelocity *= 1 - friction * frictionness; - else - tipvelocity = 0; - tipvelocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - else - whichsound = clank1sound + abs(Random() % 4); - emit_sound_at(whichsound, position, 128 * findLengthfast(&bounceness)); - } - } - - if ((objects.type[k] != boxtype && objects.type[k] != platformtype && objects.type[k] != walltype && objects.type[k] != weirdtype) || objects.pitch[k] != 0) - for (int m = 0; m < 2; m++) { - mid = (position * (21 + (float)m * 10) + tippoint * (19 - (float)m * 10)) / 40; - oldmid2 = mid; - oldmid = (oldposition * (21 + (float)m * 10) + oldtippoint * (19 - (float)m * 10)) / 40; - - start = oldmid; - end = mid; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - hitsomething = 1; - mid = colpoint; - terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; - ReflectVector(&velocity, &terrainnormal); - - bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); - if (findLengthfast(&velocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(velocity, terrainnormal)); - velocity -= bounceness; - if (1 - friction * frictionness > 0) - velocity *= 1 - friction * frictionness; - else - velocity = 0; - velocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - else - whichsound = clank1sound + abs(Random() % 4); - emit_sound_at(whichsound, mid, 128 * findLengthfast(&bounceness)); - } - position += (mid - oldmid2) * (20 / (1 + (float)m * 10)); - } - - mid = (position * (19 - (float)m * 10) + tippoint * (21 + (float)m * 10)) / 40; - oldmid2 = mid; - oldmid = (oldposition * (19 - (float)m * 10) + oldtippoint * (21 + (float)m * 10)) / 40; - - start = oldmid; - end = mid; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - hitsomething = 1; - mid = colpoint; - terrainnormal = DoRotation(objects.model[k].facenormals[whichhit], 0, objects.yaw[k], 0) * -1; - ReflectVector(&tipvelocity, &terrainnormal); - - bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); - if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); - tipvelocity -= bounceness; - if (1 - friction * frictionness > 0) - tipvelocity *= 1 - friction * frictionness; - else - tipvelocity = 0; - tipvelocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - else - whichsound = clank1sound + abs(Random() % 4); - emit_sound_at(whichsound, mid, 128 * findLengthfast(&bounceness)); - } - tippoint += (mid - oldmid2) * (20 / (1 + (float)m * 10)); - } - } - else { - start = position; - end = tippoint; - whichhit = objects.model[k].LineCheck(&start, &end, &colpoint, &objects.position[k], &objects.yaw[k]); - if (whichhit != -1) { - hitsomething = 1; - closestdistance = -1; - closestswordpoint = colpoint; //(position+tippoint)/2; - point[0] = DoRotation(objects.model[k].vertex[objects.model[k].Triangles[whichhit].vertex[0]], 0, objects.yaw[k], 0) + objects.position[k]; - point[1] = DoRotation(objects.model[k].vertex[objects.model[k].Triangles[whichhit].vertex[1]], 0, objects.yaw[k], 0) + objects.position[k]; - point[2] = DoRotation(objects.model[k].vertex[objects.model[k].Triangles[whichhit].vertex[2]], 0, objects.yaw[k], 0) + objects.position[k]; - if (DistancePointLine(&closestswordpoint, &point[0], &point[1], &distance, &colpoint )) { - if (distance < closestdistance || closestdistance == -1) { - closestpoint = colpoint; - closestdistance = distance; - } - } - if (DistancePointLine(&closestswordpoint, &point[1], &point[2], &distance, &colpoint )) { - if (distance < closestdistance || closestdistance == -1) { - closestpoint = colpoint; - closestdistance = distance; - } - } - if (DistancePointLine(&closestswordpoint, &point[2], &point[0], &distance, &colpoint )) { - if (distance < closestdistance || closestdistance == -1) { - closestpoint = colpoint; - closestdistance = distance; - } - } - if (closestdistance != -1 && isnormal(closestdistance)) { - if (DistancePointLine(&closestpoint, &position, &tippoint, &distance, &colpoint )) { - closestswordpoint = colpoint; - velocity += (closestpoint - closestswordpoint); - tipvelocity += (closestpoint - closestswordpoint); - position += (closestpoint - closestswordpoint); - tippoint += (closestpoint - closestswordpoint); - } - } - } - } - } - } - //Terrain collisions - whichhit = terrain.lineTerrain(oldposition, position, &colpoint); - if (whichhit != -1 || position.y < terrain.getHeight(position.x, position.z)) { - hitsomething = 1; - if (whichhit != -1) - position = colpoint * terrain.scale; - else - position.y = terrain.getHeight(position.x, position.z); - - terrainnormal = terrain.getNormal(position.x, position.z); - ReflectVector(&velocity, &terrainnormal); - position += terrainnormal * .002; - bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); - if (findLengthfast(&velocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(velocity, terrainnormal)); - velocity -= bounceness; - if (1 - friction * frictionness > 0) - velocity *= 1 - friction * frictionness; - else - velocity = 0; - if (terrain.getOpacity(position.x, position.z) < .2) - velocity += bounceness * elasticity * .3; - else - velocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (terrain.getOpacity(position.x, position.z) > .2) { - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - else - whichsound = clank1sound + abs(Random() % 4); - } else { - whichsound = footstepsound + abs(Random() % 2); - } - emit_sound_at(whichsound, position, - findLengthfast(&bounceness) - * (terrain.getOpacity(position.x, position.z) > .2 ? 128. : 32.)); - - if (terrain.getOpacity(position.x, position.z) < .2) { - XYZ terrainlight; - terrainlight = terrain.getLighting(position.x, position.z); - if (environment == snowyenvironment) { - if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7); - } else if (environment == grassyenvironment) { - if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5); - } else if (environment == desertenvironment) { - if (distsq(&position, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, position, velocity, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7); - } - } - } - } - whichhit = terrain.lineTerrain(oldtippoint, tippoint, &colpoint); - if (whichhit != -1 || tippoint.y < terrain.getHeight(tippoint.x, tippoint.z)) { - if (whichhit != -1) - tippoint = colpoint * terrain.scale; - else - tippoint.y = terrain.getHeight(tippoint.x, tippoint.z); - - terrainnormal = terrain.getNormal(tippoint.x, tippoint.z); - ReflectVector(&tipvelocity, &terrainnormal); - tippoint += terrainnormal * .002; - bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); - if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); - tipvelocity -= bounceness; - if (1 - friction * frictionness > 0) - tipvelocity *= 1 - friction * frictionness; - else - tipvelocity = 0; - if (terrain.getOpacity(tippoint.x, tippoint.z) < .2) - tipvelocity += bounceness * elasticity * .3; - else - tipvelocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (terrain.getOpacity(tippoint.x, tippoint.z) > .2) { - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - else - whichsound = clank1sound + abs(Random() % 4); - } else { - whichsound = footstepsound + abs(Random() % 2); - } - emit_sound_at(whichsound, tippoint, - findLengthfast(&bounceness) - * (terrain.getOpacity(tippoint.x, tippoint.z) > .2 ? 128. : 32.)); - - if (terrain.getOpacity(tippoint.x, tippoint.z) < .2) { - XYZ terrainlight; - terrainlight = terrain.getLighting(tippoint.x, tippoint.z); - if (environment == snowyenvironment) { - if (distsq(&tippoint, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, tippoint, tipvelocity, terrainlight.x, terrainlight.y, terrainlight.z, .5, .7); - } else if (environment == grassyenvironment) { - if (distsq(&tippoint, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, tippoint, tipvelocity, terrainlight.x * 90 / 255, terrainlight.y * 70 / 255, terrainlight.z * 8 / 255, .5, .5); - } else if (environment == desertenvironment) { - if (distsq(&tippoint, &viewer) < viewdistance * viewdistance / 4) - Sprite::MakeSprite(cloudsprite, tippoint, tipvelocity, terrainlight.x * 190 / 255, terrainlight.y * 170 / 255, terrainlight.z * 108 / 255, .5, .7); - } - } - } - } - - //Edges - mid = position + tippoint; - mid /= 2; - mid += (position - mid) / 20; - oldmid = mid; - if (mid.y < terrain.getHeight(mid.x, mid.z)) { - hitsomething = 1; - mid.y = terrain.getHeight(mid.x, mid.z); - - terrainnormal = terrain.getNormal(mid.x, mid.z); - ReflectVector(&velocity, &terrainnormal); - //mid+=terrainnormal*.002; - bounceness = terrainnormal * findLength(&velocity) * (abs(normaldotproduct(velocity, terrainnormal))); - if (findLengthfast(&velocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(velocity, terrainnormal)); - velocity -= bounceness; - if (1 - friction * frictionness > 0) - velocity *= 1 - friction * frictionness; - else - velocity = 0; - if (terrain.getOpacity(mid.x, mid.z) < .2) - velocity += bounceness * elasticity * .3; - else - velocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (terrain.getOpacity(mid.x, mid.z) > .2) { - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - if (type != staff) - whichsound = clank1sound + abs(Random() % 4); - } else { - whichsound = footstepsound + abs(Random() % 2); - } - emit_sound_at(whichsound, mid, - findLengthfast(&bounceness) - * (terrain.getOpacity(position.x, position.z) > .2 - ? 128. - : 32.)); - } - position += (mid - oldmid) * 20; - } - - mid = position + tippoint; - mid /= 2; - mid += (tippoint - mid) / 20; - oldmid = mid; - if (mid.y < terrain.getHeight(mid.x, mid.z)) { - hitsomething = 1; - mid.y = terrain.getHeight(mid.x, mid.z); - - terrainnormal = terrain.getNormal(mid.x, mid.z); - ReflectVector(&tipvelocity, &terrainnormal); - //mid+=terrainnormal*.002; - bounceness = terrainnormal * findLength(&tipvelocity) * (abs(normaldotproduct(tipvelocity, terrainnormal))); - if (findLengthfast(&tipvelocity) < findLengthfast(&bounceness)) - bounceness = 0; - frictionness = abs(normaldotproduct(tipvelocity, terrainnormal)); - tipvelocity -= bounceness; - if (1 - friction * frictionness > 0) - tipvelocity *= 1 - friction * frictionness; - else - tipvelocity = 0; - if (terrain.getOpacity(mid.x, mid.z) < .2) - tipvelocity += bounceness * elasticity * .3; - else - tipvelocity += bounceness * elasticity; - - if (findLengthfast(&bounceness) > 1) { - int whichsound; - if (terrain.getOpacity(mid.x, mid.z) > .2) { - if (type == staff) - whichsound = footstepsound3 + abs(Random() % 2); - if (type != staff) - whichsound = clank1sound + abs(Random() % 4); - } else { - whichsound = footstepsound + abs(Random() % 2); - } - emit_sound_at(whichsound, mid, - findLengthfast(&bounceness) - * (terrain.getOpacity(position.x, position.z) > .2 - ? 128. - : 32.)); - } - tippoint += (mid - oldmid) * 20; - } - //Gravity - velocity.y += gravity * multiplier; - tipvelocity.y += gravity * multiplier; - - //Rotation - XYZ temppoint1, temppoint2; - float distance; - - temppoint1 = position; - temppoint2 = tippoint; - distance = findDistance(&temppoint1, &temppoint2); - rotation2 = asin((temppoint1.y - temppoint2.y) / distance); - rotation2 *= 360 / 6.28; - temppoint1.y = 0; - temppoint2.y = 0; - rotation1 = acos((temppoint1.z - temppoint2.z) / findDistance(&temppoint1, &temppoint2)); - rotation1 *= 360 / 6.28; - rotation3 = 0; - smallrotation = 90; - smallrotation2 = 0; - bigtilt = 0; - bigtilt2 = 0; - bigrotation = 0; - if (temppoint1.x > temppoint2.x) - rotation1 = 360 - rotation1; - - //Stop moving - if (findLengthfast(&velocity) < .3 && findLengthfast(&tipvelocity) < .3 && hitsomething) { - freetime += multiplier; - } - - if (freetime > .4) { - velocity = 0; - tipvelocity = 0; - } - firstfree = 0; - } - } - multiplier = tempmult; - if (blooddrip && bloody) { - blooddripdelay -= blooddrip * multiplier / 2; - blooddrip -= multiplier; - if (blooddrip < 0) - blooddrip = 0; - if (blooddrip > 5) - blooddrip = 5; - if (blooddripdelay < 0 && bloodtoggle) { - blooddripdelay = 1; - XYZ bloodvel; - XYZ bloodloc; - bloodloc = position + (tippoint - position) * .7; - bloodloc.y -= .05; - if (bloodtoggle) { - bloodvel = 0; - Sprite::MakeSprite(bloodsprite, bloodloc, bloodvel, 1, 1, 1, .03, 1); - } - } - } - if (onfire) { - flamedelay -= multiplier; - if (onfire && flamedelay <= 0) { - flamedelay = .020; - flamedelay -= multiplier; - normalrot = 0; - if (owner != -1) { - normalrot = Person::players[owner]->velocity; - } - normalrot.y += 1; - if (owner != -1) { - if (Person::players[owner]->onterrain) { - normalrot.y = 1; - } - } - Sprite::MakeSprite(weaponflamesprite, position + tippoint * (((float)abs(Random() % 100)) / 600 + .05), normalrot, 1, 1, 1, (.6 + (float)abs(Random() % 100) / 200 - .25) * 1 / 3, 1); - Sprite::setLastSpriteSpeed(4); - Sprite::setLastSpriteAlivetime(.3); - } - } - - if (!onfire && owner == -1 && type != staff) { - flamedelay -= multiplier; - if (flamedelay <= 0) { - flamedelay = .020; - flamedelay -= multiplier; - normalrot = 0; - if (Random() % 50 == 0 && distsq(&position, &viewer) > 80) { - XYZ shinepoint; - shinepoint = position + (tippoint - position) * (((float)abs(Random() % 100)) / 100); - Sprite::MakeSprite(weaponshinesprite, shinepoint, normalrot, 1, 1, 1, (.1 + (float)abs(Random() % 100) / 200 - .25) * 1 / 3 * fast_sqrt(findDistance(&shinepoint, &viewer)), 1); - Sprite::setLastSpriteSpeed(4); - Sprite::setLastSpriteAlivetime(.3); - } - } - } -} - -void Weapons::DoStuff() -{ - //Move - int i = 0; - for (std::vector::iterator weapon = begin(); weapon != end(); ++weapon) { - weapon->DoStuff(i++); - } -} - -void Weapon::Draw() -{ - static XYZ terrainlight; - static GLfloat M[16]; - - if ((frustum.SphereInFrustum(position.x, position.y, position.z, 1) && - distsq(&viewer, &position) < viewdistance * viewdistance)) { - bool draw = false; - if (owner == -1) { - draw = true; - if (velocity.x && !physics) - drawhowmany = 10; - else - drawhowmany = 1; - } else { - if (Person::players[owner]->occluded < 25) - if ((frustum.SphereInFrustum(Person::players[owner]->coords.x, Person::players[owner]->coords.y + Person::players[owner]->scale * 3, Person::players[owner]->coords.z, Person::players[owner]->scale * 8) && distsq(&viewer, &Person::players[owner]->coords) < viewdistance * viewdistance) || Person::players[owner]->skeleton.free == 3) - draw = true; - if ( - (Person::players[owner]->animTarget == knifeslashstartanim || - Person::players[owner]->animTarget == swordsneakattackanim || - (Person::players[owner]->animCurrent == staffhitanim && Person::players[owner]->frameCurrent > 1) || - (Person::players[owner]->animCurrent == staffhitreversedanim && Person::players[owner]->frameCurrent > 1) || - (Person::players[owner]->animCurrent == staffspinhitanim && Person::players[owner]->frameCurrent > 1) || - (Person::players[owner]->animCurrent == staffspinhitreversedanim && Person::players[owner]->frameCurrent > 1) || - (Person::players[owner]->animCurrent == staffgroundsmashanim && Person::players[owner]->frameCurrent > 1) || - (Person::players[owner]->animTarget == swordslashanim && Person::players[owner]->frameTarget < 7) || - Person::players[owner]->animTarget == crouchstabanim || - Person::players[owner]->animTarget == swordslashreversalanim || - Person::players[owner]->animTarget == swordslashreversedanim || - Person::players[owner]->animTarget == knifefollowanim || - Person::players[owner]->animTarget == swordgroundstabanim || - Person::players[owner]->animTarget == knifethrowanim) && - Person::players[owner]->animTarget == lastdrawnanim && - !Person::players[owner]->skeleton.free - ) { - drawhowmany = 10; - } else { - drawhowmany = 1; - } - if (Person::players[owner]->animTarget == swordgroundstabanim) { - lastdrawnrotation1 = rotation1; - lastdrawnrotation2 = rotation2; - lastdrawnrotation3 = rotation3; - lastdrawnbigrotation = bigrotation; - lastdrawnbigtilt = bigtilt; - lastdrawnbigtilt2 = bigtilt2; - lastdrawnsmallrotation = smallrotation; - lastdrawnsmallrotation2 = smallrotation2; - } - } - if (draw) { - terrainlight = terrain.getLighting(position.x, position.z); - if (drawhowmany > 0) { - glAlphaFunc(GL_GREATER, 0.01); - } - for (int j = drawhowmany; j > 0; j--) { - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glColor4f(terrainlight.x, terrainlight.y, terrainlight.z, j / drawhowmany); - if (owner == -1) - glTranslatef(position.x * (((float)(j)) / drawhowmany) + lastdrawnposition.x * (1 - ((float)(j)) / drawhowmany), position.y * (((float)(j)) / drawhowmany) + lastdrawnposition.y * (1 - ((float)(j)) / drawhowmany), position.z * (((float)(j)) / drawhowmany) + lastdrawnposition.z * (1 - ((float)(j)) / drawhowmany)); - else - glTranslatef(position.x * (((float)(j)) / drawhowmany) + lastdrawnposition.x * (1 - ((float)(j)) / drawhowmany), position.y * (((float)(j)) / drawhowmany) - .02 + lastdrawnposition.y * (1 - ((float)(j)) / drawhowmany), position.z * (((float)(j)) / drawhowmany) + lastdrawnposition.z * (1 - ((float)(j)) / drawhowmany)); - glRotatef(bigrotation * (((float)(j)) / drawhowmany) + lastdrawnbigrotation * (1 - ((float)(j)) / drawhowmany), 0, 1, 0); - glRotatef(bigtilt2 * (((float)(j)) / drawhowmany) + lastdrawnbigtilt2 * (1 - ((float)(j)) / drawhowmany), 1, 0, 0); - glRotatef(bigtilt * (((float)(j)) / drawhowmany) + lastdrawnbigtilt * (1 - ((float)(j)) / drawhowmany), 0, 0, 1); - glRotatef(-rotation1 * (((float)(j)) / drawhowmany) - lastdrawnrotation1 * (1 - ((float)(j)) / drawhowmany) + 90, 0, 1, 0); - glRotatef(-rotation2 * (((float)(j)) / drawhowmany) - lastdrawnrotation2 * (1 - ((float)(j)) / drawhowmany) + 90, 0, 0, 1); - glRotatef(-rotation3 * (((float)(j)) / drawhowmany) - lastdrawnrotation3 * (1 - ((float)(j)) / drawhowmany), 0, 1, 0); - glRotatef(smallrotation * (((float)(j)) / drawhowmany) + lastdrawnsmallrotation * (1 - ((float)(j)) / drawhowmany), 1, 0, 0); - glRotatef(smallrotation2 * (((float)(j)) / drawhowmany) + lastdrawnsmallrotation2 * (1 - ((float)(j)) / drawhowmany), 0, 1, 0); - - if (owner != -1) { - if (Person::players[owner]->animTarget == staffhitanim || Person::players[owner]->animCurrent == staffhitanim || Person::players[owner]->animTarget == staffhitreversedanim || Person::players[owner]->animCurrent == staffhitreversedanim) { - glTranslatef(0, 0, -.3); - } - if (Person::players[owner]->animTarget == staffgroundsmashanim || Person::players[owner]->animCurrent == staffgroundsmashanim || Person::players[owner]->animTarget == staffspinhitreversedanim || Person::players[owner]->animCurrent == staffspinhitreversedanim || Person::players[owner]->animTarget == staffspinhitanim || Person::players[owner]->animCurrent == staffspinhitanim) { - glTranslatef(0, 0, -.1); - } - } - - glEnable(GL_LIGHTING); - switch (type) { - case knife: - if (!bloody || !bloodtoggle) - throwingknifemodel.drawdifftex(knifetextureptr); - if (bloodtoggle) { - if (bloody == 1) - throwingknifemodel.drawdifftex(lightbloodknifetextureptr); - if (bloody == 2) - throwingknifemodel.drawdifftex(bloodknifetextureptr); - } - break; - case sword: - if (!bloody || !bloodtoggle) - swordmodel.drawdifftex(swordtextureptr); - if (bloodtoggle) { - if (bloody == 1) - swordmodel.drawdifftex(lightbloodswordtextureptr); - if (bloody == 2) - swordmodel.drawdifftex(bloodswordtextureptr); - } - break; - case staff: - staffmodel.drawdifftex(stafftextureptr); - break; - } - - glPopMatrix(); - } - - lastdrawnposition = position; - lastdrawntippoint = tippoint; - lastdrawnrotation1 = rotation1; - lastdrawnrotation2 = rotation2; - lastdrawnrotation3 = rotation3; - lastdrawnbigrotation = bigrotation; - lastdrawnbigtilt = bigtilt; - lastdrawnbigtilt2 = bigtilt2; - lastdrawnsmallrotation = smallrotation; - lastdrawnsmallrotation2 = smallrotation2; - if (owner != -1) - lastdrawnanim = Person::players[owner]->animCurrent; - } - if (owner != -1) { - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glTranslatef(position.x, position.y - .02, position.z); - glRotatef(bigrotation, 0, 1, 0); - glRotatef(bigtilt2, 1, 0, 0); - glRotatef(bigtilt, 0, 0, 1); - glRotatef(-rotation1 + 90, 0, 1, 0); - glRotatef(-rotation2 + 90, 0, 0, 1); - glRotatef(-rotation3, 0, 1, 0); - glRotatef(smallrotation, 1, 0, 0); - glRotatef(smallrotation2, 0, 1, 0); - glTranslatef(0, 0, length); - glGetFloatv(GL_MODELVIEW_MATRIX, M); - tippoint.x = M[12]; - tippoint.y = M[13]; - tippoint.z = M[14]; - glPopMatrix(); - } - } -} - -void Weapon::drop(XYZ v, XYZ tv, bool sethitsomething) -{ - owner = -1; - velocity = v; - tipvelocity = tv; - missed = 1; - if (sethitsomething) { - hitsomething = 0; - } - freetime = 0; - firstfree = 1; - physics = 1; -} - -void Weapon::thrown(XYZ v, bool sethitsomething) -{ - drop(v, v, sethitsomething); - missed = 0; - physics = 0; -} - -int Weapons::Draw() -{ - glAlphaFunc(GL_GREATER, 0.9); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - glDepthMask(1); - - for (std::vector::iterator weapon = begin(); weapon != end(); ++weapon) { - weapon->Draw(); - } - return 0; -} - -Weapons::Weapons() -{ -} - -Weapons::~Weapons() -{ - Weapon::stafftextureptr.destroy(); - Weapon::knifetextureptr.destroy(); - Weapon::lightbloodknifetextureptr.destroy(); - Weapon::bloodknifetextureptr.destroy(); - Weapon::swordtextureptr.destroy(); - Weapon::lightbloodswordtextureptr.destroy(); - Weapon::bloodswordtextureptr.destroy(); -} - diff --git a/Source/Weapons.h b/Source/Weapons.h deleted file mode 100644 index 7472541..0000000 --- a/Source/Weapons.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _WEAPONS_H_ -#define _WEAPONS_H_ - -/**> HEADER FILES <**/ - -#include "gamegl.h" -#include "Quaternions.h" -#include "Animation/Skeleton.h" -#include "Models.h" -#include "Terrain.h" -#include "Sprite.h" -#include "Person.h" -#include "Texture.h" -#include - -#define max_weapons 30 -#define max_weaponinstances 20 - -#define knife 1 -#define sword 2 -#define staff 3 - -class Weapon -{ -public: - Weapon(int type, int owner); - - static Model throwingknifemodel; - static Texture knifetextureptr; - static Texture lightbloodknifetextureptr; - static Texture bloodknifetextureptr; - - static Model swordmodel; - static Texture swordtextureptr; - static Texture lightbloodswordtextureptr; - static Texture bloodswordtextureptr; - - static Model staffmodel; - static Texture stafftextureptr; - - void Draw(); - void DoStuff(int); - - int getType() { - return type; - } - void setType(int); - - void drop(XYZ velocity, XYZ tipvelocity, bool sethitsomething = true); - void thrown(XYZ velocity, bool sethitsomething = true); - - int owner; - XYZ position; - XYZ tippoint; - XYZ velocity; - XYZ tipvelocity; - bool missed; - bool hitsomething; - float freetime; - bool firstfree; - bool physics; - - float damage; - int bloody; - float blooddrip; - float blooddripdelay; - - float rotation1; - float rotation2; - float rotation3; - float bigrotation; - float bigtilt; - float bigtilt2; - float smallrotation; - float smallrotation2; -private: - int type; - - XYZ oldtippoint; - XYZ oldposition; - int oldowner; - bool onfire; - float flamedelay; - float mass; - float tipmass; - float length; - float drawhowmany; - - XYZ lastdrawnposition; - XYZ lastdrawntippoint; - float lastdrawnrotation1; - float lastdrawnrotation2; - float lastdrawnrotation3; - float lastdrawnbigrotation; - float lastdrawnbigtilt; - float lastdrawnbigtilt2; - float lastdrawnsmallrotation; - float lastdrawnsmallrotation2; - int lastdrawnanim; -}; - -class Weapons : public std::vector -{ -public: - Weapons(); - ~Weapons(); - - int Draw(); - void DoStuff(); -}; - -extern Weapons weapons; -#endif diff --git a/Source/WinDefs.h b/Source/WinDefs.h index 32c7945..3668453 100644 --- a/Source/WinDefs.h +++ b/Source/WinDefs.h @@ -48,7 +48,7 @@ typedef signed char SInt8; typedef unsigned int UInt32; -#include "Random.h" +#include "Math/Random.h" typedef struct AbsoluteTime { diff --git a/Source/binio.h b/Source/binio.h deleted file mode 100644 index dddb214..0000000 --- a/Source/binio.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef binio_h -#define binio_h - -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - - /* - Notes on format of format strings: - * whitespace is ignored - * each "group" consists of an optional count (defaults to 1), - an optional byte-order marker (defaults to H, "host-native"), - and a data-type specifier. - * when unpacking, each variable argument is a pointer to the - appropriate number of objects of the appropriate type. - * when packing, each variable argument is an object of the - appropriate type if the count is omitted, or a pointer to the - appropriate number of objects of the appropriate type if the - count is specified. - * the buffer supplied to pack/unpack must be of sufficient length - to hold all the data, or the behavior is unspecified. - * the file provided to the "f" versions of the functions must be - open in the appropriate mode, or the behavior is unspecified. - * the file supplied to funpackf must be of sufficient length to - hold all the data, or the behavior is unspecified. - * the behavior of all functions is unspecified if the format string - is incorrectly-formed. - - Data-type specifiers: - x skipped byte; no corresponding argument - b byte - s two-byte two's-complement integer - i four-byte two's-complement integer - l eight-byte two's-complement integer - f four-byte IEEE754 float - d eight-byte IEEE754 double - - Byte-order specifiers: - L little-endian - B big-endian - H host's native byte order - N network byte order - */ - -#ifndef ALREADY_DID_BINIO_STDINT -#define ALREADY_DID_BINIO_STDINT -#if defined(BinIO_STDINT_HEADER) -#include BinIO_STDINT_HEADER - typedef float float32_t; - typedef double float64_t; -#else - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned long uint32_t; -#ifdef WIN32 - typedef unsigned __int64 uint64_t; -#else - typedef unsigned long long uint64_t; -#endif - typedef float float32_t; - typedef double float64_t; -#endif -#endif - - typedef struct { - float64_t d; - uint64_t l; - int i; - float32_t f; - uint16_t s; - uint8_t b; - } - test_data; - - extern void packf ( const char *format, ...); - extern void spackf (void *buffer, const char *format, ...); - extern void fpackf (FILE *file, const char *format, ...); - extern void vspackf (void *buffer, const char *format, va_list args); - extern void vfpackf (FILE *file, const char *format, va_list args); - - extern void unpackf ( const char *format, ...); - extern void sunpackf (const void *buffer, const char *format, ...); - extern void funpackf (FILE *file, const char *format, ...); - extern void vsunpackf(const void *buffer, const char *format, va_list args); - extern void vfunpackf(FILE *file, const char *format, va_list args); - -#ifdef _MSC_VER -#ifndef va_copy -#define va_copy(dest,src) do { dest = src; } while (0) -#endif -#endif - -#if defined(__cplusplus) -} -#endif - -#endif - diff --git a/Source/gamegl.h b/Source/gamegl.h deleted file mode 100644 index 9bee196..0000000 --- a/Source/gamegl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef _LUGARU_GL_H_ -#define _LUGARU_GL_H_ - - -#include -#include -#include -#include -#include -#include - -#ifdef WIN32 - #define WIN32_LEAN_AND_MEAN - #define Polygon WinPolygon - #include - #undef Polygon -#endif - -#define GL_GLEXT_PROTOTYPES 1 -#include "GL/gl.h" -#include "GL/glu.h" -#include "GL/glext.h" -#include "MacCompatibility.h" - -using namespace std; - -/* !!! FIXME: until we replace logger better. --ryan. */ -#define LOGFUNC -void LOG(const std::string &fmt, ...); - -#endif - - diff --git a/Source/main.cpp b/Source/main.cpp index 09a10cb..2144a8c 100644 --- a/Source/main.cpp +++ b/Source/main.cpp @@ -25,15 +25,15 @@ along with Lugaru. If not, see . #include #include #include -#include "gamegl.h" #include "MacCompatibility.h" -#include "Settings.h" +#include "Graphic/gamegl.h" +#include "User/Settings.h" #include "Game.h" using namespace Game; -#include "openal_wrapper.h" +#include "Audio/openal_wrapper.h" #ifdef WIN32 #include diff --git a/Source/openal_wrapper.cpp b/Source/openal_wrapper.cpp deleted file mode 100644 index ecac525..0000000 --- a/Source/openal_wrapper.cpp +++ /dev/null @@ -1,613 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include -#include -#include - -#include "Quaternions.h" -#include "openal_wrapper.h" -#include "Sounds.h" -#include "Game.h" - -extern float slomofreq; - -// NOTE: -// FMOD uses a Left Handed Coordinate system, OpenAL uses a Right Handed -// one...so we just need to flip the sign on the Z axis when appropriate. - -typedef struct { - ALuint sid; - OPENAL_SAMPLE *sample; - bool startpaused; - float position[3]; -} OPENAL_Channels; - -typedef struct OPENAL_SAMPLE { - char *name; - ALuint bid; // buffer id. - int mode; - int is2d; -} OPENAL_SAMPLE; - -static size_t num_channels = 0; -static OPENAL_Channels *impl_channels = NULL; -static bool initialized = false; -static float listener_position[3]; - -static void set_channel_position(const int channel, const float x, - const float y, const float z) -{ - OPENAL_Channels *chan = &impl_channels[channel]; - - chan->position[0] = x; - chan->position[1] = y; - chan->position[2] = z; - - OPENAL_SAMPLE *sptr = chan->sample; - if (sptr == NULL) - return; - - const ALuint sid = chan->sid; - const bool no_attenuate = sptr->is2d; - - if (no_attenuate) { - alSourcei(sid, AL_SOURCE_RELATIVE, AL_TRUE); - alSource3f(sid, AL_POSITION, 0.0f, 0.0f, 0.0f); - } else { - alSourcei(sid, AL_SOURCE_RELATIVE, AL_FALSE); - alSource3f(sid, AL_POSITION, x, y, z); - } -} - - -AL_API void OPENAL_3D_Listener_SetAttributes(const float *pos, const float *vel, float fx, float fy, float fz, float tx, float ty, float tz) -{ - if (!initialized) - return; - if (pos != NULL) { - alListener3f(AL_POSITION, pos[0], pos[1], -pos[2]); - listener_position[0] = pos[0]; - listener_position[1] = pos[1]; - listener_position[2] = -pos[2]; - } - - ALfloat vec[6] = { fx, fy, -fz, tz, ty, -tz }; - alListenerfv(AL_ORIENTATION, vec); - - // we ignore velocity, since doppler's broken in the Linux AL at the moment... - - // adjust existing positions... - for (int i = 0; i < num_channels; i++) { - const float *p = impl_channels[i].position; - set_channel_position(i, p[0], p[1], p[2]); - } -} - -AL_API signed char OPENAL_3D_SetAttributes(int channel, const float *pos, const float *vel) -{ - if (!initialized) - return false; - if ((channel < 0) || (channel >= num_channels)) - return false; - - if (pos != NULL) - set_channel_position(channel, pos[0], pos[1], -pos[2]); - - // we ignore velocity, since doppler's broken in the Linux AL at the moment... - - return true; -} - -AL_API signed char OPENAL_3D_SetAttributes_(int channel, const XYZ &pos, const float *vel) -{ - if (!initialized) - return false; - if ((channel < 0) || (channel >= num_channels)) - return false; - - set_channel_position(channel, pos.x, pos.y, -pos.z); - - return true; -} - -AL_API signed char OPENAL_Init(int mixrate, int maxsoftwarechannels, unsigned int flags) -{ - if (initialized) - return false; - if (maxsoftwarechannels == 0) - return false; - - if (flags != 0) // unsupported. - return false; - - ALCdevice *dev = alcOpenDevice(NULL); - if (!dev) - return false; - - ALint caps[] = { ALC_FREQUENCY, mixrate, 0 }; - ALCcontext *ctx = alcCreateContext(dev, caps); - if (!ctx) { - alcCloseDevice(dev); - return false; - } - - alcMakeContextCurrent(ctx); - alcProcessContext(ctx); - - if (commandLineOptions[OPENALINFO]) { - printf("AL_VENDOR: %s\n", (char *) alGetString(AL_VENDOR)); - printf("AL_RENDERER: %s\n", (char *) alGetString(AL_RENDERER)); - printf("AL_VERSION: %s\n", (char *) alGetString(AL_VERSION)); - printf("AL_EXTENSIONS: %s\n", (char *) alGetString(AL_EXTENSIONS)); - } - - num_channels = maxsoftwarechannels; - impl_channels = new OPENAL_Channels[maxsoftwarechannels]; - memset(impl_channels, '\0', sizeof (OPENAL_Channels) * num_channels); - for (int i = 0; i < num_channels; i++) - alGenSources(1, &impl_channels[i].sid); // !!! FIXME: verify this didn't fail! - - initialized = true; - return true; -} - -AL_API void OPENAL_Close() -{ - if (!initialized) - return; - - ALCcontext *ctx = alcGetCurrentContext(); - if (ctx) { - for (int i = 0; i < num_channels; i++) { - alSourceStop(impl_channels[i].sid); - alSourcei(impl_channels[i].sid, AL_BUFFER, 0); - alDeleteSources(1, &impl_channels[i].sid); - } - ALCdevice *dev = alcGetContextsDevice(ctx); - alcMakeContextCurrent(NULL); - alcSuspendContext(ctx); - alcDestroyContext(ctx); - alcCloseDevice(dev); - } - - num_channels = 0; - delete[] impl_channels; - impl_channels = NULL; - - initialized = false; -} - -static OPENAL_SAMPLE *OPENAL_GetCurrentSample(int channel) -{ - if (!initialized) - return NULL; - if ((channel < 0) || (channel >= num_channels)) - return NULL; - return impl_channels[channel].sample; -} - -static signed char OPENAL_GetPaused(int channel) -{ - if (!initialized) - return false; - if ((channel < 0) || (channel >= num_channels)) - return false; - if (impl_channels[channel].startpaused) - return(true); - - ALint state = 0; - alGetSourceiv(impl_channels[channel].sid, AL_SOURCE_STATE, &state); - return((state == AL_PAUSED) ? true : false); -} - -static unsigned int OPENAL_GetLoopMode(int channel) -{ - if (!initialized) - return 0; - if ((channel < 0) || (channel >= num_channels)) - return 0; - ALint loop = 0; - alGetSourceiv(impl_channels[channel].sid, AL_LOOPING, &loop); - if (loop) - return(OPENAL_LOOP_NORMAL); - return OPENAL_LOOP_OFF; -} - -static signed char OPENAL_IsPlaying(int channel) -{ - if (!initialized) - return false; - if ((channel < 0) || (channel >= num_channels)) - return false; - ALint state = 0; - alGetSourceiv(impl_channels[channel].sid, AL_SOURCE_STATE, &state); - return((state == AL_PLAYING) ? true : false); -} - -static int OPENAL_PlaySoundEx(int channel, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused) -{ - if (!initialized) - return -1; - if (sptr == NULL) - return -1; - if (dsp != NULL) - return -1; - if (channel == OPENAL_FREE) { - for (int i = 0; i < num_channels; i++) { - ALint state = 0; - alGetSourceiv(impl_channels[i].sid, AL_SOURCE_STATE, &state); - if ((state != AL_PLAYING) && (state != AL_PAUSED)) { - channel = i; - break; - } - } - } - - if ((channel < 0) || (channel >= num_channels)) - return -1; - alSourceStop(impl_channels[channel].sid); - impl_channels[channel].sample = sptr; - alSourcei(impl_channels[channel].sid, AL_BUFFER, sptr->bid); - alSourcei(impl_channels[channel].sid, AL_LOOPING, (sptr->mode == OPENAL_LOOP_OFF) ? AL_FALSE : AL_TRUE); - set_channel_position(channel, 0.0f, 0.0f, 0.0f); - - impl_channels[channel].startpaused = ((startpaused) ? true : false); - if (!startpaused) - alSourcePlay(impl_channels[channel].sid); - return channel; -} - - -static void *decode_to_pcm(const char *_fname, ALenum &format, ALsizei &size, ALuint &freq) -{ -#ifdef __POWERPC__ - const int bigendian = 1; -#else - const int bigendian = 0; -#endif - - // !!! FIXME: if it's not Ogg, we don't have a decoder. I'm lazy. :/ - char *fname = (char *) alloca(strlen(_fname) + 16); - strcpy(fname, _fname); - char *ptr = strchr(fname, '.'); - if (ptr) - *ptr = '\0'; - strcat(fname, ".ogg"); - - // just in case... - FILE *io = fopen(fname, "rb"); - if (io == NULL) - return NULL; - - ALubyte *retval = NULL; - -#if 0 // untested, so disable this! - // Can we just feed it to the AL compressed? - if (alIsExtensionPresent((const ALubyte *) "AL_EXT_vorbis")) { - format = alGetEnumValue((const ALubyte *) "AL_FORMAT_VORBIS_EXT"); - freq = 44100; - fseek(io, 0, SEEK_END); - size = ftell(io); - fseek(io, 0, SEEK_SET); - retval = (ALubyte *) malloc(size); - size_t rc = fread(retval, size, 1, io); - fclose(io); - if (rc != 1) { - free(retval); - return NULL; - } - return retval; - } -#endif - - // Uncompress and feed to the AL. - OggVorbis_File vf; - memset(&vf, '\0', sizeof (vf)); - if (ov_open(io, &vf, NULL, 0) == 0) { - int bitstream = 0; - vorbis_info *info = ov_info(&vf, -1); - size = 0; - format = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; - freq = info->rate; - - if ((info->channels != 1) && (info->channels != 2)) { - ov_clear(&vf); - return NULL; - } - - char buf[1024 * 16]; - long rc = 0; - size_t allocated = 64 * 1024; - retval = (ALubyte *) malloc(allocated); - while ( (rc = ov_read(&vf, buf, sizeof (buf), bigendian, 2, 1, &bitstream)) != 0 ) { - if (rc > 0) { - size += rc; - if (size >= allocated) { - allocated *= 2; - ALubyte *tmp = (ALubyte *) realloc(retval, allocated); - if (tmp == NULL) { - free(retval); - retval = NULL; - break; - } - retval = tmp; - } - memcpy(retval + (size - rc), buf, rc); - } - } - ov_clear(&vf); - return retval; - } - - fclose(io); - return NULL; -} - - -AL_API OPENAL_SAMPLE *OPENAL_Sample_Load(int index, const char *name_or_data, unsigned int mode, int offset, int length) -{ - if (!initialized) - return NULL; - if (index != OPENAL_FREE) - return NULL; // this is all the game does... - if (offset != 0) - return NULL; // this is all the game does... - if (length != 0) - return NULL; // this is all the game does... - if ((mode != OPENAL_HW3D) && (mode != OPENAL_2D)) - return NULL; // this is all the game does... - - OPENAL_SAMPLE *retval = NULL; - ALenum format = AL_NONE; - ALsizei size = 0; - ALuint frequency = 0; - void *data = decode_to_pcm(name_or_data, format, size, frequency); - if (data == NULL) - return NULL; - - ALuint bid = 0; - alGetError(); - alGenBuffers(1, &bid); - if (alGetError() == AL_NO_ERROR) { - alBufferData(bid, format, data, size, frequency); - retval = new OPENAL_SAMPLE; - retval->bid = bid; - retval->mode = OPENAL_LOOP_OFF; - retval->is2d = (mode == OPENAL_2D); - retval->name = new char[strlen(name_or_data) + 1]; - if (retval->name) - strcpy(retval->name, name_or_data); - } - - free(data); - return(retval); -} - -AL_API void OPENAL_Sample_Free(OPENAL_SAMPLE *sptr) -{ - if (!initialized) - return; - if (sptr) { - for (int i = 0; i < num_channels; i++) { - if (impl_channels[i].sample == sptr) { - alSourceStop(impl_channels[i].sid); - alSourcei(impl_channels[i].sid, AL_BUFFER, 0); - impl_channels[i].sample = NULL; - } - } - alDeleteBuffers(1, &sptr->bid); - delete[] sptr->name; - delete sptr; - } -} - -static signed char OPENAL_Sample_SetMode(OPENAL_SAMPLE *sptr, unsigned int mode) -{ - if (!initialized) - return false; - if ((mode != OPENAL_LOOP_NORMAL) && (mode != OPENAL_LOOP_OFF)) - return false; - if (!sptr) - return false; - sptr->mode = mode; - return true; -} - -AL_API signed char OPENAL_SetFrequency(int channel, bool slomo) -{ - if (!initialized) - return false; - if (channel == OPENAL_ALL) { - for (int i = 0; i < num_channels; i++) - OPENAL_SetFrequency(i, slomo); - return true; - } - - if ((channel < 0) || (channel >= num_channels)) - return false; - if (slomo) - alSourcef(impl_channels[channel].sid, AL_PITCH, ((ALfloat) slomofreq) / 44100.0f); - else - alSourcef(impl_channels[channel].sid, AL_PITCH, 1.0f); - return true; -} - -AL_API signed char OPENAL_SetVolume(int channel, int vol) -{ - if (!initialized) - return false; - - if (channel == OPENAL_ALL) { - for (int i = 0; i < num_channels; i++) - OPENAL_SetVolume(i, vol); - return true; - } - - if ((channel < 0) || (channel >= num_channels)) - return false; - - if (vol < 0) - vol = 0; - else if (vol > 255) - vol = 255; - ALfloat gain = ((ALfloat) vol) / 255.0f; - alSourcef(impl_channels[channel].sid, AL_GAIN, gain); - return true; -} - -AL_API signed char OPENAL_SetPaused(int channel, signed char paused) -{ - if (!initialized) - return false; - - if (channel == OPENAL_ALL) { - for (int i = 0; i < num_channels; i++) - OPENAL_SetPaused(i, paused); - return true; - } - - if ((channel < 0) || (channel >= num_channels)) - return false; - - ALint state = 0; - if (impl_channels[channel].startpaused) - state = AL_PAUSED; - else - alGetSourceiv(impl_channels[channel].sid, AL_SOURCE_STATE, &state); - - if ((paused) && (state == AL_PLAYING)) - alSourcePause(impl_channels[channel].sid); - else if ((!paused) && (state == AL_PAUSED)) { - alSourcePlay(impl_channels[channel].sid); - impl_channels[channel].startpaused = false; - } - return true; -} - -AL_API void OPENAL_SetSFXMasterVolume(int volume) -{ - if (!initialized) - return; - ALfloat gain = ((ALfloat) volume) / 255.0f; - alListenerf(AL_GAIN, gain); -} - -AL_API signed char OPENAL_StopSound(int channel) -{ - if (!initialized) - return false; - - if (channel == OPENAL_ALL) { - for (int i = 0; i < num_channels; i++) - OPENAL_StopSound(i); - return true; - } - - if ((channel < 0) || (channel >= num_channels)) - return false; - alSourceStop(impl_channels[channel].sid); - impl_channels[channel].startpaused = false; - return true; -} - -static OPENAL_SAMPLE *OPENAL_Stream_GetSample(OPENAL_STREAM *stream) -{ - if (!initialized) - return NULL; - return (OPENAL_SAMPLE *) stream; -} - -static int OPENAL_Stream_PlayEx(int channel, OPENAL_STREAM *stream, OPENAL_DSPUNIT *dsp, signed char startpaused) -{ - return OPENAL_PlaySoundEx(channel, (OPENAL_SAMPLE *) stream, dsp, startpaused); -} - -static signed char OPENAL_Stream_Stop(OPENAL_STREAM *stream) -{ - if (!initialized) - return false; - for (int i = 0; i < num_channels; i++) { - if (impl_channels[i].sample == (OPENAL_SAMPLE *) stream) { - alSourceStop(impl_channels[i].sid); - impl_channels[i].startpaused = false; - } - } - return true; -} - -AL_API signed char OPENAL_Stream_SetMode(OPENAL_STREAM *stream, unsigned int mode) -{ - return OPENAL_Sample_SetMode((OPENAL_SAMPLE *) stream, mode); -} - -AL_API void OPENAL_Update() -{ - if (!initialized) - return; - alcProcessContext(alcGetCurrentContext()); -} - -AL_API signed char OPENAL_SetOutput(int outputtype) -{ - return true; -} - -extern int channels[]; - -extern "C" void PlaySoundEx(int chan, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused) -{ - const OPENAL_SAMPLE * currSample = OPENAL_GetCurrentSample(channels[chan]); - if (currSample && currSample == samp[chan]) { - if (OPENAL_GetPaused(channels[chan])) { - OPENAL_StopSound(channels[chan]); - channels[chan] = OPENAL_FREE; - } else if (OPENAL_IsPlaying(channels[chan])) { - int loop_mode = OPENAL_GetLoopMode(channels[chan]); - if (loop_mode & OPENAL_LOOP_OFF) { - channels[chan] = OPENAL_FREE; - } - } - } else { - channels[chan] = OPENAL_FREE; - } - - channels[chan] = OPENAL_PlaySoundEx(channels[chan], sptr, dsp, startpaused); - if (channels[chan] < 0) { - channels[chan] = OPENAL_PlaySoundEx(OPENAL_FREE, sptr, dsp, startpaused); - } -} - -extern "C" void PlayStreamEx(int chan, OPENAL_STREAM *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused) -{ - const OPENAL_SAMPLE * currSample = OPENAL_GetCurrentSample(channels[chan]); - if (currSample && currSample == OPENAL_Stream_GetSample(sptr)) { - OPENAL_StopSound(channels[chan]); - OPENAL_Stream_Stop(sptr); - } else { - OPENAL_Stream_Stop(sptr); - channels[chan] = OPENAL_FREE; - } - - channels[chan] = OPENAL_Stream_PlayEx(channels[chan], sptr, dsp, startpaused); - if (channels[chan] < 0) { - channels[chan] = OPENAL_Stream_PlayEx(OPENAL_FREE, sptr, dsp, startpaused); - } -} diff --git a/Source/openal_wrapper.h b/Source/openal_wrapper.h deleted file mode 100644 index 16d4f67..0000000 --- a/Source/openal_wrapper.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef OPENAL_WRAPPER_H -#define OPENAL_WRAPPER_H - -#ifdef _WIN32 -#include -#endif - -#include "AL/al.h" -#include "AL/alc.h" - -#include "ogg/ogg.h" -#include "vorbis/vorbisfile.h" - -#include "MacCompatibility.h" - -#if 0 /* this should only be enable if OPENAL doesn't provide AL_API on all platforms */ -#if (!defined(WIN32) && !defined(_WIN32) && !defined(__WIN32__) && !defined(_WIN64) && !defined(_WIN32_WCE) && !defined(_XBOX)) || (defined(__GNUC__) && defined(WIN32)) -#ifndef __cdecl -#define __cdecl -#endif -#ifndef __stdcall -#define __stdcall -#endif -#endif - -#if defined(_WIN32_WCE) -#define AL_API _cdecl -#define F_CALLBACKAPI _cdecl -#else -#define AL_API __stdcall -#define F_CALLBACKAPI __stdcall -#endif - -#ifdef DLL_EXPORTS -#define DLL_API __declspec(dllexport) -#else -#if defined(__LCC__) || defined(__MINGW32__) || defined(__CYGWIN32__) -#define DLL_API AL_API -#else -#define DLL_API -#endif /* __LCC__ || __MINGW32__ || __CYGWIN32__ */ -#endif /* DLL_EXPORTS */ -#endif /* if 0 */ - - -typedef struct OPENAL_SAMPLE OPENAL_SAMPLE; -typedef OPENAL_SAMPLE OPENAL_STREAM; -typedef struct OPENAL_DSPUNIT OPENAL_DSPUNIT; - -enum OPENAL_OUTPUTTYPES { - OPENAL_OUTPUT_NOSOUND, /* NoSound driver, all calls to this succeed but do nothing. */ - OPENAL_OUTPUT_OSS, /* Linux/Unix OSS (Open Sound System) driver, i.e. the kernel sound drivers. */ - OPENAL_OUTPUT_ALSA, /* Linux Alsa driver. */ -}; - -#define OPENAL_LOOP_OFF 0x00000001 /* For non looping samples. */ -#define OPENAL_LOOP_NORMAL 0x00000002 /* For forward looping samples. */ -#define OPENAL_HW3D 0x00001000 /* Attempts to make samples use 3d hardware acceleration. (if the card supports it) */ -#define OPENAL_2D 0x00002000 /* Tells software (not hardware) based sample not to be included in 3d processing. */ -#define OPENAL_FREE -1 /* value to play on any free channel, or to allocate a sample in a free sample slot. */ -#define OPENAL_ALL -3 /* for a channel index , this flag will affect ALL channels available! Not supported by every function. */ - -#ifdef __cplusplus -extern "C" { -#endif - -#undef AL_API -#define AL_API - - AL_API void OPENAL_3D_Listener_SetAttributes(const float *pos, const float *vel, float fx, float fy, float fz, float tx, float ty, float tz); - AL_API signed char OPENAL_3D_SetAttributes(int channel, const float *pos, const float *vel); - AL_API signed char OPENAL_3D_SetAttributes_(int channel, const XYZ &pos, const float *vel); - AL_API signed char OPENAL_Init(int mixrate, int maxsoftwarechannels, unsigned int flags); - AL_API void OPENAL_Close(); - AL_API OPENAL_SAMPLE *OPENAL_Sample_Load(int index, const char *name_or_data, unsigned int mode, int offset, int length); - AL_API void OPENAL_Sample_Free(OPENAL_SAMPLE *sptr); - AL_API signed char OPENAL_SetFrequency(int channel, bool slomo = false); - AL_API signed char OPENAL_SetVolume(int channel, int vol); - AL_API signed char OPENAL_SetPaused(int channel, signed char paused); - AL_API void OPENAL_SetSFXMasterVolume(int volume); - AL_API signed char OPENAL_StopSound(int channel); - AL_API signed char OPENAL_Stream_SetMode(OPENAL_STREAM *stream, unsigned int mode); - AL_API void OPENAL_Update(); - AL_API signed char OPENAL_SetOutput(int outputtype); - void PlaySoundEx(int chan, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused); - void PlayStreamEx(int chan, OPENAL_SAMPLE *sptr, OPENAL_DSPUNIT *dsp, signed char startpaused); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/Source/optionparser.h b/Source/optionparser.h deleted file mode 100644 index a5a8e19..0000000 --- a/Source/optionparser.h +++ /dev/null @@ -1,2821 +0,0 @@ -/* - * The Lean Mean C++ Option Parser - * - * Copyright (C) 2012 Matthias S. Benkmann - * - * The "Software" in the following 2 paragraphs refers to this file containing - * the code to The Lean Mean C++ Option Parser. - * The "Software" does NOT refer to any other files which you - * may have received alongside this file (e.g. as part of a larger project that - * incorporates The Lean Mean C++ Option Parser). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software, to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following - * conditions: - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * NOTE: It is recommended that you read the processed HTML doxygen documentation - * rather than this source. If you don't know doxygen, it's like javadoc for C++. - * If you don't want to install doxygen you can find a copy of the processed - * documentation at - * - * http://optionparser.sourceforge.net/ - * - */ - -/** - * @file - * - * @brief This is the only file required to use The Lean Mean C++ Option Parser. - * Just \#include it and you're set. - * - * The Lean Mean C++ Option Parser handles the program's command line arguments - * (argc, argv). - * It supports the short and long option formats of getopt(), getopt_long() - * and getopt_long_only() but has a more convenient interface. - * The following features set it apart from other option parsers: - * - * @par Highlights: - *
    - *
  • It is a header-only library. Just \#include "optionparser.h" and you're set. - *
  • It is freestanding. There are no dependencies whatsoever, not even the - * C or C++ standard library. - *
  • It has a usage message formatter that supports column alignment and - * line wrapping. This aids localization because it adapts to - * translated strings that are shorter or longer (even if they contain - * Asian wide characters). - *
  • Unlike getopt() and derivatives it doesn't force you to loop through - * options sequentially. Instead you can access options directly like this: - *
      - *
    • Test for presence of a switch in the argument vector: - * @code if ( options[QUIET] ) ... @endcode - *
    • Evaluate --enable-foo/--disable-foo pair where the last one used wins: - * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode - *
    • Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose): - * @code int verbosity = options[VERBOSE].count(); @endcode - *
    • Iterate over all --file=<fname> arguments: - * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) - * fname = opt->arg; ... @endcode - *
    • If you really want to, you can still process all arguments in order: - * @code - * for (int i = 0; i < p.optionsCount(); ++i) { - * Option& opt = buffer[i]; - * switch(opt.index()) { - * case HELP: ... - * case VERBOSE: ... - * case FILE: fname = opt.arg; ... - * case UNKNOWN: ... - * @endcode - *
    - *
@n - * Despite these features the code size remains tiny. - * It is smaller than uClibc's GNU getopt() and just a - * couple 100 bytes larger than uClibc's SUSv3 getopt(). @n - * (This does not include the usage formatter, of course. But you don't have to use that.) - * - * @par Download: - * Tarball with examples and test programs: - * optionparser-1.4.tar.gz @n - * Just the header (this is all you really need): - * optionparser.h - * - * @par Changelog: - * Version 1.4: Fixed 2 printUsage() bugs that messed up output with small COLUMNS values @n - * Version 1.3: Compatible with Microsoft Visual C++. @n - * Version 1.2: Added @ref option::Option::namelen "Option::namelen" and removed the extraction - * of short option characters into a special buffer. @n - * Changed @ref option::Arg::Optional "Arg::Optional" to accept arguments if they are attached - * rather than separate. This is what GNU getopt() does and how POSIX recommends - * utilities should interpret their arguments.@n - * Version 1.1: Optional mode with argument reordering as done by GNU getopt(), so that - * options and non-options can be mixed. See - * @ref option::Parser::parse() "Parser::parse()". - * - * @par Feedback: - * Send questions, bug reports, feature requests etc. to: optionparser-feedback (a) lists.sourceforge.net - * @htmlonly @endhtmlonly - * - * - * @par Example program: - * (Note: @c option::* identifiers are links that take you to their documentation.) - * @code - * #error EXAMPLE SHORTENED FOR READABILITY. BETTER EXAMPLES ARE IN THE .TAR.GZ! - * #include - * #include "optionparser.h" - * - * enum optionIndex { UNKNOWN, HELP, PLUS }; - * const option::Descriptor usage[] = - * { - * {UNKNOWN, 0,"" , "" ,option::Arg::None, "USAGE: example [options]\n\n" - * "Options:" }, - * {HELP, 0,"" , "help",option::Arg::None, " --help \tPrint usage and exit." }, - * {PLUS, 0,"p", "plus",option::Arg::None, " --plus, -p \tIncrement count." }, - * {UNKNOWN, 0,"" , "" ,option::Arg::None, "\nExamples:\n" - * " example --unknown -- --this_is_no_option\n" - * " example -unk --plus -ppp file1 file2\n" }, - * {0,0,0,0,0,0} - * }; - * - * int main(int argc, char* argv[]) - * { - * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present - * option::Stats stats(usage, argc, argv); - * option::Option options[stats.options_max], buffer[stats.buffer_max]; - * option::Parser parse(usage, argc, argv, options, buffer); - * - * if (parse.error()) - * return 1; - * - * if (options[HELP] || argc == 0) { - * option::printUsage(std::cout, usage); - * return 0; - * } - * - * std::cout << "--plus count: " << - * options[PLUS].count() << "\n"; - * - * for (option::Option* opt = options[UNKNOWN]; opt; opt = opt->next()) - * std::cout << "Unknown option: " << opt->name << "\n"; - * - * for (int i = 0; i < parse.nonOptionsCount(); ++i) - * std::cout << "Non-option #" << i << ": " << parse.nonOption(i) << "\n"; - * } - * @endcode - * - * @par Option syntax: - * @li The Lean Mean C++ Option Parser follows POSIX getopt() conventions and supports - * GNU-style getopt_long() long options as well as Perl-style single-minus - * long options (getopt_long_only()). - * @li short options have the format @c -X where @c X is any character that fits in a char. - * @li short options can be grouped, i.e. -X -Y is equivalent to @c -XY. - * @li a short option may take an argument either separate (-X foo) or - * attached (@c -Xfoo). You can make the parser accept the additional format @c -X=foo by - * registering @c X as a long option (in addition to being a short option) and - * enabling single-minus long options. - * @li an argument-taking short option may be grouped if it is the last in the group, e.g. - * @c -ABCXfoo or -ABCX foo (@c foo is the argument to the @c -X option). - * @li a lone minus character @c '-' is not treated as an option. It is customarily used where - * a file name is expected to refer to stdin or stdout. - * @li long options have the format @c --option-name. - * @li the option-name of a long option can be anything and include any characters. - * Even @c = characters will work, but don't do that. - * @li [optional] long options may be abbreviated as long as the abbreviation is unambiguous. - * You can set a minimum length for abbreviations. - * @li [optional] long options may begin with a single minus. The double minus form is always - * accepted, too. - * @li a long option may take an argument either separate ( --option arg ) or - * attached ( --option=arg ). In the attached form the equals sign is mandatory. - * @li an empty string can be passed as an attached long option argument: --option-name= . - * Note the distinction between an empty string as argument and no argument at all. - * @li an empty string is permitted as separate argument to both long and short options. - * @li Arguments to both short and long options may start with a @c '-' character. E.g. - * -X-X , -X -X or --long-X=-X . If @c -X - * and @c --long-X take an argument, that argument will be @c "-X" in all 3 cases. - * @li If using the built-in @ref option::Arg::Optional "Arg::Optional", optional arguments must - * be attached. - * @li the special option @c -- (i.e. without a name) terminates the list of - * options. Everything that follows is a non-option argument, even if it starts with - * a @c '-' character. The @c -- itself will not appear in the parse results. - * @li the first argument that doesn't start with @c '-' or @c '--' and does not belong to - * a preceding argument-taking option, will terminate the option list and is the - * first non-option argument. All following command line arguments are treated as - * non-option arguments, even if they start with @c '-' . @n - * NOTE: This behaviour is mandated by POSIX, but GNU getopt() only honours this if it is - * explicitly requested (e.g. by setting POSIXLY_CORRECT). @n - * You can enable the GNU behaviour by passing @c true as first argument to - * e.g. @ref option::Parser::parse() "Parser::parse()". - * @li Arguments that look like options (i.e. @c '-' followed by at least 1 character) but - * aren't, are NOT treated as non-option arguments. They are treated as unknown options and - * are collected into a list of unknown options for error reporting. @n - * This means that in order to pass a first non-option - * argument beginning with the minus character it is required to use the - * @c -- special option, e.g. - * @code - * program -x -- --strange-filename - * @endcode - * In this example, @c --strange-filename is a non-option argument. If the @c -- - * were omitted, it would be treated as an unknown option. @n - * See @ref option::Descriptor::longopt for information on how to collect unknown options. - * - */ - -#ifndef OPTIONPARSER_H_ -#define OPTIONPARSER_H_ - -/** @brief The namespace of The Lean Mean C++ Option Parser. */ -namespace option -{ - -#ifdef _MSC_VER -#include -#pragma intrinsic(_BitScanReverse) -struct MSC_Builtin_CLZ -{ - static int builtin_clz(unsigned x) - { - unsigned long index; - _BitScanReverse(&index, x); - return 32-index; // int is always 32bit on Windows, even for target x64 - } -}; -#define __builtin_clz(x) MSC_Builtin_CLZ::builtin_clz(x) -#endif - -class Option; - -/** - * @brief Possible results when checking if an argument is valid for a certain option. - * - * In the case that no argument is provided for an option that takes an - * optional argument, return codes @c ARG_OK and @c ARG_IGNORE are equivalent. - */ -enum ArgStatus -{ - //! The option does not take an argument. - ARG_NONE, - //! The argument is acceptable for the option. - ARG_OK, - //! The argument is not acceptable but that's non-fatal because the option's argument is optional. - ARG_IGNORE, - //! The argument is not acceptable and that's fatal. - ARG_ILLEGAL -}; - -/** - * @brief Signature of functions that check if an argument is valid for a certain type of option. - * - * Every Option has such a function assigned in its Descriptor. - * @code - * Descriptor usage[] = { {UNKNOWN, 0, "", "", Arg::None, ""}, ... }; - * @endcode - * - * A CheckArg function has the following signature: - * @code ArgStatus CheckArg(const Option& option, bool msg); @endcode - * - * It is used to check if a potential argument would be acceptable for the option. - * It will even be called if there is no argument. In that case @c option.arg will be @c NULL. - * - * If @c msg is @c true and the function determines that an argument is not acceptable and - * that this is a fatal error, it should output a message to the user before - * returning @ref ARG_ILLEGAL. If @c msg is @c false the function should remain silent (or you - * will get duplicate messages). - * - * See @ref ArgStatus for the meaning of the return values. - * - * While you can provide your own functions, - * often the following pre-defined checks (which never return @ref ARG_ILLEGAL) will suffice: - * - * @li @c Arg::None @copybrief Arg::None - * @li @c Arg::Optional @copybrief Arg::Optional - * - */ -typedef ArgStatus (*CheckArg)(const Option& option, bool msg); - -/** - * @brief Describes an option, its help text (usage) and how it should be parsed. - * - * The main input when constructing an option::Parser is an array of Descriptors. - - * @par Example: - * @code - * enum OptionIndex {CREATE, ...}; - * enum OptionType {DISABLE, ENABLE, OTHER}; - * - * const option::Descriptor usage[] = { - * { CREATE, // index - * OTHER, // type - * "c", // shortopt - * "create", // longopt - * Arg::None, // check_arg - * "--create Tells the program to create something." // help - * } - * , ... - * }; - * @endcode - */ -struct Descriptor -{ - /** - * @brief Index of this option's linked list in the array filled in by the parser. - * - * Command line options whose Descriptors have the same index will end up in the same - * linked list in the order in which they appear on the command line. If you have - * multiple long option aliases that refer to the same option, give their descriptors - * the same @c index. - * - * If you have options that mean exactly opposite things - * (e.g. @c --enable-foo and @c --disable-foo ), you should also give them the same - * @c index, but distinguish them through different values for @ref type. - * That way they end up in the same list and you can just take the last element of the - * list and use its type. This way you get the usual behaviour where switches later - * on the command line override earlier ones without having to code it manually. - * - * @par Tip: - * Use an enum rather than plain ints for better readability, as shown in the example - * at Descriptor. - */ - const unsigned index; - - /** - * @brief Used to distinguish between options with the same @ref index. - * See @ref index for details. - * - * It is recommended that you use an enum rather than a plain int to make your - * code more readable. - */ - const int type; - - /** - * @brief Each char in this string will be accepted as a short option character. - * - * The string must not include the minus character @c '-' or you'll get undefined - * behaviour. - * - * If this Descriptor should not have short option characters, use the empty - * string "". NULL is not permitted here! - * - * See @ref longopt for more information. - */ - const char* const shortopt; - - /** - * @brief The long option name (without the leading @c -- ). - * - * If this Descriptor should not have a long option name, use the empty - * string "". NULL is not permitted here! - * - * While @ref shortopt allows multiple short option characters, each - * Descriptor can have only a single long option name. If you have multiple - * long option names referring to the same option use separate Descriptors - * that have the same @ref index and @ref type. You may repeat - * short option characters in such an alias Descriptor but there's no need to. - * - * @par Dummy Descriptors: - * You can use dummy Descriptors with an - * empty string for both @ref shortopt and @ref longopt to add text to - * the usage that is not related to a specific option. See @ref help. - * The first dummy Descriptor will be used for unknown options (see below). - * - * @par Unknown Option Descriptor: - * The first dummy Descriptor in the list of Descriptors, - * whose @ref shortopt and @ref longopt are both the empty string, will be used - * as the Descriptor for unknown options. An unknown option is a string in - * the argument vector that is not a lone minus @c '-' but starts with a minus - * character and does not match any Descriptor's @ref shortopt or @ref longopt. @n - * Note that the dummy descriptor's @ref check_arg function @e will be called and - * its return value will be evaluated as usual. I.e. if it returns @ref ARG_ILLEGAL - * the parsing will be aborted with Parser::error()==true. @n - * if @c check_arg does not return @ref ARG_ILLEGAL the descriptor's - * @ref index @e will be used to pick the linked list into which - * to put the unknown option. @n - * If there is no dummy descriptor, unknown options will be dropped silently. - * - */ - const char* const longopt; - - /** - * @brief For each option that matches @ref shortopt or @ref longopt this function - * will be called to check a potential argument to the option. - * - * This function will be called even if there is no potential argument. In that case - * it will be passed @c NULL as @c arg parameter. Do not confuse this with the empty - * string. - * - * See @ref CheckArg for more information. - */ - const CheckArg check_arg; - - /** - * @brief The usage text associated with the options in this Descriptor. - * - * You can use option::printUsage() to format your usage message based on - * the @c help texts. You can use dummy Descriptors where - * @ref shortopt and @ref longopt are both the empty string to add text to - * the usage that is not related to a specific option. - * - * See option::printUsage() for special formatting characters you can use in - * @c help to get a column layout. - * - * @attention - * Must be UTF-8-encoded. If your compiler supports C++11 you can use the "u8" - * prefix to make sure string literals are properly encoded. - */ - const char* help; -}; - -/** - * @brief A parsed option from the command line together with its argument if it has one. - * - * The Parser chains all parsed options with the same Descriptor::index together - * to form a linked list. This allows you to easily implement all of the common ways - * of handling repeated options and enable/disable pairs. - * - * @li Test for presence of a switch in the argument vector: - * @code if ( options[QUIET] ) ... @endcode - * @li Evaluate --enable-foo/--disable-foo pair where the last one used wins: - * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode - * @li Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose): - * @code int verbosity = options[VERBOSE].count(); @endcode - * @li Iterate over all --file=<fname> arguments: - * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) - * fname = opt->arg; ... @endcode - */ -class Option -{ - Option* next_; - Option* prev_; -public: - /** - * @brief Pointer to this Option's Descriptor. - * - * Remember that the first dummy descriptor (see @ref Descriptor::longopt) is used - * for unknown options. - * - * @attention - * @c desc==NULL signals that this Option is unused. This is the default state of - * elements in the result array. You don't need to test @c desc explicitly. You - * can simply write something like this: - * @code - * if (options[CREATE]) - * { - * ... - * } - * @endcode - * This works because of operator const Option*() . - */ - const Descriptor* desc; - - /** - * @brief The name of the option as used on the command line. - * - * The main purpose of this string is to be presented to the user in messages. - * - * In the case of a long option, this is the actual @c argv pointer, i.e. the first - * character is a '-'. In the case of a short option this points to the option - * character within the @c argv string. - * - * Note that in the case of a short option group or an attached option argument, this - * string will contain additional characters following the actual name. Use @ref namelen - * to filter out the actual option name only. - * - */ - const char* name; - - /** - * @brief Pointer to this Option's argument (if any). - * - * NULL if this option has no argument. Do not confuse this with the empty string which - * is a valid argument. - */ - const char* arg; - - /** - * @brief The length of the option @ref name. - * - * Because @ref name points into the actual @c argv string, the option name may be - * followed by more characters (e.g. other short options in the same short option group). - * This value is the number of bytes (not characters!) that are part of the actual name. - * - * For a short option, this length is always 1. For a long option this length is always - * at least 2 if single minus long options are permitted and at least 3 if they are disabled. - * - * @note - * In the pathological case of a minus within a short option group (e.g. @c -xf-z), this - * length is incorrect, because this case will be misinterpreted as a long option and the - * name will therefore extend to the string's 0-terminator or a following '=" character - * if there is one. This is irrelevant for most uses of @ref name and @c namelen. If you - * really need to distinguish the case of a long and a short option, compare @ref name to - * the @c argv pointers. A long option's @c name is always identical to one of them, - * whereas a short option's is never. - */ - int namelen; - - /** - * @brief Returns Descriptor::type of this Option's Descriptor, or 0 if this Option - * is invalid (unused). - * - * Because this method (and last(), too) can be used even on unused Options with desc==0, you can (provided - * you arrange your types properly) switch on type() without testing validity first. - * @code - * enum OptionType { UNUSED=0, DISABLED=0, ENABLED=1 }; - * enum OptionIndex { FOO }; - * const Descriptor usage[] = { - * { FOO, ENABLED, "", "enable-foo", Arg::None, 0 }, - * { FOO, DISABLED, "", "disable-foo", Arg::None, 0 }, - * { 0, 0, 0, 0, 0, 0 } }; - * ... - * switch(options[FOO].last()->type()) // no validity check required! - * { - * case ENABLED: ... - * case DISABLED: ... // UNUSED==DISABLED ! - * } - * @endcode - */ - int type() const - { - return desc == 0 ? 0 : desc->type; - } - - /** - * @brief Returns Descriptor::index of this Option's Descriptor, or -1 if this Option - * is invalid (unused). - */ - int index() const - { - return desc == 0 ? -1 : (int)desc->index; - } - - /** - * @brief Returns the number of times this Option (or others with the same Descriptor::index) - * occurs in the argument vector. - * - * This corresponds to the number of elements in the linked list this Option is part of. - * It doesn't matter on which element you call count(). The return value is always the same. - * - * Use this to implement cumulative options, such as -v, -vv, -vvv for - * different verbosity levels. - * - * Returns 0 when called for an unused/invalid option. - */ - int count() - { - int c = (desc == 0 ? 0 : 1); - Option* p = first(); - while (!p->isLast()) - { - ++c; - p = p->next_; - }; - return c; - } - - /** - * @brief Returns true iff this is the first element of the linked list. - * - * The first element in the linked list is the first option on the command line - * that has the respective Descriptor::index value. - * - * Returns true for an unused/invalid option. - */ - bool isFirst() const - { - return isTagged(prev_); - } - - /** - * @brief Returns true iff this is the last element of the linked list. - * - * The last element in the linked list is the last option on the command line - * that has the respective Descriptor::index value. - * - * Returns true for an unused/invalid option. - */ - bool isLast() const - { - return isTagged(next_); - } - - /** - * @brief Returns a pointer to the first element of the linked list. - * - * Use this when you want the first occurrence of an option on the command line to - * take precedence. Note that this is not the way most programs handle options. - * You should probably be using last() instead. - * - * @note - * This method may be called on an unused/invalid option and will return a pointer to the - * option itself. - */ - Option* first() - { - Option* p = this; - while (!p->isFirst()) - p = p->prev_; - return p; - } - - /** - * @brief Returns a pointer to the last element of the linked list. - * - * Use this when you want the last occurrence of an option on the command line to - * take precedence. This is the most common way of handling conflicting options. - * - * @note - * This method may be called on an unused/invalid option and will return a pointer to the - * option itself. - * - * @par Tip: - * If you have options with opposite meanings (e.g. @c --enable-foo and @c --disable-foo), you - * can assign them the same Descriptor::index to get them into the same list. Distinguish them by - * Descriptor::type and all you have to do is check last()->type() to get - * the state listed last on the command line. - */ - Option* last() - { - return first()->prevwrap(); - } - - /** - * @brief Returns a pointer to the previous element of the linked list or NULL if - * called on first(). - * - * If called on first() this method returns NULL. Otherwise it will return the - * option with the same Descriptor::index that precedes this option on the command - * line. - */ - Option* prev() - { - return isFirst() ? 0 : prev_; - } - - /** - * @brief Returns a pointer to the previous element of the linked list with wrap-around from - * first() to last(). - * - * If called on first() this method returns last(). Otherwise it will return the - * option with the same Descriptor::index that precedes this option on the command - * line. - */ - Option* prevwrap() - { - return untag(prev_); - } - - /** - * @brief Returns a pointer to the next element of the linked list or NULL if called - * on last(). - * - * If called on last() this method returns NULL. Otherwise it will return the - * option with the same Descriptor::index that follows this option on the command - * line. - */ - Option* next() - { - return isLast() ? 0 : next_; - } - - /** - * @brief Returns a pointer to the next element of the linked list with wrap-around from - * last() to first(). - * - * If called on last() this method returns first(). Otherwise it will return the - * option with the same Descriptor::index that follows this option on the command - * line. - */ - Option* nextwrap() - { - return untag(next_); - } - - /** - * @brief Makes @c new_last the new last() by chaining it into the list after last(). - * - * It doesn't matter which element you call append() on. The new element will always - * be appended to last(). - * - * @attention - * @c new_last must not yet be part of a list, or that list will become corrupted, because - * this method does not unchain @c new_last from an existing list. - */ - void append(Option* new_last) - { - Option* p = last(); - Option* f = first(); - p->next_ = new_last; - new_last->prev_ = p; - new_last->next_ = tag(f); - f->prev_ = tag(new_last); - } - - /** - * @brief Casts from Option to const Option* but only if this Option is valid. - * - * If this Option is valid (i.e. @c desc!=NULL), returns this. - * Otherwise returns NULL. This allows testing an Option directly - * in an if-clause to see if it is used: - * @code - * if (options[CREATE]) - * { - * ... - * } - * @endcode - * It also allows you to write loops like this: - * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) - * fname = opt->arg; ... @endcode - */ - operator const Option*() const - { - return desc ? this : 0; - } - - /** - * @brief Casts from Option to Option* but only if this Option is valid. - * - * If this Option is valid (i.e. @c desc!=NULL), returns this. - * Otherwise returns NULL. This allows testing an Option directly - * in an if-clause to see if it is used: - * @code - * if (options[CREATE]) - * { - * ... - * } - * @endcode - * It also allows you to write loops like this: - * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) - * fname = opt->arg; ... @endcode - */ - operator Option*() - { - return desc ? this : 0; - } - - /** - * @brief Creates a new Option that is a one-element linked list and has NULL - * @ref desc, @ref name, @ref arg and @ref namelen. - */ - Option() : - desc(0), name(0), arg(0), namelen(0) - { - prev_ = tag(this); - next_ = tag(this); - } - - /** - * @brief Creates a new Option that is a one-element linked list and has the given - * values for @ref desc, @ref name and @ref arg. - * - * If @c name_ points at a character other than '-' it will be assumed to refer to a - * short option and @ref namelen will be set to 1. Otherwise the length will extend to - * the first '=' character or the string's 0-terminator. - */ - Option(const Descriptor* desc_, const char* name_, const char* arg_) - { - init(desc_, name_, arg_); - } - - /** - * @brief Makes @c *this a copy of @c orig except for the linked list pointers. - * - * After this operation @c *this will be a one-element linked list. - */ - void operator=(const Option& orig) - { - init(orig.desc, orig.name, orig.arg); - } - - /** - * @brief Makes @c *this a copy of @c orig except for the linked list pointers. - * - * After this operation @c *this will be a one-element linked list. - */ - Option(const Option& orig) - { - init(orig.desc, orig.name, orig.arg); - } - -private: - /** - * @internal - * @brief Sets the fields of this Option to the given values (extracting @c name if necessary). - * - * If @c name_ points at a character other than '-' it will be assumed to refer to a - * short option and @ref namelen will be set to 1. Otherwise the length will extend to - * the first '=' character or the string's 0-terminator. - */ - void init(const Descriptor* desc_, const char* name_, const char* arg_) - { - desc = desc_; - name = name_; - arg = arg_; - prev_ = tag(this); - next_ = tag(this); - namelen = 0; - if (name == 0) - return; - namelen = 1; - if (name[0] != '-') - return; - while (name[namelen] != 0 && name[namelen] != '=') - ++namelen; - } - - static Option* tag(Option* ptr) - { - return (Option*) ((unsigned long long) ptr | 1); - } - - static Option* untag(Option* ptr) - { - return (Option*) ((unsigned long long) ptr & ~1ull); - } - - static bool isTagged(Option* ptr) - { - return ((unsigned long long) ptr & 1); - } -}; - -/** - * @brief Functions for checking the validity of option arguments. - * - * @copydetails CheckArg - * - * The following example code - * can serve as starting place for writing your own more complex CheckArg functions: - * @code - * struct Arg: public option::Arg - * { - * static void printError(const char* msg1, const option::Option& opt, const char* msg2) - * { - * fprintf(stderr, "ERROR: %s", msg1); - * fwrite(opt.name, opt.namelen, 1, stderr); - * fprintf(stderr, "%s", msg2); - * } - * - * static option::ArgStatus Unknown(const option::Option& option, bool msg) - * { - * if (msg) printError("Unknown option '", option, "'\n"); - * return option::ARG_ILLEGAL; - * } - * - * static option::ArgStatus Required(const option::Option& option, bool msg) - * { - * if (option.arg != 0) - * return option::ARG_OK; - * - * if (msg) printError("Option '", option, "' requires an argument\n"); - * return option::ARG_ILLEGAL; - * } - * - * static option::ArgStatus NonEmpty(const option::Option& option, bool msg) - * { - * if (option.arg != 0 && option.arg[0] != 0) - * return option::ARG_OK; - * - * if (msg) printError("Option '", option, "' requires a non-empty argument\n"); - * return option::ARG_ILLEGAL; - * } - * - * static option::ArgStatus Numeric(const option::Option& option, bool msg) - * { - * char* endptr = 0; - * if (option.arg != 0 && strtol(option.arg, &endptr, 10)){}; - * if (endptr != option.arg && *endptr == 0) - * return option::ARG_OK; - * - * if (msg) printError("Option '", option, "' requires a numeric argument\n"); - * return option::ARG_ILLEGAL; - * } - * }; - * @endcode - */ -struct Arg -{ - //! @brief For options that don't take an argument: Returns ARG_NONE. - static ArgStatus None(const Option&, bool) - { - return ARG_NONE; - } - - //! @brief Returns ARG_OK if the argument is attached and ARG_IGNORE otherwise. - static ArgStatus Optional(const Option& option, bool) - { - if (option.arg && option.name[option.namelen] != 0) - return ARG_OK; - else - return ARG_IGNORE; - } -}; - -/** - * @brief Determines the minimum lengths of the buffer and options arrays used for Parser. - * - * Because Parser doesn't use dynamic memory its output arrays have to be pre-allocated. - * If you don't want to use fixed size arrays (which may turn out too small, causing - * command line arguments to be dropped), you can use Stats to determine the correct sizes. - * Stats work cumulative. You can first pass in your default options and then the real - * options and afterwards the counts will reflect the union. - */ -struct Stats -{ - /** - * @brief Number of elements needed for a @c buffer[] array to be used for - * @ref Parser::parse() "parsing" the same argument vectors that were fed - * into this Stats object. - * - * @note - * This number is always 1 greater than the actual number needed, to give - * you a sentinel element. - */ - unsigned buffer_max; - - /** - * @brief Number of elements needed for an @c options[] array to be used for - * @ref Parser::parse() "parsing" the same argument vectors that were fed - * into this Stats object. - * - * @note - * @li This number is always 1 greater than the actual number needed, to give - * you a sentinel element. - * @li This number depends only on the @c usage, not the argument vectors, because - * the @c options array needs exactly one slot for each possible Descriptor::index. - */ - unsigned options_max; - - /** - * @brief Creates a Stats object with counts set to 1 (for the sentinel element). - */ - Stats() : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel - { - } - - /** - * @brief Creates a new Stats object and immediately updates it for the - * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv, - * if you just want to update @ref options_max. - * - * @note - * The calls to Stats methods must match the later calls to Parser methods. - * See Parser::parse() for the meaning of the arguments. - */ - Stats(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel - { - add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt); - } - - //! @brief Stats(...) with non-const argv. - Stats(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel - { - add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); - } - - //! @brief POSIX Stats(...) (gnu==false). - Stats(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel - { - add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); - } - - //! @brief POSIX Stats(...) (gnu==false) with non-const argv. - Stats(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel - { - add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); - } - - /** - * @brief Updates this Stats object for the - * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv, - * if you just want to update @ref options_max. - * - * @note - * The calls to Stats methods must match the later calls to Parser methods. - * See Parser::parse() for the meaning of the arguments. - */ - void add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false); - - //! @brief add() with non-const argv. - void add(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) - { - add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); - } - - //! @brief POSIX add() (gnu==false). - void add(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) - { - add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); - } - - //! @brief POSIX add() (gnu==false) with non-const argv. - void add(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) - { - add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); - } -private: - class CountOptionsAction; -}; - -/** - * @brief Checks argument vectors for validity and parses them into data - * structures that are easier to work with. - * - * @par Example: - * @code - * int main(int argc, char* argv[]) - * { - * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present - * option::Stats stats(usage, argc, argv); - * option::Option options[stats.options_max], buffer[stats.buffer_max]; - * option::Parser parse(usage, argc, argv, options, buffer); - * - * if (parse.error()) - * return 1; - * - * if (options[HELP]) - * ... - * @endcode - */ -class Parser -{ - int op_count; //!< @internal @brief see optionsCount() - int nonop_count; //!< @internal @brief see nonOptionsCount() - const char** nonop_args; //!< @internal @brief see nonOptions() - bool err; //!< @internal @brief see error() -public: - - /** - * @brief Creates a new Parser. - */ - Parser() : - op_count(0), nonop_count(0), nonop_args(0), err(false) - { - } - - /** - * @brief Creates a new Parser and immediately parses the given argument vector. - * @copydetails parse() - */ - Parser(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) - { - parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); - } - - //! @brief Parser(...) with non-const argv. - Parser(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) - { - parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); - } - - //! @brief POSIX Parser(...) (gnu==false). - Parser(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) - { - parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); - } - - //! @brief POSIX Parser(...) (gnu==false) with non-const argv. - Parser(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) - { - parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); - } - - /** - * @brief Parses the given argument vector. - * - * @param gnu if true, parse() will not stop at the first non-option argument. Instead it will - * reorder arguments so that all non-options are at the end. This is the default behaviour - * of GNU getopt() but is not conforming to POSIX. @n - * Note, that once the argument vector has been reordered, the @c gnu flag will have - * no further effect on this argument vector. So it is enough to pass @c gnu==true when - * creating Stats. - * @param usage Array of Descriptor objects that describe the options to support. The last entry - * of this array must have 0 in all fields. - * @param argc The number of elements from @c argv that are to be parsed. If you pass -1, the number - * will be determined automatically. In that case the @c argv list must end with a NULL - * pointer. - * @param argv The arguments to be parsed. If you pass -1 as @c argc the last pointer in the @c argv - * list must be NULL to mark the end. - * @param options Each entry is the first element of a linked list of Options. Each new option - * that is parsed will be appended to the list specified by that Option's - * Descriptor::index. If an entry is not yet used (i.e. the Option is invalid), - * it will be replaced rather than appended to. @n - * The minimum length of this array is the greatest Descriptor::index value that - * occurs in @c usage @e PLUS ONE. - * @param buffer Each argument that is successfully parsed (including unknown arguments, if they - * have a Descriptor whose CheckArg does not return @ref ARG_ILLEGAL) will be stored in this - * array. parse() scans the array for the first invalid entry and begins writing at that - * index. You can pass @c bufmax to limit the number of options stored. - * @param min_abbr_len Passing a value min_abbr_len > 0 enables abbreviated long - * options. The parser will match a prefix of a long option as if it was - * the full long option (e.g. @c --foob=10 will be interpreted as if it was - * @c --foobar=10 ), as long as the prefix has at least @c min_abbr_len characters - * (not counting the @c -- ) and is unambiguous. - * @n Be careful if combining @c min_abbr_len=1 with @c single_minus_longopt=true - * because the ambiguity check does not consider short options and abbreviated - * single minus long options will take precedence over short options. - * @param single_minus_longopt Passing @c true for this option allows long options to begin with - * a single minus. The double minus form will still be recognized. Note that - * single minus long options take precedence over short options and short option - * groups. E.g. @c -file would be interpreted as @c --file and not as - * -f -i -l -e (assuming a long option named @c "file" exists). - * @param bufmax The greatest index in the @c buffer[] array that parse() will write to is - * @c bufmax-1. If there are more options, they will be processed (in particular - * their CheckArg will be called) but not stored. @n - * If you used Stats::buffer_max to dimension this array, you can pass - * -1 (or not pass @c bufmax at all) which tells parse() that the buffer is - * "large enough". - * @attention - * Remember that @c options and @c buffer store Option @e objects, not pointers. Therefore it - * is not possible for the same object to be in both arrays. For those options that are found in - * both @c buffer[] and @c options[] the respective objects are independent copies. And only the - * objects in @c options[] are properly linked via Option::next() and Option::prev(). - * You can iterate over @c buffer[] to - * process all options in the order they appear in the argument vector, but if you want access to - * the other Options with the same Descriptor::index, then you @e must access the linked list via - * @c options[]. You can get the linked list in options from a buffer object via something like - * @c options[buffer[i].index()]. - */ - void parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1); - - //! @brief parse() with non-const argv. - void parse(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) - { - parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); - } - - //! @brief POSIX parse() (gnu==false). - void parse(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) - { - parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); - } - - //! @brief POSIX parse() (gnu==false) with non-const argv. - void parse(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) - { - parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); - } - - /** - * @brief Returns the number of valid Option objects in @c buffer[]. - * - * @note - * @li The returned value always reflects the number of Options in the buffer[] array used for - * the most recent call to parse(). - * @li The count (and the buffer[]) includes unknown options if they are collected - * (see Descriptor::longopt). - */ - int optionsCount() - { - return op_count; - } - - /** - * @brief Returns the number of non-option arguments that remained at the end of the - * most recent parse() that actually encountered non-option arguments. - * - * @note - * A parse() that does not encounter non-option arguments will leave this value - * as well as nonOptions() undisturbed. This means you can feed the Parser a - * default argument vector that contains non-option arguments (e.g. a default filename). - * Then you feed it the actual arguments from the user. If the user has supplied at - * least one non-option argument, all of the non-option arguments from the default - * disappear and are replaced by the user's non-option arguments. However, if the - * user does not supply any non-option arguments the defaults will still be in - * effect. - */ - int nonOptionsCount() - { - return nonop_count; - } - - /** - * @brief Returns a pointer to an array of non-option arguments (only valid - * if nonOptionsCount() >0 ). - * - * @note - * @li parse() does not copy arguments, so this pointer points into the actual argument - * vector as passed to parse(). - * @li As explained at nonOptionsCount() this pointer is only changed by parse() calls - * that actually encounter non-option arguments. A parse() call that encounters only - * options, will not change nonOptions(). - */ - const char** nonOptions() - { - return nonop_args; - } - - /** - * @brief Returns nonOptions()[i] (@e without checking if i is in range!). - */ - const char* nonOption(int i) - { - return nonOptions()[i]; - } - - /** - * @brief Returns @c true if an unrecoverable error occurred while parsing options. - * - * An illegal argument to an option (i.e. CheckArg returns @ref ARG_ILLEGAL) is an - * unrecoverable error that aborts the parse. Unknown options are only an error if - * their CheckArg function returns @ref ARG_ILLEGAL. Otherwise they are collected. - * In that case if you want to exit the program if either an illegal argument - * or an unknown option has been passed, use code like this - * - * @code - * if (parser.error() || options[UNKNOWN]) - * exit(1); - * @endcode - * - */ - bool error() - { - return err; - } - -private: - friend struct Stats; - class StoreOptionAction; - struct Action; - - /** - * @internal - * @brief This is the core function that does all the parsing. - * @retval false iff an unrecoverable error occurred. - */ - static bool workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, - bool single_minus_longopt, bool print_errors, int min_abbr_len); - - /** - * @internal - * @brief Returns true iff @c st1 is a prefix of @c st2 and - * in case @c st2 is longer than @c st1, then - * the first additional character is '='. - * - * @par Examples: - * @code - * streq("foo", "foo=bar") == true - * streq("foo", "foobar") == false - * streq("foo", "foo") == true - * streq("foo=bar", "foo") == false - * @endcode - */ - static bool streq(const char* st1, const char* st2) - { - while (*st1 != 0) - if (*st1++ != *st2++) - return false; - return (*st2 == 0 || *st2 == '='); - } - - /** - * @internal - * @brief Like streq() but handles abbreviations. - * - * Returns true iff @c st1 and @c st2 have a common - * prefix with the following properties: - * @li (if min > 0) its length is at least @c min characters or the same length as @c st1 (whichever is smaller). - * @li (if min <= 0) its length is the same as that of @c st1 - * @li within @c st2 the character following the common prefix is either '=' or end-of-string. - * - * Examples: - * @code - * streqabbr("foo", "foo=bar",) == true - * streqabbr("foo", "fo=bar" , 2) == true - * streqabbr("foo", "fo" , 2) == true - * streqabbr("foo", "fo" , 0) == false - * streqabbr("foo", "f=bar" , 2) == false - * streqabbr("foo", "f" , 2) == false - * streqabbr("fo" , "foo=bar",) == false - * streqabbr("foo", "foobar" ,) == false - * streqabbr("foo", "fobar" ,) == false - * streqabbr("foo", "foo" ,) == true - * @endcode - */ - static bool streqabbr(const char* st1, const char* st2, long long min) - { - const char* st1start = st1; - while (*st1 != 0 && (*st1 == *st2)) - { - ++st1; - ++st2; - } - - return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '='); - } - - /** - * @internal - * @brief Returns true iff character @c ch is contained in the string @c st. - * - * Returns @c true for @c ch==0 . - */ - static bool instr(char ch, const char* st) - { - while (*st != 0 && *st != ch) - ++st; - return *st == ch; - } - - /** - * @internal - * @brief Rotates args[-count],...,args[-1],args[0] to become - * args[0],args[-count],...,args[-1]. - */ - static void shift(const char** args, int count) - { - for (int i = 0; i > -count; --i) - { - const char* temp = args[i]; - args[i] = args[i - 1]; - args[i - 1] = temp; - } - } -}; - -/** - * @internal - * @brief Interface for actions Parser::workhorse() should perform for each Option it - * parses. - */ -struct Parser::Action -{ - /** - * @brief Called by Parser::workhorse() for each Option that has been successfully - * parsed (including unknown - * options if they have a Descriptor whose Descriptor::check_arg does not return - * @ref ARG_ILLEGAL. - * - * Returns @c false iff a fatal error has occured and the parse should be aborted. - */ - virtual bool perform(Option&) - { - return true; - } - - /** - * @brief Called by Parser::workhorse() after finishing the parse. - * @param numargs the number of non-option arguments remaining - * @param args pointer to the first remaining non-option argument (if numargs > 0). - * - * @return - * @c false iff a fatal error has occurred. - */ - virtual bool finished(int numargs, const char** args) - { - (void) numargs; - (void) args; - return true; - } -}; - -/** - * @internal - * @brief An Action to pass to Parser::workhorse() that will increment a counter for - * each parsed Option. - */ -class Stats::CountOptionsAction: public Parser::Action -{ - unsigned* buffer_max; -public: - /** - * Creates a new CountOptionsAction that will increase @c *buffer_max_ for each - * parsed Option. - */ - CountOptionsAction(unsigned* buffer_max_) : - buffer_max(buffer_max_) - { - } - - bool perform(Option&) - { - if (*buffer_max == 0x7fffffff) - return false; // overflow protection: don't accept number of options that doesn't fit signed int - ++*buffer_max; - return true; - } -}; - -/** - * @internal - * @brief An Action to pass to Parser::workhorse() that will store each parsed Option in - * appropriate arrays (see Parser::parse()). - */ -class Parser::StoreOptionAction: public Parser::Action -{ - Parser& parser; - Option* options; - Option* buffer; - int bufmax; //! Number of slots in @c buffer. @c -1 means "large enough". -public: - /** - * @brief Creates a new StoreOption action. - * @param parser_ the parser whose op_count should be updated. - * @param options_ each Option @c o is chained into the linked list @c options_[o.desc->index] - * @param buffer_ each Option is appended to this array as long as there's a free slot. - * @param bufmax_ number of slots in @c buffer_. @c -1 means "large enough". - */ - StoreOptionAction(Parser& parser_, Option options_[], Option buffer_[], int bufmax_) : - parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_) - { - // find first empty slot in buffer (if any) - int bufidx = 0; - while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx]) - ++bufidx; - - // set parser's optionCount - parser.op_count = bufidx; - } - - bool perform(Option& option) - { - if (bufmax < 0 || parser.op_count < bufmax) - { - if (parser.op_count == 0x7fffffff) - return false; // overflow protection: don't accept number of options that doesn't fit signed int - - buffer[parser.op_count] = option; - int idx = buffer[parser.op_count].desc->index; - if (options[idx]) - options[idx].append(buffer[parser.op_count]); - else - options[idx] = buffer[parser.op_count]; - ++parser.op_count; - } - return true; // NOTE: an option that is discarded because of a full buffer is not fatal - } - - bool finished(int numargs, const char** args) - { - // only overwrite non-option argument list if there's at least 1 - // new non-option argument. Otherwise we keep the old list. This - // makes it easy to use default non-option arguments. - if (numargs > 0) - { - parser.nonop_count = numargs; - parser.nonop_args = args; - } - - return true; - } -}; - -inline void Parser::parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], - Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax) -{ - StoreOptionAction action(*this, options, buffer, bufmax); - err = !workhorse(gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len); -} - -inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len, - bool single_minus_longopt) -{ - // determine size of options array. This is the greatest index used in the usage + 1 - int i = 0; - while (usage[i].shortopt != 0) - { - if (usage[i].index + 1 >= options_max) - options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel - - ++i; - } - - CountOptionsAction action(&buffer_max); - Parser::workhorse(gnu, usage, argc, argv, action, single_minus_longopt, false, min_abbr_len); -} - -inline bool Parser::workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, - bool single_minus_longopt, bool print_errors, int min_abbr_len) -{ - // protect against NULL pointer - if (args == 0) - numargs = 0; - - int nonops = 0; - - while (numargs != 0 && *args != 0) - { - const char* param = *args; // param can be --long-option, -srto or non-option argument - - // in POSIX mode the first non-option argument terminates the option list - // a lone minus character is a non-option argument - if (param[0] != '-' || param[1] == 0) - { - if (gnu) - { - ++nonops; - ++args; - if (numargs > 0) - --numargs; - continue; - } - else - break; - } - - // -- terminates the option list. The -- itself is skipped. - if (param[1] == '-' && param[2] == 0) - { - shift(args, nonops); - ++args; - if (numargs > 0) - --numargs; - break; - } - - bool handle_short_options; - const char* longopt_name; - if (param[1] == '-') // if --long-option - { - handle_short_options = false; - longopt_name = param + 2; - } - else - { - handle_short_options = true; - longopt_name = param + 1; //for testing a potential -long-option - } - - bool try_single_minus_longopt = single_minus_longopt; - bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid? - - do // loop over short options in group, for long options the body is executed only once - { - int idx; - - const char* optarg; - - /******************** long option **********************/ - if (handle_short_options == false || try_single_minus_longopt) - { - idx = 0; - while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name)) - ++idx; - - if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options - { - int i1 = 0; - while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len)) - ++i1; - if (usage[i1].longopt != 0) - { // now test if the match is unambiguous by checking for another match - int i2 = i1 + 1; - while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len)) - ++i2; - - if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx - idx = i1; - } - } - - // if we found something, disable handle_short_options (only relevant if single_minus_longopt) - if (usage[idx].longopt != 0) - handle_short_options = false; - - try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group - - optarg = longopt_name; - while (*optarg != 0 && *optarg != '=') - ++optarg; - if (*optarg == '=') // attached argument - ++optarg; - else - // possibly detached argument - optarg = (have_more_args ? args[1] : 0); - } - - /************************ short option ***********************************/ - if (handle_short_options) - { - if (*++param == 0) // point at the 1st/next option character - break; // end of short option group - - idx = 0; - while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt)) - ++idx; - - if (param[1] == 0) // if the potential argument is separate - optarg = (have_more_args ? args[1] : 0); - else - // if the potential argument is attached - optarg = param + 1; - } - - const Descriptor* descriptor = &usage[idx]; - - if (descriptor->shortopt == 0) /************** unknown option ********************/ - { - // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options - idx = 0; - while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0)) - ++idx; - descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]); - } - - if (descriptor != 0) - { - Option option(descriptor, param, optarg); - switch (descriptor->check_arg(option, print_errors)) - { - case ARG_ILLEGAL: - return false; // fatal - case ARG_OK: - // skip one element of the argument vector, if it's a separated argument - if (optarg != 0 && have_more_args && optarg == args[1]) - { - shift(args, nonops); - if (numargs > 0) - --numargs; - ++args; - } - - // No further short options are possible after an argument - handle_short_options = false; - - break; - case ARG_IGNORE: - case ARG_NONE: - option.arg = 0; - break; - } - - if (!action.perform(option)) - return false; - } - - } while (handle_short_options); - - shift(args, nonops); - ++args; - if (numargs > 0) - --numargs; - - } // while - - if (numargs > 0 && *args == 0) // It's a bug in the caller if numargs is greater than the actual number - numargs = 0; // of arguments, but as a service to the user we fix this if we spot it. - - if (numargs < 0) // if we don't know the number of remaining non-option arguments - { // we need to count them - numargs = 0; - while (args[numargs] != 0) - ++numargs; - } - - return action.finished(numargs + nonops, args - nonops); -} - -/** - * @internal - * @brief The implementation of option::printUsage(). - */ -struct PrintUsageImplementation -{ - /** - * @internal - * @brief Interface for Functors that write (part of) a string somewhere. - */ - struct IStringWriter - { - /** - * @brief Writes the given number of chars beginning at the given pointer somewhere. - */ - virtual void operator()(const char*, int) - { - } - }; - - /** - * @internal - * @brief Encapsulates a function with signature func(string, size) where - * string can be initialized with a const char* and size with an int. - */ - template - struct FunctionWriter: public IStringWriter - { - Function* write; - - virtual void operator()(const char* str, int size) - { - (*write)(str, size); - } - - FunctionWriter(Function* w) : - write(w) - { - } - }; - - /** - * @internal - * @brief Encapsulates a reference to an object with a write(string, size) - * method like that of @c std::ostream. - */ - template - struct OStreamWriter: public IStringWriter - { - OStream& ostream; - - virtual void operator()(const char* str, int size) - { - ostream.write(str, size); - } - - OStreamWriter(OStream& o) : - ostream(o) - { - } - }; - - /** - * @internal - * @brief Like OStreamWriter but encapsulates a @c const reference, which is - * typically a temporary object of a user class. - */ - template - struct TemporaryWriter: public IStringWriter - { - const Temporary& userstream; - - virtual void operator()(const char* str, int size) - { - userstream.write(str, size); - } - - TemporaryWriter(const Temporary& u) : - userstream(u) - { - } - }; - - /** - * @internal - * @brief Encapsulates a function with the signature func(fd, string, size) (the - * signature of the @c write() system call) - * where fd can be initialized from an int, string from a const char* and size from an int. - */ - template - struct SyscallWriter: public IStringWriter - { - Syscall* write; - int fd; - - virtual void operator()(const char* str, int size) - { - (*write)(fd, str, size); - } - - SyscallWriter(Syscall* w, int f) : - write(w), fd(f) - { - } - }; - - /** - * @internal - * @brief Encapsulates a function with the same signature as @c std::fwrite(). - */ - template - struct StreamWriter: public IStringWriter - { - Function* fwrite; - Stream* stream; - - virtual void operator()(const char* str, int size) - { - (*fwrite)(str, size, 1, stream); - } - - StreamWriter(Function* w, Stream* s) : - fwrite(w), stream(s) - { - } - }; - - /** - * @internal - * @brief Sets i1 = max(i1, i2) - */ - static void upmax(int& i1, int i2) - { - i1 = (i1 >= i2 ? i1 : i2); - } - - /** - * @internal - * @brief Moves the "cursor" to column @c want_x assuming it is currently at column @c x - * and sets @c x=want_x . - * If x > want_x , a line break is output before indenting. - * - * @param write Spaces and possibly a line break are written via this functor to get - * the desired indentation @c want_x . - * @param[in,out] x the current indentation. Set to @c want_x by this method. - * @param want_x the desired indentation. - */ - static void indent(IStringWriter& write, int& x, int want_x) - { - int indent = want_x - x; - if (indent < 0) - { - write("\n", 1); - indent = want_x; - } - - if (indent > 0) - { - char space = ' '; - for (int i = 0; i < indent; ++i) - write(&space, 1); - x = want_x; - } - } - - /** - * @brief Returns true if ch is the unicode code point of a wide character. - * - * @note - * The following character ranges are treated as wide - * @code - * 1100..115F - * 2329..232A (just 2 characters!) - * 2E80..A4C6 except for 303F - * A960..A97C - * AC00..D7FB - * F900..FAFF - * FE10..FE6B - * FF01..FF60 - * FFE0..FFE6 - * 1B000...... - * @endcode - */ - static bool isWideChar(unsigned ch) - { - if (ch == 0x303F) - return false; - - return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6) - || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF) - || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6) - || (0x1B000 <= ch)); - } - - /** - * @internal - * @brief Splits a @c Descriptor[] array into tables, rows, lines and columns and - * iterates over these components. - * - * The top-level organizational unit is the @e table. - * A table begins at a Descriptor with @c help!=NULL and extends up to - * a Descriptor with @c help==NULL. - * - * A table consists of @e rows. Due to line-wrapping and explicit breaks - * a row may take multiple lines on screen. Rows within the table are separated - * by \\n. They never cross Descriptor boundaries. This means a row ends either - * at \\n or the 0 at the end of the help string. - * - * A row consists of columns/cells. Columns/cells within a row are separated by \\t. - * Line breaks within a cell are marked by \\v. - * - * Rows in the same table need not have the same number of columns/cells. The - * extreme case are interjections, which are rows that contain neither \\t nor \\v. - * These are NOT treated specially by LinePartIterator, but they are treated - * specially by printUsage(). - * - * LinePartIterator iterates through the usage at 3 levels: table, row and part. - * Tables and rows are as described above. A @e part is a line within a cell. - * LinePartIterator iterates through 1st parts of all cells, then through the 2nd - * parts of all cells (if any),... @n - * Example: The row "1 \v 3 \t 2 \v 4" has 2 cells/columns and 4 parts. - * The parts will be returned in the order 1, 2, 3, 4. - * - * It is possible that some cells have fewer parts than others. In this case - * LinePartIterator will "fill up" these cells with 0-length parts. IOW, LinePartIterator - * always returns the same number of parts for each column. Note that this is different - * from the way rows and columns are handled. LinePartIterator does @e not guarantee that - * the same number of columns will be returned for each row. - * - */ - class LinePartIterator - { - const Descriptor* tablestart; //!< The 1st descriptor of the current table. - const Descriptor* rowdesc; //!< The Descriptor that contains the current row. - const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help. - const char* ptr; //!< Ptr to current part within the current row. - int col; //!< Index of current column. - int len; //!< Length of the current part (that ptr points at) in BYTES - int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account). - int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs. - int line_in_block; //!< Line index within the current cell of the current part. - int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration. - bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell. - - /** - * @brief Determines the byte and character lengths of the part at @ref ptr and - * stores them in @ref len and @ref screenlen respectively. - */ - void update_length() - { - screenlen = 0; - for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len) - { - ++screenlen; - unsigned ch = (unsigned char) ptr[len]; - if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte - { - // int __builtin_clz (unsigned int x) - // Returns the number of leading 0-bits in x, starting at the most significant bit - unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); - ch = ch & mask; // mask out length bits, we don't verify their correctness - while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte - { - ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code - ++len; - } - // ch is the decoded unicode code point - if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case - ++screenlen; - } - } - } - - public: - //! @brief Creates an iterator for @c usage. - LinePartIterator(const Descriptor usage[]) : - tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0), - target_line_in_block(0), hit_target_line(true) - { - } - - /** - * @brief Moves iteration to the next table (if any). Has to be called once on a new - * LinePartIterator to move to the 1st table. - * @retval false if moving to next table failed because no further table exists. - */ - bool nextTable() - { - // If this is NOT the first time nextTable() is called after the constructor, - // then skip to the next table break (i.e. a Descriptor with help == 0) - if (rowdesc != 0) - { - while (tablestart->help != 0 && tablestart->shortopt != 0) - ++tablestart; - } - - // Find the next table after the break (if any) - while (tablestart->help == 0 && tablestart->shortopt != 0) - ++tablestart; - - restartTable(); - return rowstart != 0; - } - - /** - * @brief Reset iteration to the beginning of the current table. - */ - void restartTable() - { - rowdesc = tablestart; - rowstart = tablestart->help; - ptr = 0; - } - - /** - * @brief Moves iteration to the next row (if any). Has to be called once after each call to - * @ref nextTable() to move to the 1st row of the table. - * @retval false if moving to next row failed because no further row exists. - */ - bool nextRow() - { - if (ptr == 0) - { - restartRow(); - return rowstart != 0; - } - - while (*ptr != 0 && *ptr != '\n') - ++ptr; - - if (*ptr == 0) - { - if ((rowdesc + 1)->help == 0) // table break - return false; - - ++rowdesc; - rowstart = rowdesc->help; - } - else // if (*ptr == '\n') - { - rowstart = ptr + 1; - } - - restartRow(); - return true; - } - - /** - * @brief Reset iteration to the beginning of the current row. - */ - void restartRow() - { - ptr = rowstart; - col = -1; - len = 0; - screenlen = 0; - max_line_in_block = 0; - line_in_block = 0; - target_line_in_block = 0; - hit_target_line = true; - } - - /** - * @brief Moves iteration to the next part (if any). Has to be called once after each call to - * @ref nextRow() to move to the 1st part of the row. - * @retval false if moving to next part failed because no further part exists. - * - * See @ref LinePartIterator for details about the iteration. - */ - bool next() - { - if (ptr == 0) - return false; - - if (col == -1) - { - col = 0; - update_length(); - return true; - } - - ptr += len; - while (true) - { - switch (*ptr) - { - case '\v': - upmax(max_line_in_block, ++line_in_block); - ++ptr; - break; - case '\t': - if (!hit_target_line) // if previous column did not have the targetline - { // then "insert" a 0-length part - update_length(); - hit_target_line = true; - return true; - } - - hit_target_line = false; - line_in_block = 0; - ++col; - ++ptr; - break; - case 0: - case '\n': - if (!hit_target_line) // if previous column did not have the targetline - { // then "insert" a 0-length part - update_length(); - hit_target_line = true; - return true; - } - - if (++target_line_in_block > max_line_in_block) - { - update_length(); - return false; - } - - hit_target_line = false; - line_in_block = 0; - col = 0; - ptr = rowstart; - continue; - default: - ++ptr; - continue; - } // switch - - if (line_in_block == target_line_in_block) - { - update_length(); - hit_target_line = true; - return true; - } - } // while - } - - /** - * @brief Returns the index (counting from 0) of the column in which - * the part pointed to by @ref data() is located. - */ - int column() - { - return col; - } - - /** - * @brief Returns the index (counting from 0) of the line within the current column - * this part belongs to. - */ - int line() - { - return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line - } - - /** - * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters). - */ - int length() - { - return len; - } - - /** - * @brief Returns the width in screen columns of the part pointed to by @ref data(). - * Takes multi-byte UTF-8 sequences and wide characters into account. - */ - int screenLength() - { - return screenlen; - } - - /** - * @brief Returns the current part of the iteration. - */ - const char* data() - { - return ptr; - } - }; - - /** - * @internal - * @brief Takes input and line wraps it, writing out one line at a time so that - * it can be interleaved with output from other columns. - * - * The LineWrapper is used to handle the last column of each table as well as interjections. - * The LineWrapper is called once for each line of output. If the data given to it fits - * into the designated width of the last column it is simply written out. If there - * is too much data, an appropriate split point is located and only the data up to this - * split point is written out. The rest of the data is queued for the next line. - * That way the last column can be line wrapped and interleaved with data from - * other columns. The following example makes this clearer: - * @code - * Column 1,1 Column 2,1 This is a long text - * Column 1,2 Column 2,2 that does not fit into - * a single line. - * @endcode - * - * The difficulty in producing this output is that the whole string - * "This is a long text that does not fit into a single line" is the - * 1st and only part of column 3. In order to produce the above - * output the string must be output piecemeal, interleaved with - * the data from the other columns. - */ - class LineWrapper - { - static const int bufmask = 15; //!< Must be a power of 2 minus 1. - /** - * @brief Ring buffer for length component of pair (data, length). - */ - int lenbuf[bufmask + 1]; - /** - * @brief Ring buffer for data component of pair (data, length). - */ - const char* datbuf[bufmask + 1]; - /** - * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer - * assumes that the indentation has already been written when @ref process() - * is called, so this value is only used when a buffer flush requires writing - * additional lines of output. - */ - int x; - /** - * @brief The width of the column to line wrap. - */ - int width; - int head; //!< @brief index for next write - int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read) - - /** - * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to - * free up space. The contract of process() says that only 1 line is output. So - * this variable is used to track whether something has output a line. It is - * reset at the beginning of process() and checked at the end to decide if - * output has already occurred or is still needed. - */ - bool wrote_something; - - bool buf_empty() - { - return ((tail + 1) & bufmask) == head; - } - - bool buf_full() - { - return tail == head; - } - - void buf_store(const char* data, int len) - { - lenbuf[head] = len; - datbuf[head] = data; - head = (head + 1) & bufmask; - } - - //! @brief Call BEFORE reading ...buf[tail]. - void buf_next() - { - tail = (tail + 1) & bufmask; - } - - /** - * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line - * is flushed out of the buffer into @c write. - */ - void output(IStringWriter& write, const char* data, int len) - { - if (buf_full()) - write_one_line(write); - - buf_store(data, len); - } - - /** - * @brief Writes a single line of output from the buffer to @c write. - */ - void write_one_line(IStringWriter& write) - { - if (wrote_something) // if we already wrote something, we need to start a new line - { - write("\n", 1); - int _ = 0; - indent(write, _, x); - } - - if (!buf_empty()) - { - buf_next(); - write(datbuf[tail], lenbuf[tail]); - } - - wrote_something = true; - } - public: - - /** - * @brief Writes out all remaining data from the LineWrapper using @c write. - * Unlike @ref process() this method indents all lines including the first and - * will output a \\n at the end (but only if something has been written). - */ - void flush(IStringWriter& write) - { - if (buf_empty()) - return; - int _ = 0; - indent(write, _, x); - wrote_something = false; - while (!buf_empty()) - write_one_line(write); - write("\n", 1); - } - - /** - * @brief Process, wrap and output the next piece of data. - * - * process() will output at least one line of output. This is not necessarily - * the @c data passed in. It may be data queued from a prior call to process(). - * If the internal buffer is full, more than 1 line will be output. - * - * process() assumes that the a proper amount of indentation has already been - * output. It won't write any further indentation before the 1st line. If - * more than 1 line is written due to buffer constraints, the lines following - * the first will be indented by this method, though. - * - * No \\n is written by this method after the last line that is written. - * - * @param write where to write the data. - * @param data the new chunk of data to write. - * @param len the length of the chunk of data to write. - */ - void process(IStringWriter& write, const char* data, int len) - { - wrote_something = false; - - while (len > 0) - { - if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes) - { - output(write, data, len); - len = 0; - } - else // if (len > width) it's possible (but not guaranteed) that utf8len > width - { - int utf8width = 0; - int maxi = 0; - while (maxi < len && utf8width < width) - { - int charbytes = 1; - unsigned ch = (unsigned char) data[maxi]; - if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte - { - // int __builtin_clz (unsigned int x) - // Returns the number of leading 0-bits in x, starting at the most significant bit - unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); - ch = ch & mask; // mask out length bits, we don't verify their correctness - while ((maxi + charbytes < len) && // - (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte - { - ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code - ++charbytes; - } - // ch is the decoded unicode code point - if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case - { - if (utf8width + 2 > width) - break; - ++utf8width; - } - } - ++utf8width; - maxi += charbytes; - } - - // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits - // onto the 1st line. If maxi == len, all characters fit on the line. - - if (maxi == len) - { - output(write, data, len); - len = 0; - } - else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line - { - int i; - for (i = maxi; i >= 0; --i) - if (data[i] == ' ') - break; - - if (i >= 0) - { - output(write, data, i); - data += i + 1; - len -= i + 1; - } - else // did not find a space to split at => split before data[maxi] - { // data[maxi] is always the beginning of a character, never a continuation byte - output(write, data, maxi); - data += maxi; - len -= maxi; - } - } - } - } - if (!wrote_something) // if we didn't already write something to make space in the buffer - write_one_line(write); // write at most one line of actual output - } - - /** - * @brief Constructs a LineWrapper that wraps its output to fit into - * screen columns @c x1 (incl.) to @c x2 (excl.). - * - * @c x1 gives the indentation LineWrapper uses if it needs to indent. - */ - LineWrapper(int x1, int x2) : - x(x1), width(x2 - x1), head(0), tail(bufmask) - { - if (width < 2) // because of wide characters we need at least width 2 or the code breaks - width = 2; - } - }; - - /** - * @internal - * @brief This is the implementation that is shared between all printUsage() templates. - * Because all printUsage() templates share this implementation, there is no template bloat. - */ - static void printUsage(IStringWriter& write, const Descriptor usage[], int width = 80, // - int last_column_min_percent = 50, int last_column_own_line_max_percent = 75) - { - if (width < 1) // protect against nonsense values - width = 80; - - if (width > 10000) // protect against overflow in the following computation - width = 10000; - - int last_column_min_width = ((width * last_column_min_percent) + 50) / 100; - int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100; - if (last_column_own_line_max_width == 0) - last_column_own_line_max_width = 1; - - LinePartIterator part(usage); - while (part.nextTable()) - { - - /***************** Determine column widths *******************************/ - - const int maxcolumns = 8; // 8 columns are enough for everyone - int col_width[maxcolumns]; - int lastcolumn; - int leftwidth; - int overlong_column_threshold = 10000; - do - { - lastcolumn = 0; - for (int i = 0; i < maxcolumns; ++i) - col_width[i] = 0; - - part.restartTable(); - while (part.nextRow()) - { - while (part.next()) - { - if (part.column() < maxcolumns) - { - upmax(lastcolumn, part.column()); - if (part.screenLength() < overlong_column_threshold) - // We don't let rows that don't use table separators (\t or \v) influence - // the width of column 0. This allows the user to interject section headers - // or explanatory paragraphs that do not participate in the table layout. - if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' - || part.data()[part.length()] == '\v') - upmax(col_width[part.column()], part.screenLength()); - } - } - } - - /* - * If the last column doesn't fit on the same - * line as the other columns, we can fix that by starting it on its own line. - * However we can't do this for any of the columns 0..lastcolumn-1. - * If their sum exceeds the maximum width we try to fix this by iteratively - * ignoring the widest line parts in the width determination until - * we arrive at a series of column widths that fit into one line. - * The result is a layout where everything is nicely formatted - * except for a few overlong fragments. - * */ - - leftwidth = 0; - overlong_column_threshold = 0; - for (int i = 0; i < lastcolumn; ++i) - { - leftwidth += col_width[i]; - upmax(overlong_column_threshold, col_width[i]); - } - - } while (leftwidth > width); - - /**************** Determine tab stops and last column handling **********************/ - - int tabstop[maxcolumns]; - tabstop[0] = 0; - for (int i = 1; i < maxcolumns; ++i) - tabstop[i] = tabstop[i - 1] + col_width[i - 1]; - - int rightwidth = width - tabstop[lastcolumn]; - bool print_last_column_on_own_line = false; - if (rightwidth < last_column_min_width && // if we don't have the minimum requested width for the last column - ( col_width[lastcolumn] == 0 || // and all last columns are > overlong_column_threshold - rightwidth < col_width[lastcolumn] // or there is at least one last column that requires more than the space available - ) - ) - { - print_last_column_on_own_line = true; - rightwidth = last_column_own_line_max_width; - } - - // If lastcolumn == 0 we must disable print_last_column_on_own_line because - // otherwise 2 copies of the last (and only) column would be output. - // Actually this is just defensive programming. It is currently not - // possible that lastcolumn==0 and print_last_column_on_own_line==true - // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 => - // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes - // a bullshit value >100 for last_column_min_percent) => the above if condition - // is false => print_last_column_on_own_line==false - if (lastcolumn == 0) - print_last_column_on_own_line = false; - - LineWrapper lastColumnLineWrapper(width - rightwidth, width); - LineWrapper interjectionLineWrapper(0, width); - - part.restartTable(); - - /***************** Print out all rows of the table *************************************/ - - while (part.nextRow()) - { - int x = -1; - while (part.next()) - { - if (part.column() > lastcolumn) - continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1) - - if (part.column() == 0) - { - if (x >= 0) - write("\n", 1); - x = 0; - } - - indent(write, x, tabstop[part.column()]); - - if ((part.column() < lastcolumn) - && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' - || part.data()[part.length()] == '\v')) - { - write(part.data(), part.length()); - x += part.screenLength(); - } - else // either part.column() == lastcolumn or we are in the special case of - // an interjection that doesn't contain \v or \t - { - // NOTE: This code block is not necessarily executed for - // each line, because some rows may have fewer columns. - - LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper; - - if (!print_last_column_on_own_line || part.column() != lastcolumn) - lineWrapper.process(write, part.data(), part.length()); - } - } // while - - if (print_last_column_on_own_line) - { - part.restartRow(); - while (part.next()) - { - if (part.column() == lastcolumn) - { - write("\n", 1); - int _ = 0; - indent(write, _, width - rightwidth); - lastColumnLineWrapper.process(write, part.data(), part.length()); - } - } - } - - write("\n", 1); - lastColumnLineWrapper.flush(write); - interjectionLineWrapper.flush(write); - } - } - } - -} -; - -/** - * @brief Outputs a nicely formatted usage string with support for multi-column formatting - * and line-wrapping. - * - * printUsage() takes the @c help texts of a Descriptor[] array and formats them into - * a usage message, wrapping lines to achieve the desired output width. - * - * Table formatting: - * - * Aside from plain strings which are simply line-wrapped, the usage may contain tables. Tables - * are used to align elements in the output. - * - * @code - * // Without a table. The explanatory texts are not aligned. - * -c, --create |Creates something. - * -k, --kill |Destroys something. - * - * // With table formatting. The explanatory texts are aligned. - * -c, --create |Creates something. - * -k, --kill |Destroys something. - * @endcode - * - * Table formatting removes the need to pad help texts manually with spaces to achieve - * alignment. To create a table, simply insert \\t (tab) characters to separate the cells - * within a row. - * - * @code - * const option::Descriptor usage[] = { - * {..., "-c, --create \tCreates something." }, - * {..., "-k, --kill \tDestroys something." }, ... - * @endcode - * - * Note that you must include the minimum amount of space desired between cells yourself. - * Table formatting will insert further spaces as needed to achieve alignment. - * - * You can insert line breaks within cells by using \\v (vertical tab). - * - * @code - * const option::Descriptor usage[] = { - * {..., "-c,\v--create \tCreates\vsomething." }, - * {..., "-k,\v--kill \tDestroys\vsomething." }, ... - * - * // results in - * - * -c, Creates - * --create something. - * -k, Destroys - * --kill something. - * @endcode - * - * You can mix lines that do not use \\t or \\v with those that do. The plain - * lines will not mess up the table layout. Alignment of the table columns will - * be maintained even across these interjections. - * - * @code - * const option::Descriptor usage[] = { - * {..., "-c, --create \tCreates something." }, - * {..., "----------------------------------" }, - * {..., "-k, --kill \tDestroys something." }, ... - * - * // results in - * - * -c, --create Creates something. - * ---------------------------------- - * -k, --kill Destroys something. - * @endcode - * - * You can have multiple tables within the same usage whose columns are - * aligned independently. Simply insert a dummy Descriptor with @c help==0. - * - * @code - * const option::Descriptor usage[] = { - * {..., "Long options:" }, - * {..., "--very-long-option \tDoes something long." }, - * {..., "--ultra-super-mega-long-option \tTakes forever to complete." }, - * {..., 0 }, // ---------- table break ----------- - * {..., "Short options:" }, - * {..., "-s \tShort." }, - * {..., "-q \tQuick." }, ... - * - * // results in - * - * Long options: - * --very-long-option Does something long. - * --ultra-super-mega-long-option Takes forever to complete. - * Short options: - * -s Short. - * -q Quick. - * - * // Without the table break it would be - * - * Long options: - * --very-long-option Does something long. - * --ultra-super-mega-long-option Takes forever to complete. - * Short options: - * -s Short. - * -q Quick. - * @endcode - * - * Output methods: - * - * Because TheLeanMeanC++Option parser is freestanding, you have to provide the means for - * output in the first argument(s) to printUsage(). Because printUsage() is implemented as - * a set of template functions, you have great flexibility in your choice of output - * method. The following example demonstrates typical uses. Anything that's similar enough - * will work. - * - * @code - * #include // write() - * #include // cout - * #include // ostringstream - * #include // fwrite() - * using namespace std; - * - * void my_write(const char* str, int size) { - * fwrite(str, size, 1, stdout); - * } - * - * struct MyWriter { - * void write(const char* buf, size_t size) const { - * fwrite(str, size, 1, stdout); - * } - * }; - * - * struct MyWriteFunctor { - * void operator()(const char* buf, size_t size) { - * fwrite(str, size, 1, stdout); - * } - * }; - * ... - * printUsage(my_write, usage); // custom write function - * printUsage(MyWriter(), usage); // temporary of a custom class - * MyWriter writer; - * printUsage(writer, usage); // custom class object - * MyWriteFunctor wfunctor; - * printUsage(&wfunctor, usage); // custom functor - * printUsage(write, 1, usage); // write() to file descriptor 1 - * printUsage(cout, usage); // an ostream& - * printUsage(fwrite, stdout, usage); // fwrite() to stdout - * ostringstream sstr; - * printUsage(sstr, usage); // an ostringstream& - * - * @endcode - * - * @par Notes: - * @li the @c write() method of a class that is to be passed as a temporary - * as @c MyWriter() is in the example, must be a @c const method, because - * temporary objects are passed as const reference. This only applies to - * temporary objects that are created and destroyed in the same statement. - * If you create an object like @c writer in the example, this restriction - * does not apply. - * @li a functor like @c MyWriteFunctor in the example must be passed as a pointer. - * This differs from the way functors are passed to e.g. the STL algorithms. - * @li All printUsage() templates are tiny wrappers around a shared non-template implementation. - * So there's no penalty for using different versions in the same program. - * @li printUsage() always interprets Descriptor::help as UTF-8 and always produces UTF-8-encoded - * output. If your system uses a different charset, you must do your own conversion. You - * may also need to change the font of the console to see non-ASCII characters properly. - * This is particularly true for Windows. - * @li @b Security @b warning: Do not insert untrusted strings (such as user-supplied arguments) - * into the usage. printUsage() has no protection against malicious UTF-8 sequences. - * - * @param prn The output method to use. See the examples above. - * @param usage the Descriptor[] array whose @c help texts will be formatted. - * @param width the maximum number of characters per output line. Note that this number is - * in actual characters, not bytes. printUsage() supports UTF-8 in @c help and will - * count multi-byte UTF-8 sequences properly. Asian wide characters are counted - * as 2 characters. - * @param last_column_min_percent (0-100) The minimum percentage of @c width that should be available - * for the last column (which typically contains the textual explanation of an option). - * If less space is available, the last column will be printed on its own line, indented - * according to @c last_column_own_line_max_percent. - * @param last_column_own_line_max_percent (0-100) If the last column is printed on its own line due to - * less than @c last_column_min_percent of the width being available, then only - * @c last_column_own_line_max_percent of the extra line(s) will be used for the - * last column's text. This ensures an indentation. See example below. - * - * @code - * // width=20, last_column_min_percent=50 (i.e. last col. min. width=10) - * --3456789 1234567890 - * 1234567890 - * - * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15) - * // last_column_own_line_max_percent=75 - * --3456789 - * 123456789012345 - * 67890 - * - * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15) - * // last_column_own_line_max_percent=33 (i.e. max. 5) - * --3456789 - * 12345 - * 67890 - * 12345 - * 67890 - * @endcode - */ -template -void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) -{ - PrintUsageImplementation::OStreamWriter write(prn); - PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); -} - -template -void printUsage(Function* prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) -{ - PrintUsageImplementation::FunctionWriter write(prn); - PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); -} - -template -void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) -{ - PrintUsageImplementation::TemporaryWriter write(prn); - PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); -} - -template -void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) -{ - PrintUsageImplementation::SyscallWriter write(prn, fd); - PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); -} - -template -void printUsage(Function* prn, Stream* stream, const Descriptor usage[], int width = 80, int last_column_min_percent = - 50, - int last_column_own_line_max_percent = 75) -{ - PrintUsageImplementation::StreamWriter write(prn, stream); - PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); -} - -} -// namespace option - -#endif /* OPTIONPARSER_H_ */ diff --git a/Source/pack.c b/Source/pack.c deleted file mode 100644 index 736085d..0000000 --- a/Source/pack.c +++ /dev/null @@ -1,158 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include - -#include "binio.h" -#include "private.h" - -struct BinIOPackContext { - uint8_t *buffer; - va_list args; -}; - -static void BinIOPack(void *context, int type, int byte_order, int count) -{ - struct BinIOPackContext *ctx = (struct BinIOPackContext*)context; - if (count == -1) { - switch (type) { - case BinIO_TYPE_IGNORE_BYTE: { - ctx->buffer += 1; - } - break; - case BinIO_TYPE_BYTE: { - uint8_t value = va_arg(ctx->args, int); - BinIOConvert1(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); - ctx->buffer += 1; - } - break; - case BinIO_TYPE_INT16: { - uint16_t value = va_arg(ctx->args, int); - BinIOConvert2(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); - ctx->buffer += 2; - } - break; - case BinIO_TYPE_INT32: { - int value = va_arg(ctx->args, int); - BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); - ctx->buffer += 4; - } - break; - case BinIO_TYPE_INT64: { - uint64_t value = va_arg(ctx->args, uint64_t); - BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); - ctx->buffer += 8; - } - break; - case BinIO_TYPE_FLOAT32: { - float32_t value = (float32_t)va_arg(ctx->args, double); - BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); - ctx->buffer += 4; - } - break; - case BinIO_TYPE_FLOAT64: { - float64_t value = va_arg(ctx->args, float64_t); - BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, (const uint8_t *)&value, ctx->buffer, 1); - ctx->buffer += 8; - } - break; - } - } else { - switch (type) { - case BinIO_TYPE_IGNORE_BYTE: - ctx->buffer += 1 * count; - break; - case BinIO_TYPE_BYTE: - BinIOConvert1(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); - ctx->buffer += 1 * count; - break; - case BinIO_TYPE_INT16: - BinIOConvert2(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); - ctx->buffer += 2 * count; - break; - case BinIO_TYPE_INT32: - BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); - ctx->buffer += 4 * count; - break; - case BinIO_TYPE_INT64: - BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); - ctx->buffer += 8 * count; - break; - case BinIO_TYPE_FLOAT32: - BinIOConvert4(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); - ctx->buffer += 4 * count; - break; - case BinIO_TYPE_FLOAT64: - BinIOConvert8(BinIO_HOST_BYTE_ORDER, byte_order, va_arg(ctx->args, uint8_t *), ctx->buffer, count); - ctx->buffer += 8 * count; - break; - } - } -} - -extern void packf(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfpackf(stdout, format, args); - va_end(args); -} - -extern void spackf(void *buffer, const char *format, ...) -{ - va_list args; - va_start(args, format); - vspackf(buffer, format, args); - va_end(args); -} - -extern void fpackf(FILE *file, const char *format, ...) -{ - va_list args; - va_start(args, format); - vfpackf(file, format, args); - va_end(args); -} - -extern void vspackf(void *buffer, const char *format, va_list args) -{ - struct BinIOFormatCursor cursor; - struct BinIOPackContext context; - - BinIOInitFormatCursor(&cursor, format); - - context.buffer = (unsigned char *)buffer; - va_copy(context.args, args); - - while (BinIONextChar(&context, &cursor, BinIOPack)) {} - - va_end(context.args); -} - -extern void vfpackf(FILE *file, const char *format, va_list args) -{ - size_t n_bytes = BinIOFormatByteCount(format); - void* buffer = malloc(n_bytes); - - vspackf(buffer, format, args); - - fwrite(buffer, n_bytes, 1, file); - free(buffer); -} diff --git a/Source/private.c b/Source/private.c deleted file mode 100644 index 6ff2b59..0000000 --- a/Source/private.c +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include - -#include "private.h" - -void BinIOConvert1(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count) -{ - if (BinIONormalizeByteOrder(from_byte_order) != - BinIONormalizeByteOrder(to_byte_order)) { - unsigned int i; - for (i = 0; i < count; ++i) { - BinIOSwap1(src, dst); - src += 1; - dst += 1; - } - } else { - memcpy(dst, src, 1 * count); - } -} - -void BinIOConvert2(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count) -{ - if (BinIONormalizeByteOrder(from_byte_order) != - BinIONormalizeByteOrder(to_byte_order)) { - unsigned int i; - for (i = 0; i < count; ++i) { - BinIOSwap2(src, dst); - src += 2; - dst += 2; - } - } else { - memcpy(dst, src, 2 * count); - } -} - -void BinIOConvert4(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count) -{ - if (BinIONormalizeByteOrder(from_byte_order) != - BinIONormalizeByteOrder(to_byte_order)) { - unsigned int i; - for (i = 0; i < count; ++i) { - BinIOSwap4(src, dst); - src += 4; - dst += 4; - } - } else { - memcpy(dst, src, 4 * count); - } -} - -void BinIOConvert8(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count) -{ - if (BinIONormalizeByteOrder(from_byte_order) != - BinIONormalizeByteOrder(to_byte_order)) { - unsigned int i; - for (i = 0; i < count; ++i) { - BinIOSwap8(src, dst); - src += 8; - dst += 8; - } - } else { - memcpy(dst, src, 8 * count); - } -} - -void BinIOInitFormatCursor(struct BinIOFormatCursor *cursor, - const char *format) -{ - cursor->cursor = format; - cursor->byte_order = BinIO_HOST_BYTE_ORDER; - cursor->count = -1; -} - -int BinIONextChar(void *context, - struct BinIOFormatCursor *cursor, - BinIOProcessFunction func) -{ - int count, value; - int c; - switch (c = *(cursor->cursor)++) { - case BinIO_LITTLE_ENDIAN_BYTE_ORDER: - case BinIO_BIG_ENDIAN_BYTE_ORDER: - case BinIO_HOST_BYTE_ORDER: - case BinIO_NETWORK_BYTE_ORDER: - cursor->byte_order = c; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - count = cursor->count; - value = c - '0'; - if (count == -1) { - cursor->count = value; - } else { - cursor->count = (count * 10) + value; - } - break; - - case BinIO_TYPE_IGNORE_BYTE: - case BinIO_TYPE_BYTE: - case BinIO_TYPE_INT16: - case BinIO_TYPE_INT32: - case BinIO_TYPE_INT64: - case BinIO_TYPE_FLOAT32: - case BinIO_TYPE_FLOAT64: - func(context, c, cursor->byte_order, cursor->count); - cursor->byte_order = BinIO_HOST_BYTE_ORDER; - cursor->count = -1; - break; - - case ' ': - case '\t': - case '\r': - case '\n': - break; - - default: - return 0; - } - - return 1; -} - -extern void BinIOCountBytes(void *context, int type, int byte_order, int count) -{ - size_t type_size = 0; - - if (count == -1) { - count = 1; - } - - switch (type) { - case BinIO_TYPE_IGNORE_BYTE: - type_size = 1; - break; - case BinIO_TYPE_BYTE: - type_size = 1; - break; - case BinIO_TYPE_INT16: - type_size = 2; - break; - case BinIO_TYPE_INT32: - type_size = 4; - break; - case BinIO_TYPE_INT64: - type_size = 8; - break; - case BinIO_TYPE_FLOAT32: - type_size = 4; - break; - case BinIO_TYPE_FLOAT64: - type_size = 8; - break; - } - - *(size_t*)context += type_size * count; -} - -extern size_t BinIOFormatByteCount(const char *format) -{ - struct BinIOFormatCursor cursor; - size_t n_bytes = 0; - - BinIOInitFormatCursor(&cursor, format); - - while (BinIONextChar(&n_bytes, &cursor, BinIOCountBytes)) {} - - return n_bytes; -} diff --git a/Source/private.h b/Source/private.h deleted file mode 100644 index 787a3ad..0000000 --- a/Source/private.h +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#ifndef private_h -#define private_h - -#include -#include - -#define BinIO_TYPE_IGNORE_BYTE 'x' -#define BinIO_TYPE_BYTE 'b' -#define BinIO_TYPE_INT16 's' -#define BinIO_TYPE_INT32 'i' -#define BinIO_TYPE_INT64 'l' -#define BinIO_TYPE_FLOAT32 'f' -#define BinIO_TYPE_FLOAT64 'd' - -#define BinIO_LITTLE_ENDIAN_BYTE_ORDER 'L' -#define BinIO_BIG_ENDIAN_BYTE_ORDER 'B' -#define BinIO_HOST_BYTE_ORDER 'H' -#define BinIO_NETWORK_BYTE_ORDER 'N' - -#ifndef ALREADY_DID_BINIO_STDINT -#define ALREADY_DID_BINIO_STDINT -#if defined(BinIO_STDINT_HEADER) -#include BinIO_STDINT_HEADER -typedef float float32_t; -typedef double float64_t; -#else -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned long uint32_t; -#ifdef WIN32 -typedef unsigned __int64 uint64_t; -#else -typedef unsigned long long uint64_t; -#endif -typedef float float32_t; -typedef double float64_t; -#endif -#endif - -#ifndef BinIO_INLINE -#if defined(__GNUC__) -#define BinIO_INLINE static inline -#else -#define BinIO_INLINE static -#endif -#endif - -#ifndef BinIO_BYTE_ORDER -#if defined(__ppc__) || defined(__POWERPC__) -#define BinIO_BYTE_ORDER BinIO_BIG_ENDIAN_BYTE_ORDER -#else -#define BinIO_BYTE_ORDER BinIO_LITTLE_ENDIAN_BYTE_ORDER -#endif -#endif - -BinIO_INLINE void BinIOSwap1(const uint8_t *src, uint8_t *dst) -{ - *dst = *src; -} - -BinIO_INLINE void BinIOSwap2(const uint8_t *src, uint8_t *dst) -{ - *(dst + 1) = *(src + 0); - *(dst + 0) = *(src + 1); -} - -BinIO_INLINE void BinIOSwap4(const uint8_t *src, uint8_t *dst) -{ - *(dst + 3) = *(src + 0); - *(dst + 2) = *(src + 1); - *(dst + 1) = *(src + 2); - *(dst + 0) = *(src + 3); -} - -BinIO_INLINE void BinIOSwap8(const uint8_t *src, uint8_t *dst) -{ - *(dst + 7) = *(src + 0); - *(dst + 6) = *(src + 1); - *(dst + 5) = *(src + 2); - *(dst + 4) = *(src + 3); - *(dst + 3) = *(src + 4); - *(dst + 2) = *(src + 5); - *(dst + 1) = *(src + 6); - *(dst + 0) = *(src + 7); -} - -BinIO_INLINE int BinIONormalizeByteOrder(int byte_order) -{ - if (byte_order == BinIO_HOST_BYTE_ORDER) { - byte_order = BinIO_BYTE_ORDER; - } else if (byte_order == BinIO_NETWORK_BYTE_ORDER) { - byte_order = BinIO_BIG_ENDIAN_BYTE_ORDER; - } - - return byte_order; -} - -extern void BinIOConvert1(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count); -extern void BinIOConvert2(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count); -extern void BinIOConvert4(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count); -extern void BinIOConvert8(int from_byte_order, int to_byte_order, - const uint8_t *src, uint8_t *dst, - unsigned int count); - -struct BinIOFormatCursor { - const char *cursor; - int byte_order; - int count; -}; - -typedef void (*BinIOProcessFunction)(void *context, - int type, - int byte_order, - int count); - -extern void BinIOInitFormatCursor(struct BinIOFormatCursor *cursor, - const char *format); - -extern int BinIONextChar(void *context, - struct BinIOFormatCursor *cursor, - BinIOProcessFunction func); - -extern void BinIOCountBytes(void *context, int type, int byte_order, int count); - -extern size_t BinIOFormatByteCount(const char *format); - -#endif - diff --git a/Source/unpack.c b/Source/unpack.c deleted file mode 100644 index fbcce38..0000000 --- a/Source/unpack.c +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright (C) 2003, 2010 - Wolfire Games -Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file) - -This file is part of Lugaru. - -Lugaru is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -Lugaru 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Lugaru. If not, see . -*/ - -#include - -#include "binio.h" -#include "private.h" - -struct BinIOUnpackContext { - const uint8_t *data; - va_list args; -}; - -static void BinIOUnpack(void *context, int type, int byte_order, int count) -{ - struct BinIOUnpackContext *ctx = (struct BinIOUnpackContext*)context; - if (count == -1) { - count = 1; - } - - switch (type) { - case BinIO_TYPE_IGNORE_BYTE: - ctx->data += 1 * count; - break; - case BinIO_TYPE_BYTE: - BinIOConvert1(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); - ctx->data += 1 * count; - break; - case BinIO_TYPE_INT16: - BinIOConvert2(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); - ctx->data += 2 * count; - break; - case BinIO_TYPE_INT32: - BinIOConvert4(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); - ctx->data += 4 * count; - break; - case BinIO_TYPE_INT64: - BinIOConvert8(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); - ctx->data += 8 * count; - break; - case BinIO_TYPE_FLOAT32: - BinIOConvert4(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); - ctx->data += 4 * count; - break; - case BinIO_TYPE_FLOAT64: - BinIOConvert8(byte_order, BinIO_HOST_BYTE_ORDER, ctx->data, va_arg(ctx->args, uint8_t *), count); - ctx->data += 8 * count; - break; - } -} - -void unpackf(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfunpackf(stdin, format, args); - va_end(args); -} - -void sunpackf(const void *buffer, const char *format, ...) -{ - va_list args; - va_start(args, format); - vsunpackf(buffer, format, args); - va_end(args); -} - -void funpackf(FILE *file, const char *format, ...) -{ - va_list args; - va_start(args, format); - vfunpackf(file, format, args); - va_end(args); -} - -void vsunpackf(const void *buffer, const char *format, va_list args) -{ - struct BinIOFormatCursor cursor; - struct BinIOUnpackContext context; - - BinIOInitFormatCursor(&cursor, format); - - context.data = (const unsigned char*)buffer; - va_copy(context.args, args); - - while (BinIONextChar(&context, &cursor, BinIOUnpack)) {} - - va_end(context.args); -} - -void vfunpackf(FILE *file, const char *format, va_list args) -{ - size_t n_bytes = BinIOFormatByteCount(format); - void* buffer = malloc(n_bytes); - fread(buffer, n_bytes, 1, file); - - vsunpackf(buffer, format, args); - - free(buffer); -}