/************************************************************************
       -= SEER - C-Scripting engine v 0.94a =-
State      : under implementation
Author     : Przemyslaw Podsiadly
             email: ppodsiad@elka.pw.edu.pl
             WWW:   http://home.elka.pw.edu.pl/~ppodsiad
File:exlotr.cpp
Desc:example RPG system

This is only the example of technic how to interrupt a script by an imported
function. This is not the framework for RPG system. Here there are few things
missing for a proper system to work:
* event queue - now we may loose some events when they appear in the same
  turn (new event overrides the old). Event queue should fix that. The
  act(...) function should pause the script only when queue is waiting,
  otherwise just return the event from FIFO queue.
* there should be a proper dynamic list of characters in the game
  (here it's static for simplicity)
* there should be a way to look around - something used like:
  <example>
   //in exlotr.sh:
   #define forEachCharacter(c,t) charFindSet(t);while(c=charFindNext())
   //in character script:
   Event ev;
   Character *ch,*fastest=NULL;
   //find the fastest enemy
   forEachCharacter(ch,charEnemy|charNotFriend)
   {if (!fastest) fastest=ch;
    else
    if (ch->stats.Speed>fastest->stats.Speed)
        fastest=ch;
   }
   if (fastest) //attack the fastest enemy
   while(act(e,actAttack,fastest,attackFurious),e!=evSuccess||e!=evUnable)
   {handleEvent(e);
   }
   //he's dead or not here (evUnable) or I've killed him (evSuccess)
  </example>
  The same thing goes to any object around (like finding valuable thing
  in the range of sight)

Other suggestions:
* event priority - this would make it much better when more important event
  is sent to script as first one
* script may ignore some events - some way of telling which, like
  act(actWait,onAttacked|onTold|onFatal) will ignore any event but evAttacked,
  evTold or any event with Fatal priority (that may cause death, etc).
************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../seer.h"
#ifndef WIN32
#include <unistd.h>
#else
void sleep(int sec)
{int i,x;
 for(i=0;i<3000000;i++) x=i?10-i:i*3;
}
#endif

#define DEBUG

/* THE RPG SYSTEM HELPER FUNCTIONS *************************************/
void delay()
{
 sleep(1);
}

int rnd(int x)
{return rand()%x;
}

void compile(seerScript &scrpt,char *name)
{
 if (scrpt.compileFile(name))
   {
      printf("%s\n> %s\n",scErrorMsg,scErrorLine);exit(scErrorNo);
   }
 if (!scrpt.valid())
   { printf("Unable to find %s!\n",name);exit(101);}
}

void instant(seerInstance &i,seerScript& s)
{
 i.create(s);
//Error?
 if (scErrorNo)
   {
      printf("%s\n",scErrorMsg);exit(scErrorNo);
   }
 if (!i.valid())
   { printf("Unknown error\n");exit(101);}
}

/* THE RPG SYSTEM EXPORTABLE PART **************************************/

class Character;

#define evNone          -1
#define evSuccess       0
#define evNotice        1       //notice a thing
#define evHear          2       //hear a sound
#define evAttacked      3       //be attacked by someone
#define evUnable        4       //unable to complete action, like
                                //got lost, door are locked, etc.
#define evMeet          5
#define evTold          6       //somebody told you something
struct Event{
 int nr;
 void *with;
 void *matter;
 bool operator==(int _code) {return _code==nr;}
 bool operator!=(int _code) {return _code!=nr;}
};


struct Stats{
 int Vitality,Strength,Agility,Speed,Intelect,Wisdom,Faith;
};

struct Position{
 int x,y,z;

 bool operator==(Position& p)
  {if (x==p.x && y==p.y && z==p.z) return true;
   return false;
  }
};

struct Place{
 char *name;
 int index;
 Position pos;
};

Place listPlaces[]=
{
 {"Rivendell",0,{10,10,0}},
 {"Rivendell:Door",1,{11,10,0}},
 {"Frodo's House",2,{30,14,0}},
 {"Sam's House",3,{34,16,0}},
 {"Merry's House",4,{38,14,0}},
 {"Pippin's House",5,{33,16,0}},
 {NULL,-1,{0,0,0}}//end marker
};

Place *getPlace(char* name)
{int i;
 for(i=0;listPlaces[i].name;i++)
  if (strcmp(name,listPlaces[i].name)==0)
     return &listPlaces[i];
 return NULL;
}

Character *getCharacter(char *name);

/* The JOB */
#define actNone         -1
#define actWait         0
#define actOpen         1
#define actGoTo         2
#define actFollow       3
#define actMoveNormal   101
#define actMoveSneak    102
#define actWield        110
#define actPickUp       111
#define actPutDown      112
#define actBreak        113
#define actTell         200

struct Job{
  int nr;//one of act*
  void *data;//the additional paramters, like target, destination
  Event *event;//here we should store the event result
  Character *by;//who makes this Job?

  int getDataSize(int action)
  {switch(action)
   {
    case actGoTo:return sizeof(Position);
    case actFollow:return sizeof(Character*);
    case actTell:return sizeof(Character*)+sizeof(char*);
    case actWait:return 0;
    default:return 0;//other should follow, but we're using only those above.
   }
  }

  //start specific action
  void init(Event *ev,Character *_by,int action,void *params)
  {
   int size=getDataSize(action);
   nr=action;
   event=ev;
   event->nr=evNone;
   by=_by;
   data=malloc(size);
   memcpy(data,params,size);
  }

  //Do the job. Returns true, if ends with success already
  //(like reached destination in actGoTo)
  bool proceed(Character *me);
};

/* The Character */
//All the data your system posses about a character (except to graphics/sound)
//If you want your hero to be unable to access some of them, just add private;
//or const for read-only members.
class Character{
public:
 char* name;
 Stats stats;
 Position pos;
//and much more...

//not visible in script:
 Job job;
 seerInstance ins;

 void init(char *_name,char *place,seerScript& script)
 {
  name=_name;
  pos=getPlace(place)->pos;//set position
  instant(ins,script);
 //set 'Character *me' to this (here ins.var returns pointer to 'me')
  *(Character**)(ins.var("me"))=this;
//  scNameVar(ins.get(),"me",Character *)=this;
 }

 void done()
 {
  ins.free();
 }

 void sendEvent(int nr,void *data)
 {
  if (job.event->nr==evNone)
  {//we were waiting for event, and we've got it, so
   //wake him up
//   printf("%s:unpause\n",name);
   ins.pause(0);
  }
//  printf("Send event to %s:%d\n",name,nr);
//  printf("Title:%s\n",ins.title());
  //set the event
  job.event->nr=nr;
  job.event->with=data;
 }

 bool initJob(Event *ev,int action,void *params)
 {job.init(ev,this,action,params);
  //sleep him
  ins.pause(1);
  return true;
 }

 void doJob()
 {bool t;
  //if instance is not running or paused, finish
  if (ins.status==scstatus_FREE) return;
  //continue the job
  t=job.proceed(this);
  //if job finished, send evSuccess
  if (t)
    {
    job.nr=actNone;
    sendEvent(evSuccess,NULL);
    }
  return;
 }

 void checkMeetingWith(Character *c)
 {
  if (this==c) return;//cannot meet yourself

  if (pos==c->pos)//they meet
  {//printf("**********%s meets %s\n",c->name,name);
   c->sendEvent(evMeet,this);
   this->sendEvent(evMeet,c);
  }
 }

 void checkLocation();

};
void ShortestPathAlgo(Position *act,Position *dest)
{
    if (dest->x < act->x) act->x--;
    if (dest->x > act->x) act->x++;
    if (dest->y < act->y) act->y--;
    if (dest->y > act->y) act->y++;
}

bool Job::proceed(Character *me)
{ switch(nr)
  {
   case actWait:printf("%s is waiting\n",me->name);return false;
   case actGoTo:
        {Position *p=(Position*)data;
         //very simple "shortest path algorythm"...
         ShortestPathAlgo(&me->pos,p);
         //check if we meet anybody
         me->checkLocation();
         if (*p == me->pos) return true;
//         printf("%s is going to [%d,%d] (now at [%d,%d])...\n",
//               me->name,p->x,p->y,me->pos.x,me->pos.y);
        }
        return false;
   case actFollow:
        {Character* p=*(Character**)data;
         if (!p) me->sendEvent(evUnable,NULL);
         //very simple "shortest path algorythm"...
         ShortestPathAlgo(&me->pos,&p->pos);
         //check if we meet anybody
//         me->checkLocation();
//         printf("%s is following %s...\n",me->name,p->name);
        }
        return false;
   case actTell:
        {Character *p=*(Character**)data;
         //check if we meet anybody
         printf("%s tells %s:'%s'\n",me->name,p->name,*((char**)data+1));
         p->sendEvent(evTold,data);
        }
        return true;
  }
  return true;
}

int say(char *format,...)
{ int i;
  va_list arg;
  va_start (arg, format);
  i=printf("%s says:%c",scNameVar(scActual_Instance,"me",Character *)->name,'"');
  i+=vprintf (format, arg);
  i+=printf("\"\n");
  return i;
}

int act(Event *ev,int action,...)
{
 va_list arg;
 Character *me=scNameVar(scActual_Instance,"me",Character *);
// printf("act from instance at %x\n",scActual_Instance);
 if (!me) {printf("Fatal error!\n");exit(1);}
 va_start (arg, action);
 me->initJob(ev,action,arg);
 va_end(arg);
 return 0;
}

/*
char *stdio=
"import int printf(char *,...);                 \n"
"import int scanf(char *,...);                  \n"
;
*/
void exportAll()
{
 scAddExtSym(rnd);

 scAddExtSym(say);
 scAddExtSym(act);

 scAddExtSym(getPlace);
 scAddExtSym(getCharacter);
}

/* THE RPG SYSTEM IMPLEMENTATION ***************************************/

struct Scene{
seerScript FrodoScript,SamScript;
Character Frodo;
Character Sam,Merry,Pippin;
Scene():FrodoScript(),SamScript() {}
}scene;

void Character::checkLocation()
{ //printf("%s is checking location\n",name);

  //check meeting with anybody
  this->checkMeetingWith(&scene.Sam);
  this->checkMeetingWith(&scene.Frodo);
  this->checkMeetingWith(&scene.Merry);
  this->checkMeetingWith(&scene.Pippin);

  //this should be according to the map or something
  if (pos.x==22&&pos.y==10)//at 22x10 there is a stone
  {
   sendEvent(evNotice,"a big carved stone");
  }
  else
  if (pos.x==12&&pos.y==10)//at 12x10 you here a sound of a river
  {
   sendEvent(evHear,"a river");
  }
  else
  if (pos.x==11&&pos.y==10)//at 11x10 Nazguls will attack!
  {
   sendEvent(evAttacked,"by horrible Nazguls");
  }
}

#define CHECK(t) if (strcmp(name,(t).name)==0) return &(t);
Character *getCharacter(char *name)
{
 CHECK(scene.Frodo);
 CHECK(scene.Sam);
 CHECK(scene.Merry);
 CHECK(scene.Pippin);
 return NULL;
}
#undef CHECK


void loadAll()
{
 compile(scene.FrodoScript,"exlfrodo.sc");
 compile(scene.SamScript,"exlsam.sc");

 scene.Frodo.init("Frodo","Frodo's House",scene.FrodoScript);
 scene.Merry.init("Merry","Merry's House",scene.SamScript);
 scene.Pippin.init("Pippin","Pippin's House",scene.SamScript);
 scene.Sam.init("Sam","Sam's House",scene.SamScript);
}

void freeAll()
{
 scene.Frodo.done();
 scene.Sam.done();
 scene.Merry.done();
 scene.Pippin.done();

 scene.FrodoScript.free();
 scene.SamScript.free();
}

void jobAll()
{
  //this should be somewhere in the list or array
  //not staticly here, but...
  scene.Frodo.doJob();
  scene.Sam.doJob();
  scene.Pippin.doJob();
  scene.Merry.doJob();
}

void runAll()
{int time=10*60;//10 o'clock
 //launch all instances...
 //this 10 is unimportant here, unless we call scSeeR_Set(scSeeR_ExprOpcode,1);
 //then each instance would be interrupted every 10 statements
 scene.Sam.ins.launch(10,"live");
 scene.Pippin.ins.launch(10,"live");
 scene.Merry.ins.launch(10,"live");
 scene.Frodo.ins.launch(10,"live");

 //do till all instances are not running
 while (scContinue_Instances())
 {
  time+=20;
  if (scErrorNo)
   {printf("Error\n%s\n!",scErrorMsg);exit(2);}
  //now do the job
  jobAll();
  printf("Frodo [%d,%d] Sam [%d,%d] Merry [%d,%d] Pippin [%d,%d]         Time:%02d:%02d\r",
    scene.Frodo.pos.x,scene.Frodo.pos.y,
    scene.Sam.pos.x,scene.Sam.pos.y,
    scene.Merry.pos.x,scene.Merry.pos.y,
    scene.Pippin.pos.x,scene.Pippin.pos.y,
    time/60,time%60);
//  printf("   ---===### Next turn ###===---\n");
  fflush(stdout);
  delay();
  printf("                                                                            \r");
 }

 //kill all instances - here we don't need this
 scene.Frodo.ins.kill();
 scene.Sam.ins.kill();
 scene.Pippin.ins.kill();
 scene.Merry.ins.kill();
}


/* THE RPG SYSTEM MAIN *************************************************/
int main()
{FILE *file;

//setup SeeR
 scInit_SeeR();

 scOpen_Debug("exlotr.dbg");//start debugging

// scSeeR_Set(scSeeR_ExprOpcode,1);//generate EXPR opcodes
 //it'd be needed in multitasking (only in EXPR SeeR can interrupt script)

 exportAll();

 loadAll();

 runAll();

 freeAll();

 scClose_Debug();

 return 0;
}

