]> git.jsancho.org Git - lugaru.git/commitdiff
Sorted all source files in folders
authorCôme Chilliet <come@chilliet.eu>
Sun, 11 Dec 2016 10:34:33 +0000 (17:34 +0700)
committerCôme Chilliet <come@chilliet.eu>
Sun, 11 Dec 2016 10:34:33 +0000 (17:34 +0700)
143 files changed:
CMakeLists.txt
Source/Account.cpp [deleted file]
Source/Account.h [deleted file]
Source/Animation/Animation.cpp
Source/Animation/Animation.h
Source/Animation/Joint.cpp
Source/Animation/Joint.h
Source/Animation/Muscle.cpp
Source/Animation/Skeleton.cpp
Source/Animation/Skeleton.h
Source/Audio/Sounds.cpp [new file with mode: 0644]
Source/Audio/Sounds.def [new file with mode: 0644]
Source/Audio/Sounds.h [new file with mode: 0644]
Source/Audio/openal_wrapper.cpp [new file with mode: 0644]
Source/Audio/openal_wrapper.h [new file with mode: 0644]
Source/Awards.cpp [deleted file]
Source/Awards.def [deleted file]
Source/Awards.h [deleted file]
Source/Bonuses.def [deleted file]
Source/Campaign.cpp [deleted file]
Source/Campaign.h [deleted file]
Source/ConsoleCmds.cpp [deleted file]
Source/ConsoleCmds.def [deleted file]
Source/ConsoleCmds.h [deleted file]
Source/Devtools/ConsoleCmds.cpp [new file with mode: 0644]
Source/Devtools/ConsoleCmds.def [new file with mode: 0644]
Source/Devtools/ConsoleCmds.h [new file with mode: 0644]
Source/Dialog.cpp [deleted file]
Source/Dialog.h [deleted file]
Source/Environment/Lights.cpp [new file with mode: 0644]
Source/Environment/Lights.h [new file with mode: 0644]
Source/Environment/Skybox.cpp [new file with mode: 0644]
Source/Environment/Skybox.h [new file with mode: 0644]
Source/Environment/Terrain.cpp [new file with mode: 0644]
Source/Environment/Terrain.h [new file with mode: 0644]
Source/Frustum.cpp [deleted file]
Source/Frustum.h [deleted file]
Source/Game.cpp
Source/Game.h
Source/GameDraw.cpp
Source/GameInitDispose.cpp
Source/GameTick.cpp
Source/Globals.cpp
Source/Graphic/Models.cpp [new file with mode: 0644]
Source/Graphic/Models.h [new file with mode: 0644]
Source/Graphic/Sprite.cpp [new file with mode: 0644]
Source/Graphic/Sprite.h [new file with mode: 0644]
Source/Graphic/Stereo.cpp [new file with mode: 0644]
Source/Graphic/Stereo.h [new file with mode: 0644]
Source/Graphic/Text.cpp [new file with mode: 0644]
Source/Graphic/Text.h [new file with mode: 0644]
Source/Graphic/Texture.cpp [new file with mode: 0644]
Source/Graphic/Texture.h [new file with mode: 0644]
Source/Graphic/gamegl.h [new file with mode: 0644]
Source/Hotspot.cpp [deleted file]
Source/Hotspot.h [deleted file]
Source/ImageIO.cpp [deleted file]
Source/ImageIO.h [deleted file]
Source/Input.cpp [deleted file]
Source/Input.h [deleted file]
Source/Level/Awards.cpp [new file with mode: 0644]
Source/Level/Awards.def [new file with mode: 0644]
Source/Level/Awards.h [new file with mode: 0644]
Source/Level/Bonuses.def [new file with mode: 0644]
Source/Level/Campaign.cpp [new file with mode: 0644]
Source/Level/Campaign.h [new file with mode: 0644]
Source/Level/Dialog.cpp [new file with mode: 0644]
Source/Level/Dialog.h [new file with mode: 0644]
Source/Level/Hotspot.cpp [new file with mode: 0644]
Source/Level/Hotspot.h [new file with mode: 0644]
Source/Lights.cpp [deleted file]
Source/Lights.h [deleted file]
Source/MacCompatibility.h
Source/Math/Frustum.cpp [new file with mode: 0644]
Source/Math/Frustum.h [new file with mode: 0644]
Source/Math/PhysicsMath.h [new file with mode: 0644]
Source/Math/Quaternions.cpp [new file with mode: 0644]
Source/Math/Quaternions.h [new file with mode: 0644]
Source/Math/Random.h [new file with mode: 0644]
Source/Menu.cpp [deleted file]
Source/Menu.h [deleted file]
Source/Menu/Menu.cpp [new file with mode: 0644]
Source/Menu/Menu.h [new file with mode: 0644]
Source/Models.cpp [deleted file]
Source/Models.h [deleted file]
Source/Objects.cpp [deleted file]
Source/Objects.h [deleted file]
Source/Objects/Objects.cpp [new file with mode: 0644]
Source/Objects/Objects.h [new file with mode: 0644]
Source/Objects/Person.cpp [new file with mode: 0644]
Source/Objects/Person.h [new file with mode: 0644]
Source/Objects/Weapons.cpp [new file with mode: 0644]
Source/Objects/Weapons.h [new file with mode: 0644]
Source/Person.cpp [deleted file]
Source/Person.h [deleted file]
Source/PhysicsMath.h [deleted file]
Source/Quaternions.cpp [deleted file]
Source/Quaternions.h [deleted file]
Source/Random.h [deleted file]
Source/Settings.cpp [deleted file]
Source/Settings.h [deleted file]
Source/Skybox.cpp [deleted file]
Source/Skybox.h [deleted file]
Source/Sounds.cpp [deleted file]
Source/Sounds.def [deleted file]
Source/Sounds.h [deleted file]
Source/Sprite.cpp [deleted file]
Source/Sprite.h [deleted file]
Source/Stereo.cpp [deleted file]
Source/Stereo.h [deleted file]
Source/Terrain.cpp [deleted file]
Source/Terrain.h [deleted file]
Source/Text.cpp [deleted file]
Source/Text.h [deleted file]
Source/Texture.cpp [deleted file]
Source/Texture.h [deleted file]
Source/Thirdparty/optionparser.h [new file with mode: 0644]
Source/User/Account.cpp [new file with mode: 0644]
Source/User/Account.h [new file with mode: 0644]
Source/User/Settings.cpp [new file with mode: 0644]
Source/User/Settings.h [new file with mode: 0644]
Source/Utils/ImageIO.cpp [new file with mode: 0644]
Source/Utils/ImageIO.h [new file with mode: 0644]
Source/Utils/Input.cpp [new file with mode: 0644]
Source/Utils/Input.h [new file with mode: 0644]
Source/Utils/binio.h [new file with mode: 0644]
Source/Utils/pack.c [new file with mode: 0644]
Source/Utils/private.c [new file with mode: 0644]
Source/Utils/private.h [new file with mode: 0644]
Source/Utils/unpack.c [new file with mode: 0644]
Source/Weapons.cpp [deleted file]
Source/Weapons.h [deleted file]
Source/WinDefs.h
Source/binio.h [deleted file]
Source/gamegl.h [deleted file]
Source/main.cpp
Source/openal_wrapper.cpp [deleted file]
Source/openal_wrapper.h [deleted file]
Source/optionparser.h [deleted file]
Source/pack.c [deleted file]
Source/private.c [deleted file]
Source/private.h [deleted file]
Source/unpack.c [deleted file]

index 087eafe333cacaa94e79cd6ab3390dd2943fd073..62a62f29e501b6a47b3e01396aef79676467094a 100644 (file)
@@ -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 (file)
index 6a1d3bb..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "Account.h"
-#include "binio.h"
-#include <fstream>
-#include "MacCompatibility.h"
-#include "string.h"
-#include <iostream>
-
-using namespace std;
-
-extern bool devtools;
-
-vector<Account> 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<string, CampaignProgress>::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 (file)
index 385c6fa..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _Account_H_
-#define _Account_H_
-
-#include <vector>
-#include <string>
-#include <map>
-#include <fstream>
-
-struct CampaignProgress {
-    float highscore;
-    float fasttime;
-    float score;
-    float time;
-    std::vector<int> 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<Account> 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<std::string, CampaignProgress> campaignProgress;
-};
-
-#endif
index 83e2c0e1da31e199f9fb978c3f3b81642bb48dd2..4433800db337622be2b242c7c17373fe8de3c515 100644 (file)
@@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License
 along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include "Animation/Skeleton.h"
 #include "Animation/Animation.h"
+#include "Animation/Skeleton.h"
 #include "Utils/Folders.h"
 #include "Game.h"
 
index 4a74c5c1d0afbcdacb0fc8d9e0ad04319b75bd0d..91205abe0341400bca884f6f0d1f62fc14f96e26 100644 (file)
@@ -21,6 +21,7 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 #define ANIMATION_H
 
 #include <vector>
+#include "Math/Quaternions.h"
 
 enum anim_attack_type {
     neutral, normalattack, reversed, reversal
index 4d3dd7c3e98d110bf0c1070ebe412148bd1e3156..4428cb816cfdc2227425dc6d329d04db442bc62a 100644 (file)
@@ -19,7 +19,7 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "Animation/Joint.h"
-#include "binio.h"
+#include "Utils/binio.h"
 
 Joint::Joint() :
     blurred(0),
index c4c8e3b4318809c79fc3b05110dcbf2e59107ca5..124d7a6fc5b503650a1e454537a3673f21930c17 100644 (file)
@@ -21,7 +21,7 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 #ifndef _JOINT_H_
 #define _JOINT_H_
 
-#include "Quaternions.h"
+#include "Math/Quaternions.h"
 #include <vector>
 
 enum bodypart {
index 94ed5b15d19e9677c2d682a2f8a8ad3f28b5440e..c4f201a20e5505c4973267aaee0fb39e404e110d 100644 (file)
@@ -19,7 +19,7 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "Animation/Muscle.h"
-#include "binio.h"
+#include "Utils/binio.h"
 
 extern float multiplier;
 extern bool freeze;
index ee247c51f70e135778c7c1b8a728a0d1859a0650..f5252403368f74b023aa04c3ba9a2e6d16244d2f 100644 (file)
@@ -20,9 +20,9 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 
 /**> 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;
index db03e75af512cbf9fba57349ee90ea27cc3d8108..19db503c23395ef4bd645e86670c399bbf3182c9 100644 (file)
@@ -21,17 +21,16 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 #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 (file)
index 0000000..2e47da0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..6fde586
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+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 (file)
index 0000000..7557993
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..e1e2346
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 (file)
index 0000000..b8de914
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef OPENAL_WRAPPER_H
+#define OPENAL_WRAPPER_H
+
+#ifdef _WIN32
+#include <malloc.h>
+#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 (file)
index 5b27563..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index fd8e5c4..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-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 (file)
index 882fbef..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index bcc1a95..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-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 (file)
index fd75198..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "Campaign.h"
-#include "Game.h"
-#include "Utils/Folders.h"
-#include <dirent.h>
-
-using namespace Game;
-
-std::vector<CampaignLevel> campaignlevels;
-
-bool campaign = false;
-
-int actuallevel = 0;
-
-std::vector<std::string> 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<std::string> 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 (file)
index 05c7859..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <vector>
-#include <string>
-
-#include "Quaternions.h"
-
-extern bool campaign;
-
-extern int actuallevel;
-
-std::vector<std::string> 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<int> 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<CampaignLevel> campaignlevels;
diff --git a/Source/ConsoleCmds.cpp b/Source/ConsoleCmds.cpp
deleted file mode 100644 (file)
index df26f82..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index d3f1684..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-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 (file)
index 9670aa0..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-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 (file)
index 0000000..dc4c275
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..d3f1684
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+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 (file)
index 0000000..9670aa0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+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 (file)
index 1f3adc5..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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> 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 (file)
index 59c791e..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _DIALOG_H_
-#define _DIALOG_H_
-
-#include "stdio.h"
-#include "Quaternions.h"
-#include <vector>
-
-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<DialogScene> 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<Dialog> dialogs;
-};
-
-#endif /*_DIALOG_H_*/
diff --git a/Source/Environment/Lights.cpp b/Source/Environment/Lights.cpp
new file mode 100644 (file)
index 0000000..17efb4c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/**> 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 (file)
index 0000000..ba1fd6c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..ce58adf
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..d2b5d72
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..8c79cd4
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..f9319f7
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index b5f2129..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "Frustum.h"
-#include <math.h>
-
-#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 (file)
index 918a047..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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
index d05bb44a9d223fb2aff7deae0ba4b46cc671d29d..e740aa607f7125da558cb120d91ee423b2dad051 100644 (file)
@@ -18,10 +18,11 @@ You should have received a copy of the GNU General Public License
 along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#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;
 
index 2dbc9af532a7d7646b82916f0065a2ced39684dd..176d245f5afa2659e296e2fc4abb61a1a671a6e6 100644 (file)
@@ -23,26 +23,26 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 
 #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 <fstream>
-#include "gamegl.h"
-#include "Stereo.h"
-#include "Account.h"
-#include "Sounds.h"
-#include "Texture.h"
-#include "optionparser.h"
 
 #define NB_CAMPAIGN_MENU_ITEM 7
 
index a346f23f2b519a1c353cefad412bc58f5f783e41..d46b9b6a693c0f5b8cc71d8a257d0f2775858bf8 100644 (file)
@@ -19,12 +19,12 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #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;
index c070d293872a430e7b922908e5adb8cedcec805e..036f80f9a4fc737d8c4d8ed2085958b8318dc2e6 100644 (file)
@@ -19,11 +19,11 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #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;
index 1b865f13c9b2b63a77d8478b0d6a21bccf986a42..37a29f583de2c8f30d14c0faf36a1bca2b6c3029 100644 (file)
@@ -35,17 +35,17 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 #include <cmath>
 #include <dirent.h>
 #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 <algorithm>
 #include <set>
index d8d430d0ab7549f2823b54f9e0d51836d1e304d9..66c6c9bc6a37781e244c9a52ee62c5d4dedec902 100644 (file)
@@ -20,21 +20,11 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 
 #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 <string>
 
+#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 (file)
index 0000000..a9833d0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..e14ebe6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _MODELS_H_
+#define _MODELS_H_
+
+/**> Model Loading <**/
+//
+// Model Maximums
+//
+#include "Graphic/gamegl.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <vector>
+
+#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 (file)
index 0000000..4276ce5
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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*> Sprite::sprites = vector<Sprite*>();
+
+//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 (file)
index 0000000..bed8439
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <vector>
+
+#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<Sprite*> 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 (file)
index 0000000..efb300b
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..8e70e5e
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..31bbe9f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/**> 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 (file)
index 0000000..b5cc8a1
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..3f5211a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "Graphic/gamegl.h"
+#include "Graphic/Texture.h"
+#include "Utils/Folders.h"
+#include "Utils/ImageIO.h"
+
+using namespace std;
+
+extern bool trilinear;
+
+vector<TextureRes*> 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<TextureRes*>::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 (file)
index 0000000..95547bf
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TEXTURE_H_
+#define _TEXTURE_H_
+
+#include <map>
+#include <vector>
+#include <string>
+using namespace std;
+
+class TextureRes
+{
+private:
+    static vector<TextureRes*> 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 (file)
index 0000000..9bee196
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LUGARU_GL_H_
+#define _LUGARU_GL_H_
+
+
+#include <cstring>
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+#include <map>
+#include <string>
+
+#ifdef WIN32
+  #define WIN32_LEAN_AND_MEAN
+  #define Polygon WinPolygon
+  #include <windows.h>
+  #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 (file)
index 03bf3a1..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "Hotspot.h"
-
-std::vector<Hotspot> 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 (file)
index 3139f98..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _HOTSPOT_H_
-#define _HOTSPOT_H_
-
-#include "Quaternions.h"
-#include <vector>
-
-class Hotspot
-{
-public:
-    static std::vector<Hotspot> 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 (file)
index 3e6a72e..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-/**> HEADER FILES <**/
-
-#include <stdio.h>
-#include <jpeglib.h>
-#include <png.h>
-#include <zlib.h>
-
-#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 (file)
index b3d0a49..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _IMAGE_IO_H_
-#define _IMAGE_IO_H_
-
-#ifdef _MSC_VER
-#pragma once
-#endif
-
-
-/**> HEADER FILES <**/
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#define Polygon WinPolygon
-#include <windows.h>
-#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 (file)
index b18ec6e..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-/**> 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 (file)
index 3bf8d79..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 0000000..0a561e5
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..fd8e5c4
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+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 (file)
index 0000000..882fbef
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..bcc1a95
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+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 (file)
index 0000000..2aa5f17
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "Game.h"
+#include "Level/Campaign.h"
+#include "Utils/Folders.h"
+
+#include <dirent.h>
+
+using namespace Game;
+
+std::vector<CampaignLevel> campaignlevels;
+
+bool campaign = false;
+
+int actuallevel = 0;
+
+std::vector<std::string> 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<std::string> 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 (file)
index 0000000..3cd4d51
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <vector>
+#include <string>
+
+#include "Math/Quaternions.h"
+
+extern bool campaign;
+
+extern int actuallevel;
+
+std::vector<std::string> 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<int> 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<CampaignLevel> campaignlevels;
diff --git a/Source/Level/Dialog.cpp b/Source/Level/Dialog.cpp
new file mode 100644 (file)
index 0000000..915fba1
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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> 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 (file)
index 0000000..50691c5
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DIALOG_H_
+#define _DIALOG_H_
+
+#include "stdio.h"
+#include <vector>
+
+#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<DialogScene> 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<Dialog> dialogs;
+};
+
+#endif /*_DIALOG_H_*/
diff --git a/Source/Level/Hotspot.cpp b/Source/Level/Hotspot.cpp
new file mode 100644 (file)
index 0000000..1f7956e
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "Level/Hotspot.h"
+
+std::vector<Hotspot> 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 (file)
index 0000000..a8ef2ec
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _HOTSPOT_H_
+#define _HOTSPOT_H_
+
+#include "Math/Quaternions.h"
+#include <vector>
+
+class Hotspot
+{
+public:
+    static std::vector<Hotspot> 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 (file)
index cd20a66..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-/**> 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 (file)
index e846fb6..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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
index 5d6f9550aa694d07784fa51ada4dad751217a5a1..26b0b60827453f30c2711c2f73a3b9830c9a8cee 100644 (file)
@@ -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 (file)
index 0000000..164074b
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "Math/Frustum.h"
+#include <math.h>
+
+#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 (file)
index 0000000..918a047
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..7dc9461
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PHYSICSMATH_H_
+#define _PHYSICSMATH_H_
+
+//#include <Carbon.h>
+
+#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 (file)
index 0000000..7e68b97
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..de07302
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#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 (file)
index 0000000..096cedc
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RANDOM_H_
+#define _RANDOM_H_
+
+#include <stdlib.h>
+
+static inline short Random()
+{
+    return rand();
+}
+
+#endif
diff --git a/Source/Menu.cpp b/Source/Menu.cpp
deleted file mode 100644 (file)
index f1fb94f..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <vector>
-#include <string>
-#include <set>
-#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<std::pair<int,int>> resolutions;
-extern int mainmenu;
-extern std::vector<CampaignLevel> 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<MenuItem> 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<MenuItem>::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<MenuItem>::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<MenuItem>::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<MenuItem>::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<MenuItem>::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<pair<int,int>>::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<string> campaigns = ListCampaigns();
-                vector<string>::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 (file)
index bf99bdc..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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<MenuItem> items;
-};
-
-#endif
diff --git a/Source/Menu/Menu.cpp b/Source/Menu/Menu.cpp
new file mode 100644 (file)
index 0000000..3639740
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <vector>
+#include <string>
+#include <set>
+
+#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<std::pair<int,int>> resolutions;
+extern int mainmenu;
+extern std::vector<CampaignLevel> 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<MenuItem> 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<MenuItem>::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<MenuItem>::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<MenuItem>::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<MenuItem>::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<MenuItem>::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<pair<int,int>>::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<string> campaigns = ListCampaigns();
+                vector<string>::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 (file)
index 0000000..bf99bdc
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<MenuItem> items;
+};
+
+#endif
diff --git a/Source/Models.cpp b/Source/Models.cpp
deleted file mode 100644 (file)
index 2c30168..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 3874e23..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _MODELS_H_
-#define _MODELS_H_
-
-/**> Model Loading <**/
-//
-// Model Maximums
-//
-#include "gamegl.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <vector>
-
-#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 (file)
index dd4f4a7..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 8b5f592..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <vector>
-//
-// 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 (file)
index 0000000..20e117c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..83f17c2
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <vector>
+//
+// 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 (file)
index 0000000..c1eb88d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/**> 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<std::shared_ptr<Person>> Person::players(1, std::shared_ptr<Person>(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 (file)
index 0000000..4fa115f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <memory>
+#include <cmath>
+
+#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<Person>
+{
+public:
+    static std::vector<std::shared_ptr<Person>> 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<Person> 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 (file)
index 0000000..c56779d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/**> 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<Weapon>::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<Weapon>::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 (file)
index 0000000..51ab000
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <cmath>
+
+#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<Weapon>
+{
+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 (file)
index fb3c445..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-/**> 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<std::shared_ptr<Person>> Person::players(1, std::shared_ptr<Person>(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 (file)
index cb62622..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <cmath>
-#include <memory>
-#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<Person>
-{
-public:
-    static std::vector<std::shared_ptr<Person>> 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<Person> 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 (file)
index 7dc9461..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _PHYSICSMATH_H_
-#define _PHYSICSMATH_H_
-
-//#include <Carbon.h>
-
-#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 (file)
index ff1ad71..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index f090ef9..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-
-#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 (file)
index 096cedc..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _RANDOM_H_
-#define _RANDOM_H_
-
-#include <stdlib.h>
-
-static inline short Random()
-{
-    return rand();
-}
-
-#endif
diff --git a/Source/Settings.cpp b/Source/Settings.cpp
deleted file mode 100644 (file)
index eca9efd..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index e5f983a..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 8fcc967..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 5619a2a..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 735d939..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 6fde586..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-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 (file)
index 7557993..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index a0502d2..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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*> Sprite::sprites = vector<Sprite*>();
-
-//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 (file)
index 6f679ce..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <vector>
-
-#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<Sprite*> 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 (file)
index 8887a52..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 8e70e5e..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 34da00e..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index c375687..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 0a31c3e..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-/**> 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 (file)
index c2a2ccc..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 (file)
index 3106c8f..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "gamegl.h"
-#include "Texture.h"
-#include "ImageIO.h"
-#include "Utils/Folders.h"
-
-using namespace std;
-
-extern bool trilinear;
-
-vector<TextureRes*> 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<TextureRes*>::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 (file)
index 95547bf..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _TEXTURE_H_
-#define _TEXTURE_H_
-
-#include <map>
-#include <vector>
-#include <string>
-using namespace std;
-
-class TextureRes
-{
-private:
-    static vector<TextureRes*> 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 (file)
index 0000000..2e178ce
--- /dev/null
@@ -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:
+ * <ul style="padding-left:1em;margin-left:0">
+ * <li> It is a header-only library. Just <code>\#include "Thirdparty/optionparser.h"</code> and you're set.
+ * <li> It is freestanding. There are no dependencies whatsoever, not even the
+ *      C or C++ standard library.
+ * <li> 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).
+ * <li> Unlike getopt() and derivatives it doesn't force you to loop through
+ *     options sequentially. Instead you can access options directly like this:
+ *     <ul style="margin-top:.5em">
+ *     <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=&lt;fname> arguments:
+ *     @code for (Option* opt = options[FILE]; opt; opt = opt->next())
+ *   fname = opt->arg; ... @endcode
+ *     <li> 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
+ *     </ul>
+ * </ul> @n
+ * Despite these features the code size remains tiny.
+ * It is smaller than <a href="http://uclibc.org">uClibc</a>'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:
+ * <a style="font-size:larger;font-weight:bold" href="http://sourceforge.net/projects/optionparser/files/optionparser-1.4.tar.gz/download">optionparser-1.4.tar.gz</a> @n
+ * Just the header (this is all you really need):
+ * <a style="font-size:larger;font-weight:bold" href="http://optionparser.sourceforge.net/optionparser.h">optionparser.h</a>
+ *
+ * @par Changelog:
+ * <b>Version 1.4:</b> Fixed 2 printUsage() bugs that messed up output with small COLUMNS values @n
+ * <b>Version 1.3:</b> Compatible with Microsoft Visual C++. @n
+ * <b>Version 1.2:</b> 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
+ * <b>Version 1.1:</b> 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: <tt><b>optionparser-feedback<span id="antispam">&nbsp;(a)&nbsp;</span>lists.sourceforge.net</b></tt>
+ * @htmlonly <script type="text/javascript">document.getElementById("antispam").innerHTML="@"</script> @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 <iostream>
+ * #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 <code>getopt()</code> conventions and supports
+ *     GNU-style <code>getopt_long()</code> long options as well as Perl-style single-minus
+ *     long options (<code>getopt_long_only()</code>).
+ * @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. <code>-X -Y</code> is equivalent to @c -XY.
+ * @li a short option may take an argument either separate (<code>-X foo</code>) 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 <code> -ABCX foo </code> (@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 (<code> --option arg </code>) or
+ *     attached (<code> --option=arg </code>). In the attached form the equals sign is mandatory.
+ * @li an empty string can be passed as an attached long option argument: <code> --option-name= </code>.
+ *     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.
+ *     <code> -X-X </code>, <code>-X -X</code> or <code> --long-X=-X </code>. 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 <intrin.h>
+#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 <code>Parser::error()==true</code>. @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=&lt;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 <code> operator const Option*() </code>.
+   */
+  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 <code> last()->type() </code> 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 <code> min_abbr_len > 0 </code> 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
+   *               <code> -f -i -l -e </code> (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 <code>nonOptionsCount() >0 </code>).
+   *
+   * @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 <b><code>nonOptions()[i]</code></b> (@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",<anything>) == 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",<anything>)  == false
+   * streqabbr("foo", "foobar" ,<anything>)  == false
+   * streqabbr("foo", "fobar"  ,<anything>)  == false
+   * streqabbr("foo", "foo"    ,<anything>)  == 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 <code>args[-count],...,args[-1],args[0]</code> to become
+   *        <code>args[0],args[-count],...,args[-1]</code>.
+   */
+  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 <code>func(string, size)</code> where
+   * string can be initialized with a const char* and size with an int.
+   */
+  template<typename Function>
+  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 <code>write(string, size)</code>
+   * method like that of @c std::ostream.
+   */
+  template<typename OStream>
+  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<typename Temporary>
+  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 <code>func(fd, string, size)</code> (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<typename Syscall>
+  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<typename Function, typename Stream>
+  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 <code> i1 = max(i1, i2) </code>
+   */
+  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 <code> x > want_x </code>, 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 <code> "1 \v 3 \t 2 \v 4" </code> 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.
+ *
+ * <b>Table formatting:</b>
+ *
+ * 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
+ *
+ * <b>Output methods:</b>
+ *
+ * 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 <unistd.h>  // write()
+ * #include <iostream>  // cout
+ * #include <sstream>   // ostringstream
+ * #include <cstdio>    // 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<typename OStream>
+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<OStream> write(prn);
+  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Function>
+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<Function> write(prn);
+  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Temporary>
+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<Temporary> write(prn);
+  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Syscall>
+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<Syscall> write(prn, fd);
+  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Function, typename Stream>
+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<Function, Stream> 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 (file)
index 0000000..ff4b04f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "User/Account.h"
+#include "Utils/binio.h"
+#include "MacCompatibility.h"
+#include <fstream>
+#include "string.h"
+#include <iostream>
+
+using namespace std;
+
+extern bool devtools;
+
+vector<Account> 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<string, CampaignProgress>::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 (file)
index 0000000..385c6fa
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _Account_H_
+#define _Account_H_
+
+#include <vector>
+#include <string>
+#include <map>
+#include <fstream>
+
+struct CampaignProgress {
+    float highscore;
+    float fasttime;
+    float score;
+    float time;
+    std::vector<int> 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<Account> 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<std::string, CampaignProgress> campaignProgress;
+};
+
+#endif
diff --git a/Source/User/Settings.cpp b/Source/User/Settings.cpp
new file mode 100644 (file)
index 0000000..9948e63
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..e5f983a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..7a405fc
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/**> HEADER FILES <**/
+
+#include <stdio.h>
+#include <jpeglib.h>
+#include <png.h>
+#include <zlib.h>
+
+#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 (file)
index 0000000..0c587bf
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _IMAGE_IO_H_
+#define _IMAGE_IO_H_
+
+#ifdef _MSC_VER
+#pragma once
+#endif
+
+
+/**> HEADER FILES <**/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#define Polygon WinPolygon
+#include <windows.h>
+#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 (file)
index 0000000..9376522
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/**> 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 (file)
index 0000000..3bf8d79
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..dddb214
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef binio_h
+#define binio_h
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#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 (file)
index 0000000..736085d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#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 (file)
index 0000000..6ff2b59
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+
+#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 (file)
index 0000000..787a3ad
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef private_h
+#define private_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#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 (file)
index 0000000..fbcce38
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#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 (file)
index bc610d7..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-/**> 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<Weapon>::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<Weapon>::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 (file)
index 7472541..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <cmath>
-
-#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<Weapon>
-{
-public:
-    Weapons();
-    ~Weapons();
-
-    int Draw();
-    void DoStuff();
-};
-
-extern Weapons weapons;
-#endif
index 32c7945f8068104280683648ec8324f55ef1fba3..366845398961bb3fa54fbbf08f37a7f26bb90a15 100644 (file)
@@ -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 (file)
index dddb214..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef binio_h
-#define binio_h
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#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 (file)
index 9bee196..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _LUGARU_GL_H_
-#define _LUGARU_GL_H_
-
-
-#include <cstring>
-#include <iostream>
-#include <fstream>
-#include <algorithm>
-#include <map>
-#include <string>
-
-#ifdef WIN32
-  #define WIN32_LEAN_AND_MEAN
-  #define Polygon WinPolygon
-  #include <windows.h>
-  #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
-
-
index 09a10cb9d785429d6e9a2e96806b69cb73fcb157..2144a8cf9c8612187fcdfd712ea71434f9518fa9 100644 (file)
@@ -25,15 +25,15 @@ along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
 #include <iostream>
 #include <zlib.h>
 #include <set>
-#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 <windows.h>
diff --git a/Source/openal_wrapper.cpp b/Source/openal_wrapper.cpp
deleted file mode 100644 (file)
index ecac525..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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 (file)
index 16d4f67..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef OPENAL_WRAPPER_H
-#define OPENAL_WRAPPER_H
-
-#ifdef _WIN32
-#include <malloc.h>
-#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 (file)
index a5a8e19..0000000
+++ /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:
- * <ul style="padding-left:1em;margin-left:0">
- * <li> It is a header-only library. Just <code>\#include "optionparser.h"</code> and you're set.
- * <li> It is freestanding. There are no dependencies whatsoever, not even the
- *      C or C++ standard library.
- * <li> 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).
- * <li> Unlike getopt() and derivatives it doesn't force you to loop through
- *     options sequentially. Instead you can access options directly like this:
- *     <ul style="margin-top:.5em">
- *     <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=&lt;fname> arguments:
- *     @code for (Option* opt = options[FILE]; opt; opt = opt->next())
- *   fname = opt->arg; ... @endcode
- *     <li> 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
- *     </ul>
- * </ul> @n
- * Despite these features the code size remains tiny. 
- * It is smaller than <a href="http://uclibc.org">uClibc</a>'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:
- * <a style="font-size:larger;font-weight:bold" href="http://sourceforge.net/projects/optionparser/files/optionparser-1.4.tar.gz/download">optionparser-1.4.tar.gz</a> @n
- * Just the header (this is all you really need):
- * <a style="font-size:larger;font-weight:bold" href="http://optionparser.sourceforge.net/optionparser.h">optionparser.h</a>
- *
- * @par Changelog:
- * <b>Version 1.4:</b> Fixed 2 printUsage() bugs that messed up output with small COLUMNS values @n
- * <b>Version 1.3:</b> Compatible with Microsoft Visual C++. @n
- * <b>Version 1.2:</b> 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
- * <b>Version 1.1:</b> 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: <tt><b>optionparser-feedback<span id="antispam">&nbsp;(a)&nbsp;</span>lists.sourceforge.net</b></tt>
- * @htmlonly <script type="text/javascript">document.getElementById("antispam").innerHTML="@"</script> @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 <iostream>
- * #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 <code>getopt()</code> conventions and supports
- *     GNU-style <code>getopt_long()</code> long options as well as Perl-style single-minus
- *     long options (<code>getopt_long_only()</code>).
- * @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. <code>-X -Y</code> is equivalent to @c -XY.
- * @li a short option may take an argument either separate (<code>-X foo</code>) 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 <code> -ABCX foo </code> (@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 (<code> --option arg </code>) or
- *     attached (<code> --option=arg </code>). In the attached form the equals sign is mandatory.
- * @li an empty string can be passed as an attached long option argument: <code> --option-name= </code>.
- *     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.
- *     <code> -X-X </code>, <code>-X -X</code> or <code> --long-X=-X </code>. 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 <intrin.h>
-#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 <code>Parser::error()==true</code>. @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=&lt;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 <code> operator const Option*() </code>.
-   */
-  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 <code> last()->type() </code> 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 <code> min_abbr_len > 0 </code> 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
-   *               <code> -f -i -l -e </code> (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 <code>nonOptionsCount() >0 </code>).
-   *
-   * @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 <b><code>nonOptions()[i]</code></b> (@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",<anything>) == 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",<anything>)  == false
-   * streqabbr("foo", "foobar" ,<anything>)  == false
-   * streqabbr("foo", "fobar"  ,<anything>)  == false
-   * streqabbr("foo", "foo"    ,<anything>)  == 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 <code>args[-count],...,args[-1],args[0]</code> to become
-   *        <code>args[0],args[-count],...,args[-1]</code>.
-   */
-  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 <code>func(string, size)</code> where
-   * string can be initialized with a const char* and size with an int.
-   */
-  template<typename Function>
-  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 <code>write(string, size)</code>
-   * method like that of @c std::ostream.
-   */
-  template<typename OStream>
-  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<typename Temporary>
-  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 <code>func(fd, string, size)</code> (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<typename Syscall>
-  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<typename Function, typename Stream>
-  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 <code> i1 = max(i1, i2) </code>
-   */
-  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 <code> x > want_x </code>, 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 <code> "1 \v 3 \t 2 \v 4" </code> 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.
- *
- * <b>Table formatting:</b>
- *
- * 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
- *
- * <b>Output methods:</b>
- *
- * 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 <unistd.h>  // write()
- * #include <iostream>  // cout
- * #include <sstream>   // ostringstream
- * #include <cstdio>    // 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<typename OStream>
-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<OStream> write(prn);
-  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
-}
-
-template<typename Function>
-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<Function> write(prn);
-  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
-}
-
-template<typename Temporary>
-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<Temporary> write(prn);
-  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
-}
-
-template<typename Syscall>
-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<Syscall> write(prn, fd);
-  PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
-}
-
-template<typename Function, typename Stream>
-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<Function, Stream> 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 (file)
index 736085d..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdlib.h>
-
-#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 (file)
index 6ff2b59..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <string.h>
-
-#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 (file)
index 787a3ad..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef private_h
-#define private_h
-
-#include <stdarg.h>
-#include <stddef.h>
-
-#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 (file)
index fbcce38..0000000
+++ /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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdlib.h>
-
-#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);
-}