Files
fs2open.github.com/code/variables/variables.h
T
portej05 719825f80d The big CVS comment removal. This chops approx. 3.5mb off a full checkout.
I've tested this through compiling and through hand checking. 
Normalises line endings in ALL files to \n.
2009-06-20 08:08:20 +00:00

308 lines
13 KiB
C++

/*
Bobboau -
variable system: takes a string and constructs an expression includeing game variables in it
to use an expression, make a pointer to a variable, then allocate (useing new, not malloc) it
to a new expression passing an input string containing a mathematical expression consisting
of constants defined variables and opperators. to get the value of the expression, call
value() and pass it a pointer to a variable_call_data struct with as much data as you can
give packed inside it value(&variable_call_data(pointer_to_an_object)) is probly going to
be the most common call
syntax for expression is normal mathematical notation, ie 1+1
each expression is made of a combineation of floating point constants,
floating point game variables, and binary and unary opperators
a binary operator is on charicter opperator that has two opperands,
operand 1 is on the left side of it, and opperand 2 the right
1+3 for example has + as the opperator, 1 as opperand1 and 3 as opperand2
parenthises () make a subexpression act like a single opperand, the statment 2*(3+2)
for example has * as the opperator, 2 as opperant1, and (3+2) as opperand2, this is
actualy exactly how it will look in the expression tree.
as the tree is built, it looks to see if it can remove nodes, if all operands
of an opperatorare constant that the opperator will be reomved and replaced with a constant
equal to the evaluation of the opperator with it's constant opperands.
different opperators have diferent priority levels.
comparison opperators have the lowest prority value, they are evaluated last
addition and subtraction are the next highest, they are performed after exponation
and multiplication, but before comparison
multiplication level operators are next,
and finaly exponent opperators the highest priority are performed first, before any multiplication
if two opperators of the same priority are encountered then the one furthest to the
left is evaluated first
the current list of available binary opperators, by priority level are:
level 1:comparison (return a floating 0.0 for true or 1.0 for false)
=,<,>
level 2:addition
+,-
level 3:multiplication
*,/,% (modulus treats operands as intigers, floating parts of both opperands are ignored and will be lost)
level 4:exponation
^,#(b#x == b'th root of x),~(b~x == the log base b of x)
remember highest priority is evaluated first
unary opperators are performed before any binary opperator. a unary opperator is defined as a function with
one parameter, for example sin(x). a unary opperator can have as many charicters as desiered to define it
and it's one opperand must always be on it's right side (though not nesisaraly within parenthises, though
it's generaly a good idea to follow this for clarity, there is no evaluation penalty for additional
parenthises).
the current list of defined unary opperators is:
(all trig use radian input)
hypsin - hyperbolic trig
hyptan
hyptan
arcsin - inverse trig
arccos
arctan
sin - standard trig
cos
tan
log - base 10
ln - base E
sqrt - square root
deg - converts radian to degree
rad - converts degree into radian
! - factorial
abs - absolute value
ipart - intiger part (drops floating point)
fpart - floating point (drops everyting <= 1.0)
sign - if(>0)returns 1 if(<0)returns -1 if(==0) returns 0
not - logical not, if any value other than 0 it returns 0, if ==0 returns 1
there are also some constants defined, when the expression string is first
read these are replaced with numarical approximations
PI = 3.14159265358979323846264338327
E = 2.718281828459045235360287471352
game variables.
the last important aspect of the variable system is the game variables,
these give accsess to variables within the game such as :
global variables (time, fov, ect)
object variables (the currently relevent object, speed, hull streignth, engine output, ect)
texture (number of frames, frames per second (both for animation))
other stuff... this is still heavily under construction, and not many variables have been made yet
all game variables rely on a data pack passed to it, not all evaluations will have all data
items relevent to them. for example texture variables will not be relevent to per ship evaluations
ship variables (like shield strength) will not be relevent to per weapon evaluations.
in the instance that irrelevent variables are used in expressions the default return value for
them should , generaly, be 0.0f. however there may be instances in wich a diferent irrelevent
return would be mor logical, it is up to the person adding the new game variable to make sure
they cover all the bases.
to make a new game variable, in variables.cpp derive from the class game_variable (feel free to
make a diferent style constructor, like one that takes the name of an element within a linked
list, but do remember to add the apropriate instansiation code and if posable default values),
and define the value(variable_call_data*data) function with an appropriate function, keep in
mind that the default value for data is NULL, and the default value for all member pointers
within data is NULL, take care of these cases at all times.
once you have the class defined you need to add parseing code to get_object_variable, there is
a big if/elseif cascade and it should be obvius what to do if you look at it for five minutes.
there is the class level and the member leve cascades, all you need to do is copy and paste from
a variable similar to what you want, changeing the names as apropriate.
game variable syntax goes as such
:scope.member[element].submember:
the oppening and closeing collens are requiered to identify the string within as a single game
variable. scope is the object or area that the variable inhabits. member is the actual variable
the same named variable in diferent scopes may do totaly diferent things. and a variable may
requier some information, such as an index to an element in an array, or the name of a item in
a linked list, or something else to identify a subset in some sort of list, this should go within
brakets, although at the time of this documentation no variable actualy uses this. finaly, member
submembers, members of, members same syntax as member, if your reading this then you undoubtably
understand the thinking behind this and I won't insult your intelect by describeing it any
further basic access of mebers within a structure. though if this gets used for public documentation
someone probly will have to.
if there is a syntax error in a game variable the default error behavior is to create a
constant of value 0.0f
*/
//#include "object/object.h" //WMC - not really needed
#include "globalincs/pstypes.h"
#include <math.h>
#ifndef _VARIABLE_H__
#define _VARIABLE_H__
enum variable_type {VAR_BASE, VAR_CONSTANT, VAR_OPP, VAR_EXP, VAR_GAME};
//data structure for holding information about a texture
struct texture_variable_data{
texture_variable_data():n_frames(1),fps(1){};
texture_variable_data(texture_variable_data*t){
if(t){
n_frames = t->n_frames;
fps = t->fps;
}else{
n_frames = 1;
fps = 1;
}
};
int n_frames;
int fps;
};
//a pointer passed when evaluateing an expression gives information to the
//expression about wich object is the current object and wich texture is the
//current texture, crap like that.
struct variable_call_data{
variable_call_data(struct object*o=NULL,texture_variable_data*t=NULL):obj(o),texture(t){};
void operator = (int){};
object*obj;
texture_variable_data texture;
};
//base class of all variables from wich all other variables
//(includeing full expressions) are derived
class variable{
protected:
variable_type type;//an identifier, needed so I can perform constant colapseing
public:
variable(variable_type t):type(t){};//all ariables must define what type of variable they are
virtual~variable(){};//virtual so derived destructors will get called when you expect them to be
// all variable's wich child variables _*must*_ kill there childeren when they die
virtual float value(variable_call_data*data=NULL) = 0;//this is the critical function of
//the varialbe system it will be diferent on each derived class, it returns the value of
//a variable, if it's a game variable, it's usualy calling a accessor function like
//timestamp() or returning something from the passed data
variable_type get_type(){return type;};
//returns what type of variable this is, used for constant colapseing,
//type should never change after instansiation
};
class expression:public variable{
//this is a class made to mbuild and manage an expression tree
variable *head; //the head of the expression tree
variable* parse_next(char*&);//part of the constructor,
//is recursively called to build the expression tree from a postfix expression
//each call moves the string as it parses it, hence the reference
variable* get_object_variable(char**);//this takes a pointer to a cstring
//that contains game variable formatting
/*game variable syntax goes like this
:scope.member[element]:
the colens denote the begining and end of a game variable
scope is wich object or area the requiered variable is in,
for example global, or object, or texture
member is the name of the specific variable
and [element] is used for variables that are in the form of
arrays or lists. element is usualy a number, but sometimes a string or enumeration
like parse_next it moves the input string as it parses it so upon return the calling
function's string is at the next item ready to be parsed
*/
int expression::wich_unary(char*c);
//utility function for determineing wich if any unary opperator the input string denotes
//the function prefix() used to be a member, but it requiered the string class and I
//didn't want to requier the include of that file for one function declairation
public:
expression(char*);//constructor, takes an expression string and builds the tree
~expression(){delete head;};//kills the head. the head in turn kills it's
//childeren wich them selves kill there childeren,
//culminateing in a cascade of infantacidal dealocation horror!
float value(variable_call_data*data=NULL);
//recursively evaluates all elements of the tree.
//actualy, it doesn't call expression::value
//so maybe recursive isn't quite the right word for it... hmmm...
};
bool is_value(char c);//utility, tells if a charicter is part of a number
class var_matrix{
//a matrix of expressions
//neat huh?
//built to minimize evaluation calls
//if no evaluations are requiered, it returns identity
variable**mat;//will always be allocated to a 4*4 matrix of expressions
int size[2];//the largest direction in a given dimention assigned a value
//I was going to use this for something, but I forgot what... something
//about it auto-maticly being able to make variable vectors and/or N*M matriciese
public:
void set_element(int i,int j, char*str){
//set's an element in the matrix, if the value to be set
//is what it would be if the matrix were an identity at the
//specified element no allocation is performed
//code in the get_value function will find a NULL there and return the apropriate value
Assert(j<4);
Assert(i<4);
if(i+1 > size[0])size[0]=i+1;
if(j+1 > size[1])size[1]=j+1;
char *s = str;
while(is_value(*s))
s++;
//if there is a number at the front of the expression go to the next item
if(!*s){
//if there is nothing after the first element in the expression see if it's
//an identity element if it is then we ignore it
float f = (float)atof(str);
if(i == j && f == 1.0f)return;
if(i != j && f == 0.0f)return;
}
//not identity, well we have to allocate an expression
//if there is no element pointer matrix yet,
//it would probly be a good idea to build that first though
if(!mat){
mat = new variable*[16];
memset(mat,NULL,sizeof(expression*)*16);
}
//ok get the index of the element
int idx = j*4+i;
//if something lives there now kill it and take it's land
if(mat[idx]){
delete mat[idx];
mat[idx] = NULL;
}
//ah, an empy home, well I'll just move right on in
mat[idx] = new expression(str);
}
float get_element(int i,int j, object*obj=NULL){
Assert(j<4);
Assert(i<4);
int idx = j*4+i;
//grab the index
//if there is a matrix, and there is an element in the matrix at the defined location
//return the value of the expression
if(mat && mat[idx])return mat[idx]->value(&variable_call_data(obj));
//otherwise retrun identity
if(i==j)return 1.0f;
return 0.0f;
}
int get_size(int d){return size[d%2];}//get how big the matrix is in a dimention
var_matrix():mat(NULL){};//construct to identity
~var_matrix(){
//kill off each element within the matrix, them kill the list of elements it'self
if(mat){
for(int i = 0; i<16; i++)
if(mat[i])delete mat[i];
delete[]mat;
}
}
};
#endif