//
// A library of useful high-level game functions
// These are mostly intended to be used by the scripting system
//
#define IRE_SYSTEM_MODULE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#define NO_FORTIFY
#include "library.hpp"
#include "ithelib.h"
#include "media.h"
#include "sound.h"
#include "loadfile.h"
#include "nuspeech.hpp"
#include "linklist.hpp"
#include "object.hpp"
#include "gamedata.h"
#include "console.h"
#include "map.hpp"
#include "init.h"
#define SOUND_DIST 4 // maximum distance without attenuation
//#define SOUND_DROP 16 // 8 volume points every square
#define SOUND_DROP 8 // 8 volume points every square
// Variables
extern "C" char show_vrm_calls; // From OSCLI
extern long COtot;
extern long do_lightning;
extern char pending_delete;
extern char fullrestore;
extern long sf_volume;
static char *nullstr="<NULL>";
static float ire_sintab[3600];
static float ire_costab[3600];
static float ire_sqrt[32768];
// Master function
void Init_VRMs();
// These functions and variables are exported
extern OBJECT *OB_Alloc();
extern void OB_Free(OBJECT *a);
extern void DestroyObject(OBJECT *a);
extern void DeletePending();
extern int OB_Init(OBJECT *a,char *name);
extern int OB_SetDir(OBJECT *objsel,int dir, int force);
extern void OB_SetSeq(OBJECT *objsel,char *name);
extern TILE *GetTile(int x,int y);
extern int WeighObject(OBJECT *obj);
extern int GetBulk(OBJECT *obj);
extern int AddToParty(OBJECT *new_member);
extern void SubFromParty(OBJECT *member);
extern int get_key();
extern int get_key_debounced();
extern int Restart(void);
extern void SetDarkness(int l);
extern void spillcontents(OBJECT *bag,int x,int y);
extern int NPC_Converse(char *file, char *start);
extern void CheckHurt(OBJECT *list);
extern void RedrawMap();
extern int IsTileSolid(int x,int y);
extern int IsTileWater(int x,int y);
extern void Inactive(OBJECT *o);
extern void ActivityName(OBJECT *o,char *activity, OBJECT *target);
extern void ActivityNum(OBJECT *o,int activity, OBJECT *target);
extern OBJECT *GetBestPlace(int x,int y);
extern OBJECT *GetTopObject(int x,int y);
extern OBJECT *GetRawObjectBase(int x,int y);
extern void MoveToPocket(OBJECT *src,OBJECT *dest);
extern void TransferToPocket(OBJECT *src,OBJECT *dest);
extern int MoveFromPocket(OBJECT *obj,OBJECT *container,int x,int y);
extern int ForceFromPocket(OBJECT *obj,OBJECT *container,int x,int y);
extern void MoveToTop(OBJECT *obj);
extern void MoveToFloor(OBJECT *obj);
extern int MoveToMap(int x, int y, OBJECT *object);
extern void TakeObject(int x,int y, OBJECT *container);
extern int MoveObject(OBJECT *object,int x,int y,int pushed);
extern void TransferObject(OBJECT *object,int x,int y);
extern OBJECT *GetObject(int x,int y);
extern OBJECT *GetSolidObject(int x,int y);
extern OBJECT *GetSolidObject(int x,int y);
extern OBJECT *GameGetObject(int x,int y);
extern OBJECT *GetObjectBase(int x,int y);
extern void CreateContents(OBJECT *o);
extern int TakeQuantity(OBJECT *c,char *o,int q);
extern int MoveQuantity(OBJECT *s,OBJECT *d,char *o,int q);
extern void AddQuantity(OBJECT *c,char *o,int q);
extern int SumObjects(OBJECT *c,char *o,int q);
extern OBJECT *GetFirstObject(OBJECT *cont, char *name);
extern int getYN(char *q);
extern void gen_largemap();
extern int get_num(int no);
extern int get_str();
extern char user_input[128];
extern int FindPath(OBJECT *object, OBJECT *end, int diagonal);
extern int CanRoute(OBJECT *start, OBJECT *end, int diagonal);
extern int IsOnscreen(char *pname);
extern void CheckTime();
extern void ResumeSchedule(OBJECT *o);
extern void ResyncEverything();
extern char *BestName(OBJECT *o);
extern void CallVM(char *function);
extern void CallVMnum(int pos);
extern OBJECT *find_object_with_tag(int tag,char *name);
extern void draw_fatline(BITMAP *dest, int x, int y, int radius);
extern void check_object(OBJECT *o, char *label);
// unused functions
/*
static void Call_VM(char *c);
static void Call_VM_num(int n);
static void sysBug(char *msg, ...);
*/
extern char debcurfunc[];
extern void VMstacktrace();
void set_light(int x, int y, int x2, int y2, int light);
///////////////////////////////////
OBJECT *create_object(STRING name, int x, int y)
{
OBJECT *temp;
//boot2("***%s\n",name);
temp = OB_Alloc();
temp->curdir = temp->dir[0];
OB_Init(temp,name);
OB_SetDir(temp,CHAR_D,FORCE_SHAPE|FORCE_FRAME);
MoveToMap(x,y,temp);
CreateContents(temp);
return(temp);
}
void remove_object(OBJECT *object)
{
if(!object)
{
Bug("remove_object: attempt to remove NULL\n");
redraw();
return;
}
CHECK_OBJECT(object);
if(object->flags.decor) // Mustn't remove a decor, that's bad
return;
if(object == player)
{
Bug("remove_object: cannot remove the PLAYER\n");
redraw();
return;
}
DeleteObject(object);
}
void delete_pending()
{
DeletePending();
}
void change_object(OBJECT *obj,char *name)
{
STATS ostats;
char resname[32];
char pname[32];
char speech[128];
char user1[128];
char user2[128];
int tcache;
int partie; // Must preserve this for dead party members
CHECK_OBJECT(obj);
if(obj && name)
{
if(obj->flags.decor) // Mustn't change a decor, that's bad
return;
if(obj->flags.system) // Mustn't change a system object, that's bad
return;
partie = obj->flags.party;
memcpy(&ostats,obj->stats,sizeof(STATS));
strcpy(resname,obj->funcs->resurrect);
strcpy(speech,obj->funcs->talk);
strcpy(user1,obj->funcs->user1);
strcpy(user2,obj->funcs->user2);
tcache = obj->funcs->tcache;
strcpy(pname,obj->personalname);
OB_Init(obj,name);
memcpy(obj->stats,&ostats,sizeof(STATS));
strcpy(obj->funcs->resurrect,resname);
strcpy(obj->personalname,pname);
strcpy(obj->funcs->talk,speech);
strcpy(obj->funcs->user1,user1);
strcpy(obj->funcs->user2,user2);
obj->flags.party = partie;
obj->funcs->tcache = tcache;
AL_Add(&ActiveList,obj); // Make active (if it can be)
}
}
void replace_object(OBJECT *obj,STRING name)
{
int k;
if(obj && name)
{
if(obj->flags.decor) // Mustn't change a decor, that's bad
return;
if(obj->flags.system) // Mustn't change a system object, that's bad
return;
CHECK_OBJECT(obj);
k=fullrestore;
fullrestore=0;
OB_Init(obj,name);
fullrestore=k;
AL_Add(&ActiveList,obj); // Make active (if it can be)
gen_largemap(); // Recalc solid objects cache
}
}
void set_object_direction(OBJECT *obj,int dir)
{
if(obj)
{
CHECK_OBJECT(obj);
OB_SetDir(obj,dir,0);
}
}
void set_object_sequence(OBJECT *obj,char *seq)
{
if(obj && seq)
{
CHECK_OBJECT(obj);
OB_SetSeq(obj,seq);
}
}
void print(char *msg, ...)
{
char temp[4096];
va_list ap;
if(!msg)
{
Bug("Attemped to print NULL message\n");
va_end(ap);
redraw();
return;
}
va_start(ap, msg);
vsprintf(temp,msg,ap); // Temp is now the message
va_end(ap);
irecon_print(temp);
}
void printxy(int x,int y,char *msg, ...)
{
char temp[128];
va_list ap;
va_start(ap, msg);
vsprintf(temp,msg,ap); // Temp now is the message
irecon_printxy(x,y,temp);
va_end(ap);
}
void TextColor(int r,int g, int b)
{
irecon_colour(r,g,b);
}
void show_object(OBJECT *z,int x,int y)
{
if(!z)
return;
CHECK_OBJECT(z);
draw_rle_sprite(swapscreen,z->form->seq[0]->image,x,y);
}
OBJECT *get_first_object(int x,int y)
{
return GetObjectBase(x,y);//GetTopObject(x,y);
}
OBJECT *get_object(int x,int y)
{
return GameGetObject(x,y);
}
OBJECT *get_solid_object(int x,int y)
{
return GetSolidObject(x,y);
}
TILE *get_tile(int x,int y)
{
return GetTile(x,y);
}
void move_to_pocket(OBJECT *src,OBJECT *dest)
{
if(!src || !dest)
return;
CHECK_OBJECT(src);
CHECK_OBJECT(dest);
if(src->flags.quantity)
{
AddQuantity(dest,src->name,src->stats->quantity);
DeleteObject(src);
}
else
MoveToPocket(src,dest);
}
void transfer_to_pocket(OBJECT *src,OBJECT *dest)
{
if(!src || !dest)
return;
CHECK_OBJECT(src);
CHECK_OBJECT(dest);
if(src->flags.quantity)
{
AddQuantity(dest,src->name,src->stats->quantity);
DeleteObject(src);
}
else
TransferToPocket(src,dest);
}
int move_from_pocket(OBJECT *obj,OBJECT *container,int x,int y)
{
CHECK_OBJECT(obj);
CHECK_OBJECT(container);
if(!MoveFromPocket(obj,container,x,y))
return 0;
return 1;
}
int force_from_pocket(OBJECT *obj,OBJECT *container,int x,int y)
{
CHECK_OBJECT(obj);
CHECK_OBJECT(container);
if(!ForceFromPocket(obj,container,x,y))
return 0;
return 1;
}
int move_object(OBJECT *obj,int x, int y)
{
CHECK_OBJECT(obj);
if(!MoveObject(obj,x,y,0))
return 0;
return 1;
}
int push_object(OBJECT *obj,int x, int y)
{
CHECK_OBJECT(obj);
if(!MoveObject(obj,x,y,1))
return 0;
return 1;
}
void transfer_object(OBJECT *obj,int x, int y)
{
CHECK_OBJECT(obj);
TransferObject(obj,x,y);
}
void spill_contents(OBJECT *bag)
{
CHECK_OBJECT(bag);
spillcontents(bag,bag->x,bag->y);
}
void spill_contents_at(OBJECT *bag,int x, int y)
{
CHECK_OBJECT(bag);
spillcontents(bag,x,y);
}
int weigh_object(OBJECT *obj)
{
CHECK_OBJECT(obj);
return(WeighObject(obj));
}
int get_bulk(OBJECT *obj)
{
CHECK_OBJECT(obj);
return(GetBulk(obj));
}
void play_song(char *a)
{
S_PlayMusic(a);
}
void play_sound(char *a)
{
S_PlaySample(a,sf_volume);
}
void object_sound(char *a, OBJECT *b)
{
int vol,dist,dx,dy,svol;
OBJECT *temp;
// Safety check
if(!a || !b || !player)
return;
svol = sf_volume;
if(SoundFixed)
{
S_PlaySample(a,sf_volume);
return;
}
if(!b)
{
S_PlaySample(a,sf_volume);
return;
}
CHECK_OBJECT(b);
if(!b->flags.on)
{
S_PlaySample(a,sf_volume);
return;
}
if(b->parent.objptr)
{
temp = b->parent.objptr;
if(temp != player)
{
// if(temp->flags.person)
// return;
b->x = temp->x;
b->y = temp->y;
svol = sf_volume >> 2;
svol = (svol<<1) + svol; // Vol is now 3/4 sf_volume
}
}
vol = svol;
dx = player->x - b->x;
if(dx<0) dx=-dx;
dy = player->y - b->y;
if(dy<0) dy=-dy;
dist=dx;
if(dy>dx) dist=dy;
if(dist>SOUND_DIST)
{
dist-=SOUND_DIST;
vol = svol - (dist*SOUND_DROP);
if(vol<0) vol=0;
}
S_PlaySample(a,vol);
}
void stop_song()
{
S_StopMusic();
}
int object_is_called(OBJECT *obj,char *str)
{
if(!obj) return 0;
if(!str) return 0;
CHECK_OBJECT(obj);
/*
if(!OB_Check(obj))
{
Bug("check_object detected a Fuck-up in object_is_called(\"%s\")\n",str);
Bug("Detailed report follows:\n");
CHECK_OBJECT(obj);
ithe_panic("Fuck-up detected, check logfile",NULL);
}
*/
if(!obj->name)
ithe_panic("check_object","Object has NULL name");
if(!istricmp(obj->name,str))
return 1;
return 0;
}
void redraw()
{
irecon_update(); // Redraw all text output
Show(); // And blit the screen again
}
void get_input()
{
irekey=get_key_debounced();
}
int get_number(int no)
{
if(!get_num(no))
return 0;
return atoi(user_input);
}
int is_solid(int x,int y)
{
return isSolid(x,y);
}
int rnd(int max)
{
if(max==0) return 0; // Prevent division by 0
return(rand() % max);
}
int choose_member(OBJECT *x, char *player_call)
{
int ctr;
if(!x)
return 0;
//C_printf("choose member: %s\n",x->personalname?x->personalname:x->name);
CHECK_OBJECT(x);
for(ctr=0;ctr<MAX_MEMBERS;ctr++)
if(party[ctr])
Inactive(party[ctr]);
player = x;
SubAction_Wipe(player); // Erase any sub-tasks
ActivityName(player,player_call,NULL);
return 1;
}
int choose_leader(OBJECT *x, char *player_call, char *follower_call)
{
int ctr;
if(!x)
return 0;
CHECK_OBJECT(x);
//C_printf("choose leader: %s\n",x->personalname?x->personalname:x->name);
for(ctr=0;ctr<MAX_MEMBERS;ctr++)
if(party[ctr])
{
Inactive(party[ctr]);
if(party[ctr] != x)
{
SubAction_Wipe(party[ctr]); // Erase any sub-tasks
ActivityName(party[ctr],follower_call,NULL);
}
// ilog_printf("setting '%s' to follow function '%s'\n",party[ctr]->name,follower_call);
}
player = x;
SubAction_Wipe(player); // Erase any sub-tasks
ActivityName(player,player_call,NULL);
return 1;
}
int add_to_party(OBJECT *new_member)
{
CHECK_OBJECT(new_member);
return(AddToParty(new_member));
}
void remove_from_party(OBJECT *member)
{
CHECK_OBJECT(member);
SubFromParty(member);
ResumeSchedule(member);
}
void add_goal(OBJECT *o, int priority, char *vrm, OBJECT *target)
{
if(o && vrm)
{
CHECK_OBJECT(o);
#ifdef CHECK_OBJECT_STRICT
if(target)
{
CHECK_OBJECT(target);
}
#endif
if(o->flags.decor) // Mustn't change a decor, that's bad
return;
if(o->flags.system) // Mustn't change a system object
return;
// Bug("%s does %s to %x\n",o->name,vrm,target);
SubAction_Wipe(o); // Erase any sub-tasks
ActivityName(o,vrm,target);
}
}
void end_goal(OBJECT *o)
{
if(o)
{
CHECK_OBJECT(o);
Inactive(o);
}
}
void wipe_goals(OBJECT *o)
{
if(o)
{
CHECK_OBJECT(o);
Inactive(o);
}
}
void kill_goals(OBJECT *o, int priority)
{
if(o)
{
CHECK_OBJECT(o);
Inactive(o);
}
}
void restart()
{
Restart();
}
void set_darkness(int l)
{
if(l<0)
l=0;
if(l>255)
l=255;
SetDarkness(l);
}
int talk_to(char *speechfile, char *start)
{
char filename[1024];
if(!speechfile)
return 0;
if(!speechfile[0])
return 0;
if(loadfile(speechfile,filename))
return NPC_Converse(filename,start);
Bug("CONVERSATION FILE '%s' NOT FOUND\n",speechfile);
return 0;
}
void check_hurt(OBJECT *obj)
{
#ifdef CHECK_OBJECT_STRICT
if(obj)
{
CHECK_OBJECT(obj);
}
#endif
CheckHurt(obj);
}
void redraw_map()
{
RedrawMap();
}
void waitfor(unsigned ms)
{
rest(ms);
//rest_callback(ms,S_PollMusic); // Music is now polled by thread
}
void waitredraw(unsigned ms)
{
rest_callback(ms,RedrawMap);
}
// Line of sight routine, using J. Grant's interpretation of Bresenham
// From the ITG32 graphics library (1995)
int line_of_sight(int xa, int ya, int xb, int yb)
{
int t, distance,steps;
int xerr=0, yerr=0, delta_x, delta_y;
int incx, incy;
int xo,yo;
OBJECT *obj;
// Don't count the origin as a solid object
xo = xa;
yo = ya;
steps=0;
// Find the distance to go in each plane.
delta_x = xb - xa;
delta_y = yb - ya;
// Find out the direction
if(delta_x > 0) incx = 1;
else if(delta_x == 0) incx = 0;
else incx = -1;
if(delta_y > 0) incy = 1;
else if(delta_y == 0) incy = 0;
else incy = -1;
// Find which way is were mostly going
delta_x = abs(delta_x);
delta_y = abs(delta_y);
if(delta_x > delta_y)
distance=delta_x;
else
distance=delta_y;
// Draw the god dam line.
for(t = 0; t <= distance + 1; t++)
{
if(isSolid(xa,ya))
if(!(xa == xo && ya == yo))
if(!(xa == xb && ya == yb))
{
obj = GetSolidObject(xa,ya);
if(!obj)
return 0;
CHECK_OBJECT(obj);
if(!obj->flags.tabletop)
return 0;
}
xerr += delta_x;
yerr += delta_y;
steps++;
if(xerr > distance)
{
xerr -= distance;
xa += incx;
}
if(yerr > distance)
{
yerr -= distance;
ya += incy;
}
}
return steps;
}
void move_to_top(OBJECT *object)
{
CHECK_OBJECT(object);
MoveToTop(object);
}
void move_to_floor(OBJECT *object)
{
CHECK_OBJECT(object);
MoveToFloor(object);
}
int in_pocket(OBJECT *obj)
{
CHECK_OBJECT(obj);
if(obj->pocket.objptr)
return 1;
return 0;
}
void set_flag(OBJECT *target, int flag, int value)
{
if(!target)
return;
CHECK_OBJECT(target);
switch(flag)
{
case IS_ON:
/*
if(target == player)
{
Bug("set_flag: Attempted to switch off the player\n");
redraw();
return;
}
target->flags.on=value;
if(value == 0)
pending_delete = 1;
*/
Bug("Flag IS_ON is read-only, use 'destroy' to delete objects\n");
redraw();
return;
break;
case CAN_OPEN:
target->flags.willopen=value;
break;
case IS_WINDOW:
target->flags.window=value;
break;
case IS_SOLID:
target->flags.solid=value;
break;
case IS_FRAGILE:
target->flags.fragile=value;
break;
case IS_TRIGGER:
// Only allow this to be changed if there is a Stand function
if(target->funcs && target->funcs->scache > 0)
target->flags.trigger=value;
break;
case IS_INVISIBLE:
target->flags.invisible=value;
break;
case IS_PARTY:
target->flags.party=value;
break;
case IS_FIXED:
target->flags.fixed=value;
break;
case IS_CONTAINER:
target->flags.container=value;
break;
case IS_TRANSLUCENT:
target->flags.translucent=value;
break;
case IS_LARGE:
Bug("Flag LARGE is read-only\n");
redraw();
break;
case IS_SPIKEPROOF:
target->flags.spikeproof=value;
break;
case CAN_WIELD:
target->flags.wield=value;
break;
case DID_STEPUPDATE:
target->flags.stepupdated=value;
break;
// case IS_RANGED:
// target->flags.rangeweapon=value;
// break;
case DOES_BLOCKLIGHT:
target->flags.blocklight=value;
break;
case IS_TABLETOP:
target->flags.tabletop=value;
break;
case DID_INIT:
target->flags.didinit=value;
break;
case DID_UPDATE:
target->flags.didupdate=value;
break;
case IS_PERSON:
target->flags.person=value;
break;
/*
case IN_POCKET:
target->flags.inpocket=value;
break;
*/
case IS_QUANTITY:
target->flags.quantity=value;
break;
case IS_WATER:
target->flags.watery=value;
break;
case IS_SHADOW:
target->flags.shadow=value;
break;
case IS_DECOR:
Bug("Flag IS_DECOR is read-only\n");
redraw();
case IS_SYSTEM:
Bug("Flag IS_SYSTEM is read-only\n");
redraw();
case IS_HORRIBLE:
target->flags.horror=value;
break;
case IS_FEMALE:
target->stats->npcflags.female=value;
break;
case KNOW_NAME:
target->stats->npcflags.know_name=value;
break;
case IS_HERO:
target->stats->npcflags.is_hero=value;
break;
case CANT_EAT:
target->stats->npcflags.cant_eat=value;
break;
case IS_CRITICAL:
target->stats->npcflags.critical=value;
break;
case NOT_CLOSE_DOOR:
target->stats->npcflags.no_shutdoor=value;
break;
case IS_SYMLINK:
target->stats->npcflags.symlink=value;
break;
case IS_BIOLOGICAL:
target->stats->npcflags.biological=value;
break;
case IS_GUARD:
target->stats->npcflags.guard=value;
break;
case IS_SPAWNED:
target->stats->npcflags.spawned=value;
break;
case NOT_OPEN_DOOR:
target->stats->npcflags.no_opendoor=value;
break;
case IN_BED:
target->stats->npcflags.in_bed=value;
break;
case NO_SCHEDULE:
target->stats->npcflags.ignoreschedule=value;
break;
default:
Bug("Attempt to set unknown flag %08x in function '%s'\n",flag,debcurfunc);
redraw();
break;
}
}
int get_flag(OBJECT *target, int flag)
{
if(!target)
return 0;
CHECK_OBJECT(target);
switch(flag)
{
case IS_ON:
return (target->flags.on);
break;
case CAN_OPEN:
return (target->flags.willopen);
break;
case IS_WINDOW:
return (target->flags.window);
break;
case IS_SOLID:
return (target->flags.solid);
break;
case IS_FRAGILE:
return (target->flags.fragile);
break;
case IS_TRIGGER:
return (target->flags.trigger);
break;
case IS_INVISIBLE:
return (target->flags.invisible);
break;
case IS_PARTY:
return (target->flags.party);
break;
case IS_FIXED:
return (target->flags.fixed);
break;
case IS_CONTAINER:
return (target->flags.container);
break;
case IS_TRANSLUCENT:
return (target->flags.translucent);
break;
case IS_LARGE:
return (target->flags.large);
break;
case IS_SPIKEPROOF:
return (target->flags.spikeproof);
break;
case CAN_WIELD:
return (target->flags.wield);
break;
case DID_STEPUPDATE:
return (target->flags.stepupdated);
break;
// case IS_RANGED:
// return (target->flags.rangeweapon);
// break;
case DOES_BLOCKLIGHT:
return (target->flags.blocklight);
break;
case IS_TABLETOP:
return (target->flags.tabletop);
break;
case DID_INIT:
return (target->flags.didinit);
break;
case DID_UPDATE:
return (target->flags.didupdate);
break;
case IS_PERSON:
return (target->flags.person);
break;
/*
case IN_POCKET:
return (target->flags.inpocket);
break;
*/
case IS_QUANTITY:
return (target->flags.quantity);
break;
case IS_WATER:
return (target->flags.watery);
break;
case IS_SHADOW:
return (target->flags.shadow);
break;
case IS_DECOR:
return (target->flags.decor);
break;
case IS_SYSTEM:
return (target->flags.system);
break;
case IS_HORRIBLE:
return (target->flags.system);
break;
case IS_FEMALE:
return (target->stats->npcflags.female);
break;
case KNOW_NAME:
return (target->stats->npcflags.know_name);
break;
case IS_HERO:
return (target->stats->npcflags.is_hero);
break;
case CANT_EAT:
return (target->stats->npcflags.cant_eat);
break;
case IS_CRITICAL:
return (target->stats->npcflags.critical);
break;
case NOT_CLOSE_DOORS:
return (target->stats->npcflags.no_shutdoor);
break;
case IS_SYMLINK: // Protection: if there's nothing to link to, fail
if(target->stats->owner.objptr)
return (target->stats->npcflags.symlink);
else
return 0;
break;
case IS_BIOLOGICAL:
return (target->stats->npcflags.biological);
break;
case IS_GUARD:
return (target->stats->npcflags.guard);
break;
case IS_SPAWNED:
return (target->stats->npcflags.spawned);
break;
case IN_BED:
return (target->stats->npcflags.in_bed);
break;
case NOT_OPEN_DOORS:
return (target->stats->npcflags.no_opendoor);
break;
case NO_SCHEDULE:
return (target->stats->npcflags.ignoreschedule);
break;
}
Bug("Attempt to read unknown flag %08x in function '%s'\n",flag,debcurfunc);
redraw();
return 0;
}
void default_flag(OBJECT *target, int flag)
{
int id;
if(!target)
return;
CHECK_OBJECT(target);
if(target->flags.decor) // Mustn't change a decor, that's bad
return;
id = getnum4char(target->name);
if(id == -1)
return;
switch(flag)
{
case IS_ON:
target->flags.on=1;
break;
case CAN_OPEN:
target->flags.willopen=CHlist[id].flags.willopen;
break;
case IS_WINDOW:
target->flags.window=CHlist[id].flags.window;
break;
case IS_SOLID:
target->flags.solid=CHlist[id].flags.solid;
break;
case IS_FRAGILE:
target->flags.fragile=CHlist[id].flags.fragile;
break;
case IS_TRIGGER:
Bug("Flag TRIGGER is read-only\n");
redraw();
break;
case IS_INVISIBLE:
target->flags.invisible=CHlist[id].flags.invisible;
break;
case IS_PARTY:
target->flags.party=CHlist[id].flags.party;
break;
case IS_FIXED:
target->flags.fixed=CHlist[id].flags.fixed;
break;
case IS_CONTAINER:
target->flags.container=CHlist[id].flags.container;
break;
case IS_TRANSLUCENT:
target->flags.translucent=CHlist[id].flags.translucent;
break;
case IS_LARGE:
Bug("Flag LARGE is read-only\n");
redraw();
break;
case IS_SPIKEPROOF:
target->flags.spikeproof=CHlist[id].flags.spikeproof;
break;
case CAN_WIELD:
target->flags.wield=CHlist[id].flags.wield;
break;
case DID_STEPUPDATE:
target->flags.stepupdated=CHlist[id].flags.stepupdated;
break;
// case IS_RANGED:
// target->flags.rangeweapon=CHlist[id].flags.rangeweapon;
// break;
case DOES_BLOCKLIGHT:
target->flags.blocklight=CHlist[id].flags.blocklight;
break;
case IS_TABLETOP:
target->flags.tabletop=CHlist[id].flags.tabletop;
break;
case DID_INIT:
target->flags.didinit=CHlist[id].flags.didinit;
break;
case DID_UPDATE:
target->flags.didupdate=CHlist[id].flags.didupdate;
break;
case IS_PERSON:
target->flags.person=CHlist[id].flags.person;
break;
/*
case IN_POCKET:
target->flags.inpocket=CHlist[id].flags.inpocket;
break;
*/
case IS_QUANTITY:
target->flags.quantity=CHlist[id].flags.quantity;
break;
case IS_WATER:
target->flags.shadow=CHlist[id].flags.watery;
break;
case IS_SHADOW:
target->flags.shadow=CHlist[id].flags.shadow;
break;
case IS_DECOR:
Bug("Flag IS_DECOR is read-only\n");
redraw();
break;
case IS_SYSTEM:
Bug("Flag IS_SYSTEM is read-only\n");
redraw();
break;
case IS_HORRIBLE:
target->flags.horror=CHlist[id].flags.horror;
break;
case IS_FEMALE:
target->stats->npcflags.female=CHlist[id].stats->npcflags.female;
break;
case KNOW_NAME:
target->stats->npcflags.know_name=CHlist[id].stats->npcflags.know_name;
break;
case IS_HERO:
target->stats->npcflags.is_hero=CHlist[id].stats->npcflags.is_hero;
break;
case CANT_EAT:
target->stats->npcflags.cant_eat=CHlist[id].stats->npcflags.cant_eat;
break;
case IS_CRITICAL:
target->stats->npcflags.critical=CHlist[id].stats->npcflags.critical;
break;
case NOT_CLOSE_DOOR:
target->stats->npcflags.no_shutdoor=CHlist[id].stats->npcflags.no_shutdoor;
break;
case IS_SYMLINK:
target->stats->npcflags.symlink=CHlist[id].stats->npcflags.symlink;
break;
case IS_BIOLOGICAL:
target->stats->npcflags.biological=CHlist[id].stats->npcflags.biological;
break;
case IS_GUARD:
target->stats->npcflags.guard=CHlist[id].stats->npcflags.guard;
break;
case IS_SPAWNED:
target->stats->npcflags.spawned=CHlist[id].stats->npcflags.spawned;
break;
case NOT_OPEN_DOOR:
target->stats->npcflags.no_opendoor=CHlist[id].stats->npcflags.no_opendoor;
break;
case IN_BED:
target->stats->npcflags.in_bed=CHlist[id].stats->npcflags.in_bed;
break;
case NO_SCHEDULE:
target->stats->npcflags.ignoreschedule=CHlist[id].stats->npcflags.ignoreschedule;
break;
default:
Bug("Attempt to default unknown flag %08x in function '%s'\n",flag,debcurfunc);
redraw();
break;
}
}
int get_tileflag(TILE *target, int flag)
{
if(!target)
return 0;
switch(flag)
{
case IS_ON:
return (target->flags.on);
break;
case CAN_OPEN:
return (target->flags.willopen);
break;
case IS_WINDOW:
return (target->flags.window);
break;
case IS_SOLID:
return (target->flags.solid);
break;
case IS_FRAGILE:
return (target->flags.fragile);
break;
case IS_TRIGGER:
return (target->flags.trigger);
break;
case IS_INVISIBLE:
return (target->flags.invisible);
break;
case IS_PARTY:
return (target->flags.party);
break;
case IS_FIXED:
return (target->flags.fixed);
break;
case IS_CONTAINER:
return (target->flags.container);
break;
case IS_TRANSLUCENT:
return (target->flags.translucent);
break;
case IS_LARGE:
return (target->flags.large);
break;
case IS_SPIKEPROOF:
return (target->flags.spikeproof);
break;
case CAN_WIELD:
return (target->flags.wield);
break;
case DID_STEPUPDATE:
return (target->flags.stepupdated);
break;
// case IS_RANGED:
// return (target->flags.rangeweapon);
// break;
case DOES_BLOCKLIGHT:
return (target->flags.blocklight);
break;
case IS_TABLETOP:
return (target->flags.tabletop);
break;
case DID_INIT:
return (target->flags.didinit);
break;
case DID_UPDATE:
return (target->flags.didupdate);
break;
case IS_PERSON:
return (target->flags.person);
break;
/*
case IN_POCKET:
return (target->flags.inpocket);
break;
*/
case IS_QUANTITY:
return (target->flags.quantity);
break;
case IS_WATER:
return (target->flags.watery);
break;
case IS_SHADOW:
return (target->flags.shadow);
break;
case IS_FEMALE:
Bug("Tried to read flag IS_FEMALE from a Tile: not allowed for tiles\n");
return 0;
break;
case KNOW_NAME:
Bug("Tried to read flag KNOW_NAME from a Tile: not allowed for tiles\n");
return 0;
break;
case IS_HERO:
Bug("Tried to read flag IS_HERO from a Tile: not allowed for tiles\n");
return 0;
break;
case CANT_EAT:
Bug("Tried to read flag CANT_EAT from a Tile: not allowed for tiles\n");
return 0;
break;
case IS_CRITICAL:
Bug("Tried to read flag IS_CRITICAL from a Tile: not allowed for tiles\n");
return 0;
break;
case NOT_CLOSE_DOORS:
Bug("Tried to read flag NOT_CLOSE_DOORS from a Tile: not allowed for tiles\n");
return 0;
break;
case IS_SYMLINK:
Bug("Tried to read flag IS_SYMLINK from a Tile: not allowed for tiles\n");
return 0;
break;
case IS_BIOLOGICAL:
Bug("Tried to read flag IS_BIOLOGICAL from a Tile: not allowed for tiles\n");
return 0;
break;
case IS_GUARD:
Bug("Tried to read flag IS_GUARD from a Tile: not allowed for tiles\n");
return 0;
break;
case IS_SPAWNED:
Bug("Tried to read flag IS_SPAWNED from a Tile: not allowed for tiles\n");
return 0;
break;
case IN_BED:
Bug("Tried to read flag IN_BED from a Tile: not allowed for tiles\n");
return 0;
break;
case NOT_OPEN_DOORS:
Bug("Tried to read flag NOT_OPEN_DOORS from a Tile: not allowed for tiles\n");
return 0;
break;
case NO_SCHEDULE:
Bug("Tried to read flag NO_SCHEDULE from a Tile: not allowed for tiles\n");
return 0;
break;
}
Bug("Attempt to read unknown flag %08x\n",flag);
redraw();
return 0;
}
int take_quantity(OBJECT *container, char *objecttype, int quantity)
{
CHECK_OBJECT(container);
return (TakeQuantity(container,objecttype,quantity));
}
void add_quantity(OBJECT *container, char *objecttype, int quantity)
{
CHECK_OBJECT(container);
AddQuantity(container,objecttype,quantity);
}
int move_quantity(OBJECT *src, OBJECT *dest, char *objecttype, int quantity)
{
if(!src)
{
Bug("move_quantity with NULL source\n");
return 0;
}
CHECK_OBJECT(src);
if(!dest)
{
Bug("move_quantity with NULL destination\n");
return 0;
}
CHECK_OBJECT(dest);
return (MoveQuantity(src,dest,objecttype,quantity));
}
int count_objects(OBJECT *container, char *objecttype)
{
CHECK_OBJECT(container);
if(container->pocket.objptr)
return SumObjects(container->pocket.objptr,objecttype,0);
return 0;
}
OBJECT *find_object_with_tag(int tag,char *name)
{
OBJLIST *t;
for(t=MasterList;t;t=t->next)
if(t->ptr->tag == tag)
if(name)
{
if(!istricmp(name,t->ptr->name))
return t->ptr;
}
else
return t->ptr;
return NULL;
}
int get_yn(char *question)
{
return(getYN(question));
}
void set_user_flag(char *a, int s)
{
Set_tFlag(a,s);
}
int get_user_flag(char *a)
{
return(Get_tFlag(a));
}
void set_local(OBJECT *plyr, OBJECT *obj, char *a, int s)
{
NPC_set_lFlag(obj,a,plyr,s);
}
int get_local(OBJECT *plyr, OBJECT *obj, char *a)
{
return(NPC_get_lFlag(obj,a,plyr));
}
int move_forward(OBJECT *a)
{
if(!a)
return 0;
CHECK_OBJECT(a);
a->flags.stepupdated=1; // Force animation
if(a->curdir == CHAR_U)
return MoveObject(a,a->x,a->y-1,0);
if(a->curdir == CHAR_D)
return MoveObject(a,a->x,a->y+1,0);
if(a->curdir == CHAR_L)
return MoveObject(a,a->x-1,a->y,0);
if(a->curdir == CHAR_R)
return MoveObject(a,a->x+1,a->y,0);
return 0;
}
int move_backward(OBJECT *a)
{
if(!a)
return 0;
CHECK_OBJECT(a);
a->flags.stepupdated=1; // Force animation
if(a->curdir == CHAR_U)
return MoveObject(a,a->x,a->y+1,0);
if(a->curdir == CHAR_D)
return MoveObject(a,a->x,a->y-1,0);
if(a->curdir == CHAR_L)
return MoveObject(a,a->x+1,a->y,0);
if(a->curdir == CHAR_R)
return MoveObject(a,a->x-1,a->y,0);
return 0;
}
int turn_l(OBJECT *a)
{
if(!a)
return 0;
CHECK_OBJECT(a);
switch(a->curdir)
{
case CHAR_U:
OB_SetDir(a,CHAR_L,FORCE_SHAPE);
break;
case CHAR_L:
OB_SetDir(a,CHAR_D,FORCE_SHAPE);
break;
case CHAR_D:
OB_SetDir(a,CHAR_R,FORCE_SHAPE);
break;
case CHAR_R:
OB_SetDir(a,CHAR_U,FORCE_SHAPE);
break;
}
return a->curdir;
}
int turn_r(OBJECT *a)
{
if(!a)
return 0;
CHECK_OBJECT(a);
switch(a->curdir)
{
case CHAR_U:
OB_SetDir(a,CHAR_R,FORCE_SHAPE);
break;
case CHAR_R:
OB_SetDir(a,CHAR_D,FORCE_SHAPE);
break;
case CHAR_D:
OB_SetDir(a,CHAR_L,FORCE_SHAPE);
break;
case CHAR_L:
OB_SetDir(a,CHAR_U,FORCE_SHAPE);
break;
}
return a->curdir;
}
void wait_for_animation(OBJECT *obj)
{
if(!obj)
return;
CHECK_OBJECT(obj);
if(obj->form->flags&6) // Ignore if looped or stepped
return; // (Otherwise it will never come back)
if(obj->parent.objptr) // Objects in pockets do not animate
return;
for(;obj->sdir;RedrawMap());
}
void ___lightning(int ticks)
{
do_lightning=ticks;
}
int is_tile_solid(int x,int y)
{
return IsTileSolid(x,y);
}
int is_tile_water(int x,int y)
{
return IsTileWater(x,y);
}
void move_party_to_object(OBJECT *o)
{
if(!o)
return;
CHECK_OBJECT(o);
for(int ctr=0;ctr<MAX_MEMBERS;ctr++)
if(party[ctr])
TransferToPocket(party[ctr],o);
}
void move_party_from_object(OBJECT *o, int x, int y)
{
if(!o)
return;
CHECK_OBJECT(o);
for(int ctr=0;ctr<MAX_MEMBERS;ctr++)
if(party[ctr])
ForceFromPocket(party[ctr],o,x,y);
}
int move_towards(OBJECT *object, OBJECT *end)
{
char *s1,*s2;
if(!object || !end)
{
s1="Null";
s2="Null";
if(object)
s1=BestName(object);
if(end)
s2=BestName(end);
Bug("FindPath (%s,%s)\n",s1,s2);
return 0;
}
CHECK_OBJECT(object);
CHECK_OBJECT(end);
if(object->flags.decor)
return 0;
if(end->flags.decor)
return 0;
if(object->flags.system)
return 0;
/*
if(!stricmp(object->name,"plate_broken"))
ithe_panic("Oh no not again","plate is posessed");
*/
return FindPath(object,end,1);
}
int move_towards_4(OBJECT *object, OBJECT *end)
{
char *s1,*s2;
if(!object || !end)
{
s1="Null";
s2="Null";
if(object)
s1=BestName(object);
if(end)
s2=BestName(end);
Bug("FindPath (%s,%s)\n",s1,s2);
return 0;
}
CHECK_OBJECT(object);
CHECK_OBJECT(end);
if(object->flags.decor)
return 0;
if(end->flags.decor)
return 0;
if(object->flags.system)
return 0;
return FindPath(object,end,0);
}
int move_thick(OBJECT *object, OBJECT *end)
{
char *s1,*s2;
if(!object || !end)
{
s1="Null";
s2="Null";
if(object)
s1=object->name;
if(end)
s2=end->name;
Bug("MoveThick (%s,%s)\n",s1,s2);
return 0;
}
CHECK_OBJECT(object);
CHECK_OBJECT(end);
if(object->flags.decor)
return 0;
if(end->flags.decor)
return 0;
if(object->flags.system)
return 0;
object->user->dx=0;
if(object->x < end->x)
object->user->dx=1;
if(object->x > end->x)
object->user->dx=-1;
object->user->dy=0;
if(object->y < end->y)
object->user->dy=1;
if(object->y > end->y)
object->user->dy=-1;
if(object->user->dy<0)
OB_SetDir(object,CHAR_U,FORCE_SHAPE);
if(object->user->dy>0)
OB_SetDir(object,CHAR_D,FORCE_SHAPE);
if(object->user->dx<0)
OB_SetDir(object,CHAR_L,FORCE_SHAPE);
if(object->user->dx>0)
OB_SetDir(object,CHAR_R,FORCE_SHAPE);
MoveObject(object,object->x+object->user->dx,object->y+object->user->dy,0);
object->flags.stepupdated=1; // Force animation
return 1;
}
int character_onscreen(char *pname)
{
return IsOnscreen(pname);
}
void scroll_things()
{
int ctr,x,y;
for(ctr=0;ctr<TItot;ctr++)
{
x = TIlist[ctr].name[1];
if(x<'A' || x>'Z')
break;
x =((x-'A')%3)-1;
y = TIlist[ctr].name[1];
if(y<'A' || y>'Z')
break;
y =((y-'A')%5)-2;
TIlist[ctr].sdx=x;
TIlist[ctr].sdy=y;
}
}
void scroll_tile(char *name, int x, int y)
{
int ctr;
if(x<0)
x=32+x;
if(y<0)
y=32+y;
for(ctr=0;ctr<TItot;ctr++)
if(!istricmp(TIlist[ctr].name,name))
{
TIlist[ctr].sdx=x;
TIlist[ctr].sdy=y;
// If zero, reset the scroll offsets, since the user probably wants it to
// go back to normal
if(x == 0 && y == 0)
{
TIlist[ctr].sx=0;
TIlist[ctr].sy=0;
}
return;
}
Bug("No such tile '%s'\n",name);
}
void scroll_tile_number(int no, int x, int y)
{
if(no<0 || no>=TItot)
{
Bug("No such tile number %d\n",no);
return;
}
if(x<0)
x=32+x;
if(y<0)
y=32+y;
TIlist[no].sdx=x;
TIlist[no].sdy=y;
// If zero, reset the scroll offsets, since the user probably wants it to
// go back to normal
if(x == 0 && y == 0)
{
TIlist[no].sx=0;
TIlist[no].sy=0;
}
}
/* unused function
void sysBug(char *msg, ...)
{
char temp[4096];
va_list ap;
if(!msg)
{
Bug("Attemped to Bug-print NULL message\n");
va_end(ap);
redraw();
return;
}
va_start(ap, msg);
vsprintf(temp,msg,ap); // Temp is now the message
va_end(ap);
Bug(temp);
}
*/
OBJECT *search_container(OBJECT *container,char *name)
{
if(!container)
return NULL;
CHECK_OBJECT(container);
if(container->pocket.objptr)
return GetFirstObject(container->pocket.objptr, name);
return NULL;
}
void vrmfade_in()
{
for(Fader=255;Fader>0;Fader-=8)
{
RedrawMap();
rest(1);
// S_PollMusic(); // Music is now polled by thread
}
Fader=0;
}
void vrmfade_out()
{
for(Fader=0;Fader<255;Fader+=8)
{
RedrawMap();
rest(1);
// S_PollMusic(); // Music is now polled by thread
}
Fader=255;
}
void check_time()
{
CheckTime();
}
OBJECT *find_nearest(OBJECT *o, char *type)
{
#define INF 2000000000
char *s1,*s2;
OBJECT *temp;
int x,y,mix,miy,max,may,steps;
// the three possible cases, best of each
OBJECT *mine_ob=NULL;
int mine_st=INF;
OBJECT *public_ob=NULL;
int public_st=INF;
OBJECT *private_ob=NULL;
int private_st=INF;
if(!o || !type)
{
s1="Null";
s2="Null";
if(o)
s1=o->name;
if(type)
s2=type;
Bug("find_nearest(%s,%s)\n",s1,s2);
return 0;
}
CHECK_OBJECT(o);
// Now build the search area and make sure it is within the bounds of the map
mix = o->x - 16;
miy = o->y - 16;
if(mix<0)
mix=0;
if(miy<0)
miy=0;
max=mix+32;
may=miy+32;
if(max>curmap->w)
max=curmap->w;
if(may>curmap->h)
may=curmap->h;
// Ok, start searching
for(y=miy;y<may;y++)
for(x=mix;x<max;x++)
for(temp=GetRawObjectBase(x,y);temp;temp=temp->next)
if(temp->flags.on)
if(!istricmp(temp->name,type))
{
steps = CanRoute(o,temp,1);
if(steps >= 0)
{
// best case, mine
if(temp->stats->owner.objptr == o)
{
if(steps<mine_st)
{
mine_st = steps;
mine_ob = temp;
}
}
else
if(temp->stats->owner.objptr == NULL) // next best case, public
{
if(steps<public_st)
{
public_st = steps;
public_ob = temp;
}
}
else // worst case, someone else's
{
if(steps<private_st)
{
private_st = steps;
private_ob = temp;
}
}
}
}
// Now we should have the best possible case for each of the three types
if(mine_ob)
return mine_ob;
if(public_ob)
return public_ob;
return private_ob;
}
// Find up to 16 of the nearest objects of the type requested
void find_nearby(OBJECT *o, char *type, OBJECT **list, int listsize)
{
char *s1,*s2;
OBJECT *temp;
int x,y,mix,miy,max,may,ptr;
if(!o || !type)
{
s1="Null";
s2="Null";
if(o)
s1=o->name;
if(type)
s2=type;
Bug("find_nearby(%s,%s,%x,%d)\n",s1,s2,list,listsize);
return;
}
// Blank the list
memset(list,0,listsize*sizeof(OBJECT *));
CHECK_OBJECT(o);
// Now build the search area and make sure it is within the bounds of the map
mix = o->x - 16;
miy = o->y - 16;
if(mix<0)
mix=0;
if(miy<0)
miy=0;
max=mix+32;
may=miy+32;
if(max>curmap->w)
max=curmap->w;
if(may>curmap->h)
may=curmap->h;
// Ok, start searching, and keep adding to the list if we can get there
ptr=0;
for(y=miy;y<may;y++)
for(x=mix;x<max;x++)
for(temp=GetRawObjectBase(x,y);temp;temp=temp->next)
if(temp->flags.on)
if(!istricmp_fuzzy(temp->name,type))
if(CanRoute(o,temp,1) >= 0)
if(ptr<listsize)
list[ptr++]=temp;
return;
}
// Find object by tag (searching nearby for speed)
OBJECT *find_neartag(OBJECT *o, char *type, int tag)
{
OBJECT *temp;
int x,y,mix,miy,max,may;
if(!o)
{
Bug("find_neartag(NULL,%s,%d)\n",NULL,type,tag);
return NULL;
}
CHECK_OBJECT(o);
// Now build the search area and make sure it is within the bounds of the map
mix = o->x - 16;
miy = o->y - 16;
if(mix<0)
mix=0;
if(miy<0)
miy=0;
max=mix+32;
may=miy+32;
if(max>curmap->w)
max=curmap->w;
if(may>curmap->h)
may=curmap->h;
// Search
for(y=miy;y<may;y++)
for(x=mix;x<max;x++)
for(temp=GetRawObjectBase(x,y);temp;temp=temp->next)
if(temp->tag == tag)
if(!type)
return temp;
else
if(!istricmp_fuzzy(temp->name,type))
return temp;
return NULL;
}
// Find a path marker
OBJECT *find_pathmarker(OBJECT *o, char *name)
{
char *s1,*s2;
OBJECT *temp;
int x,y,mix,miy,max,may,ptr;
if(!o || !name)
{
s1="Null";
s2="Null";
if(o)
s1=o->name;
if(name)
s2=name;
Bug("find_pathmarker(%s,%s)\n",s1,s2);
return NULL;
}
CHECK_OBJECT(o);
// Now build the search area and make sure it is within the bounds of the map
mix = o->x - 32;
miy = o->y - 32;
if(mix<0)
mix=0;
if(miy<0)
miy=0;
max=mix+64;
may=miy+64;
if(max>curmap->w)
max=curmap->w;
if(may>curmap->h)
may=curmap->h;
// Ok, start searching.
ptr=0;
for(y=miy;y<may;y++)
for(x=mix;x<max;x++)
for(temp=GetRawObjectBase(x,y);temp;temp=temp->next)
if(temp->flags.on)
if(!istricmp(temp->name,"pathmarker"))
if(!istricmp(temp->labels->location,name))
if(CanRoute(o,temp,1) >= 0)
return temp;
return NULL;
}
/*
* return a string with the optimal description of the object
*/
char *best_name(OBJECT *o)
{
if(!o)
return nullstr;
CHECK_OBJECT(o);
return BestName(o);
}
/*
* count_active_objects() - diagnostic data
*/
int count_active_objects()
{
OBJLIST *t;
int ctr=0;
for(t=ActiveList;t;t=t->next) ctr++;
return ctr;
}
int object_onscreen(OBJECT *a)
{
if(!a)
{
Bug("Tried to find out if NULL is onscreen\n");
return 0;
}
CHECK_OBJECT(a);
// Is it inside the window?
if(a->x >=mapx)
if(a->x <= mapx2)
if(a->y >=mapy)
if(a->y <= mapy2)
return 1; // Yes
return 0; // No
}
void resume_schedule(OBJECT *o)
{
if(!o)
return;
CHECK_OBJECT(o);
ResumeSchedule(o);
}
void resync_everything()
{
ResyncEverything();
}
OBJECT *get_object_below(OBJECT *o)
{
OBJECT *temp;
if(!o)
{
Bug("get_object_below(NULL);\n");
return NULL;
}
CHECK_OBJECT(o);
if(o->parent.objptr) // Don't bother
return NULL;
temp = GetObjectBase(o->x,o->y);
if(!temp)
{
Bug("get_object_below found nothing: map corrupt?\n");
return NULL;
}
if(temp == o) // Nothing below it
return NULL;
for(;temp->next;temp=temp->next)
if(temp->next == o)
return temp;
//Bug("Ooh bugger!\n");
return NULL;
}
void check_object(OBJECT *target, char *label)
{
if(!target)
return;
if(!OB_Check(target))
{
Bug("check_object detected a F___-up in PE-space (\"%s\")\n",label);
ithe_panic("F___-up detected, check logfile",NULL);
}
}
void set_light(int x, int y, int x2, int y2, int light)
{
int ctr,w;
if(x<0 || y<0 || x2>curmap->w || y2>curmap->h)
{
Bug("Tried to Set Light (%d,%d-%d,%d) outside map (%d,%d-%d,%d)\n",x,y,x2,y2,0,0,curmap->w,curmap->h);
return;
}
w=(x2-x)+1;
if(w<0)
{
Bug("Bogus light range (%d,%d to %d,%d)\n",x,y,x2,y2);
return;
}
for(ctr=y;ctr<=y2;ctr++)
memset(&curmap->lightst[(ctr*curmap->w)+x],light,w);
}
void find_objects_with_tag(long tag,char *name, long *num, OBJECT **obj)
{
OBJLIST *t;
int ctr;
if(!obj)
return;
if(!num)
return;
if(*num<1)
return;
ctr=0;
for(t=MasterList;t;t=t->next)
if(t->ptr->tag == tag)
if(name)
if(!istricmp(name,t->ptr->name))
if(ctr < *num)
obj[ctr++]=t->ptr;
*num=ctr;
return;
}
// unused functions
/*
void Call_VM(char *c)
{
CallVM(c);
}
void Call_VM_num(int n)
{
CallVMnum(n);
}
*/
void draw_fatline(BITMAP *dest, int x, int y, int radius)
{
circle(dest,x,y,radius,tfx_Colour);
}
void InitOrbit()
{
int ctr=0;
for(ctr=0;ctr<3600;ctr++)
{
ire_sintab[ctr] = sin(((float)(ctr)/3600.0)*6.28);
ire_costab[ctr] = cos(((float)(ctr)/3600.0)*6.28);
}
for(ctr=0;ctr<32768;ctr++)
ire_sqrt[ctr]=(float)sqrt((double)ctr);
}
void CalcOrbit(long *angle, long radius, long drift, long speed, long *x, long *y, long ox, long oy)
{
float fx,fy;
long turb;
// Calculate path turbulence
turb = 0;
if(drift > 0)
turb = qrand()%drift;
if(drift < 0)
turb = -(qrand()%(-drift));
// Update angle
*angle += speed;
*angle %= 3600;
// Translate around origin
fx = ire_sintab[*angle] * (float)(radius + turb);
fy = ire_costab[*angle] * (float)(radius + turb);
// Write finished coordinates
*x = ox + (long)fx;
*y = oy + (long)fy;
}
// Project a corona effect
#define INC_COLOUR(cl,val) {cl+=val; if(cl>255) cl=255;}
void ProjectCoronaOld(int xc,int yc, int w, int h,int intensity, int falloff)
{
int x,y,x1,y1,w1,h1;
int r,g,b,pixel,inc;
double xa,yb;
// Get half width
w1=(w>>1);
h1=(h>>1);
// Calculate new top-left (from centre position)
x1=xc-w1;
y1=yc-h1;
for(y=0;y<h;y++)
for(x=0;x<w;x++)
{
pixel=getpixel(gamewin,x+x1,y+y1);
r=getr(pixel);
g=getg(pixel);
b=getb(pixel);
xa=(double)w1-(double)(x);
yb=(double)h1-(double)(y);
inc=(int)((double)intensity-(((double)falloff/100.0)*(xa*xa)+(yb*yb)));
if(inc<0) inc=0;
if(inc)
{
INC_COLOUR(r,inc);
INC_COLOUR(g,inc);
INC_COLOUR(b,inc);
}
putpixel(gamewin,x+x1,y+y1,makecol(r,g,b));
}
}
void ProjectCorona(long xc,long yc, long radius,long intensity, long falloff, long tint)
{
long x,y,x1,y1,diam,xa,yb;
long r,g,b,pixel,inc,xayb;
// Get diameter
diam=radius<<1;
// Calculate new top-left (from centre position)
x1=xc-radius;
y1=yc-radius;
for(y=0;y<diam;y++)
for(x=0;x<diam;x++)
{
xa=radius-x;
yb=radius-y;
// Calculate the squared value and clip it for the SQRT table
xayb=(xa*xa)+(yb*yb);
if(xayb>32768)
xayb=32768;
if(xayb<0)
xayb=0;
inc=intensity-(int)(((float)falloff/100.0)*ire_sqrt[xayb]);
if(inc>0)
{
// Only do the pixel stuff if we need to
pixel=getpixel(gamewin,x+x1,y+y1);
r=getr(pixel);
g=getg(pixel);
b=getb(pixel);
if(tint&TINT_RED)
INC_COLOUR(r,inc);
if(tint&TINT_GREEN)
INC_COLOUR(g,inc);
if(tint&TINT_BLUE)
INC_COLOUR(b,inc);
putpixel(gamewin,x+x1,y+y1,makecol(r,g,b));
}
}
}