If you appreciate the work done within the wiki, please consider supporting The Cutting Room Floor on Patreon. Thanks for all your support!

Overgrowth/Unused Scripts

From The Cutting Room Floor
Jump to navigation Jump to search

This is a sub-page of Overgrowth.

Unused Scripts

Data/Scripts/challengelevel.as

#include "ui_effects.as"
#include "ui_tools.as"
#include "threatcheck.as"
#include "music_load.as"
#include "arena_meta_persistence.as"

bool reset_allowed = true;
float time = 0.0f;
float no_win_time = 0.0f;
string level_name;
int in_victory_trigger = 0;
const float _reset_delay = 4.0f;
float reset_timer = _reset_delay;

AHGUI::FontSetup titleFont("edosz", 120,HexColor("#fff"));
AHGUI::FontSetup subTitleFont("edosz", 65, HexColor("#fff"));
AHGUI::FontSetup greenValueFont("edosz", 65, HexColor("#0f0"));
AHGUI::FontSetup redValueFont("edosz", 65, HexColor("#f00"));
AHGUI::FontSetup tealValueFont("edosz", 65, HexColor("#028482"));

MusicLoad ml("Data/Music/challengelevel.xml");

int menu_item_spacing = 20;

AHGUI::MouseOverPulseColor buttonHover(
                                        HexColor("#ffde00"),
                                        HexColor("#ffe956"), .25 );

void Init(string p_level_name) {
    level_name = p_level_name;
}

SavedLevel@ GetSave() {
    SavedLevel @saved_level;
    if(save_file.GetLoadedVersion() == 1 && save_file.SaveExist("","",level_name)) {
        @saved_level = save_file.GetSavedLevel(level_name);
        saved_level.SetKey(GetCurrentLevelModsourceID(),"challenge_level",level_name);
    } else {
        @saved_level = save_file.GetSave(GetCurrentLevelModsourceID(),"challenge_level",level_name);
    }
    return saved_level; 
}

class Achievements {
    bool flawless_;
    bool no_first_strikes_;
    bool no_counter_strikes_;
    bool no_kills_;
    bool no_alert_;
    bool injured_;
    float total_block_damage_;
    float total_damage_;
    float total_blood_loss_;
    void Init() {
        flawless_ = true;
        no_first_strikes_ = true;
        no_counter_strikes_ = true;
        no_kills_ = true;
        no_alert_ = true;
        injured_ = false;
        total_block_damage_ = 0.0f;
        total_damage_ = 0.0f;
        total_blood_loss_ = 0.0f;
    }
    Achievements() {
        Init();
    }
    void UpdateDebugText() {
        DebugText("achmt0", "Flawless: "+flawless_, 0.5f);
        DebugText("achmt1", "No Injuries: "+!injured_, 0.5f);
        DebugText("achmt2", "No First Strikes: "+no_first_strikes_, 0.5f);
        DebugText("achmt3", "No Counter Strikes: "+no_counter_strikes_, 0.5f);
        DebugText("achmt4", "No Kills: "+no_kills_, 0.5f);
        DebugText("achmt5", "No Alerts: "+no_alert_, 0.5f);
        DebugText("achmt6", "Time: "+no_win_time, 0.5f);
        //DebugText("achmt_damage0", "Block damage: "+total_block_damage_, 0.5f);
        //DebugText("achmt_damage1", "Impact damage: "+total_damage_, 0.5f);
        //DebugText("achmt_damage2", "Blood loss: "+total_blood_loss_, 0.5f);

        SavedLevel @level = GetSave();
        DebugText("saved_achmt0", "Saved Flawless: "+(level.GetValue("flawless")=="true"), 0.5f);
        DebugText("saved_achmt1", "Saved No Injuries: "+(level.GetValue("no_injuries")=="true"), 0.5f);
        DebugText("saved_achmt2", "Saved No Kills: "+(level.GetValue("no_kills")=="true"), 0.5f);
        DebugText("saved_achmt3", "Saved No Alert: "+(level.GetValue("no_alert")=="true"), 0.5f);
        DebugText("saved_achmt4", "Saved Time: "+level.GetValue("time"), 0.5f);
    }
    void Save() {
        SavedLevel @saved_level = GetSave();
        if(flawless_) saved_level.SetValue("flawless","true");
        if(!injured_) saved_level.SetValue("no_injuries","true");
        if(no_kills_) saved_level.SetValue("no_kills","true");
        if(no_alert_) saved_level.SetValue("no_alert","true");
        string time_str = saved_level.GetValue("time");
        if(time_str == "" || no_win_time < atof(saved_level.GetValue("time"))){
            saved_level.SetValue("time", ""+no_win_time);
        }
        save_file.WriteInPlace();
    }
    void PlayerWasHit() {
        flawless_ = false;
    }
    void PlayerWasInjured() {
        injured_ = true;
        flawless_ = false;
    }
    void PlayerAttacked() {
        no_first_strikes_ = false;
    }
    void PlayerSneakAttacked() {
        no_first_strikes_ = false;
    }
    void PlayerCounterAttacked() {
        no_counter_strikes_ = false;
    }
    void EnemyDied() {
        no_kills_ = false;
    }
    void EnemyAlerted() {
        no_alert_ = false;
    }
    void PlayerBlockDamage(float val) {
        total_block_damage_ += val;
        PlayerWasHit();
    }
    void PlayerDamage(float val) {
        total_damage_ += val;
        PlayerWasInjured();
    }
    void PlayerBloodLoss(float val) {
        total_blood_loss_ += val;
        PlayerWasInjured();
    }
    bool GetValue(const string &in key){
        if(key == "flawless"){
            return flawless_;
        } else if(key == "no_kills"){
            return no_kills_;
        } else if(key == "no_injuries"){
            return !injured_;
        }
        return false; 
    }
};

Achievements achievements;

bool HasFocus(){
    return (challenge_end_gui.target_visible == 1.0f);
}

void Reset(){
    time = 0.0f;
    reset_allowed = true;
    reset_timer = _reset_delay;
    achievements.Init();
    challenge_end_gui.target_visible = 0.0;
}

void ReceiveMessage(string msg) {
    TokenIterator token_iter;
    token_iter.Init();
    if(!token_iter.FindNextToken(msg)){
        return;
    }
    string token = token_iter.GetToken(msg);
    if(token == "reset"){
        Reset();
    } else if(token == "achievement_event"){
        token_iter.FindNextToken(msg);
        AchievementEvent(token_iter.GetToken(msg));
    } else if(token == "achievement_event_float"){
        token_iter.FindNextToken(msg);
        string str = token_iter.GetToken(msg);
        token_iter.FindNextToken(msg);
        float val = atof(token_iter.GetToken(msg));
        AchievementEventFloat(str, val);
    } else if(token == "victory_trigger_enter"){
        ++in_victory_trigger;
        in_victory_trigger = max(1,in_victory_trigger);
    } else if(token == "victory_trigger_exit"){
        --in_victory_trigger;
    }
}

void DrawGUI() {
    challenge_end_gui.DrawGUI();
}

void AchievementEvent(string event_str){
    if(event_str == "player_was_hit"){
        achievements.PlayerWasHit();
    } else if(event_str == "player_was_injured"){
        achievements.PlayerWasInjured();
    } else if(event_str == "player_attacked"){
        achievements.PlayerAttacked();
    } else if(event_str == "player_sneak_attacked"){
        achievements.PlayerSneakAttacked();
    } else if(event_str == "player_counter_attacked"){
        achievements.PlayerCounterAttacked();
    } else if(event_str == "enemy_died"){
        achievements.EnemyDied();
    } else if(event_str == "enemy_alerted"){
        achievements.EnemyAlerted();
    }
}

void AchievementEventFloat(string event_str, float val){
    if(event_str == "player_block_damage"){
        achievements.PlayerBlockDamage(val);
    } else if(event_str == "player_damage"){
        achievements.PlayerDamage(val);
    } else if(event_str == "player_blood_loss"){
        achievements.PlayerBloodLoss(val);
    }
}

string StringFromFloatTime(float time){
    string time_str;
    int minutes = int(time) / 60;
    int seconds = int(time)-minutes*60;
    time_str += minutes + ":";
    if(seconds < 10){
        time_str += "0";
    }
    time_str += seconds;
    return time_str;
}

class ChallengeEndGUI : AHGUI::GUI{
    float visible;
    float target_visible;
    RibbonBackground ribbon_background;

    ChallengeEndGUI() {
        restrict16x9(false);

        super();

        ribbon_background.Init();

        Init();
    }

    ~ChallengeEndGUI() {
        
    }

    void RegenerateGUI()  {


    }

    void Init() {
        visible = 0.0;
        target_visible = 0.0;

        //setGuides(true);
    }

    void Update(){
        visible = UpdateVisible(visible, target_visible);

        if( visible > 0.1 ) {
            if( isVisible == false) {
                CreateGUI();
            }
            setVisible(true); 
        } else {
            setVisible(false);
        }

        AHGUI::GUI::update();

        ribbon_background.Update();
        UpdateMusic();
    }

    void CreateGUI() {
        array<string> mission_objectives;
        array<string> mission_objective_colors;
        bool success = true;

        for(int i=0; i<level.GetNumObjectives(); ++i){
            string objective = level.GetObjective(i);
            string mission_objective;
            string mission_objective_color;
            if(objective == "destroy_all"){
                int threats_possible = ThreatsPossible();
                int threats_remaining = ThreatsRemaining();
                if(threats_possible <= 0){
                    mission_objective = "  Defeat all enemies (N/A)";
                    mission_objective_color = "red";
                } else {
                    if(threats_remaining == 0){
                        mission_objective += "v ";
                        mission_objective_color = "green";
                    } else {
                        mission_objective += "x ";
                        mission_objective_color = "red";
                        success = false;
                    }
                    mission_objective += "defeat all enemies (" ;
                    mission_objective += (threats_possible - threats_remaining);
                    mission_objective += "/" ;
                    mission_objective += threats_possible;
                    mission_objective += ")";
                }
            }
            if(objective == "reach_a_trigger"){
                if(in_victory_trigger > 0){
                    mission_objective += "v ";
                    mission_objective_color = "green";
                } else {
                    mission_objective += "x ";
                    mission_objective_color = "red";
                    success = false;
                }
                mission_objective += "Reach the goal";
            }
            if(objective == "must_visit_trigger"){
                if(NumUnvisitedMustVisitTriggers() == 0){
                    mission_objective += "v ";
                    mission_objective_color = "green";
                } else {
                    mission_objective += "x ";
                    mission_objective_color = "red";
                    success = false;
                }
                mission_objective += "Visit all checkpoints";
            }
            if(objective == "reach_a_trigger_with_no_pursuers"){
                if(in_victory_trigger > 0 && NumActivelyHostileThreats() == 0){
                    mission_objective += "v ";
                    mission_objective_color = "green";
                } else {
                    mission_objective += "x ";
                    mission_objective_color = "red";
                    success = false;
                }
                mission_objective += "Reach the goal without any pursuers";
            }

            if(objective == "collect"){
                if(NumUnsatisfiedCollectableTargets() != 0){
                    success = false;
                    mission_objective += "x ";
                    mission_objective_color = "red";
                }  else {
                    mission_objective += "v ";
                    mission_objective_color = "green";
                }
                mission_objective += "Collect items";
            }

            mission_objectives.insertLast(mission_objective);
            mission_objective_colors.insertLast(mission_objective_color);
        }

        string title = success ? 'challenge complete' : 'challenge incomplete';

        root.clear(); 

        AHGUI::Divider@ horiz_root = root.addDivider( DDTop,
                                                    DOHorizontal,
                                                    ivec2( UNDEFINEDSIZEI, 2000 ) );
        
        horiz_root.addSpacer(200);

        AHGUI::Divider@ mainpane = horiz_root.addDivider( DDLeft,
                                                    DOVertical,
                                                    ivec2( UNDEFINEDSIZEI, 2000 ) );


        mainpane.addSpacer(300,DDTop);

        AHGUI::Text titleText = AHGUI::Text(title, titleFont);

        AHGUI::Image titleUnderline = AHGUI::Image("Textures/ui/challenge_mode/divider_strip_c.tga");
        titleUnderline.scaleToSizeY(40);

        mainpane.addElement(titleText, DDTop);
        mainpane.addElement(titleUnderline, DDTop);

        AHGUI::Text titleObjectives = AHGUI::Text("Objectives", subTitleFont);

        mainpane.addElement( titleObjectives, DDTop ); 

        for( uint i = 0; i < mission_objectives.size(); i++ ) {
            AHGUI::FontSetup fs;

            if(mission_objective_colors[i] == "red") {
                fs = redValueFont;
            } else {
                fs = greenValueFont;
            }

            AHGUI::Text objective = AHGUI::Text("  " + mission_objectives[i], fs);
            mainpane.addElement(objective,DDTop);
        }


        AHGUI::Text titleTime = AHGUI::Text("Time", subTitleFont);

        mainpane.addElement( titleTime, DDTop ); 

        AHGUI::FontSetup timefont;
        if(success){
            timefont = greenValueFont;
        } else {
            timefont = redValueFont;
        }

        AHGUI::Text time1Text = AHGUI::Text("  " + StringFromFloatTime(no_win_time),timefont);
        mainpane.addElement( time1Text, DDTop ); 
       
        SavedLevel @saved_level = GetSave();
        float best_time = atof(saved_level.GetValue("time"));
        if(best_time > 0.0f){
            AHGUI::Text time2Text = AHGUI::Text("  " + StringFromFloatTime(no_win_time),tealValueFont);
            mainpane.addElement( time2Text, DDTop );
        }

        int player_id = GetPlayerCharacterID();
        if(player_id != -1){
            for(int i=0; i<level.GetNumObjectives(); ++i){
                string objective = level.GetObjective(i);
                if(objective == "destroy_all"){
                    AHGUI::Text titleEnemies = AHGUI::Text("Enemies", subTitleFont);

                    mainpane.addElement( titleEnemies, DDTop ); 

                    AHGUI::Divider@ kills = mainpane.addDivider( DDTop, 
                                                                 DOHorizontal, 
                                                                 ivec2( UNDEFINEDSIZEI, 100 ) );
                    MovementObject@ player_char = ReadCharacter(player_id);
                    int num = GetNumCharacters();
                    for(int j=0; j<num; ++j){
                        MovementObject@ char = ReadCharacter(j);
                        if(!player_char.OnSameTeam(char)){
                            int knocked_out = char.GetIntVar("knocked_out");
                            if(knocked_out == 1 && char.GetFloatVar("blood_health") <= 0.0f){
                                knocked_out = 2;
                            }
                            AHGUI::Image img;
                            switch(knocked_out){
                            case 0:    
                                img = AHGUI::Image("Textures/ui/challenge_mode/ok.png");
                                break;
                            case 1:    
                                img = AHGUI::Image("Textures/ui/challenge_mode/ko.png");
                                break;
                            case 2:    
                                img = AHGUI::Image("Textures/ui/challenge_mode/dead.png");
                                break;
                            }
                            img.scaleToSizeY(70);
                            kills.addElement(img,DDLeft);
                        }
                    }
                }
            }
        }

        AHGUI::Text titleExtra = AHGUI::Text("Extra", subTitleFont);

        mainpane.addElement( titleExtra, DDTop ); 

        int num_achievements = level.GetNumAchievements();
        for(int i=0; i<num_achievements; ++i){
            string achievement = level.GetAchievement(i);
            string display_str;

            AHGUI::FontSetup extraFont = redValueFont;

            if(saved_level.GetValue(achievement) == "true"){
                extraFont = tealValueFont;
            }
            if(achievements.GetValue(achievement)){
                extraFont = greenValueFont;
            }

            if(achievement == "flawless"){
                display_str += "flawless";
            } else if(achievement == "no_kills"){
                display_str += "no kills";
            } else if(achievement == "no_injuries"){
                display_str = "never hurt";
            } else if(achievement == "no_alert"){
                display_str = "never seen";
            }
            AHGUI::Text extra_val = AHGUI::Text(display_str, extraFont);
            mainpane.addElement( extra_val, DDTop );
        }

        AHGUI::Divider@ buttonpane = mainpane.addDivider( DDTop,
                                                      DOHorizontal,
                                                      ivec2(1000, 400) );

        AHGUI::Image quitButton = AHGUI::Image("Textures/ui/challenge_mode/quit_icon_c.tga");
        quitButton.scaleToSizeX(250);
        quitButton.addMouseOverBehavior( buttonHover );
        quitButton.addLeftMouseClickBehavior(AHGUI::FixedMessageOnClick("quit"));
        buttonpane.addElement(quitButton,DDLeft);

        AHGUI::Image retryButton = AHGUI::Image("Textures/ui/challenge_mode/retry_icon_c.tga");
        retryButton.scaleToSizeX(250);
        retryButton.addMouseOverBehavior( buttonHover );
        retryButton.addLeftMouseClickBehavior(AHGUI::FixedMessageOnClick("retry"));
        buttonpane.addElement(retryButton,DDLeft);

        if( success ) {
            AHGUI::Image continueButton = AHGUI::Image("Textures/ui/challenge_mode/continue_icon_c.tga");
            continueButton.scaleToSizeX(250); 
            continueButton.addMouseOverBehavior( buttonHover );
            continueButton.addLeftMouseClickBehavior(AHGUI::FixedMessageOnClick("continue"));
            buttonpane.addElement(continueButton,DDLeft);
        }
    }

    ~ChallengeEndGUI() {
    }

    void DrawGUI(){
        float ui_scale = 0.5f;

        ribbon_background.DrawGUI(visible);

        hud.Draw();

        AHGUI::GUI::render(); 
    }

    void processMessage( AHGUI::Message@ message ) {
        if( message.name == "quit" ) {
            level.SendMessage("go_to_main_menu");
        } else if( message.name == "retry" ) {
            level.SendMessage("reset"); 
        } else if( message.name == "continue" ) {
            target_visible = 0.0f;
            SendGlobalMessage("levelwin");
        }
    }
}

ChallengeEndGUI challenge_end_gui;


void Update() {
    bool display_achievements = false;
    if(display_achievements && GetPlayerCharacterID() != -1){
        achievements.UpdateDebugText();
    }
    challenge_end_gui.Update();
    time += time_step;
    VictoryCheckNormal();
}

int NumUnvisitedMustVisitTriggers() {
    int num_hotspots = GetNumHotspots();
    int return_val = 0;
    for(int i=0; i<num_hotspots; ++i){
        Hotspot@ hotspot = ReadHotspot(i);
        if(hotspot.GetTypeString() == "must_visit_trigger"){
            if(!hotspot.GetBoolVar("visited")){
                ++return_val;
            }
        }
    }
    return return_val;
}

int NumUnsatisfiedCollectableTargets() {
    int num_hotspots = GetNumHotspots();
    int return_val = 0;
    for(int i=0; i<num_hotspots; ++i){
        Hotspot@ hotspot = ReadHotspot(i);
        if(hotspot.GetTypeString() == "collectable_target"){
            if(!hotspot.GetBoolVar("condition_satisfied")){
                ++return_val;
            }
        }
    }
    return return_val;
}

void VictoryCheckNormal() {
    int player_id = GetPlayerCharacterID();
    if(player_id == -1){
        return;
    }
    bool victory = true;
    bool display_victory_conditions = false;

    float max_reset_delay = _reset_delay;
    for(int i=0; i<level.GetNumObjectives(); ++i){
        string objective = level.GetObjective(i);
        if(objective == "destroy_all"){
            int threats_remaining = ThreatsRemaining();
            int threats_possible = ThreatsPossible();
            if(threats_remaining > 0 || threats_possible == 0){
                victory = false;
                if(display_victory_conditions){
                    DebugText("victory_a","Did not yet defeat all enemies",0.5f);
                }
            }
        }
        if(objective == "reach_a_trigger"){
            max_reset_delay = 1.0;
            if(in_victory_trigger <= 0){
                victory = false;
                if(display_victory_conditions){
                    DebugText("victory_b","Did not yet reach trigger",0.5f);
                }
            }
        }
        if(objective == "reach_a_trigger_with_no_pursuers"){
            max_reset_delay = 1.0;
            if(in_victory_trigger <= 0){
                victory = false;
                if(display_victory_conditions){
                    DebugText("victory_c","Did not yet reach trigger",0.5f);
                }
            } else if(NumActivelyHostileThreats() > 0){
                victory = false;
                if(display_victory_conditions){
                    DebugText("victory_c","Reached trigger, but still pursued",0.5f);
                }
            } 
        }
        if(objective == "must_visit_trigger"){
            max_reset_delay = 1.0;
            if(NumUnvisitedMustVisitTriggers() != 0){
                victory = false;
                if(display_victory_conditions){
                    DebugText("victory_d","Did not visit all must-visit triggers",0.5f);
                }
            } 
        }
        if(objective == "collect"){
            max_reset_delay = 1.0;
            if(NumUnsatisfiedCollectableTargets() != 0){
                victory = false;
                if(display_victory_conditions){
                    DebugText("victory_d","Did not visit all must-visit triggers",0.5f);
                }
            } 
        }
    }
    reset_timer = min(max_reset_delay, reset_timer);

    bool failure = false;
    MovementObject@ player_char = ReadCharacter(player_id);
    if(player_char.GetIntVar("knocked_out") != _awake){
        failure = true;
    }
    if(reset_timer > 0.0f && (victory || failure)){
        reset_timer -= time_step;
        if(reset_timer <= 0.0f){
            if(reset_allowed){
                challenge_end_gui.target_visible = 1.0;
                reset_allowed = false;
            }
            if(victory){
                achievements.Save();
            }
        }
    } else {
        reset_timer = _reset_delay;
        no_win_time = time;
    }
}

void UpdateMusic() {
    int player_id = GetPlayerCharacterID();
    if(player_id != -1 && ReadCharacter(player_id).GetIntVar("knocked_out") != _awake){
        PlaySong("sad");
        return;
    }
    int threats_remaining = ThreatsRemaining();
    if(threats_remaining == 0){
        PlaySong("ambient-happy");
        return;
    }
    if(player_id != -1 && ReadCharacter(player_id).QueryIntFunction("int CombatSong()") == 1){
        PlaySong("combat");
        return;
    }
    PlaySong("ambient-tense");
}

A level script for challenge levels. Unused in the final game, as there are no challenge levels.


Data/Scripts/lugaruchallengelevel.as

#include "ui_effects.as"
#include "arena_meta_persistence.as"
#include "ui_tools.as"

enum ChallengeGUIState {
    agsFighting,
    agsEndScreen,
	agsInvalidState
};

class ChallengeGUI : AHGUI::GUI {

    // fancy ribbon background stuff
    float visible = 0.0f;
    float target_visible = 1.0f;

    ChallengeGUIState currentState = agsFighting; // Token for our state machine
    ChallengeGUIState lastState = agsInvalidState;    // Last seen state, to detect changes

    // Selection screen
    bool dataOutdated = false;
    JSON profileData;
    bool dataLoaded = false;
    int profileId = -1;         // Which profile are we working with
	string difficulty = "";
    string user_name;
	int score = 0;
    bool showBorders = true;
    int textSize = 60;
    int challengeTextSize = 120;
    vec4 textColor = vec4(0.8, 0.8, 0.8, 1.0);
    int minimapTextSize = 30;
    int minimapIconSize = 30;
    int levels_finished = -1;
    bool inputEnabled = false;
    array<string> inputName;
    bool bloodEffectEnabled = false;
    float bloodAlpha = 1.0;
    float bloodDisplayTime = 0.0f;
    float inputTime = 0.0f;
	JSONValue challengeData;
	int challengeLevelsFinished = -1;
	string levelName;
	float timerTime = 0;
	float challengeDuration = 0;
	bool timerStarted = false;
	int points = 0;
	int pointsToAdd = 0;
	int activeLevelIndex = 0;

    /*******************************************************************************************/
    /**
     * @brief  Constructor
     *
     */
    ChallengeGUI() {
        // Call the superclass to set things up
        super();

    }

	void handleStateChange() {
		//see if anything has changed
		if( lastState == currentState ) {
			return;
		}
		// Record the change
		lastState = currentState;
		// First clear the old screen
		clear();
		switch( currentState ) {
			case agsInvalidState: {
				// For completeness -- throw an error and move on
				DisplayError("GUI Error", "GUI in invalid state");
			}
			break;
			case agsFighting: {
				ShowFightingUI();
			}
			break;
			case agsEndScreen: {
				ShowEndScreenUI();
				updateHighscore();
				WritePersistentInfo(true);
			}
			break;
		}
	}
	void ShowFightingUI(){
        AHGUI::Divider@ mainPane = root.addDivider( DDTop, DOHorizontal, ivec2( AH_UNDEFINEDSIZE, AH_UNDEFINEDSIZE ) );
		mainPane.addSpacer( 50, DDLeft );
		DisplayText(mainPane, DDTop, "Score: " + points, challengeTextSize, textColor, true, "ScoreText");
        if(showBorders){
            mainPane.setBorderSize( 1 );
            mainPane.setBorderColor( 0.0, 0.0, 1.0, 0.6 );
            mainPane.showBorder();
        }
    }
	void ShowEndScreenUI(){
        AHGUI::Divider@ mainPane = root.addDivider( DDTop, DOVertical, ivec2( 2562, 1440 ) );
		mainPane.setHorizontalAlignment(BALeft);
		mainPane.setBackgroundImage("Textures/LugaruMenu/LugaruChallengeBackground.png");
		AHGUI::Divider@ title = mainPane.addDivider( DDTop, DOHorizontal, ivec2( AH_UNDEFINEDSIZE, 300 ) );
		DisplayText(title, DDCenter, "Level Cleared!", textSize, textColor, true);

		AHGUI::Divider@ scorePane = mainPane.addDivider( DDTop, DOVertical, ivec2( AH_UNDEFINEDSIZE, 300 ) );
		DisplayText(scorePane, DDTop, "Score:          " + (points + pointsToAdd), textSize, textColor, true);
		DisplayText(scorePane, DDTop, "Time:           " + GetTime(int(challengeDuration)), textSize, textColor, true);
		DisplayText(scorePane, DDTop, "Merciful", textSize, textColor, true);
		DisplayText(scorePane, DDTop, "Divide and Conquer", textSize, textColor, true);

		AHGUI::Divider@ footer = mainPane.addDivider( DDBottom, DOHorizontal, ivec2( AH_UNDEFINEDSIZE, 300 ) );
		DisplayText(footer, DDCenter, "Press escape to return to menu or space to continue", textSize, textColor, true);

        if(showBorders){
            mainPane.setBorderSize( 1 );
            mainPane.setBorderColor( 0.0, 0.0, 1.0, 0.6 );
            mainPane.showBorder();

			title.setBorderSize( 1 );
            title.setBorderColor( 1.0, 0.0, 1.0, 0.6 );
            title.showBorder();

			scorePane.setBorderSize( 1 );
            scorePane.setBorderColor( 1.0, 1.0, 1.0, 0.6 );
            scorePane.showBorder();

			footer.setBorderSize( 1 );
            footer.setBorderColor( 1.0, 0.0, 0.0, 0.6 );
            footer.showBorder();
        }
    }
	void updateHighscore(){
		for(uint i = 0; i < challengeData.size(); i++){
			if(challengeData[i]["levelname"].asString() == levelName){
				activeLevelIndex = i;
				int newScore = points + pointsToAdd;
				Log(info,"highscore " + challengeData[i]["highscore"].asInt() + " points " + newScore + "!");
				if(challengeData[i]["highscore"].asInt() < newScore){
					Log(info,"New Highscore!");
					challengeData[i]["highscore"] = JSONValue(newScore);
					challengeData[i]["besttime"] = JSONValue(int(challengeDuration));
					if(challengeLevelsFinished <= activeLevelIndex){
						challengeLevelsFinished++;
					}
				}else{
					Log(info,"Not a new Highscore :(");
				}
				break;
			}
		}
	}
	string GetTime(int seconds){
		string bestTime;
		int numSeconds = seconds % 60;
		int numMinutes = seconds / 60;

		if(numSeconds < 10){
			bestTime = numMinutes + ":0" + numSeconds;
		}else{
			bestTime = numMinutes + ":" + numSeconds;
		}
		return bestTime;
	}
	void processMessage( AHGUI::Message@ message ) {
        // Check to see if an exit has been requested
        if( message.name == "mainmenu" ) {
            //WritePersistentInfo( false );
            //this_ui.SendCallback("back");
        }
	}
	void update() {
        // Do state machine stuff
        handleStateChange();
        // Update the GUI
        AHGUI::GUI::update();
    }

	void render() {
	   hud.Draw();
	   // Update the GUI
	   AHGUI::GUI::render();
	}

    SavedLevel@ GetSave() {
        return save_file.GetSave("","linear_campaign_progress","lugaru_challanges");
    }

	void ReadPersistentInfo() {
        UpgradeSave();
        SavedLevel@ saved_level = GetSave();
        // First we determine if we have a session -- if not we're not going to read data
        JSONValue sessionParams = getSessionParameters();
        if( !sessionParams.isMember("started") ) {
            // Nothing is started, so we shouldn't actually read the data
            Log(info,"could not find started :(");
            return;
        }
        // read in campaign_started
        string profiles_str = saved_level.GetValue("lugaru_profiles");

        //Print("Profiles string : " + profiles_str + "\n");


        if( profiles_str != "" ) {
            // Parse the JSON
            if( !profileData.parseString( profiles_str ) ) {
                DisplayError("Persistence Error", "Unable to parse profile information");
            }

            // Now check the version
            if( profileData.getRoot()[ "version" ].asInt() == SAVEVERSION ) {
                dataOutdated = false;
            }
            else {
                // We have a save version from a previous incarnation
                // For now we'll just nuke it and restart
                dataOutdated = true;
                //profileData = generateNewProfileSet();
            }
        }
        dataLoaded = true;
        // Now see if we have a profile in this session -- if so load it
        JSONValue profiles = profileData.getRoot()["profiles"];
		Log(info,profileData.writeString(true));
		for( uint i = 0; i < profiles.size(); ++i ) {
			if(profiles[i]["active"].asString() == "true"){
				setDataFrom(profiles[i]["id"].asInt());
				break;
			}
		}
    }
	JSONValue getSessionParameters() {

        SavedLevel @saved_level = GetSave();

        string lugaru_session_str = saved_level.GetValue("lugaru_session");

        // sanity check
        if( lugaru_session_str == "" ) {
            lugaru_session_str = "{}";

            // write it back
            saved_level.SetValue("lugaru_session", lugaru_session_str );
            Log(info,"Write back the lugaru session");
        }

        JSON sessionJSON;

        // sanity check
        if( !sessionJSON.parseString( lugaru_session_str ) ) {
            DisplayError("Persistence Error", "Unable to parse session information");

        }
        return sessionJSON.getRoot();
    }
	void setDataFrom( int targetId ) {

		JSONValue profiles = profileData.getRoot()["profiles"];

		bool profileFound = false;

		for( uint i = 0; i < profiles.size(); ++i ) {
			if( profiles[ i ]["id"].asInt() == targetId ) {
				profileFound = true;
				// Copy all the values back
				profileId = targetId;
				levels_finished = profiles[ i ]["levels_finished"].asInt();
				user_name = profiles[ i ]["user_name"].asString();
				difficulty = profiles[ i ]["difficulty"].asString();
				challengeData = profiles[ i ]["challengeData"];
				challengeLevelsFinished = profiles[ i ]["challenge_levels_finished"].asInt();
				// We're done here
				break;
			}
		}
		// Sanity checking
		if( !profileFound ) {
			DisplayError("Persistence Error", "Profile id " + targetId + " not found in store.");
		}
	}
	void setSessionParameters( JSONValue session ) {
        SavedLevel @saved_level = GetSave();

        // set the value to the stringified JSON
        JSON sessionJSON;
        sessionJSON.getRoot() = session;
        string arena_session_str = sessionJSON.writeString(false);
        saved_level.SetValue("lugaru_session", arena_session_str );

        // write out the changes
        Log(info,"Writing session");
        save_file.WriteInPlace();

    }
    void WritePersistentInfo( bool moveDataToStore = true ) {

        // Make sure we've got information to write -- this is not an error
        if( !dataLoaded ) return;

        // Make sure our current data has been written back to the JSON structure
        if( moveDataToStore ) {
            Log(info,"Writing data to profile");
            writeDataToProfiles(); // This'll do nothing if we haven't set a profile
        }

        SavedLevel @saved_level = GetSave();

        // Render the JSON to a string
        string profilesString = profileData.writeString(false);

        // Set the value and write to disk
        saved_level.SetValue( "lugaru_profiles", profilesString );
        //Print("Profiles string : " + profilesString + "\n");
		Log( info, profileData.writeString(true) );
        save_file.WriteInPlace();

    }
	void writeDataToProfiles() {
        // Make sure that the data is good
        if( profileId == -1  ) {
            DisplayError("Persistence Error", "Trying to store an uninitialized profile.");
        }

        bool profileFound = false;

        for( uint i = 0; i < profileData.getRoot()["profiles"].size(); ++i ) {
            if( profileData.getRoot()["profiles"][ i ]["id"].asInt() == profileId ) {
                profileFound = true;

                // Copy all the values back
                profileData.getRoot()["profiles"][ i ][ "levels_finished" ] = JSONValue( levels_finished );
                profileData.getRoot()["profiles"][ i ][ "difficulty" ] = JSONValue( difficulty );
				profileData.getRoot()["profiles"][ i ]["challengeData"] = challengeData;
				profileData.getRoot()["profiles"][ i ]["challenge_levels_finished"] = JSONValue(challengeLevelsFinished);
                // We're done here
                break;
            }
        }
        // Sanity checking
        if( !profileFound ) {
            DisplayError("Persistence Error", "Profile id " + profileId + " not found in store.");
        }
    }
	void updateTimer(){
		bool triggered = true;
		array<int> characterIDs;
		GetCharacters(characterIDs);
		for(uint i = 0; i < characterIDs.size(); i++){
			MovementObject @char = ReadCharacterID(characterIDs[i]);
			if(!char.controlled && char.GetIntVar("knocked_out") == _awake){
				triggered = false;
			}
		}
		if(!timerStarted && triggered){
			timerStarted = true;
		}else if(timerStarted){
			timerTime += time_step;
			if(timerTime > 5.0f){
				currentState = agsEndScreen;
			}
		}
		challengeDuration += time_step;
	}
	void updatePoints(){
		if(pointsToAdd != 0){
			points++;
			pointsToAdd--;
			updateScore();
		}
	}
	void updateScore(){
		AHGUI::Text@ scoreText = cast<AHGUI::Text>(root.findElement( "ScoreText" ));
		if(scoreText !is null){
			scoreText.setText("Score " + points);
		}
	}
	void updateKeypresses(){
		if(currentState == agsEndScreen){
			if(GetInputPressed(0, "esc")){
				level.SendMessage("go_to_main_menu");
			}else if(GetInputPressed(0, "space")){
				//Load next challenge level
				string nextLevelName = challengeData[activeLevelIndex+1]["levelname"].asString();
				if(nextLevelName != ""){
					LoadLevel("LugaruChallenge/" + nextLevelName + ".xml");
				}
			}
		}
	}
	void DisplayText(AHGUI::Divider@ div, DividerDirection dd, string text, int textSize, vec4 color, bool shadowed, string textName = "singleSentence"){
        AHGUI::Text singleSentence( text, "OpenSans-Regular", textSize, color.x, color.y, color.z, color.a );
		singleSentence.setName(textName);
		singleSentence.setShadowed(shadowed);
        div.addElement(singleSentence, dd);
        if(showBorders){
            singleSentence.setBorderSize(1);
            singleSentence.setBorderColor(1.0, 1.0, 1.0, 1.0);
            singleSentence.showBorder( false );
        }
    }
}
ChallengeGUI challengeGUI;

void ReceiveMessage(string msg) {
    TokenIterator token_iter;
    token_iter.Init();
    if(!token_iter.FindNextToken(msg)){
        return;
    }
    string token = token_iter.GetToken(msg);
    if(token == "dispose_level"){
        gui.RemoveAll();
    } else if(token == "achievement_event"){
		//Print("Token: " + token + "\n");
        token_iter.FindNextToken(msg);
		//Print("Message: " + msg + "\n");
        AchievementEvent(token_iter.GetToken(msg));
    } else if(token == "achievement_event_float"){
		//Print("Token: " + token + "\n");
        token_iter.FindNextToken(msg);
        string str = token_iter.GetToken(msg);
		//Print("Message: " + str + "\n");
        token_iter.FindNextToken(msg);
        float val = atof(token_iter.GetToken(msg));
        AchievementEventFloat(str, val);
    }
}

void AchievementEvent(string event_str){
    if(event_str == "player_was_hit"){
        //achievements.PlayerWasHit();
    }
}

void AchievementEventFloat(string event_str, float val){
	if(challengeGUI.currentState == agsFighting){

	    if(event_str == "ai_damage"){
	        challengeGUI.pointsToAdd += int(val*100);
	    }
	}
}

bool HasFocus(){
    return false;
}

void Init(string str){
	challengeGUI.levelName = str;
    challengeGUI.ReadPersistentInfo();
}

void Update(){
    challengeGUI.update();
	challengeGUI.updateTimer();
	challengeGUI.updatePoints();
	challengeGUI.updateKeypresses();
}

void DrawGUI(){
    challengeGUI.render();
}

void Draw(){
}

bool CanGoBack(){
	return false;
}
void Dispose(){

}

A level script for Lugaru challenge levels. Unused in the final game, as there are no Lugaru challenge levels.


Data/Scripts/enemycontroldebug.as

class DebugTextWrapper
{
    int debugId = -1;  
    string prev_debug_string = ""; 
    bool active;

    DebugTextWrapper()
    {
        active = false;
    }

    ~DebugTextWrapper()
    {
        if( debugId != -1 )
        {
            DebugDrawRemove(debugId);
        }
    }

    void SetText( string debug_string, vec3 pos )
    {
        if( active )
        {
            if( prev_debug_string != debug_string && debugId != -1 )
            {
                DebugDrawRemove(debugId);
                debugId = -1;
            }

            if( debugId == -1 )
            {
                debugId = DebugDrawText(pos, debug_string, 1.0f, true, _persistent);
            }
            else
            {
                DebugSetPosition( debugId, pos ); 
            }
            prev_debug_string = debug_string;
        }
        else
        {
            if( debugId != -1 )
            {
                DebugDrawRemove(debugId);
            }
        }
    }

    void SetActive( bool a )
    {
        active = a;
    }
}

class DebugPath
{
    array<int> path_lines;

    DebugPath()
    {
    }

    ~DebugPath()
    {
        ClearPath();
    }

    void ClearPath()
    {
        for(int i=0; i<int(path_lines.length()); ++i){
            DebugDrawRemove(path_lines[i]);
        }
        path_lines.resize(0);
    }

    void UpdatePath()
    {
        ClearPath();
        int num_points = path.NumPoints();

        for(int i=1; i<num_points; i++){
            vec3 color(1.0f);
            uint32 flag = path.GetFlag(i-1);

            if( DT_STRAIGHTPATH_OFFMESH_CONNECTION & flag != 0 )
            {
                color = vec3(1.0f,0,0);
            }

            path_lines.insertLast(DebugDrawLine(path.GetPoint(i-1) + vec3(0.0, 0.1, 0.0), path.GetPoint(i) + vec3(0.0, 0.1, 0.0), color, _persistent));

            path_lines.insertLast(DebugDrawLine(path.GetPoint(i-1) + vec3(0.0, 0.1, 0.0), path.GetPoint(i-1) + vec3(0.0, 0.5, 0.0), vec3(1.0f,0,0),_persistent));
            path_lines.insertLast(DebugDrawLine(path.GetPoint(i) + vec3(0.0, 0.1, 0.0), path.GetPoint(i) + vec3(0.0, 0.5, 0.0), vec3(1.0f,0,0),_persistent));
        }
    }
}

class DebugInvestigatePoints
{
    array<int> path_lines;  
    DebugInvestigatePoints()
    {
    }

    ~DebugInvestigatePoints()
    {
        ClearPath();
    }

    void ClearPath()
    {
        for(int i=0; i<int(path_lines.length()); ++i){
            DebugDrawRemove(path_lines[i]);
        }
        path_lines.resize(0);
    }

    void UpdatePath()
    {
        ClearPath();
        int num_points = investigate_points.size();

        if( num_points > 0 )
        {
            path_lines.insertLast(
                DebugDrawLine(
                    this_mo.position + vec3(0.0, 0.1, 0.0), 
                    investigate_points[0].pos + vec3(0.0, 0.1, 0.0), 
                    vec3(5.0f,5.0f,1.0f), 
                    _persistent
                )
            );
            
        }

        for(int i=1; i<num_points; i++){
            path_lines.insertLast(DebugDrawLine(
                investigate_points[i-1].pos + vec3(0.0, 0.1, 0.0), 
                investigate_points[i].pos + vec3(0.0, 0.1, 0.0), 
                vec3(5.0f,5.0f,1.0f), 
                _persistent
                )
            );

            path_lines.insertLast(DebugDrawLine(
                investigate_points[i-1].pos + vec3(0.0, 0.1, 0.0), 
                investigate_points[i-1].pos + vec3(0.0, 0.5, 0.0), 
                vec3(0.0f,0.0f,1.0f), 
                _persistent
                )
            );

            path_lines.insertLast(DebugDrawLine(
                investigate_points[i].pos + vec3(0.0, 0.1, 0.0),
                investigate_points[i].pos + vec3(0.0, 0.5, 0.0),
                vec3(0.0f,0.0f,1.0f), 
                _persistent
                )
            );
        }
    }
}


DebugTextWrapper ai_state_debug;
void DebugDrawAIState()
{
    mat4 transform = this_mo.rigged_object().GetAvgIKChainTransform("head");
    vec3 head_pos = transform * vec4(0.0f,0.0f,0.0f,1.0f);
    head_pos += vec3(0,0.5f,0);

    //Log( warning, "" + head_pos )
    if( GetConfigValueBool( "debug_show_ai_state" ) )
    {
        ai_state_debug.SetActive( true  );
        ai_state_debug.SetText("Player "+this_mo.GetID() + "\n" + GetAIGoalString(goal) + "\n" + GetAISubGoalString(sub_goal) + "\n" + GetGeneralStateString(state) + "\n", head_pos );

        string label = "P"+this_mo.GetID()+"goal: ";
        string text = label;
        text += GetAIGoalString(goal) + ", " + GetAISubGoalString(sub_goal) + ", " + GetPathFindTypeString(path_find_type) + ", " + GetClimbStageString(trying_to_climb) + ", " + GetGeneralStateString(state);
        DebugText(label, text,0.1f);
    }
    else
    {
        ai_state_debug.SetActive( false );
    }
}

DebugPath debug_path;
DebugInvestigatePoints debug_investigate_points;
void DebugDrawAIPath()
{
    if( GetConfigValueBool( "debug_show_ai_path" ) )
    {
        debug_path.UpdatePath();
        debug_investigate_points.UpdatePath();
    }
    else
    {
        debug_path.ClearPath();
        debug_investigate_points.ClearPath();
    }
}

string GetAIGoalString( AIGoal g )
{
    switch( g )
    {
        case _patrol: return "_patrol";
        case _attack: return "_attack";
        case _investigate : return "_investigate";
        case _get_help: return "_get_help";
        case _escort: return "_escort";
        case _get_weapon: return "_get_weapon";
        case _navigate: return "_navigate";
        case _struggle: return "_struggle";
        case _hold_still: return "_hold_still";
        default: return "unknown";
    }
    return "unknown";
}

string GetAISubGoalString( AISubGoal g )
{
    switch( g )
    {
        case _unknown: return "_unknown";
        case _provoke_attack: return "_provoke_attack";
        case _avoid_jump_kick: return "_avoid_jump_kick";
        case _wait_and_attack: return "_wait_and_attack";
        case _rush_and_attack: return "_rush_and_attack";
        case _defend: return "_defend";
        case _surround_target: return "_surround_target";
        case _escape_surround: return "_escape_surround";
        case _investigate_slow: return "_investigate_slow";
        case _investigate_urgent: return "_investigate_urgent";
        case _investigate_body: return "_investigate_body";
        case _investigate_around: return "_investigate_around";
    }

    return "unknown";
}

string GetPathFindTypeString( PathFindType g )
{
    switch( g )
    {
        case _pft_nav_mesh: return "_pft_nav_mesh";
        case _pft_climb: return "_pft_climb";
        case _pft_drop: return "_pft_drop";
        case _pft_jump: return "_pft_jump";
    }
    return "Unknown";
} 

string GetClimbStageString(ClimbStage g )
{
    switch( g )
    {
        case _nothing: return "_nothing";
        case  _jump: return "_jump";
        case  _wallrun: return "_wallrun";
        case  _grab: return "_grab";
        case  _climb_up: return " _climb_up";
    }
    return "Unknown";
}

string GetGeneralStateString( int state )
{
    switch( state )
    {
        case _movement_state: return "movement_state";
        case _ground_state: return "ground_state";
        case _attack_state: return "attack_state";
        case _hit_reaction_state: return "hit_reaction_state";
        case _ragdoll_state: return "ragdoll_state";
    }
    return "unknown";
}

An enemy control script used for debugging character movement.


Data/Scripts/tutorial.as

#include "ui_effects.as"
#include "threatcheck.as"
#include "ui_tools.as"
#include "tutorial_assignment_checks.as"
#include "music_load.as"

bool resetAllowed = true;
float time = 0.0f;
float noWinTime = 0.0f;
string levelName;
int inVictoryTrigger = 0;
const float ResetDelay = 4.0f;
float resetTimer = ResetDelay;
int currentAssignment = 0;
int lastAssignment = -1;
bool showBorders = false;
int assignmentTextSize = 70;
int footerTextSize = 50;
int enemyID = -1;
bool enemyAttacking = false;
bool enemyHighlighted = false;
float highlightTimer = 0.0f;
float highlightDuration = 0.5f;
bool highlightEnemy = false;

string enemyRabbitPath = "Data/Objects/IGF_Characters/IGF_GuardActor.xml";
string enemyWolfPath = "Data/Objects/IGF_Characters/IGF_WolfActor.xml";

array<int> playerKnifeID = {-1,-1,-1};
int knifeIDCounter = 0;

int enemyKnifeID = -1;
string knifePath = "Data/Items/rabbit_weapons/rabbit_knife.xml";
string swordPath = "Data/Items/DogWeapons/DogSword.xml";

string enemyKnifePath = "Data/Items/rabbit_weapons/rabbit_knife.xml";
int screen_height = 1500;
int screen_width = 2560;
vec4 backgroundColor = vec4(0.0f,0.0f,0.0f,0.5f);
bool inCombat = false;

float spawn_knife_countdown = 0.0;

MusicLoad ml("Data/Music/challengelevel.xml");

class Assignment{
    string text;
	string extraText = "";
    string origText = "";
    AssignmentCallback@ callback;
    Assignment(string _text, AssignmentCallback _callback){
        text = _text;
        @callback = @_callback;
    }
}

class KnockoutCountdown {
    int character_id;
    float countdown;
}

array<KnockoutCountdown> knockout_countdown;

array<Assignment@> assignments =
{Assignment("Welcome to the tutorial! Complete the tasks, or skip them with tab.", Delay(5.0f)),

Assignment("Move the mouse to rotate the camera.", MouseMove()),
Assignment("Use @up@, @left@, @down@ and @right@ to move around.", WASDMove()), //TODO get the input names dynamically so that it works with controllers and keyboard
Assignment("Press @jump@ to jump. Holding the button will result in a longer jump.", SpaceJump()),
Assignment("Hold @crouch@ to crouch and move quietly.", ShiftSneak()),
Assignment("Press @crouch@ while moving to roll. Rolling is useful for putting out fires or quickly picking up weapons.", ShiftRoll()),
Assignment("Press @crouch@ in the air to flip. Try different directions, but be careful how you land!", ShiftFlip()),
Assignment("Jump into a wall to wall-run, then press @jump@ or @crouch@ to jump or flip off of it.", WallJump()),
Assignment("Hold the Right Mouse Button while close to a ledge to grab it, then move towards it to climb up.", LedgeGrab()),

Assignment("There is now an enemy in the middle of the training area.", SendInEnemy("rabbit")),
Assignment("Hold Left Mouse Button near the enemy to attack. Your attack changes based on your distance, which direction you're moving, and whether you're crouching.", AnyAttack()),
Assignment("Perform a rabbit kick by attacking while in the air close to the enemy. This is your most powerful attack, but also the most dangerous and difficult.", LegCannon()),
Assignment("Hold Right Mouse Button and sneak behind the enemy unnoticed to choke them.", ChokeHold()),

Assignment("The enemy can now block your attacks. Vary your attacks to break through his guard!", ActivateEnemy()),
Assignment("When your attack is blocked or dodged, you are vulnerable to throws. Attack the enemy and press Right Mouse Button at the right moment to escape from his throw.", ThrowEscape()),

Assignment("Click the Right Mouse Button to block incoming attacks. You can even block while lying on the ground!", BlockAttack()),
Assignment("When knocked over, you can press @crouch@ to quickly roll to your feet. Don't let enemies hit you while you're down.", RollFromGround()),
Assignment("If you keep holding Right Mouse Button after a block, you can attempt a throw.", ReverseAttack()),
Assignment("Defeat the enemy five times, however you like!", AttackCountDown()),
//Assignment("WEAPONS:", Delay(3.0f)),
Assignment("There's now a knife in the center of the training area. Hold @drop@ near the knife to pick it up. This also works while flipping or rolling!", PickUpKnife()),
Assignment("Sheathe and unsheathe weapons with the @item@ key.", SheatheKnife()),
Assignment("Cut the enemy by holding Left Mouse Click while in cutting range.", KillWolfSharp()),
Assignment("Grab the enemy from behind by pressing Right Mouse Click and press Left Mouse Click or @drop@ to perform a quick and silent execution.", KnifeGrabExecution()),
Assignment("When an enemy is nearby, throw the knife with @drop@.", KnifeThrow()),
Assignment("The enemy now has your knife! Without a weapon, you can't block sharp attacks. Try dodging by standing still, and then suddenly moving backwards or to the side.", Dodge()),
Assignment("After dodging, you can throw the enemy and take their weapon.", ReverseAttackArmed()),

Assignment("There is now a Wolf in the training area.", SendInEnemy("wolf")),
Assignment("Wolves are incredibly strong. Punching one is like punching a tree. The only unarmed rabbit attack they'll even notice is the jump kick.", LegCannon()),
Assignment("Pick up the sword on the pedestal.", PickUpSword()),
Assignment("Wolves are strong, but not impenetrable. Use the sword to kill the Wolf.", KillWolfSharp()),
Assignment("Try throwing the sword at the wolf using @drop@.", KnifeThrow()),
Assignment("You can increase the damage of thrown weapons by twisting them out again. Try holding @drop@ while flipping past the sword stuck in the wolf.", PullSword()),
Assignment("You are now ready to fight in a real battle! Press @quit@ and click 'main menu' to exit the tutorial, or 'retry' to start over.", EndLevel())};

string bottomText = "Press '@slow@' to skip to the next item. Press @quit@ to open the menu.";

string InsertKeysToString( string text )
{
    for( uint i = 0; i < text.length(); i++ ) {
        if( text[i] == '@'[0] ) {
            for( uint j = i + 1; j < text.length(); j++ ) {
                if( text[j] == '@'[0] ) {
                    string first_half = text.substr(0,i);
                    string second_half = text.substr(j+1);
                    string input = text.substr(i+1,j-i-1);
                    string middle = GetStringDescriptionForBinding("key", input);

                    text = first_half + middle + second_half;
                    i += middle.length();
                    break;    
                }
            }
        }
    }
    return text;
}

void Init(string _levelName) {
    bottomText = InsertKeysToString(bottomText);

    ActivateKeyboardEvents();
    levelName = _levelName;
    //lugaruGUI.AddFooter();
    lugaruGUI.AddInstruction();
}

class LugaruGUI : AHGUI::GUI {
    LugaruGUI() {
        // Call the superclass to set things up
        super();
    }
    void Render() {

        // Update the background
        // TODO: fold this into AHGUI
        hud.Draw();

        // Update the GUI
        AHGUI::GUI::render();
     }

     void processMessage( AHGUI::Message@ message ) {

        // Check to see if an exit has been requested
        if( message.name == "mainmenu" ) {
            //this_ui.SendCallback("back");
        }
    }

     void AddFooter() {
		AHGUI::Divider@ footerBackground = root.addDivider( DDBottomRight,  DOVertical, ivec2( AH_UNDEFINEDSIZE, 300 ) );
		footerBackground.setName("footerbackground");
		//Dark background
		AHGUI::Image background( "Textures/diffuse.tga" );
		background.setName("footerbackgroundimage");
		background.setColor(vec4(0.0,0.0,0.0,0.3));
		background.setSizeX(800);
		background.setSizeY(40 * 3);
		footerBackground.addFloatingElement(background, "footerbackgroundimage", ivec2(int(screen_width / 2.0f) - background.getSizeX() / 2, 0.0f), 0);

        AHGUI::Divider@ footer = footerBackground.addDivider( DDBottomRight,  DOVertical, ivec2( AH_UNDEFINEDSIZE, 300 ) );
        footer.setName("footer");
        footer.setVeritcalAlignment(BACenter);
        DisplayText(DDTop, footer, 8, bottomText, footerTextSize, vec4(0,0,0,1));
        if(showBorders){
            footer.setBorderSize( 10 );
            footer.setBorderColor( 0.0, 0.0, 1.0, 0.6 );
            footer.showBorder();
        }
    }
    void UpdateFooter(){
        AHGUI::Element@ footerElement = root.findElement("footer");
        if( footerElement is null  ) {
            DisplayError("GUI Error", "Unable to find footer");
        }
        AHGUI::Divider@ footer = cast<AHGUI::Divider>(footerElement);

        // Get rid of the old contents
        footer.clear();
        footer.clearUpdateBehaviors();
        footer.setDisplacement();
        DisplayText(DDTop, footer, 8, bottomText, footerTextSize, vec4(1,1,1,1));
    }
    void AddInstruction(){
		AHGUI::Divider@ container = root.addDivider( DDTop,  DOVertical, ivec2( AH_UNDEFINEDSIZE, 400 ) );
		container.setVeritcalAlignment(BACenter);
		AHGUI::Image background( "Textures/diffuse.tga" );
		background.setName("headerbackgroundimage");
		background.setColor(vec4(0.0,0.0,0.0,0.3));
		int sizeX = 1500;
		int sizeY = assignmentTextSize * 5;
		background.setSizeX(sizeX);
		background.setSizeY(sizeY);
		container.addFloatingElement(background, "headerbackground", ivec2(int(screen_width / 2.0f) - background.getSizeX() / 2, int(container.getSizeY() / 2.0f) - (sizeY / 2)), 0);
        AHGUI::Divider@ header = container.addDivider( DDCenter,  DOVertical, ivec2( AH_UNDEFINEDSIZE, AH_UNDEFINEDSIZE ) );
        header.setName("header");
        header.setVeritcalAlignment(BACenter);
        DisplayText(DDTop, header, 8, bottomText, assignmentTextSize, vec4(0,0,0,1));

        if(showBorders){
            header.setBorderSize( 10 );
            header.setBorderColor( 1.0, 0.0, 0.0, 0.6 );
            header.showBorder();

			container.setBorderSize( 10 );
            container.setBorderColor( 0.0, 1.0, 0.0, 0.6 );
            container.showBorder();
        }
    }
    void UpdateInstruction(bool fadein = false){
        AHGUI::Element@ headerElement = root.findElement("header");
        if( headerElement is null  ) {
            DisplayError("GUI Error", "Unable to find header");
        }
        AHGUI::Divider@ header = cast<AHGUI::Divider>(headerElement);
        // Get rid of the old contents
        header.clear();
        header.clearUpdateBehaviors();
        header.setDisplacement();
        if(lastAssignment != -1 && uint(lastAssignment) < assignments.size()){
            DisplayText(DDTop, header, 8, assignments[lastAssignment].text, assignmentTextSize, vec4(1,1,1,1), assignments[lastAssignment].extraText, footerTextSize);
        }
    }

    void HandleAssignmentChange(){
        if(currentAssignment == lastAssignment || uint(currentAssignment) == assignments.size()){
            return;
        }
        if(lastAssignment != -1 && uint(lastAssignment) < assignments.size()) {
			assignments[lastAssignment].callback.OnCompleted();
		}
        //Print("INITIALIZED THE NEXT ASSIGNMENT!----------\n");
        lastAssignment = currentAssignment;
        //UpdateFooter();
        UpdateInstruction();
        if(lastAssignment != -1 && uint(lastAssignment) < assignments.size()) {
            assignments[lastAssignment].callback.Init();
            if(assignments[lastAssignment].origText == ""){
                assignments[lastAssignment].origText = assignments[lastAssignment].text;
            }
        }
    }
    void Update(){
        if( spawn_knife_countdown > 0.0f ) {
            spawn_knife_countdown -= time_step;
            if( (spawn_knife_countdown > 0.0f) == false ) {
                SendInWeapon("");
            }
        }
        HandleAssignmentChange();
        CheckCurrentAssignment();
        AHGUI::GUI::update();
        ReviveCharacters();
        HandleEnemyHighlight();
    }

    void HandleEnemyHighlight(){
        if( enemyID != -1 ) {
            if(highlightEnemy){
                MovementObject@ enemy = ReadCharacterID(enemyID);
                if(enemyAttacking){
                    if(!enemyHighlighted){
                        //Print("Setting color to red\n");
                        Object@ obj = ReadObjectFromID(enemyID);
                        for(int i=0; i<4; ++i){
                            obj.SetPaletteColor(i, vec3(1,0,0));
                        }
                        enemyHighlighted = true;
                        highlightTimer = 0.0f;
                    }
                }
            }
            if( enemyHighlighted ) {
                if(highlightTimer > highlightDuration ) {
                    //Print("Setting color to white\n");
                    enemyAttacking = false;
                    enemyHighlighted = false;
                    Object@ obj = ReadObjectFromID(enemyID);
                    for(int i=0; i<4; ++i) {
                        obj.SetPaletteColor(i, vec3(1));
                    }
                } else {
                    highlightTimer += time_step;
                }
            }
        }
    }

    void DisplayText(DividerDirection dd, AHGUI::Divider@ div, int maxWords, string text, int textSize, vec4 color, string extraText = "", int extraTextSize = 0) {
        //The maxWords is the amount of words per line.
        array<string> sentences;

        text = InsertKeysToString( text );

        array<string> words = text.split(" ");
        string sentence;
        for(uint i = 0; i < words.size(); i++){
            sentence += words[i] + " ";
            if((i+1) % maxWords == 0 || words.size() == (i+1)){
                sentences.insertLast(sentence);
                sentence = "";
            }
        }
        for(uint k = 0; k < sentences.size(); k++){
            AHGUI::Text singleSentence( sentences[k], "OpenSans-Regular", textSize, color.x, color.y, color.z, color.a );
			singleSentence.setShadowed(true);
            //singleSentence.addUpdateBehavior( AHGUI::FadeIn( 1000, @inSine ) );
            div.addElement(singleSentence, dd);
            if(showBorders){
                singleSentence.setBorderSize(1);
                singleSentence.setBorderColor(1.0, 1.0, 1.0, 1.0);
                singleSentence.showBorder();
            }
        }
		if(extraText != ""){
			AHGUI::Text extraSentence( extraText, "OpenSans-Regular", extraTextSize, color.x, color.y, color.z, color.a );
			extraSentence.setShadowed(true);
			div.addElement(extraSentence, dd);
		}
	}
    void CheckCurrentAssignment() {
		/*else if(GetInputPressed(0, "esc")){
			level.SendMessage("dispose_level");
	        LoadLevel("back");
		}*/
        //Print("Got returned " + newBool + "\n");
        if(lastAssignment != -1 && uint(lastAssignment) < assignments.size()){
            if(assignments[lastAssignment].callback.CheckCompleted() ){
                currentAssignment++;
            }
        }
    }
}


void Reset(){
    time = 0.0f;
    resetAllowed = true;
    resetTimer = ResetDelay;
}

void PostReset(){
    for(uint i = 0; i < assignments.size(); i++){
        assignments[i].callback.Reset();
    }
    currentAssignment = 0;
    lastAssignment = -1;
}

int GetPlayerID() {
    int id = -1;
    int num = GetNumCharacters();
    for(int i=0; i<num; ++i){
        MovementObject@ char = ReadCharacter(i);
        if(char.controlled){
            id = char.GetID();
			break;
        }
    }
    return id;
}

void ReceiveMessage(string msg) {
    TokenIterator token_iter;
    token_iter.Init();

    if(!token_iter.FindNextToken(msg)){
        return;
    }
    string token = token_iter.GetToken(msg);
    //Log(info,"Token: " + token);
    //Log(info,"Full: " + msg);
    if( token == "hotspot_announce_items" ) {
        //If one of the player knifes leaves the knife area, respawn a new knife
        token_iter.FindNextToken(msg); 
        string source_id = token_iter.GetToken(msg);

        if( source_id == "player_knife_monitor" ) {
            token_iter.FindNextToken(msg); 
            string type = token_iter.GetToken(msg);

            if( type == "inside_list" ) {
                bool found_knife_on_zone = false;
                bool have_active_weapon = false;
                while( token_iter.FindNextToken(msg) ) {
                    string item_id = token_iter.GetToken(msg);

                    for( uint i = 0; i < playerKnifeID.length(); i++ ) {
                        if( playerKnifeID[i] != -1 ) {
                            have_active_weapon = true;
                        }
                        if( ("" + playerKnifeID[i]) == item_id ) {
                            found_knife_on_zone = true;
                        }
                    }
                }

                if( found_knife_on_zone == false && have_active_weapon == true ) {
                    spawn_knife_countdown = 5.0f;
                }
            }
        }
    } else if(token == "reset") {
        Reset();
    } else if(token == "achievement_event") {
        token_iter.FindNextToken(msg);
        string achievement = token_iter.GetToken(msg);
        Log(info,"achievement: " + achievement);
        if( lastAssignment >= 0 && lastAssignment < int(assignments.size()) ) {
            assignments[lastAssignment].callback.ReceiveAchievementEvent(achievement);
        }
        if(achievement == "ai_attacked"){
            if(!enemyAttacking){
                enemyAttacking = true;
            }
        }
    } else if(token == "achievement_event_float"){
        token_iter.FindNextToken(msg);
        string achievement = token_iter.GetToken(msg);
        Log(info, "achievement: " + achievement);
        token_iter.FindNextToken(msg);
        string value = token_iter.GetToken(msg);
        Log(info, "value: " + value);
        if( lastAssignment >= 0 && lastAssignment < int(assignments.size()) ) {
            assignments[lastAssignment].callback.ReceiveAchievementEventFloat(achievement, atof(value));
        }
    } else if(token == "send_in_enemy") {
        if( token_iter.FindNextToken(msg) ) {
            SendInEnemyChar( token_iter.GetToken(msg) );
        }
    } else if(token == "give_enemy_knife" ) {
        GiveEnemyKnife();
    } else if(token == "delete_enemy") {
        if( enemyKnifeID != -1 ) {
            DeleteObjectID(enemyKnifeID);
        }
        enemyKnifeID = -1;
        if( enemyID != -1 ) {
            DeleteObjectID(enemyID);
        }
        enemyID = -1;
    } else if(token == "send_in_weapon") {
        if( token_iter.FindNextToken(msg) ) {
            SendInWeapon(token_iter.GetToken(msg));
        }
    } else if(token == "delete_weapon") {
        for( uint i = 0; i < playerKnifeID.length(); i++ )
        {
            if( playerKnifeID[i] != -1 )
                DeleteObjectID(playerKnifeID[i]);
            playerKnifeID[i] = -1;
        }
    } else if(token == "set_highlight") {
		token_iter.FindNextToken(msg);
        string command = token_iter.GetToken(msg);
		if(command == "true") {
			highlightEnemy = true;
		} else if(command == "false") {
			highlightEnemy = false;
		}
	} else if(token == "post_reset") {
        PostReset();
    } else if(token == "character_knocked_out" || token == "character_died" || token == "cut_throat") {
		token_iter.FindNextToken(msg);
        string character_id = token_iter.GetToken(msg);
        
        KnockoutCountdown kc;
        kc.character_id = parseInt(character_id);
        kc.countdown = 2.0f;

        knockout_countdown.insertLast(kc);
        Log( info, "Added player " + character_id + " to queue for reawekening" );
	} else if(token == "revive_all") {
		for(int i = 0; i < GetNumCharacters(); i++){
            MovementObject@ char = ReadCharacter(i);
            char.Execute("Recover();");
        }
    } else if(token == "level_execute"){
        token_iter.FindNextToken(msg);
        string command = token_iter.GetToken(msg);
        level.Execute(command);
    } else if(token == "player_execute"){
        token_iter.FindNextToken(msg);
        string command = token_iter.GetToken(msg);
		int playerID = GetPlayerID();
		if(playerID != -1){
	        MovementObject@ player = ReadCharacterID(playerID);
	        player.Execute(command);
		}
    } else if(token == "enemy_execute"){
        token_iter.FindNextToken(msg);
        string command = token_iter.GetToken(msg);
		if(enemyID != -1){
            MovementObject@ enemy = ReadCharacterID(enemyID);
            //Print("Command: " + command + "\n");
            enemy.Execute(command);
        }
    }else if(token == "update_text_variables"){
        array<string> values;
        string lastVariable = "";
        while(true){
            bool nextToken = token_iter.FindNextToken(msg);
            if(nextToken){
                string new_value = token_iter.GetToken(msg);
                lastVariable = new_value;
                values.insertLast(new_value);
            }else{
                break;
            }
        }
        UpdateTextVariables(values);
	}else if(token == "extra_assignment_text"){
		string completeSentence;
		while(true){
            bool nextToken = token_iter.FindNextToken(msg);
            if(nextToken){
                completeSentence += token_iter.GetToken(msg);
				completeSentence += " ";
            }else{
                break;
            }
        }
		AddExtraAssignmentText(InsertKeysToString(completeSentence));
    }else if(token == "set_combat"){
		token_iter.FindNextToken(msg);
        string command = token_iter.GetToken(msg);
		if(command == "true"){
			inCombat = true;
		}else if(command == "false"){
			inCombat = false;
		}
	} else if(token == "player_invincible") {
        int player_id = GetPlayerID();
        if( player_id != -1 ) {
            Log(info, "PLAYER CAN'T DIE NOW" );
            MovementObject@ player = ReadCharacterID(player_id);
            player.Execute("invincible = true;"); 
        }
    }
}

LugaruGUI lugaruGUI;

bool HasFocus(){
    return false;
}

void DrawGUI() {
    lugaruGUI.Render();
}

string StringFromFloatTime(float time){
    string time_str;
    int minutes = int(time) / 60;
    int seconds = int(time)-minutes*60;
    time_str += minutes + ":";
    if(seconds < 10){
        time_str += "0";
    }
    time_str += seconds;
    return time_str;
}

void Update() {
    time += time_step;
    lugaruGUI.Update();
    SetPlaceholderPreviews();
	UpdateMusic();
}

void UpdateTextVariables(array<string> new_variables){
    string currentText = "";
    if( lastAssignment >= 0 && lastAssignment < int(assignments.size()) ) {
        currentText = assignments[lastAssignment].origText;
    }
    //Don't do anything if the string is empty. This is because it's not been assigned yet.
    if(currentText == ""){
        return;
    }
    for(uint i = 0; i<new_variables.size(); i++){
        currentText = join(currentText.split("%variable" + i + "%"), new_variables[i]);
    }
    if(lastAssignment != -1 && uint(lastAssignment) < assignments.size()) {
        assignments[lastAssignment].text = currentText;
    }
    //Print("New text " + currentText + "\n");
    lugaruGUI.UpdateInstruction();
}

void AddExtraAssignmentText(string extra_text){
    if(lastAssignment != -1 && uint(lastAssignment) < assignments.size()) {
        assignments[lastAssignment].extraText = extra_text;
    }
    lugaruGUI.UpdateInstruction();
}

void Initialize(){

}

void SendInEnemyChar(string type){
    string enemyPath;

    if( type == "wolf" ) {
        enemyPath = enemyWolfPath;
    } else {
        enemyPath = enemyRabbitPath;
    }

    if( enemyKnifeID != -1 ) {
        DeleteObjectID(enemyKnifeID);
    }
    enemyKnifeID = -1;

    if(enemyID != -1) {
        DeleteObjectID(enemyID);
        enemyID = -1;
    }
    //Don't spawn more than one enemy.
    if(enemyID == -1){
        enemyID = CreateObject(enemyPath);
        Object@ charObj = ReadObjectFromID(enemyID);
        //At first the enemy can't fight back and cannot die
        MovementObject@ enemy = ReadCharacterID(enemyID);
        enemy.Execute("SetHostile(false);
                       combat_allowed = false;
                       allow_active_block = false;");
        //Find the enemy spawn placeholder and put the new enemy at that point.
        array<int> @object_ids = GetObjectIDs();
        int num_objects = object_ids.length();
        for(int i=0; i<num_objects; ++i){
            Object @obj = ReadObjectFromID(object_ids[i]);
            ScriptParams@ params = obj.GetScriptParams();
            if(params.HasParam("Name")){
                string name_str = params.GetString("Name");
                if("enemy_spawn" == name_str){
                    charObj.SetTranslation(obj.GetTranslation());
                    break;
                }
            }
        }
		array<int> nav_points = GetObjectIDsType(33);
		if(nav_points.size() > 0){
			Object@ navObj = ReadObjectFromID(nav_points[0]);
			navObj.ConnectTo(charObj);
		}

    } else {
        MovementObject@ enemy = ReadCharacterID(enemyID);
        enemy.Execute("combat_allowed = false; 
                       allow_active_block = false;");
    }

    if(enemyID != -1){
        MovementObject@ enemy = ReadCharacterID(enemyID);
        if( type == "wolf" ) {
            enemy.Execute("max_ko_shield = 3; ko_shield = max_ko_shield;");
        } else {
            enemy.Execute("p_block_skill = 0.5;");
        }
    }
}

void GiveEnemyKnife() {
    Log(info, "Giving enemy knife");
    
    if( enemyID != -1 ) {
        if(enemyKnifeID != -1 ) {
            DeleteObjectID(enemyKnifeID);
        }
        enemyKnifeID = CreateObject(enemyKnifePath);
        //Object@ knifeObj = ReadObjectFromID(enemyKnifeID);
        MovementObject@ enemy = ReadCharacterID(enemyID);
        enemy.Execute("AttachWeapon(" + enemyKnifeID + ");");
    } else {
        Log(error, "Unable to give enemy knife, no enemy");
    }
}

string last_type = "knife";
void SendInWeapon( string type ) {
    string weapon;

    if( type == "" ) {
        type = last_type;
    }
    last_type = type;   

    if( type == "sword" ) {
        weapon = swordPath;
    } else {
        weapon = knifePath;
    }
      
    Log( info, "Spawning new player knife" );

    int player_id = GetPlayerID();
    knifeIDCounter = (knifeIDCounter + 1) % playerKnifeID.length();

    int knifeID =  playerKnifeID[knifeIDCounter];
    if(knifeID != -1 ) {
        DeleteObjectID(knifeID);
        knifeID = -1;
    }
    if(knifeID == -1){
        knifeID = CreateObject(weapon);
        Object@ knifeObj = ReadObjectFromID(knifeID);
        //Find the knife spawn placeholder and put the new knife at that point.
        array<int> @object_ids = GetObjectIDs();
        int num_objects = object_ids.length();
        for(int i=0; i<num_objects; ++i){
            Object @obj = ReadObjectFromID(object_ids[i]);
            ScriptParams@ params = obj.GetScriptParams();
            if(params.HasParam("Name")){
                string name_str = params.GetString("Name");
                if("weapon_spawn" == name_str){
                    knifeObj.SetTranslation(obj.GetTranslation());
                    knifeObj.SetRotation(obj.GetRotation());
                    break;
                }
            }
        }
    }
    playerKnifeID[knifeIDCounter] = knifeID;
}

bool HavePlayerKnifeInWorld() {
    for( uint i = 0; i < playerKnifeID.length(); i++ )
    {
        if( playerKnifeID[i] != -1 )
            return true;
    }
    return false;
}

int GetCharPrimaryWeapon(MovementObject@ mo) {
    return mo.GetArrayIntVar("weapon_slots",mo.GetIntVar("primary_weapon_slot"));
}

int GetCharPrimarySheathedWeapon(MovementObject@ mo) {
    return mo.GetArrayIntVar("weapon_slots", 3);
}

void ReviveCharacters() {
    for( uint i = 0; i < knockout_countdown.length(); i++ ) {
        if( knockout_countdown[i].countdown < 0.0f ) {
            if( knockout_countdown[i].character_id != -1 ){
                MovementObject@ char = ReadCharacterID(knockout_countdown[i].character_id);
                if( char !is null ) {
                    if(char.GetIntVar("knocked_out") != _awake){
                        char.Execute("Recover();");
                        if(!char.controlled) {
                            int playerID = GetPlayerID();
                            if(playerID != -1) {
                                char.Execute("situation.Notice(" + playerID + ");");
                            }
                        }
                    }
                }
            }
            knockout_countdown.removeAt(i);
            i -= 1;
        } else {
            knockout_countdown[i].countdown -= time_step;
        }
    }
}

void SomeFunction(){
    Log(info, "works");
}

// Attach a specific preview path to a given placeholder object
void SetSpawnPointPreview(Object@ spawn, string &in path){
    PlaceholderObject@ placeholder_object = cast<PlaceholderObject@>(spawn);
    placeholder_object.SetPreview(path);
}

// Find spawn points and set which object is displayed as a preview
void SetPlaceholderPreviews() {
    array<int> @object_ids = GetObjectIDs();
    int num_objects = object_ids.length();
    for(int i=0; i<num_objects; ++i){
        Object @obj = ReadObjectFromID(object_ids[i]);
        ScriptParams@ params = obj.GetScriptParams();
        if(params.HasParam("Name")){
            string name_str = params.GetString("Name");
            if("enemy_spawn" == name_str){
                SetSpawnPointPreview(obj, "Data/Objects/IGF_Characters/IGF_Guard.xml");
            }else if("weapon_spawn" == name_str){
                SetSpawnPointPreview(obj, "Data/Objects/Weapons/rabbit_weapons/rabbit_knife.xml");
            }else if("bush_spawn" == name_str){
                SetSpawnPointPreview(obj, "Data/Objects/Plants/Trees/temperate/green_bush.xml");
            }else if("pillar_spawn" == name_str){
                SetSpawnPointPreview(obj, "Data/Objects/Buildings/pillar1.xml");
            }
        }
    }
}

void UpdateMusic() {
	if(inCombat){
		PlaySong("combat");
        return;
	}else{
		PlaySong("ambient-happy");
        return;
	}
}

void KeyPressed( string command, bool repeated ) {
	Log(info,"Pressed " + command);
    if( command == "n" && repeated == false ) {
        if(lastAssignment != -1 && uint(lastAssignment) < assignments.size()){
            currentAssignment++;
        }
    }
}

void KeyReleased( string command ) {
}

A level script meant for the cut Tutorial level.


Data/Scripts/tutorial_assignment_checks.as

class AssignmentCallback
{
	float time;
	float delay;
	bool timerStarted = false;
	bool completed = false;
	bool disabled = false;
	MovementObject@ player = ReadCharacter(GetPlayerCharacter());

    void Completed(){}
    //Every assignment has some functions that are the same.
    //Handling them in the baseclass saves some duplicate code.
    void Init(){}
    bool CheckCompleted(){
		//Don't start the timer if it's already triggered.
		if(GetInputPressed(0, "tab")){
			return true;
        }
		if(!disabled){
			if(timerStarted){
	    		UpdateTimer();
	    	}else if(!timerStarted && !completed){
	    		Completed();
	    	}
	    	if(completed){
	    		disabled = true;
	    	}
	    	//When the assignment is completed it only needs to send back ONE true.
	    	return completed;
		}else{
			return false;
		}
    }
	void OnCompleted(){}
    void StartTimer(float _delay){
    	if(!timerStarted){
    		//Print("THE ASSIGNMENT IS COMPLETED!-----------\n");
    		PlaySound("Data/Sounds/lugaru/consolesuccess.ogg");
			delay = _delay;
			timerStarted = true;
		}
    }
    void StartTimerNoSound(float _delay){
    	if(!timerStarted){
			delay = _delay;
			timerStarted = true;
		}
    }
    void UpdateTimer(){
		time += time_step;
		if(time > delay){
			completed = true;
		}
    }
    void Reset(){
		time = 0;
		//delay = 0;
		timerStarted = false;
		completed = false;
		disabled = false;
		LocalReset();
    }

    void LocalReset(){

    }

    int GetPlayerCharacter() {
	    int num = GetNumCharacters();
	    for(int i=0; i<num; ++i){
	        MovementObject@ char = ReadCharacter(i);
	        if(char.controlled){
	        	return char.GetID();
	        }
	    }
	    return 0;
	}
	void LevelExecute(string command){
		SendCommand("level_execute", command);
	}
	void EnemyExecute(string command){
		SendCommand("enemy_execute", command);
	}
	void PlayerExecute(string command){
		SendCommand("player_execute", command);
	}
	void SendCommand(string firstCommand, string secondCommand) {
		//Remove all the spaces from the actual command or else the command will be split up in the ReceiveMessage
		level.SendMessage(firstCommand + " " + join(secondCommand.split( " " ), "" ));
	}
	void SendCommand(string singleCommand) {
		level.SendMessage(singleCommand);
	}
	void ReceiveAchievementEvent(string _achievement){}
	void ReceiveAchievementEventFloat(string _achievement, float _value){}
}

class Delay : AssignmentCallback
{
	Delay(float _delay){delay = _delay;}
	void Completed()
	{
		//Wait for n seconds and then continue.
		StartTimerNoSound(delay);
	}
}

class TabToContinue : AssignmentCallback
{
	TabToContinue(){}
	void Init(){
		SendCommand("extra_assignment_text " + "Press @slow@ to continue." );
	}
	void Completed(){
	}
}

class MouseMove : AssignmentCallback
{
	float negX;
	float posX;
	float negY;
	float posY;
	float threshold;
	float prevXAxis;
	float prevYAxis;

	void Init(){
		negX = 0;
		posX = 0;
		negY = 0;
		posY = 0;
		threshold = 100.0f;
		prevXAxis = 0;
		prevYAxis = 0;
	}

    void Completed()
    {
    	//To make sure the player has looked in every direction add the movement
    	float diffXAxis = GetLookXAxis(player.controller_id) - prevXAxis;
    	float diffYAxis = GetLookYAxis(player.controller_id) - prevYAxis;
    	if(diffXAxis < 0){
    		negX += (diffXAxis * -1);
    	}else if(diffXAxis > 0){
    		posX += diffXAxis;
    	}
    	if(diffYAxis < 0){
    		negY += (diffYAxis * -1);
    	}else if(diffYAxis > 0){
    		posY += diffYAxis;
    	}
    	float success = ((negX + negY + posX + posY) / threshold) * 0.25f;
    	DebugText("Success", "Success: "+success, 0.5f);
    	//Checking for all mousemovements are large enough
    	if(success >= 1.0){
    		StartTimer(1.0f);
    	}
    }
}

class WASDMove : AssignmentCallback
{

	float success;

	void Init(){
		success = 0.0f;
	}

	void Completed()
	{
		if(length(player.velocity) > 1.0f){
			success += time_step * 0.2;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}

class SpaceJump : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "player_jumped"){
			success += 0.2f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}

class ShiftCrouch : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void Completed()
	{
		if(GetInputDown(0, "crouch") && player.GetBoolVar("on_ground")){
			success += time_step * 0.25f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}

class ShiftRoll : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "character_start_roll"){
			success += 0.2f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}

class ShiftFlip : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "character_start_flip"){
			success += 0.2f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}

class ShiftSneak : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void Completed()
	{
		if(GetInputDown(0, "crouch") && length(player.velocity) > 1.0f && player.GetBoolVar("on_ground")){
			success += time_step * 0.2;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}
class AnimalRun : AssignmentCallback
{
	void Completed()
	{
		if(GetInputDown(0, "crouch")){
			StartTimer(1.0f);
		}
	}
}
class WallJump : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "jump_off_wall" || _achievement == "wall_flip"){
			success += 0.34;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}
class WallFlip : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "wall_flip"){
			StartTimer(1.0f);
		}
	}
}

class SendInEnemy : AssignmentCallback
{
    string type;
    SendInEnemy(string _type) {
        type = _type; 
    }

	void LocalReset(){
		level.SendMessage("delete_enemy");
	}

	void Init() {
		level.SendMessage("send_in_enemy " + type);
		StartTimerNoSound(3.0f);
	}
}
class AnyAttack : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(	_achievement == "attack_stationary_close" ||
			_achievement == "attack_stationary_far" ||
			_achievement == "attack_moving_close" ||
			_achievement == "attack_moving_far" ||
			_achievement == "attack_low")
		{
			success += 0.1f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}
class AttackCloseStationary : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "attack_stationary_close"){
			StartTimer(1.0f);
		}
	}
}
class AttackFarStationary : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "attack_stationary_far"){
			StartTimer(1.0f);
		}
	}
}
class AttackCloseMoving : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "attack_moving_close"){
			StartTimer(1.0f);
		}
	}
}
class KneeStrike : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "attack_stationary_close"){
			StartTimer(1.0f);
		}
	}
}
class SpinKick : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "attack_moving_far"){
			StartTimer(1.0f);
		}
	}
}
class Sweep : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "attack_low"){
			StartTimer(1.0f);
		}
	}
}
class LegCannon : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "leg_cannon_hit"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}

class ChokeHold : AssignmentCallback
{
	void Init(){
		EnemyExecute("always_unaware = true;");
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "choke_hold_kill"){
			StartTimer(1.0f);
		}
	}
}
class Dodge : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
		level.SendMessage("delete_weapon");
        level.SendMessage("give_enemy_knife");
		SendCommand("set_combat true");
		EnemyExecute(
		   "SetHostile(true);
			always_unaware = false;
			combat_allowed = true;
			chase_allowed = false;
			allow_active_block = true;
			always_active_block = false;
			goal = _attack;");
        SendCommand("player_invincible");
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "active_dodging"){
			success += 0.2f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}
class ActivateEnemy : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
		EnemyExecute("SetHostile(true);
				  	  always_unaware = false;
					  combat_allowed = false;
					  chase_allowed = true;
					  allow_active_block = true;
					  allow_throw = false;
					  goal = _attack;");
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "enemy_ko"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}
class ThrowEscape : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
		EnemyExecute("always_active_block = true;
					  allow_throw = true;
					  goal = _attack;");
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "character_throw_escape"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}

class ThrowEnemy : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
		EnemyExecute("always_active_block = false;
					  goal = _attack;");
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "character_throw_escape"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}

class TwoThrowEscape : AssignmentCallback
{
	int successfull;
	void Init(){
		successfull = 0;
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "character_throw_escape"){
			successfull++;
		}else if (_achievement == "player_damage"){
			successfull = 0;
		}
		if(successfull >= 2){
			StartTimer(1.0f);
		}
	}
}
class CountDown : AssignmentCallback
{
	int seconds;
	int lastTime;
	void Init(){
		seconds = 8;
		lastTime = -1;
	}
	void Completed(){
		time += time_step;
		if(floor(time) != lastTime){
			lastTime = int(floor(time));
			seconds -= 1;
			//Print("time " + seconds + "\n");
			SendCommand("update_text_variables " + seconds );
			SendCommand("set_combat true");
		}
		if(seconds == 0){
			StartTimerNoSound(0.0f);
		}
	}
	void OnCompleted(){
		level.SendMessage("set_highlight true");
	}
}
class BlockAttack : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
		SendCommand("set_combat true");
		EnemyExecute("always_unaware = false;");
		EnemyExecute("combat_allowed = true;");
		EnemyExecute("chase_allowed = false;");
		EnemyExecute("allow_active_block = true;");
		EnemyExecute("always_active_block = false;");
		EnemyExecute("goal = _attack;");
		player.Execute("max_ko_shield = 9999; ko_shield = max_ko_shield;");
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "player_blocked"){
			success += 0.1f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}
class RollFromGround : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "player_wake_roll"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}
class ReverseAttack : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
		
		EnemyExecute("always_unaware = false;");
		EnemyExecute("combat_allowed = true;");
		EnemyExecute("chase_allowed = false;");
		EnemyExecute("allow_active_block = true;");
		EnemyExecute("always_active_block = false;");
		EnemyExecute("goal = _attack;");
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "player_counter_attacked"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}
class ReverseAttackArmed : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
        level.SendMessage("give_enemy_knife");
		EnemyExecute("SetHostile(true);");
		EnemyExecute("always_unaware = false;");
		EnemyExecute("combat_allowed = true;");
		EnemyExecute("chase_allowed = false;");
		EnemyExecute("allow_active_block = true;");
		EnemyExecute("always_active_block = false;");
		EnemyExecute("goal = _attack;");
        SendCommand("player_invincible");
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "player_counter_attacked"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}
class AttackCountDown : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
		level.SendMessage("set_highlight false");
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "enemy_ko"){
			success += 0.2f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
			SendCommand("set_combat false");
		}
	}
}
class PickUpKnife : AssignmentCallback
{
	void Init(){
		EnemyExecute("SetHostile(false);");
		EnemyExecute("hostile = false;");
		EnemyExecute("always_unaware = false;");
		EnemyExecute("combat_allowed = false;");
		EnemyExecute("chase_allowed = false;");
		EnemyExecute("allow_active_block = false;");
		EnemyExecute("always_active_block = false;");
		level.SendMessage("delete_weapon");
		level.SendMessage("send_in_weapon knife");
	}
	void Completed(){
		if(GetCharPrimaryWeapon(player) != -1){
			StartTimer(1.0f);
		}
	}
	void LocalReset(){
		level.SendMessage("delete_weapon");
	}
}

class PickUpSword : AssignmentCallback
{
	void Init() {
		EnemyExecute("SetHostile(false);");
		EnemyExecute("hostile = false;");
		EnemyExecute("always_unaware = false;");
		EnemyExecute("combat_allowed = false;");
		EnemyExecute("chase_allowed = false;");
		EnemyExecute("allow_active_block = false;");
		EnemyExecute("always_active_block = false;");
		level.SendMessage("delete_weapon");
		level.SendMessage("send_in_weapon sword");
	}
	void Completed(){
		if(GetCharPrimaryWeapon(player) != -1){
			StartTimer(1.0f);
		}
	}
	void LocalReset(){
		level.SendMessage("delete_weapon");
	}
}

class RemoveWeapon : AssignmentCallback
{
	RemoveWeapon(float _delay){delay = _delay;}
	void Init() {
		level.SendMessage("delete_weapon");
	}

	void Completed()
	{
		//Wait for n seconds and then continue.
		StartTimerNoSound(delay);
	}
}

class SheatheKnife : AssignmentCallback
{
	void Completed(){
		if(GetCharPrimarySheathedWeapon(player) != -1){
			StartTimer(1.0f);
		}
	}
}

class SharpDamage : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "ai_took_sharp_damage"){
			success += 0.1f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0f){			
			StartTimer(1.0f);
		}
	}
}

class KillWolfSharp : AssignmentCallback
{
	float success;

	void Init(){
		success = 0.0f;
	}

	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "ai_took_sharp_damage"){
			success += 0.1f;
		}
		if(success >= 1.0f && _achievement == "enemy_died"){			
			StartTimer(1.0f);
		}
	}
}

class PullSword : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "ai_alive_weapon_removed_from_body"){
			StartTimer(1.0f);
		}
	}
}
class KnifeGrabExecution : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "choke_hold_knife_cut_kill"){
            StartTimer(1.0f);
        }
    }
}
class KnifeThrow : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "player_threw_knife"){
			StartTimer(1.0f);
		}
	}
}

class EnemyKnife : AssignmentCallback
{
	void Init() {
        level.SendMessage("give_enemy_knife");
        StartTimerNoSound(5.0f);
    }
	void ReceiveAchievementEvent(string _achievement) {
	}
}

class LedgeGrab : AssignmentCallback
{
	int pillarID = -1;
	float offsetY = 3.0f;
	float speed = 0.01f;
	float movedY = 0.0f;
	float success;

	void Init(){
		success = 0.0f;
        array<int> @object_ids = GetObjectIDs();
        int num_objects = object_ids.length();
        for(int i=0; i<num_objects; ++i){
            Object @obj = ReadObjectFromID(object_ids[i]);
            ScriptParams@ params = obj.GetScriptParams();
            if(params.HasParam("Name")){
                string name_str = params.GetString("Name");
                if("pillar_spawn" == name_str){
					pillarID = CreateObject("Data/Objects/Buildings/pillar1.xml");
			        Object@ pillarObj = ReadObjectFromID(pillarID);
                    pillarObj.SetTranslation(obj.GetTranslation() - vec3(0,offsetY,0));
                    pillarObj.SetScale(obj.GetScale());
                    pillarObj.SetRotation(obj.GetRotation());
                    break;
                }
            }
        }
	}
	void Completed(){
		if(pillarID != -1){
			if(movedY < offsetY){
				Object@ pillarObj = ReadObjectFromID(pillarID);
				pillarObj.SetTranslation(pillarObj.GetTranslation() + vec3(0,speed,0));
				movedY += speed;
			}
		}
	}
	void OnCompleted(){
		if(pillarID != -1){
			if(movedY < offsetY){
				Object@ pillarObj = ReadObjectFromID(pillarID);
				pillarObj.SetTranslation(pillarObj.GetTranslation() + vec3(0,(offsetY - movedY),0));
			}
		}
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "climbed_up"){
			success += 0.34f;
		}
    	DebugText("Success", "Success: "+success, 0.5f);
		if(success >= 1.0){			
			StartTimer(1.0f);
		}
	}
}
class Plants : AssignmentCallback
{
	int bushID = -1;
	void Init(){
        array<int> @object_ids = GetObjectIDs();
        int num_objects = object_ids.length();
        for(int i=0; i<num_objects; ++i){
            Object @obj = ReadObjectFromID(object_ids[i]);
            ScriptParams@ params = obj.GetScriptParams();
            if(params.HasParam("Name")){
                string name_str = params.GetString("Name");
                if("bush_spawn" == name_str){
					bushID = CreateObject("Data/Objects/Plants/Trees/temperate/green_bush.xml");
			        Object@ bushObj = ReadObjectFromID(bushID);
                    bushObj.SetTranslation(obj.GetTranslation());
                    bushObj.SetScale(obj.GetScale());
                    bushObj.SetRotation(obj.GetRotation());
                    break;
                }
            }
        }
	}
	void Completed()
	{
		if(player.GetFloatVar("in_plant") > 0.5f){
			StartTimer(1.0f);
		}
	}
	void OnCompleted(){
		if(bushID != -1){
			DeleteObjectID(bushID);
		}
	}
}

class Fire : AssignmentCallback
{
	int fireID = -1;
	bool player_on_fire = false;
	void Init(){
        array<int> @object_ids = GetObjectIDs();
        int num_objects = object_ids.length();
        for(int i=0; i<num_objects; ++i){
            Object @obj = ReadObjectFromID(object_ids[i]);
            ScriptParams@ params = obj.GetScriptParams();
            if(params.HasParam("Name")){
                string name_str = params.GetString("Name");
                if("fire_spawn" == name_str){
					fireID = CreateObject("Data/Objects/Hotspots/fire_test.xml");
			        Object@ fireObj = ReadObjectFromID(fireID);
                    fireObj.SetTranslation(obj.GetTranslation());
                    fireObj.SetScale(obj.GetScale());
                    fireObj.SetRotation(obj.GetRotation());
                    break;
                }
            }
        }
	}
	void Completed()
	{
		if(player.GetBoolVar("on_fire")){
			player_on_fire = true;
		}else{
			player_on_fire = false;
		}
	}
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "character_start_roll" && player_on_fire){
			StartTimer(1.0f);
		}
	}
	void OnCompleted(){
		level.SendMessage("revive_all");
		if(fireID != -1){
			DeleteObjectID(fireID);
		}
	}
}

class EndLevel : AssignmentCallback
{
	void ReceiveAchievementEvent(string _achievement){
		if(_achievement == "character_reset_hotspot"){
			level.SendMessage("go_to_main_menu");
		}
	}
}

Works in tandem with tutorial.as.


Data/Scripts/world_sim.as

const int kNumGoodTypes = 5;

class Town {
    int faction;
    int wealth;
    int population;
    string name;
    array<int> goods;
    int vis_id;
    array<int> goods_vis_id;
};

class Caravan {
    int town_destination;
    int vis_id;
};

enum RoadType {
    trail = 0,
    dirt = 1,
    paved = 2
};

class Road {
    float length;
    RoadType type;
    int start;
    int end;
};

array<Town> towns;
array<Caravan> caravans;

void Init(string str){
}

void Dispose(){
    int num_towns = towns.size();
    for(int i=0; i<num_towns; ++i){
        Log(info, "Test");
        DeleteObjectID(towns[i].vis_id);
        int len=towns[i].goods_vis_id.size();
        for(int j=0; j<len; ++j){
            Log(info, "Test");
            DeleteObjectID(towns[i].goods_vis_id[j]);
        }
    }
    towns.resize(0);
    
    int num_caravans = caravans.size();
    for(int i=0; i<num_caravans; ++i){
        Log(info, "Test");
        DeleteObjectID(caravans[i].vis_id);
    }
    caravans.resize(0);
}

bool HasFocus() {
    return false;
}

void Update() {
    SetPlaceholderPreviews();

    if(GetInputPressed(0,"b") && GetInputDown(0,"ctrl")){
        array<Object@> town_spawns;
        array<int> @object_ids = GetObjectIDs();
        int num_objects = object_ids.length();
        for(int i=0; i<num_objects; ++i){
            Object @obj = ReadObjectFromID(object_ids[i]);
            if(obj.IsSelected()){
                if(obj.GetType() == _placeholder_object){
                    ScriptParams@ params = obj.GetScriptParams();
                    if(params.GetString("type") == "town_spawn"){
                        Log(info,"Town selected with ID: "+object_ids[i]);
                    }
                }
            }
        }
    }

    int num_caravans = caravans.size();
    for(int i=0; i<num_caravans; ++i){
        Caravan @caravan = caravans[i];
        if(caravan.town_destination < int(towns.size())){
            vec3 dest = ReadObjectFromID(towns[caravan.town_destination].vis_id).GetTranslation();
            Object@ caravan_obj = ReadObjectFromID(caravans[i].vis_id);
            vec3 curr_pos = caravan_obj.GetTranslation();
            float dist = xz_distance(dest, curr_pos);
            float speed = time_step;
            vec3 dir = dest - curr_pos;
            vec3 flat_dir = dir;
            flat_dir.y = 0;
            flat_dir = normalize(flat_dir);
            if(dist < speed){
                float old_height = curr_pos.y;
                curr_pos = dest;
                curr_pos.y = old_height;
                caravan.town_destination = (caravan.town_destination + 1) % towns.size();
            } else {
                curr_pos += flat_dir * speed;
            }
            caravan_obj.SetTranslation(curr_pos);
            quaternion rotation_x(vec4(1,0,0,3.1415f));
            quaternion rotation_y(vec4(0,1,0,atan2(flat_dir.x, flat_dir.z)));
            caravan_obj.SetRotation(rotation_y);
        }
    }
}

void DrawGUI() {
}

void ReceiveMessage(string msg) {
    TokenIterator token_iter;
    token_iter.Init();
    if(!token_iter.FindNextToken(msg)){
        return;
    }
    string token = token_iter.GetToken(msg);
    if(token == "reset"){
        Dispose();
        SetupSim();    
    } else if(token == "dispose_level"){
    }
}

void SetPlaceholderPreviews() {
    array<int> @object_ids = GetObjectIDs();
    int num_objects = object_ids.length();
    for(int i=0; i<num_objects; ++i){
        Object @obj = ReadObjectFromID(object_ids[i]);
        ScriptParams@ params = obj.GetScriptParams();
        if(params.HasParam("Name")){
            string name_str = params.GetString("Name");
            if("town_spawn" == name_str){
                PlaceholderObject@ placeholder_object = cast<PlaceholderObject@>(obj);
                placeholder_object.SetPreview("Data/Objects/world_map/city.xml");
            }
        }
    }
}


void SetupSim(){
    array<Object@> town_spawns;
    array<int> @object_ids = GetObjectIDs();
    array<int> city_markers;

    int num_objects = object_ids.length();
    for(int i=0; i<num_objects; ++i){
       Object @obj = ReadObjectFromID(object_ids[i]);
        ScriptParams@ params = obj.GetScriptParams();
        if(params.HasParam("Name")){
            string name_str = params.GetString("Name");
            if("town_spawn" == name_str){
                city_markers.push_back(object_ids[i]);
            }
        }
    }
    
    int num_city_markers = city_markers.size();
    towns.resize(num_city_markers);
    Log(info,"Num city markers: "+num_city_markers);
    for(int i=0; i<num_city_markers; ++i){
        int city_marker_id = city_markers[i];
        Object@ city_marker = ReadObjectFromID(city_marker_id);
        int new_city_id = CreateObject("Data/Objects/world_map/city_prefab.xml", true);
        Object@ new_city = ReadObjectFromID(new_city_id);
        new_city.SetTranslation(city_marker.GetTranslation());

        towns[i].vis_id = new_city_id;
        towns[i].wealth = rand()%5000;
        towns[i].population = rand()%5000;
        towns[i].goods.resize(kNumGoodTypes);
        towns[i].faction = rand()%4;
        vec3 color = vec3(1,1,1);
        switch(towns[i].faction){
        case 0:
            color = vec3(0,1,1);
            break;
        case 1:
            color = vec3(1,1,0);
            break;
        case 2:
            color = vec3(1,0,1);
            break;
        case 3:
            color = vec3(1,0,1);
            break;
        }
        //new_flag.SetTint(color);
    }
    

    caravans.resize(1);
    caravans[0].vis_id = CreateObject("Data/Objects/world_map/caravan.xml", true);
    Object@ new_obj = ReadObjectFromID(caravans[0].vis_id);
    vec3 town_pos = ReadObjectFromID(towns[0].vis_id).GetTranslation();
    new_obj.SetTranslation(vec3(town_pos.x,new_obj.GetTranslation().y,town_pos.z));
    ScriptParams@ params = new_obj.GetScriptParams();
    params.AddIntCheckbox("No Save", true);
    caravans[0].town_destination = 1;
}

A level script meant for the cut procedural arena campaign.


Data/Scripts/hotspots/fixedragdollpart.as

void Init() {

}
Object@ thisHotspot = ReadObjectFromID(hotspot.GetID());

void SetParameters() {
}

void Reset(){

}

void HandleEvent(string event, MovementObject @mo){
    if(event == "enter"){
        OnEnter(mo);
    } else if(event == "exit"){
        OnExit(mo);
    }
}

void OnEnter(MovementObject @mo) {
   mo.rigged_object().FixedRagdollPart(1,thisHotspot.GetTranslation());
}

void OnExit(MovementObject @mo) {
    mo.rigged_object().ClearBoneConstraints();
}

void Update(){
    
}

Upon entering this hotspot, a character would have a certain bone fixed and stuck in place.


Data/Scripts/hotspots/announce_items.as

array<int> contained_items;
void Init() {
}

void SetParameters() {
    params.AddString("Hotspot ID", "ID for the announcing hotspot");
}

void HandleEventItem( string event, ItemObject @obj ) {
    Log( info, "" + event + " occurred" );
    if( event == "enter" ) {
        bool has_id = false;
        for( uint i = 0; i < contained_items.length(); i++ ) {
            if( contained_items[i] == obj.GetID() ) {
                has_id = true; 
            }
        }
        if( has_id == false ) {
            contained_items.insertLast( obj.GetID() );
        }
    }

    if( event == "exit" ) {
        for( uint i = 0; i < contained_items.length(); i++ ) {
            if( contained_items[i] == obj.GetID() ) {
                contained_items.removeAt(i);
                i--;
            }
        }
    }

    if( event == "enter" || event == "exit" ) {
        level.SendMessage( "hotspot_announce_items " + params.GetString("Hotspot ID") + " " + event + " " + obj.GetID() );
        string message = "hotspot_announce_items " + params.GetString("Hotspot ID") + " inside_list";
        for( uint i = 0; i < contained_items.length(); i++ ) {
            message += " " + contained_items[i];
        }
        level.SendMessage( message );
    }
}

void OnEnter(MovementObject @mo) {
}

Upon bringing items (swords, bags, bones, weapons) into this hotspot, the script would "announce" the item's ID.


Data/Scripts/hotspots/destinationtrail.as

array<int> points;
string placeholder = "Data/Objects/placeholder/empty_placeholder.xml";
int pointIndex = 0;
int trailIndex = 0;
bool postInitDone = false;
vec3 trailPos = vec3(0);
float trailDistance = 0.0f;
vec3 originOffset = vec3(0, -0.5f, 0);

class Point{
	int pointID = -1;
	bool enabled = true;
	Point(int id){
		pointID = id;
	}
}

void SetParameters() {
    params.AddInt("NumPoints",4);
}

void Init(){
}

void Dispose(){
	//Remove all the points if the main hotspot is deleted
	//for(uint i = 0; i < points.size(); i++){
	//	DeleteObjectID(points[i]);
	//}
}

void HandleEventItem(string event, ItemObject @obj){
    //Print("ITEMOBJECT EVENT: "+event+"\n");
    if(event == "enter"){
        OnEnterItem(obj);
    }
    if(event == "exit"){
        OnExitItem(obj);
    }
}

void OnEnterItem(ItemObject @obj) {
}

void OnExitItem(ItemObject @obj) {
}

void Reset(){
	pointIndex = 0;
	trailIndex = pointIndex;
}

void Update(){
	if(!postInitDone){
		Object@ hotspotObj = ReadObjectFromID(hotspot.GetID());
		hotspotObj.SetScale(vec3(0.125f));
		hotspotObj.SetCopyable(false);
		hotspotObj.SetDeletable(true);
		hotspotObj.SetSelectable(true);
		hotspotObj.SetTranslatable(true);
		hotspotObj.SetScalable(false);
		hotspotObj.SetRotatable(false);
		points.insertLast(hotspot.GetID());
		//CheckForDeletedPoints();
		CheckForNewPoints();
		postInitDone = true;
	}
	//Add a point when the there are not enough
	if(points.size() < 2){
		AddPoint();
	}
	if(EditorModeActive()){
		CheckForDeletedPoints();
		CheckForNewPoints();
		DrawConnectionLines();
		DrawBoxes();
	}else{
		CheckForPreviousPoint();
		CheckForNextPoint();
		//DrawConnectionLines();
		//DrawBoxes();
		UpdateTrail();
		MovementObject@ player = ReadCharacter(0);
		Object@ closestPointObj = ReadObjectFromID(points[pointIndex]);

		//DebugDrawLine(player.position, closestPointObj.GetTranslation(), vec3(0.5f), _delete_on_update);
	}
}

void UpdateTrail(){
	MovementObject@ player = ReadCharacter(0);
	Object@ closestPointObj = ReadObjectFromID(points[trailIndex]);
	if(trailPos == vec3(0) || trailDistance > 10.0f){
		trailPos = player.position + originOffset;
		trailIndex = pointIndex;
		trailDistance = 0.0f;
	}
	vec3 targetPos = closestPointObj.GetTranslation();
	//DebugDrawLine(player.position, closestPointObj.GetTranslation(), vec3(0.5f), _delete_on_update);
	vec3 direction = normalize(targetPos - trailPos);
	MakeParticle("Data/Particles/destination_particle.xml", trailPos, direction);
	trailPos += (direction * 0.05f);

	float radius = 0.1f;
	//DebugDrawWireSphere(targetPos, radius, vec3(0), _fade);
	if(trailPos.x >  targetPos.x - radius && trailPos.x <  targetPos.x + radius &&
	trailPos.y >  targetPos.y - radius && trailPos.y <  targetPos.y + radius &&
	trailPos.z >  targetPos.z - radius && trailPos.z <  targetPos.z + radius){
		if(int(points.size()) > (trailIndex + 1)){
			trailIndex++;
		}else{
			trailPos = player.position + originOffset;
			trailIndex = pointIndex;
			trailDistance = 0.0f;
		}
	}
}

void CheckForNextPoint(){
	if(int(points.size()) > pointIndex + 1){
		MovementObject@ player = ReadCharacter(0);
		Object@ currentPoint = ReadObjectFromID(points[pointIndex]);
		Object@ nextPoint = ReadObjectFromID(points[pointIndex + 1]);
		float pointsDistance = distance(currentPoint.GetTranslation(), nextPoint.GetTranslation());
		float playerDistance = distance(player.position, nextPoint.GetTranslation());
		/*
		DebugText("awe", "pointsDistance " + pointsDistance, _fade);
		DebugText("awe2", "playerDistance " + playerDistance, _fade);
		DebugText("awe3", "size " + points.size(), _fade);
		DebugText("awe4", "pointIndex " + pointIndex, _fade);
		*/
		if(playerDistance < pointsDistance){
			pointIndex++;
		}
	}
}
void CheckForPreviousPoint(){
	if((pointIndex - 1) >= 0){
		MovementObject@ player = ReadCharacter(0);
		Object@ currentPoint = ReadObjectFromID(points[pointIndex]);
		Object@ previousPoint = ReadObjectFromID(points[pointIndex - 1]);
		float pointsDistance = distance(currentPoint.GetTranslation(), previousPoint.GetTranslation());
		float playerDistance = distance(player.position, previousPoint.GetTranslation());
		if(playerDistance < pointsDistance){
			pointIndex--;
		}
	}
}

void CheckForNewPoints(){
	array<int> placeholderIDs = GetObjectIDsType(35);
	for(uint o = 0; o < placeholderIDs.size(); o++){
		if(points.find(placeholderIDs[o]) == -1){
			Object@ placeholder = ReadObjectFromID(placeholderIDs[o]);
			ScriptParams@ placeholderParams = placeholder.GetScriptParams();
			if(placeholderParams.HasParam("belongsto")){
				if(placeholderParams.GetString("belongsto") == ("" + hotspot.GetID())){
					points.insertLast(placeholderIDs[o]);
					Log(info, "add at " + placeholderIDs[o]);
				}
			}
		}
	}
}

void CheckForDeletedPoints(){
	for(uint i = 0; i < points.size(); i++){
		if(!ObjectExists(points[i])){
			points.removeAt(i);
			Log(info, "delete at " + i);
			i--;
		}
	}
}

void DrawConnectionLines(){
	Object@ hotspotObj = ReadObjectFromID(hotspot.GetID());
	vec3 lastPos = hotspotObj.GetTranslation();
	for(uint i = 0; i < points.size(); i++){
		Object@ thisPoint = ReadObjectFromID(points[i]);
		DebugDrawLine(lastPos, thisPoint.GetTranslation(), vec3(1), _delete_on_update);
		lastPos = thisPoint.GetTranslation();
	}
}

void DrawBoxes(){
	Object@ hotspotObj = ReadObjectFromID(hotspot.GetID());
	Object@ lastPoint = ReadObjectFromID(points[points.size() - 1]);
	vec3 red = vec3(1,0,0);
	vec3 green = vec3(0,1,0);
	DebugDrawWireBox(hotspotObj.GetTranslation(), vec3(0.5f), red, _delete_on_update);
	DebugDrawWireBox(lastPoint.GetTranslation(), vec3(0.5f), green, _delete_on_update);
}

void AddPoint(){
	int objectID = CreateObject(placeholder);
	points.insertLast(objectID);
	Object@ obj = ReadObjectFromID(objectID);
	obj.SetScale(vec3(0.5f));
	obj.SetCopyable(true);
	obj.SetDeletable(true);
	obj.SetSelectable(true);
	obj.SetTranslatable(true);
	obj.SetScalable(false);
	obj.SetRotatable(false);
	obj.SetTranslation(ReadObjectFromID(hotspot.GetID()).GetTranslation() + vec3(0,1,0));
	ScriptParams@ placeholderParams = obj.GetScriptParams();
	placeholderParams.AddString("belongsto", "" + hotspot.GetID());
}

Still trying to figure this one out...


Data/Scripts/hotspots/collectable_target.as

void Init() {
}

int collectables_needed;
int collectables_contained = 0;
bool condition_satisfied = false;

string GetTypeString() {
    return "collectable_target";
}

void SetParameters() {
    params.AddString("Collectables needed","1");
    collectables_needed = max(1, params.GetInt("Collectables needed"));
}

void HandleEventItem(string event, ItemObject @obj){
    //Print("ITEMOBJECT EVENT: "+event+"\n");
    if(event == "enter"){
        OnEnterItem(obj);
    } 
    if(event == "exit"){
        OnExitItem(obj);
    } 
}

void OnEnterItem(ItemObject @obj) {
    if(obj.GetType() == _collectable){
        ++collectables_contained;
        condition_satisfied = IsConditionSatisfied();
        //Print("Containing "+collectables_contained+" collectables\n");
    }
}

void OnExitItem(ItemObject @obj) {
    if(obj.GetType() == _collectable){
        collectables_contained = max(0, collectables_contained-1);
        condition_satisfied = IsConditionSatisfied();
        //Print("Containing "+collectables_contained+" collectables\n");
    }
}

bool IsConditionSatisfied() {
    //DebugText("a","Collectables needed: "+collectables_needed, 0.5f);
    //DebugText("b","Collectables contained: "+collectables_contained, 0.5f);
    return collectables_needed <= collectables_contained;
}

Essential for the unused challenge level script goal of bringing items to a certain point in the level.


Data/Scripts/hotspots/dark_world_level.as

#include "threatcheck.as"

bool dark_world = true;
bool initialized = false;

enum VisParam {
    kWhitePoint,
    kBlackPoint,
    kBloomMult,
    kAmbient,
    kSkyTintr,
    kSkyTintg,
    kSkyTintb,
    kNumVisParam
};

array<float> vis_params;
array<float> target_vis_params;

void SetParameters() {
    params.AddInt("light_world_only", -1);
    params.AddInt("dark_world_only", -1);
}

void Init() {
    AddMusic("Data/Music/dark_world.xml");
    initialized = false;
}

void Dispose() {
}

void Update() {
    if(!initialized){
        SetDark(false);
        SetDark(true);
        initialized = true;
    }
}

float last_time = 0.0;

void PreDraw(float curr_game_time) {
    if(target_vis_params.size() != 0){
        if(vis_params.size() == 0){
            for(int i=0, len=target_vis_params.size(); i<len; ++i){
                vis_params.push_back(target_vis_params[i]);
            }
        } else {
            float time_step = curr_game_time - last_time;
            for(int i=0, len=target_vis_params.size(); i<len; ++i){
                vis_params[i] = mix(target_vis_params[i], vis_params[i], pow(0.1, time_step));
            }

            SetHDRWhitePoint(vis_params[kWhitePoint]);
            SetHDRBlackPoint(vis_params[kBlackPoint]);
            SetHDRBloomMult(vis_params[kBloomMult]);
            SetSunAmbient(vis_params[kAmbient]);
            SetSkyTint(vec3(vis_params[kSkyTintr],vis_params[kSkyTintg],vis_params[kSkyTintb]));
        }
    }
    last_time = curr_game_time;
}

void SetDark(bool val){
    if(dark_world != val){
        target_vis_params.resize(kNumVisParam);
        dark_world = val;

        for(int dark=0; dark<2; ++dark){
            string obj_list;
            if(dark == 0){
                obj_list = params.GetString("light_world_only");
            } else {
                obj_list = params.GetString("dark_world_only");                
            }

            TokenIterator token_iter;
            token_iter.Init();
            while(token_iter.FindNextToken(obj_list)){
                string token = token_iter.GetToken(obj_list);
                int target_id = atoi(token);
                if(ObjectExists(target_id)){
                    Object @obj = ReadObjectFromID(target_id);
                    if(dark == 0){
                        obj.SetEnabled(!dark_world);
                    } else {
                        obj.SetEnabled(dark_world);
                    }
                }
            }
        }

        if(!dark_world){
            target_vis_params[kWhitePoint] = 0.8;
            target_vis_params[kBlackPoint] = 0.04;
            target_vis_params[kBloomMult] = 2.0;
            target_vis_params[kAmbient] = 2.0;
            target_vis_params[kSkyTintr] = 1.0;
            target_vis_params[kSkyTintg] = 1.0;
            target_vis_params[kSkyTintb] = 1.0;
            PlaySong("light");
            if(GetPlayerCharacterID() != -1){
                MovementObject@ mo = ReadCharacter(GetPlayerCharacterID());
                mo.Execute("SwitchCharacter(\"Data/Characters/pale_turner.xml\");");
            }
        } else {
            target_vis_params[kWhitePoint] = 1.0;
            target_vis_params[kBlackPoint] = 0.3;
            target_vis_params[kBloomMult] = 1.0;
            target_vis_params[kAmbient] = 0.5;
            target_vis_params[kSkyTintr] = 1.0;
            target_vis_params[kSkyTintg] = 1.0;
            target_vis_params[kSkyTintb] = 1.0;
            PlaySong("dark");
            if(GetPlayerCharacterID() != -1){
                MovementObject@ mo = ReadCharacter(GetPlayerCharacterID());
                mo.Execute("SwitchCharacter(\"Data/Characters/rabbot.xml\");");
            }
        }
    }
}

void ReceiveMessage(string msg) {
    if(msg == "trigger_enter"){
        SetDark(!dark_world);
    }
    if(msg == "reset"){
        SetDark(true);
    }
}

void Draw(){
    if(EditorModeActive()){
        Object@ obj = ReadObjectFromID(hotspot.GetID());
        DebugDrawBillboard("Data/Textures/ui/eclipse.tga",
                           obj.GetTranslation(),
                           obj.GetScale()[1]*2.0,
                           vec4(vec3(0.5), 1.0),
                           _delete_on_draw);
    }
}

Works with dark_world_trigger to change level parametres and enable/disable objects depending on whether you're in the "Dark World" or the "Light World". Presumably used for the cut Light/Dark World story concept.


Data/Scripts/hotspots/dark_world_trigger.as

void SetParameters() {
  params.AddInt("dark_world_level_id", -1);
}

void HandleEvent(string event, MovementObject @mo){
    if(event == "enter"){
        OnEnter(mo);
    } if(event == "exit"){
        //Print("Exited lava\n");
    }
}


void OnEnter(MovementObject @mo) {
    if(mo.controlled){
        int dark_world_level_id = params.GetInt("dark_world_level_id");
        if(ObjectExists(dark_world_level_id)){
            Object@ obj = ReadObjectFromID(dark_world_level_id);
            obj.ReceiveScriptMessage("trigger_enter");
        }
    }
}

void Update() {
}

Works with dark_world_level to change level parametres and enable/disable objects depending on whether you're in the "Dark World" or the "Light World". Presumably used for the cut Light/Dark World story concept.