/* Princed V3 - Prince of Persia Level Editor for PC Version
Copyright (C) 2003 Princed Development Team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
The authors of this program may be contacted at http://forum.princed.com.ar
*/
/*
states.c: FreePrince : State object
��������
Copyright 2004 Princed Development Team
Created: 16 Oct 2004
Authors: Enrique Calot <ecalot.cod@princed.com.ar>
Rodrigo Campos Catelin <rodrigocc@gmail.com>
Note:
DO NOT remove this copyright notice
*/
#include "states.h"
#include <stdlib.h> /* malloc free */
#include "tiles.h" /* isIn "group definitions" */
#include "object.h" /* DIR_LEFT DIR_RIGHT */
#include "room.h" /* getTile */
#ifdef DEBUGSTATES
#include <stdio.h> /* For debug purposes */
void debugShowFlag(short optionflag) {
if (optionflag&STATES_FLAG_F) printf("Falling ");
if (optionflag&STATES_FLAG_P) printf("PressFloor ");
if (optionflag&STATES_FLAG_C) printf("PressCeiling ");
if (optionflag&STATES_FLAG_S) printf("Sound");
printf("\n");
}
#endif
#define STATES_STEPS_PER_TILE TILE_W
/* Private static state graph */
static tsAction statesActionList[]=STATES_ACTIONS;
static short statesAnimationList[]=STATES_ANIMATIONS;
static tsCondition statesConditionList[]=STATES_CONDITIONS;
void state_GetAnimation(int action,tState *state) {
/* TODO: check this function, it may not work in 64 bits architectures*/
tsAction* a=statesActionList+action;
short i=a->animSize;
short* j=statesAnimationList+(a->animStart*4);
short totaloffset=a->moveOffset;
state->frame=i;
state->animation=(short*)malloc(sizeof(short)*i);
state->steps=(short*)malloc(sizeof(short)*i);
state->flags=(short*)malloc(sizeof(short)*i);
state->offsx=(short*)malloc(sizeof(short)*i);
#ifdef DEBUGSTATES
printf("* Animsize=%d Animstart=%d. (new animation allocated) Next:\n",i,a->animStart);
#endif
state->step=(float)(totaloffset)/(float)(i); /* this is the full step to be used in case a non step walk is set TODO: delete this system */
while (i--) {
state->animation[i] = *(j++); /* the first short is the frame */
state->flags[i] = *(j++); /* the second short is the flag */
state->steps[i] = *(j++); /* the third short is the frame step */
state->offsx[i] = *(j++); /* the fourth short is the height */
}
}
/* public functions interface */
int stateKidInLevel(int level) {
static short statesLevelList[]=STATES_LEVELS;
return statesLevelList[level];
}
tState stateCreate(short stateId) {
tState start;
state_GetAnimation(stateId,&start);
start.currentState=statesActionList[stateId].nextStateId;
return start;
}
void stateFree(tState* state) {
free(state->animation);
free(state->flags);
free(state->steps);
free(state->offsx);
}
void stateReset(tState* state, short stateId) {
stateFree(state);
*state=stateCreate(stateId);
}
/* private functions */
/* Evaluates a condition indexed in the condition table */
#define DefaultTrue(pointer) if (!pointer) return STATES_CONDRESULT_TRUE
#define DefaultFalse(pointer) if (!pointer) return STATES_CONDRESULT_FALSE
#define statesKidLeft (kid->direction==DIR_LEFT)
#define whereInTile (statesKidLeft? \
(kid->location%STATES_STEPS_PER_TILE): \
STATES_STEPS_PER_TILE-(kid->location%STATES_STEPS_PER_TILE))
#define kidX (kid->location/STATES_STEPS_PER_TILE+1)
#define kidY (kid->floor+1)
#define statesTile(x,y) roomGetTile(room,kidX+x,kidY+y)
#define kidDirection (statesKidLeft?-1:1)
#define statesCondRet(a) return (a)?STATES_CONDRESULT_TRUE:STATES_CONDRESULT_FALSE
/* Memory structure interpreter */
int evaluateCondition(int condition,tKey* key, tObject* kid, tRoom* room) {
tsCondition c=statesConditionList[condition];
switch(c.type) {
case esKeyUp:
DefaultFalse(key);
statesCondRet(c.argument^(!inputGetUp(key->status)));
case esKeyDown:
DefaultFalse(key);
statesCondRet(inputGetDown(key->status));
case esKeyForward:
DefaultFalse(key);
statesCondRet((statesKidLeft?inputGetLeft(key->status):inputGetRight(key->status)));
case esKeyBack:
DefaultFalse(key);
statesCondRet((statesKidLeft?inputGetRight(key->status):inputGetLeft(key->status)));
case esKeyShift:
DefaultFalse(key);
statesCondRet(inputGetShift(key->status));
case esMapUpForward:
DefaultFalse(room);
statesCondRet(isIn(statesTile(kidDirection,-1),c.argument));
case esMapUp:
DefaultFalse(room);
statesCondRet(isIn(statesTile(0,-1),c.argument));
case esMapDown:
DefaultFalse(room);
statesCondRet(isIn(statesTile(0,1),c.argument));
case esMapForward:
DefaultFalse(room);
statesCondRet(isIn(statesTile(kidDirection,0),c.argument));
case esMapNotForward:
DefaultFalse(room);
statesCondRet(!isIn(statesTile(kidDirection,0),c.argument));
case esMapBackUp:
DefaultFalse(room);
statesCondRet(isIn(statesTile(-kidDirection,-1),c.argument));
case esMapBack:
DefaultFalse(room);
statesCondRet(isIn(statesTile(-kidDirection,0),c.argument));
case esMapNotBack:
DefaultFalse(room);
statesCondRet(!isIn(statesTile(-kidDirection,0),c.argument));
case esMapDownBack:
DefaultFalse(room);
statesCondRet(isIn(statesTile(-kidDirection,1),c.argument));
case esMapNotDownBack:
DefaultFalse(room);
statesCondRet(!isIn(statesTile(-kidDirection,1),c.argument));
case esMapOn:
DefaultFalse(room);
statesCondRet(isIn(statesTile(0,0),c.argument));
case esMapNotOn:
DefaultFalse(room);
statesCondRet(!isIn(statesTile(0,0),c.argument));
case esForwardTileNearerThan:
DefaultFalse(kid);
statesCondRet((whereInTile<c.argument));
case esForwardTileFartherThan:
DefaultFalse(kid);
statesCondRet((whereInTile>c.argument));
case esInScreen:
DefaultFalse(room);
statesCondRet((room->id==c.argument));
case esInLevel:
DefaultFalse(room);
statesCondRet((room->level->levelNumber==c.argument));
case esForwardChangeToScreen:
DefaultFalse(kid);
statesCondRet(((statesKidLeft)&&(kidX==1))||((!statesKidLeft)&&(kidX==10)));
case esKidLooking:
DefaultFalse(kid);
statesCondRet(kid->direction==c.argument);
case esLast:
return STATES_CONDRESULT_END;
default:
return STATES_CONDRESULT_FALSE;
}
}
/* Evaluates all conditions needed for an action and returns true or false */
int evaluateAction(int currentAction,tKey* key, tObject* kid,tRoom* room) {
int i=statesActionList[currentAction].conditionId;
int result;
while ((result=evaluateCondition(i,key,kid,room))==STATES_CONDRESULT_TRUE) i++;
if (result==STATES_CONDRESULT_FALSE) return 0;
return 1; /* Only returns true if STATES_CONDRESULT_END is reached */
}
/* Evaluates a state: all conditions for all actions until a true is found and returns the next state id*/
int evaluateState(int state, tKey* key, tObject* kid, tRoom* room) {
int i=state;
while (!evaluateAction(i,key,kid,room)) i++;
return i;
}
#define alternate(i,n) (((i)%2)?(n-(((i)+1)>>1)):((i)>>1))
/* This function should return the image frame and actions to be performed by this call
* returns the animation number corresponding to this frame */
short stateUpdate(tKey* key, tObject* kid,tRoom* room) {
/* TODO: check this function, it may not work in 64 bits architectures*/
tState* current=&(kid->action);
/*static float step;
static float acumLocation;*/
short flags;
short steps;
current->frame--;
current->image=current->animation[current->frame];
flags =current->flags [current->frame];
steps =current->steps [current->frame];
current->imgoffx=current->offsx [current->frame];
current->mirror=flags&STATES_FLAG_M?1:0;
/* BEGIN DEBUGSTATES */
#ifdef DEBUGSTATES
printf("stateUpdate: animation=%d steps=%d ",current->image,steps);
debugShowFlag(flags);
#endif
/* END DEBUGSTATES */
if (!current->frame) {
int action;
free(current->animation);
free(current->flags);
free(current->steps);
free(current->offsx);
/* Find matching action */
action=evaluateState(current->currentState,key,kid,room);
/* Performs the events called by this action */
/* Remember the animation and flags for the next current->frame frames */
state_GetAnimation(action,current);
/* Remember the state where we are now */
current->currentState=statesActionList[action].nextStateId;
#ifdef DEBUGSTATES
printf("NEW STATE: action=%d next=%d\n",action,current->currentState);
#endif
/* Move the kid (turn+traslate) */
switch(statesActionList[action].moveType) {
case STATES_MOVETYPES_ABSOLUTEONSTART:
/* AbsoluteOnStart (x)
* Moves the kid x step units from the first forward tile change
* and starts the animation there
*/
/* 1) move current location to the left tileChange */
kid->location-=(kid->location%STATES_STEPS_PER_TILE);
/* 2) if looking right add one tile to reach the right tileChange
* 3) if looking right add x, if looking left substract x */
if (kid->direction!=DIR_LEFT)
kid->location+=STATES_STEPS_PER_TILE+statesActionList[action].moveOffset;
else
kid->location-=statesActionList[action].moveOffset;
break;
case STATES_MOVETYPES_ABSOLUTEONSTOP: {
/* AbsoluteOnStop (x)
* Deletes frames (in the middle) to make sure that, at the end of
* the animation, the kid had moved only x step units from the first
* forward tile change
* if there is a lack of movements by frame it stops before reaching it.
*/
/* 1) calculate the number of frames the guy will move */
int accumulate=0;
int i,j;
int from,to;
int moveTo=statesActionList[action].moveOffset;
if (kid->direction!=DIR_LEFT)
moveTo+=STATES_STEPS_PER_TILE-(kid->location%STATES_STEPS_PER_TILE);
else
moveTo+=kid->location%STATES_STEPS_PER_TILE;
/* First iteration: determine i=number of frames not cropped */
for (i=0;(i<current->frame)&&(accumulate<=moveTo);i++) {
accumulate+=current->steps[alternate(i,current->frame)];
}
for (j=0;j<i;j++) {
from=alternate(j,current->frame);
to=alternate(j,i);
if (j%2) {
/* the first frames are ok, so I'll fix the last frames */
#ifdef DEBUGSTATES
printf("from=%d to=%d ok\n",from,to);
#endif
current->animation[to]=current->animation[from];
current->flags[to]=current->flags[from];
current->steps[to]=current->steps[from];
}
}
#ifdef DEBUGSTATES
printf("total frames=%d number of frames to be used=%d. wanted movement=%d movement to be performed=%d\n",current->frame,i,statesActionList[action].moveOffset,accumulate);
#endif
/* force at least one animation */
if (!i) i=1;
current->frame=i; /* now the last frames are moved, crop the animation */
break;
}
case STATES_MOVETYPES_RELATIVETURN:
/* relative but turning */
kid->direction=(kid->direction==DIR_LEFT)?DIR_RIGHT:DIR_LEFT;
kid->location+=(kid->direction==DIR_LEFT)?
-statesActionList[action].moveOffset:
statesActionList[action].moveOffset;
break;
case STATES_MOVETYPES_RELATIVE:
kid->location+=(kid->direction==DIR_LEFT)?
-statesActionList[action].moveOffset:
statesActionList[action].moveOffset;
break;
}
}
kid->location+=(kid->direction==DIR_LEFT)?-steps:steps;
if ((current->frame==1) && (current->currentState<0))
return current->currentState; /* if this is the last frame of the last state, return exit code */
return flags;
}