[Module]SideViewFL - Side-View FaceLocation

Started by Billbis, Mon 11/08/2014 21:34:08

Previous topic - Next topic

Billbis

Beware another of my useless modules!


Introduction:
This module contains alternative functions to FaceLocation, FaceCharacter and FaceObject functions that take in account the perspective of the room.

Build-in functions work with hard-coded fixed angle (see right image) which correspond to a top-bottom view (see, the Mexican).
If player is at the center of the circle and the point to look is at point A, if the angle between horizontal line and A is less that pi/4 radiant (45°), the character will look at the right. But on a more side view perspective, this "decision circle" is flatten on the ground, and the "decision angle" is smaller (see left image, and also, the Mexican).

It is also the case for 8-direction view characters.


Usage:
This module contains 4 functions that are used like that:
Code: ags
cEgo.FaceSVDirection(eUp);
cEgo.FaceSVLocation(150,150);
cEgo.FaceSVCharacter(cBillbis);
cEgo.FaceSVObject(oKey);


If your characters are using 8 direction views, you must declare it like that, for example in a game_start() function located below the module script:
Code: ags
function game_start() {
    SideViewFL.CharacterWithDiagonalLoops(cEgo, true);
    SideViewFL.CharacterWithDiagonalLoops(cSamuel, true);
}


On "inverted" floor, when the more the character goes up, the more he comes closer to the screen:
Code: ags
function region1_WalksOnto() {
    SideViewFL.CharacterWalkingOnTheRoof(cEgo, true);
}


You can specify the perspective of the room or the "decision angles" by several methods. On most side view perspective, the default value should be OK (horizon line close to the middle of the room, flatness set to 0.5).

First, you can set the flatness of the perspective:
Code: ags
SideViewFL.Flatness = 0.5; // float between 0 and 1

Flatness correspond to the degree of flattening of the circles on the first images: it is the ratio B/A where:
-A is the distance on the floor walked by the character when moving of X pixel to the side.
-B is the distance on the floor walked by the character when moving of X pixel to the back.
It's default value is 0.5.

Second, you can specify the coordinate of the horizon on your room:
Code: ags
SideViewFL.HorizonLevel = 120;


Third, you can directly specify the "decision" angle at which the character will change its view:
Code: ags
SideViewFL.Angle4Dir = Maths.Pi/6.0;
SideViewFL.Angle8Dir1 = Maths.Pi/8.0;
SideViewFL.Angle8Dir2 = Maths.Pi/4.0; // Angle8Dir1 <= Angle8Dir2




/!\ Modifying flatness or Horizon level will modify Angle4Dir, Angle8Dir1 and Angle8Dir2. /!\
/!\ This module is compatible with AGS 3.3.0 and (likely) AGS 3.2.1., but contains global eDirection enum and FaceDirection function. Draconian edition and AGS 3.3.1 already contains a enum eDirection (or similar), and some template also comes with FaceDirection functions, therefor beware of potential incompatibilities! I will try to propose an AGS 3.3.1 compatible version soon. /!\

Thanks:
Kitai, Pidem, valoulef.

Licence:
Do whatever you want.

Sources:
header:
Code: ags
// SideViewFL header, version 1.2
//
// Author: Billbis
//
// Abstract:
//
//   Alternative FaceLocation, FaceCharacter and FaceObject functions that take into account
//   the perspective of the room.
//
// Dependencies:
//
//   Made for AGS 3.3.0. Might work with previous and future AGS versions.
//
// Functions:
//
//   Character.FaceSVDirection(SpecifyDirection Dir, BlockingStyle BStyle)
//   Will trun the character to face the specified direction.
//   e.g.: cEgo.FaceSVDirection(eUp);
//
//   Character.FaceSVLocation(int x, int y, BlockingStyle BStyle)
//   Will turn the character to face the specified location (Room coordinates).
//   e.g.: cEgo.FaceSVLocation(150, 150);
//
//   Character.FaceSVCharacter(Character* toFace, BlockingStyle BStyle)
//   Will turn the character to face the specified character.
//   e.g.: cEgo.FaceSVCharacter(cBillbis);
//
//   Character.FaceSVObject(Object* toFace, BlockingStyle BStyle)
//   Will turn the character to face the specified object.
//   e.g.: cEgo.FaceSVCharacter(oKey);
//
// Configuartion:
//
//   If your characters are using 8 direction loops, you must declare it with the CharacterWithDiagonalLoops function,
//   for example in a game_start() function located bellow this script module.
//   e.g.: SideViewFL.CharacterWithDiagonalLoops(cEgo, true);
//
//   Optionnal:
//
//      When located on a 'inverte perspective' surface (i.e. a area when the lower theY coordinate, the closer the things are, e.g. a roof),
//      you should call the CharacherWalkingOnTheRoof function.
//      e.g.: SideViewFL.CharacherWalkingOnTheRoof(cEgo, true);
//
//      You can specify the 'flatness' of the game with SideViewFL.Flatness. Float value between 0 and 1.
//      Warning: resets Angle4Dir, Angle8Dir1 and Angle8Dir2 values.
//      e.g.: SideViewFL.Flatness = 0.5;
//
//      Alternatively, you can specify the Y coordinate of your horizon. Int value.
//      Consider using 'SideViewFL.Flatness = 1;' if your horizon is at infinity.
//      Warning: resets Flatness, Angle4Dir, Angle8Dir1 and Angle8Dir2 values.
//      e.g.: SideViewFL.HorizonLevel = 120;
//
//      N.B.: the following angle are relative to the horizontal.
//
//      You can specify the decision angle for 4 direction loops character, in Radiant. Float value between 0 and Pi/2.
//      e.g.: SideViewFL.Angle4Dir = Maths.Pi/3.0;
//
//      You can specify the decision angle for 8 direction loops character separating horizontal to digonal loops, in Radiant.
//      Float value between 0 and min(Pi/2, Angle8Dir2).
//      e.g.: SideViewFL.Angle8Dir1 = Maths.Pi/6.0;
//
//      You can specify the decision angle for 8 direction loops character separating diagonal to vertical loops, in Radiant.
//      Float value between max(0, Angle8Dir1) and Pi/2.
//      e.g.: SideViewFL.Angle8Dir2 = Maths.Pi/2.0;
//
// Caveats:
//
//   Contain a direction enum that will become obsolete in AGS 3.3.1. Beware of potential conflicts!
//
// Revision history:
//
//   2014/03/12: Fix another math error and a stupid mistake. Add 'invert perspective' surfaces support.
//   2014/03/11: Fix two math errors + new model behind HorizonLevel method.
//   2014/03/10: original release.
//
// License:
//
//   SideViewFL is publish under the terms of the 
//   Do What The Fuck You Want To Public License, Version 2
// 
//   This program is free software. It comes without any warranty, to
//   the extent permitted by applicable law. You can redistribute it
//   and/or modify it under the terms of the Do What The Fuck You Want
//   To Public License, Version 2, as published by Sam Hocevar. See
//   http://sam.zoy.org/wtfpl/COPYING for more details.
//
// Thanks:
//
//   Kitai, Pidem

//////////////////////////
// Various declarations //
//////////////////////////

enum SpecifyDirection {
  eUp,
  eLeft,
  eRight,
  eDown,
  eUpRight,
  eUpLeft,
  eDownRight,
  eDownLeft, 
  eNone
};

struct SideViewFL {
    
    import static attribute float Angle4Dir;
    import static float get_Angle4Dir();// $AUTOCOMPLETEIGNORE$
    import static void set_Angle4Dir(float input);// $AUTOCOMPLETEIGNORE$
    
    import static attribute float Angle8Dir1;
    import static float get_Angle8Dir1();// $AUTOCOMPLETEIGNORE$
    import static void set_Angle8Dir1(float input);// $AUTOCOMPLETEIGNORE$
    import static attribute float Angle8Dir2;
    import static float get_Angle8Dir2();// $AUTOCOMPLETEIGNORE$
    import static void set_Angle8Dir2(float input);// $AUTOCOMPLETEIGNORE$
    
    import static attribute float Flatness;
    import static float get_Flatness();// $AUTOCOMPLETEIGNORE$
    import static void set_Flatness(float input);// $AUTOCOMPLETEIGNORE$
    
    import static attribute int HorizonLevel;
    import static int get_HorizonLevel();// $AUTOCOMPLETEIGNORE$
    import static void set_HorizonLevel(int input);// $AUTOCOMPLETEIGNORE$
    
    /// Does the character have 8 direction loops?
    import static void CharacterWithDiagonalLoops(Character *chara,  bool boule);
    /// Does the carracter currently standing on an invert perspective surface (i.e. a roof)?
    import static void CharacherWalkingOnTheRoof(Character *chara,  bool boule);
};

/// Trun the character to face the specified direction.
import void FaceSVDirection(this Character*, SpecifyDirection=eNone, BlockingStyle=eBlock);
/// Turn the character to face the specified location (Room coordinates).
import void FaceSVLocation(this Character*, int x, int y, BlockingStyle BStyle = eBlock);
/// Turn the character to face the specified character.
import void FaceSVCharacter(this Character*, Character* toFace, BlockingStyle BStyle = eBlock);
/// Turn the character to face the specified object.
import void FaceSVObject(this Character*, Object* toFace, BlockingStyle BStyle = eBlock);


script:
Code: ags
// FaceIsoLocation script

float Angle4Dir;
float Angle8Dir1;
float Angle8Dir2;
float Flatness;
int HorizonLevel;
bool IsCharacterWithDiagonalLoops[];
bool IsCharacterWalkingOnTheRoof[];

// Utilitary function
float SideView_Abs(float x) // return absolute value
{ 
    if (x >= 0.0) return x;
    else return -1.0*x;
}

// struct static definitions
static float SideViewFL::get_Angle4Dir() 
{
    return Angle4Dir;
}

static void SideViewFL::set_Angle4Dir(float input) 
{
    if (input >= 0.0 && input <= Maths.Pi/2.0) {
        Angle4Dir = input;
    }
}

static float SideViewFL::get_Angle8Dir1() 
{
    return Angle8Dir1;
}

static void SideViewFL::set_Angle8Dir1(float input) 
{
    if (input >= 0.0 && input <= Maths.Pi/2.0 && input <= Angle8Dir2) {
        Angle8Dir1 = input;
    }
}

static float SideViewFL::get_Angle8Dir2() 
{
    return Angle8Dir2;
}

static void SideViewFL::set_Angle8Dir2(float input) 
{
    if (input >= 0.0 && input <= Maths.Pi/2.0 && input >= Angle8Dir1) {
        Angle8Dir2 = input;
    }
}

static float SideViewFL::get_Flatness() 
{
    return Flatness;
}

static void SideViewFL::set_Flatness(float input) 
{
    if (input >= 0.0 && input <= 1.0) {
        Flatness = input;
        Angle4Dir = Maths.ArcCos(Flatness)/2.0;
        Angle8Dir1 = Maths.ArcCos(Flatness)/3.0;
        Angle8Dir2 = Maths.ArcCos(Flatness)/1.5;
    }
}

static int SideViewFL::get_HorizonLevel() 
{
    return HorizonLevel;
}

static void SideViewFL::set_HorizonLevel(int input)
{
    float horizonYf;
    horizonYf = IntToFloat(input);
    float RoomHeightf = IntToFloat(Room.Height);
    Flatness = Maths.ArcTan((RoomHeightf - 2.0*horizonYf)/RoomHeightf)/Maths.Pi + 0.5; // ^^
    Angle4Dir = Maths.ArcCos(Flatness)/2.0;
    Angle8Dir1 = Maths.ArcCos(Flatness)/3.0;
    Angle8Dir2 = Maths.ArcCos(Flatness)/1.5;
}

static void SideViewFL::CharacterWithDiagonalLoops(Character *chara,  bool boule)
{
    IsCharacterWithDiagonalLoops[chara.ID] = boule;
}

static void SideViewFL::CharacherWalkingOnTheRoof(Character *chara,  bool boule)
{
    IsCharacterWalkingOnTheRoof[chara.ID] = boule;
}


// Module core functions
void FaceSVDirection(this Character*, SpecifyDirection Dir, BlockingStyle BStyle)
{
    if(Dir == eUp) this.FaceLocation(this.x, this.y-1, BStyle);
    else if(Dir == eLeft) this.FaceLocation(this.x-1, this.y, BStyle);
    else if(Dir == eRight) this.FaceLocation(this.x+1, this.y, BStyle);
    else if(Dir == eDown) this.FaceLocation(this.x, this.y+1, BStyle);
    else if(Dir == eUpRight) this.FaceLocation(this.x+1, this.y-1, BStyle);
    else if(Dir == eUpLeft) this.FaceLocation(this.x-1, this.y-1, BStyle);
    else if(Dir == eDownRight) this.FaceLocation(this.x+1, this.y+1, BStyle);
    else if(Dir == eDownLeft) this.FaceLocation(this.x-1, this.y+1, BStyle);
}

void FaceSVLocation(this Character*, int x, int y, BlockingStyle BStyle)
{
    int deltaX = x - this.x;
    int deltaY = this.y - y; //AGS Y axes is pointing down
    float deltaXf = IntToFloat(deltaX);
    float deltaYf = IntToFloat(deltaY);
    float alpha;
    if (deltaX >= 0) {
        alpha = Maths.ArcTan2(deltaYf, deltaXf);
    } else {
        alpha = Maths.ArcTan2(deltaYf, -deltaXf);
    }
    
    if (IsCharacterWithDiagonalLoops[this.ID] == false) { // Four loops character
        if (deltaX >= 0 && SideView_Abs(alpha) <= Angle4Dir) {
            this.FaceSVDirection(eRight, BStyle);
        } else if (deltaX < 0 && SideView_Abs(alpha) <= Angle4Dir) {
            this.FaceSVDirection(eLeft, BStyle);
        } else if (deltaY >0) {
            if (IsCharacterWalkingOnTheRoof[this.ID]) { // Invert perspective.
                this.FaceSVDirection(eDown, BStyle);
            } else {
                this.FaceSVDirection(eUp, BStyle);
            }
        } else {
            if (IsCharacterWalkingOnTheRoof[this.ID]) {
                this.FaceSVDirection(eUp, BStyle);
            } else {
                this.FaceSVDirection(eDown, BStyle);
            }
        }
    } else { // Eight loops character
        if (deltaX >= 0 && SideView_Abs(alpha) <= Angle8Dir1) {
            this.FaceSVDirection(eRight, BStyle);
        } else if (deltaX >= 0 && SideView_Abs(alpha) <= Angle8Dir2) {
            if (deltaY >=0) {
                if (IsCharacterWalkingOnTheRoof[this.ID]) {
                    this.FaceSVDirection(eDownRight, BStyle);
                } else {
                    this.FaceSVDirection(eUpRight, BStyle);
                }
            } else {
                if (IsCharacterWalkingOnTheRoof[this.ID]) {
                    this.FaceSVDirection(eUpRight, BStyle);
                } else {
                    this.FaceSVDirection(eDownRight, BStyle);
                }
            }
        } else if (deltaX < 0 && SideView_Abs(alpha) <= Angle8Dir1) {
            this.FaceSVDirection(eLeft, BStyle);
        } else if (deltaX < 0 && SideView_Abs(alpha) <= Angle8Dir2) {
            if (deltaY >=0) {
                if (IsCharacterWalkingOnTheRoof[this.ID]) {
                    this.FaceSVDirection(eDownLeft, BStyle);
                } else {
                    this.FaceSVDirection(eUpLeft, BStyle);
                }
            } else {
                if (IsCharacterWalkingOnTheRoof[this.ID]) {
                    this.FaceSVDirection(eUpLeft, BStyle);
                } else {
                    this.FaceSVDirection(eDownLeft, BStyle);
                }
            }
        } else if (deltaY >=0) {
            if (IsCharacterWalkingOnTheRoof[this.ID]) {
                this.FaceSVDirection(eDown, BStyle);
            } else {
                this.FaceSVDirection(eUp, BStyle);
            }
        } else {
            if (IsCharacterWalkingOnTheRoof[this.ID]) {
                this.FaceSVDirection(eUp, BStyle);
            } else {
                this.FaceSVDirection(eDown, BStyle);
            }
        }
    }
}

void FaceSVCharacter(this Character*, Character* toFace, BlockingStyle BStyle)
{
    this.FaceSVLocation(toFace.x, toFace.y, BStyle);
}

void FaceSVObject(this Character*, Object* toFace, BlockingStyle BStyle)
{
    int x, y;
    x = toFace.X + Game.SpriteWidth[toFace.Graphic]/2;
    y = toFace.Y - Game.SpriteHeight[toFace.Graphic]/2;
    this.FaceSVLocation( x, y, BStyle);
}

function game_start () {
    IsCharacterWithDiagonalLoops = new bool[Game.CharacterCount];
    IsCharacterWalkingOnTheRoof = new bool[Game.CharacterCount];
    int i = 0;
    while (i < Game.CharacterCount) {
        IsCharacterWithDiagonalLoops[i] = false;
        IsCharacterWalkingOnTheRoof[i] = false;
        i++;
    }
    SideViewFL.set_Flatness(0.5);
}

ToothpasteBruva

This looks like a really useful module Billbis. Does it change the flatness of the diagonal walking angle as well?

SMF spam blocked by CleanTalk