///////////////////////////////////////////////////////////////////////
// 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



///////////////////////////////////////////////////////////////////////
// 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 fixed func(long) - or - DLLFUNC fixed func(fixed)
// fixed is a long integer interpreted as 22.10 fixed point value

///////////////////////////////////////////////////////////////////////
// example for creating a new C-Script arithmetic function
// dllfunction Square(var);
// returns var*var
DLLFUNC fixed Square(fixed var)
{
	return (FLOAT2FIX(FIX2FLOAT(var)*FIX2FLOAT(var)));
}

///////////////////////////////////////////////////////////////////////
// example for manipulating an entity
// dllfunction FlipUpsideDown(entity);
// rolls the given entity by 180 degrees
DLLFUNC fixed 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(90);
// return something
	return INT2FIX(1);
}

DLLFUNC fixed 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(10);

// return something
	return INT2FIX(1);
}

///////////////////////////////////////////////////////////////////////
// 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 fixed AddToSkySpeed(fixed 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
	fixed *skyspeed = (fixed *)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 fixed SetCloudSpeed(long array)
{
// get the address of the variable (which is an array of 3 by default)
	fixed *cloudspeed = (fixed *)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] = ((fixed *)array)[0];	// cloudspeed X value
	cloudspeed[1] = ((fixed *)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 fixed ZoomIn(fixed 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 fixed 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
	fixed *tracemode = (fixed *)a5dll_getwdlobj("trace_mode");
	wdlfunc2 trace = (wdlfunc2)a5dll_getwdlfunc("trace");
	wdlfunc2 vecrotate = (wdlfunc2)a5dll_getwdlfunc("vec_rotate");
	if (!tracemode || !trace || !vecrotate) return 0;

	fixed 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
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 fixed 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)(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 fixed WDLBeep(fixed 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);

fixed *var_time = NULL;
long func_alphafade = 0;

DLLFUNC fixed 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 fixed DLLEffect_Explo(long particle)
{
	if (!particle) return 0;

// initialize time var and alphafade function (must only be done once)
	if (!var_time)
		var_time = (fixed *)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 fixed 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 fixed 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