///////////////////////////////////////////////////////////////////////
// ackdll.cpp : Basic example DLL for A5
// (c) conitec jcl 2000,2001
// use this as template for your own DLLs
///////////////////////////////////////////////////////////////////////
// Note: The SDK license includes the right to freely distribute DLLs
// created with it, as long as the DLL functions only provide application
// functionality. It is not allowed to distribute DLLs that work as a
// 'wrapper' for the library or provide functions that allow external
// access to the library variables or functions.
///////////////////////////////////////////////////////////////////////
// 06-01-01 (JCL) WDLBeep(n) example added
// 06-19-01 (JCL) PaintEntitiesRed() example added
// 07-06-01 (JCL) DLLEffect_Explode() example added
///////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <math.h> 
#include "stdafx.h"   
#include "a5dll.h"
#include "a5funcs.h"	// C++ inline functions for the library

//  ****************************************************
//  CS 326 Fall 2003, Assignment 3 code
//  ****************************************************
//   (do not forget to make the global replacement of 
//   "FixedPoint" for "fixed" for ALL the files in your 
//   VC++ or VStudio.NET project.  This means ALL of the 
//   header (.h) and code (.cpp) files.)  Do this BEFORE
//   pasting in this code, or make sure you check off the
//   "Match Whole Word" box when you choose the "Edit/Find and
//   Replace/Replace in Files" menu option (and also choose
//   to "Look In" "All Open Documents").  

#include "BasicEntity.h"
#include "Controller.h"



DLLFUNC fixedPoint StartTheMatrix()
{
	Controller * theMatrix = new Controller();
//	theMatrix->show();
	return (fixedPoint) theMatrix;
}
 
DLLFUNC fixedPoint runTheMatrix(long input_ptr)
{
	Controller* theMatrixPtr = (Controller*) input_ptr;
	theMatrixPtr->update();
	return INT2FIX(1);
}

DLLFUNC fixedPoint dll_event_catcher(long input_ptr)
{
	BasicEntity* BasicEntityPtr = (BasicEntity*) input_ptr; 
//	BasicEntityPtr->event_catcher(
	return INT2FIX(1);
}

DLLFUNC fixedPoint Straighten(long entity)
{
	if (!entity) return 0;
// retrieve the pointer to the given entity
	A4_ENTITY *ent = (A4_ENTITY *)entity;

//  Rotate entity 10 degrees
	ent->roll -=  INT2FIX(1);

// return something
	return INT2FIX(1);
}

DLLFUNC fixedPoint MoveCPP(long entity)
{ 	
	if (!entity) return 0;
// retrieve the pointer to the given entity
	A4_ENTITY *ent = (A4_ENTITY *)entity;

	ent->scale_x += 10;
	ent->scale_y += 10;
	ent->scale_z += 10;

	return INT2FIX(1);
}

DLLFUNC fixedPoint SpinMe(long entity)
{
//		A4_ENTITY *g;
		A4_ENTITY *ent;
	//	ent = (A4_VIEW*) entity; 
		ent = (A4_ENTITY*) a5dll_getwdlobj("camera");
        if (!ent) return 0;
        ent->roll +=FLOAT2FIX(10);
        return INT2FIX(1);
}

//  ****************************************************
//  END OF CS 326, Fall 2003 Assignment 3 Code
//  ****************************************************

///////////////////////////////////////////////////////////////////////
// DLL main entry point - normally this needs not to be changed
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved )
{
    return TRUE;
}

///////////////////////////////////////////////////////////////////////
// All exported DLL functions must be of type
// DLLFUNC fixedPoint func(long) - or - DLLFUNC fixedPoint func(fixedPoint)
// fixedPoint is a long integer interpreted as 22.10 fixedPoint point value

///////////////////////////////////////////////////////////////////////
// example for creating a new C-Script arithmetic function
// dllfunction Square(var);
// returns var*var
DLLFUNC fixedPoint Square(fixedPoint var)
{
	return (FLOAT2FIX(FIX2FLOAT(var)*FIX2FLOAT(var)));
}

///////////////////////////////////////////////////////////////////////
// example for manipulating an entity
// dllfunction FlipUpsideDown(entity);
// rolls the given entity by 180 degrees
DLLFUNC fixedPoint FlipUpsideDown(long entity)
{
	if (!entity) return 0;
// retrieve the pointer to the given entity
	A4_ENTITY *ent = (A4_ENTITY *)entity;
// set the entity's roll angle to 180 degrees
	ent->roll = FLOAT2FIX(180);
// return something
	return INT2FIX(1);
}



DLLFUNC fixedPoint creatEntity(long my)
{
	if (!my) return 0;
// retrieve the pointer to the given entity
	A4_ENTITY *ent = (A4_ENTITY *)my;

// get the address of some C-Script variables and functions
// normally this should be done only once in an init dll function
// C-Script variable and function addresses won't change during gameplay
	fixedPoint *tracemode = (fixedPoint *)a5dll_getwdlobj("trace_mode");
	wdlfunc2 trace = (wdlfunc2)a5dll_getwdlfunc("trace");
	wdlfunc2 vecrotate = (wdlfunc2)a5dll_getwdlfunc("vec_rotate");
	if (!tracemode || !trace || !vecrotate) return 0;

	fixedPoint target[3] = { FLOAT2FIX(1000.0),0,0 };	// trace target vector

// rotate vector by entity engles, just as in C-Script
	(*vecrotate)((long)target,(long)&(ent->pan));

// add entity position to target
	target[0] += ent->x;
	target[1] += ent->y;
	target[2] += ent->z;

// set trace_mode, then trace a line between entity and target,
// and return the result
	*tracemode = INT2FIX(TRMF_IGNORE_ME + TRMF_IGNORE_PASSABLE + TRMF_USE_BOX);
	return (*trace)((long)&(ent->x),(long)target);
}











///////////////////////////////////////////////////////////////////////
// example for accessing a C-Script variable
// dllfunction AddToSkySpeed(value);
// adds the given value to the predefined variables sky_speed.x and sky_speed.y
DLLFUNC fixedPoint AddToSkySpeed(fixedPoint value)
{
// get the address of the variable (which is an array of 3 by default)
// normally this should be done only once in an init dll function, for speed reasons
// the C-Script variable addresses won't change during gameplay
	fixedPoint *skyspeed = (fixedPoint *)a5dll_getwdlobj("sky_speed");	
	if (!skyspeed) return 0; // can only happen when "sky_speed" doesn't exist

// add the value to the x and y components
	skyspeed[0] += value;	// skyspeed X value
	skyspeed[1] += value;	// skyspeed y value

// return something
 	return INT2FIX(1);
}

///////////////////////////////////////////////////////////////////////
// example for handing over an array
// dllfunction SetCloudSpeed(&array);
// adds the first 2 elements of the array to predefined variables cloud_speed.x,y
DLLFUNC fixedPoint SetCloudSpeed(long array)
{
// get the address of the variable (which is an array of 3 by default)
	fixedPoint *cloudspeed = (fixedPoint *)a5dll_getwdlobj("cloud_speed");
	if (!cloudspeed) return 0;

// unlike dll_exec, a pointer to the variable is transferred here
// add the value to the x and y components
	cloudspeed[0] = ((fixedPoint *)array)[0];	// cloudspeed X value
	cloudspeed[1] = ((fixedPoint *)array)[1];	// cloudspeed y value

// return something
 	return INT2FIX(1);
}

///////////////////////////////////////////////////////////////////////
// example for accessing a C-Script object
// dllfunction ZoomIn(value);
// reduces the camera FOV angle by the given degrees
DLLFUNC fixedPoint ZoomIn(fixedPoint value)
{
// get the address of a view object
// Note: call a5dll_getwdlobj only at initialisation and NOT during gameplay - it is SLOW!
	A4_VIEW *camera = (A4_VIEW *)a5dll_getwdlobj("camera");
	if (!camera) return 0;

// decrease the camera FOV by the value
	camera->arc -= value;

// return the new value
 	return camera->arc;
}

///////////////////////////////////////////////////////////////////////
// example for executing a C-Script function
// dllfunction DistAhead(entity);
// distance_ahead = DistAhead(my); returns the free distance
// in front of the MY entity to the next obstacle,
// useable for AI calculations
DLLFUNC fixedPoint DistAhead(long my)
{
	if (!my) return 0;
// retrieve the pointer to the given entity
	A4_ENTITY *ent = (A4_ENTITY *)my;

// get the address of some C-Script variables and functions
// normally this should be done only once in an init dll function
// C-Script variable and function addresses won't change during gameplay
	fixedPoint *tracemode = (fixedPoint *)a5dll_getwdlobj("trace_mode");
	wdlfunc2 trace = (wdlfunc2)a5dll_getwdlfunc("trace");
	wdlfunc2 vecrotate = (wdlfunc2)a5dll_getwdlfunc("vec_rotate");
	if (!tracemode || !trace || !vecrotate) return 0;

	fixedPoint target[3] = { FLOAT2FIX(1000.0),0,0 };	// trace target vector

// rotate vector by entity engles, just as in C-Script
	(*vecrotate)((long)target,(long)&(ent->pan));

// add entity position to target
	target[0] += ent->x;
	target[1] += ent->y;
	target[2] += ent->z;

// set trace_mode, then trace a line between entity and target,
// and return the result
	*tracemode = INT2FIX(TRMF_IGNORE_ME + TRMF_IGNORE_PASSABLE + TRMF_USE_BOX);
	return (*trace)((long)&(ent->x),(long)target);
}

///////////////////////////////////////////////////////////////////////
// example for passing a string to create an entity
namespace ackString {
	long PSTRING(char* chars)
	{
	// works only for strings no longer than 79 chars!
		static A4_STRING tempstring;
		static char tempname[80];
		strcpy(tempname,chars);
		tempstring.chars = tempname;
		return (long)&tempstring;
	}
}

DLLFUNC fixedPoint create_warlock(long vec_pos)
{
// Note: call a5dll_getwdlfunc only at initialisation and NOT during gameplay - it is SLOW!
	wdlfunc3 ent_create = (wdlfunc3)a5dll_getwdlfunc("ent_create");
	return (*ent_create)(ackString::PSTRING("warlock.mdl"),vec_pos,0);
}

///////////////////////////////////////////////////////////////////////
// example for calling a user-defined C-Script function
// dllfunction WDLBeep(value);
// calls the following script function:
// function beeps(n) { while (n>0) { beep; n-=1; } }
DLLFUNC fixedPoint WDLBeep(fixedPoint value)
{
	return a5dll_callname("beeps",value);
}

///////////////////////////////////////////////////////////////////////
// examples for a particle effect function
// dllfunction DLLEffect_Explo(particle);
// dllfunction DLLPart_Alphafade(particle);
// start the particle effect by
//	effect(DLLEffect_Explo,1000,my.x,nullvector);

fixedPoint *var_time = NULL;
long func_alphafade = 0;

DLLFUNC fixedPoint DLLPart_Alphafade(long particle)
{
	if (!var_time || !particle) return 0;
	A4_PARTICLE* p = (A4_PARTICLE*) particle;
	p->alpha -= *var_time * 4;
	if (p->alpha <= 0) p->lifespan = 0;
	return 0;
}

float random(float max)
{
	return (float)(rand()*max)/(float)RAND_MAX;
}

DLLFUNC fixedPoint DLLEffect_Explo(long particle)
{
	if (!particle) return 0;

// initialize time var and alphafade function (must only be done once)
	if (!var_time)
		var_time = (fixedPoint *)a5dll_getwdlobj("time");
	if (!func_alphafade)
		func_alphafade = a5dll_getscript("DLLPart_Alphafade");
	A4_PARTICLE* p = (A4_PARTICLE*) particle;

// some internal flags are already set, don't reset them
	p->flags |= EPF_STREAK|EPF_MOVE|ENF_FLARE|ENF_BRIGHT;
	p->vel_x = FLOAT2FIX(random(10) - 5);
	p->vel_y = FLOAT2FIX(random(10) - 5);
	p->vel_z = FLOAT2FIX(random(10) - 5);
	p->red = 0;
	p->green = 0;
	p->blue = INT2FIX(255);
	p->alpha = FLOAT2FIX(50 + random(50));
	p->function = func_alphafade;

	return 0;
}

////////////////////////////////////////////////////////////////////////
// example for creating D3D effects through DLL functions
// Note that the below examples use DX8.1 and only work with A5.50 and above!

//#define DX_SDK // comment this in if you have the DIRECTX8.1 sdk
#ifdef DX_SDK
#include <d3dx8.h>

// dllfunction PaintD3DTriangle();
// draws a red/blue/green triangle in D3D mode
DLLFUNC fixedPoint PaintD3DTriangle (void)
{
// define a suited vertex structure
	struct VERTEX_FLAT {
		float x,y,z;
		float rhw;
		D3DCOLOR color;
	};

	#define D3DFVF_FLAT (D3DFVF_XYZRHW | D3DFVF_DIFFUSE )

// get the active D3D device
	LPDIRECT3DDEVICE8 pd3ddev = (LPDIRECT3DDEVICE8) a5dx->pd3ddev8;
	if (!pd3ddev) return 0;

// define three corner vertices
	VERTEX_FLAT v[3];

	v[0].x = 10.0; v[0].y = 10.0; v[0].color = 0xFFFF0000; 	// the red corner
	v[1].x = 310.0; v[1].y = 10.0; v[1].color = 0xFF0000FF;	// the blue corner
	v[2].x = 10.0; v[2].y = 310.0; v[2].color = 0xFF00FF00;	// the green corner

	v[0].z = v[1].z = v[2].z = 0.0;			// z buffer - paint over everything
	v[0].rhw = v[1].rhw = v[2].rhw = 1.0;	// no perspective

// begin a scene - needed before D3D draw operations
	pd3ddev->BeginScene();

// set some render and stage states (you have to set some more, normally)
	pd3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
	pd3ddev->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_DIFFUSE);
	pd3ddev->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG2);

// now draw the triangle
	pd3ddev->SetVertexShader(D3DFVF_FLAT);
	pd3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN,1,(LPVOID)v,sizeof(VERTEX_FLAT));

// do not forget to do a clean closing of the scene
	pd3ddev->EndScene();
	return 0;
}

///////////////////////////////////////////////////////////////////////
// example for manipulating level entities and retrieving D3D textures

// dllfunction PaintEntitiesRed();
// paints the first mipmap of all sprite and model entities red
DLLFUNC fixedPoint PaintEntitiesRed(void)
{
// find the first entity in the level
	A4_ENTITY *ent = NULL;
	while (1) {
// find the next entity
		ent = a5dll_entnext(ent);
		if (!ent) break;

// we can not be sure that the entity texture exists - it could be purged
		A4_TEX *tex = a5dll_tex4ent(ent,0,0);
		if (!tex) continue;
		LPDIRECT3DTEXTURE8 dx8tex = (LPDIRECT3DTEXTURE8) tex->pd3dtex;
		if (!dx8tex) continue;

// check the texture format
		D3DSURFACE_DESC	ddsd;
		if (FAILED(dx8tex->GetLevelDesc(0,&ddsd))) continue;

// lock the texture and retrieve a pointer to the surface
		D3DLOCKED_RECT d3dlr;
		if (FAILED(dx8tex->LockRect(0,&d3dlr,0,0))) continue;
		byte *pixels = (byte *)(d3dlr.pBits);

// do we have a 16 bit or 32 bit format? All 4 formats are possible:
		if (ddsd.Format == D3DFMT_A8R8G8B8)
			for (unsigned y = 0; y < ddsd.Height; y++ ) {
				DWORD *target = (DWORD *)(pixels + y*d3dlr.Pitch);
				for (unsigned x = 0; x < ddsd.Width; x++ )
					*target++ = 0xFFFF0000;	// that's red in 8888
			}
		else if (ddsd.Format == D3DFMT_A4R4G4B4)
			for (unsigned y = 0; y < ddsd.Height; y++ ) {
				WORD *target = (WORD *)(pixels + y*d3dlr.Pitch);
				for (unsigned x = 0; x < ddsd.Width; x++ )
					*target++ = 0xFF00;	// that's red in 4444
			}
		else if (ddsd.Format == D3DFMT_A1R5G5B5)
			for (unsigned y = 0; y < ddsd.Height; y++ ) {
				WORD *target = (WORD *)(pixels + y*d3dlr.Pitch);
				for (unsigned x = 0; x < ddsd.Width; x++ )
					*target++ = 0xFC00;	// that's red in 1555
			}
		else if (ddsd.Format == D3DFMT_R5G6B5)
			for (unsigned y = 0; y < ddsd.Height; y++ ) {
				WORD *target = (WORD *)(pixels + y*d3dlr.Pitch);
				for (unsigned x = 0; x < ddsd.Width; x++ )
					*target++ = 0xF800;	// that's red in 565
			}

// Unlock the surface again
		dx8tex->UnlockRect(0);
	}

	a5dll_errormessage("Entities are now red!");
	return 0;
}

///////////////////////////////////////////////////////////////////////
#endif	// DX_SDK
