1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * Additional copyright for this file:
8  * Copyright (C) 1999-2000 Revolution Software Ltd.
9  * This code is based on source code created by Revolution Software,
10  * used with permission.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25  *
26  */
27 
28 #include "engines/icb/common/px_common.h"
29 #include "engines/icb/p4.h"
30 #include "engines/icb/p4_generic.h"
31 #include "engines/icb/debug.h"
32 #include "engines/icb/mission.h"
33 #include "engines/icb/direct_input.h"
34 #include "engines/icb/global_objects.h"
35 #include "engines/icb/global_objects_psx.h"
36 #include "engines/icb/p4.h"
37 #include "engines/icb/actor.h"
38 #include "engines/icb/actor_pc.h"
39 #include "engines/icb/drawpoly_pc.h"
40 #include "engines/icb/common/px_capri_maths.h"
41 #include "engines/icb/gfx/gfxstub.h"
42 #include "engines/icb/gfx/gfxstub_dutch.h"
43 #include "engines/icb/gfx/gfxstub_rev_dutch.h"
44 #include "engines/icb/actor_view_pc.h"
45 #include "engines/icb/res_man.h"
46 
47 #include "common/keyboard.h"
48 
49 namespace ICB {
50 
51 // Cute little puppies
52 extern char *pRGB;
53 extern char *pZa;
54 extern int32 RGBWidth;
55 extern int32 RGBHeight;
56 extern int32 RGBPitch;
57 extern int32 RGBBytesPerPixel;
58 
59 RevRenderDevice renderingDevice;
60 
61 extern int32 texturesUsedThisCycle;
62 
63 // This controls autoanimation (0 = None, 1 = Backward, 2 = Forward)
64 uint32 auto_anim = 2;
65 
66 // Camera and animation structures
67 PXanim *pxanim;
68 SVECTOR rot;   // Actor rotation
69 SVECTOR _crot; // Camera rotation
70 int32 uvframe = 0;
71 
72 // Global filename stuff
73 char cluster_name[32];
74 uint32 cluster_name_hash;
75 char raj_name[128];
76 uint32 raj_name_hash;
77 
78 // Pointers to useful strings for the current actor
79 char *character_name;
80 char *anim_name;
81 const char *weapon_name;
82 char *outfit_name;
83 
84 int32 framenum;
85 int32 g_repeats;
86 
87 // Do we allow keyboard input to affect the actor viewing
88 bool8 g_av_userControlled = FALSE8;
89 
90 // Lighting structure and coordinates, colour components
91 int16 av_LightX;
92 int16 av_LightY;
93 int16 av_LightZ;
94 int32 av_LightR;
95 int32 av_LightG;
96 int32 av_LightB;
97 int32 av_LightA;
98 bool8 av_LightDir;
99 bool8 av_autoR;
100 bool8 av_autoG;
101 bool8 av_autoB;
102 
103 // Render coordinates
104 int16 av_x, av_y, av_z;
105 
106 // Prototypes for functions used in this module
107 TextureHandle *GetRegisteredTexture(const char *, uint32, const char *, uint32, const char *, uint32);
108 void DrawFrame(const int32 frame);
109 void MakeCameraView();
110 void ResetCamera();
111 void ResetActor();
112 void InitLight();
113 void SetLight(int32 falloff);
114 void AutoCycleLight();
115 
116 #define LIGHT_DISTANCE_FROM_ACTOR 100
117 #define LIGHT_HEIGHT_LIMIT 200
118 
InitActorView(const char * name,const char * outfit,const char * weapon,const char * anim,int16 ix,int16 iy,int16 iz)119 void InitActorView(const char *name, const char *outfit, const char *weapon, const char *anim, int16 ix, int16 iy, int16 iz) {
120 	// Store initial offset coordinates
121 	av_x = ix;
122 	av_y = iy;
123 	av_z = iz;
124 
125 	// Make hash filename of the character
126 	char h_character[8];
127 	HashFile(name, h_character);
128 	// Make hash filename of the outfit
129 	char h_outfit[8];
130 	HashFile(outfit, h_outfit);
131 	// Make the cluster name
132 	sprintf(cluster_name, "\\C\\%s\\%s.OFT", h_character, h_outfit);
133 	// Hash value for this cluster name
134 	cluster_name_hash = NULL_HASH;
135 
136 	ResetCamera();
137 	ResetActor();
138 
139 	raj_name_hash = NULL_HASH;
140 
141 	sprintf(raj_name, "%s\\%s.raj", weapon, anim);
142 
143 	anim_name = const_cast<char *>(anim);
144 	weapon_name = const_cast<char *>(weapon);
145 	outfit_name = const_cast<char *>(outfit);
146 	character_name = const_cast<char *>(name);
147 
148 	// Animate the shape from frame to frame, looping
149 	framenum = 0;
150 	g_repeats = 0;
151 	auto_anim = 2;
152 
153 	// Initialise a light to use
154 	InitLight();
155 
156 	// Start the psx poly drawing ganky bit
157 	InitDrawing();
158 
159 	// Load and select the appropriate texture
160 	char texture_name[128];
161 	uint32 texture_name_hash = NULL_HASH;
162 
163 	sprintf(texture_name, "material.revtex");
164 
165 	TextureHandle *texHan = GetRegisteredTexture(texture_name, texture_name_hash, texture_name, texture_name_hash, cluster_name, cluster_name_hash);
166 
167 	ChooseTexture(texHan);
168 }
169 
ChangeAnimPlaying(const char * pose,const char * anim,bool8 forwards,int32 repeats,int16 ix,int16 iy,int16 iz)170 void ChangeAnimPlaying(const char *pose, const char *anim, bool8 forwards, int32 repeats, int16 ix, int16 iy, int16 iz) {
171 	// Set pose
172 	if (pose) {
173 		// New pose
174 		weapon_name = const_cast<char *>(pose);
175 	} else {
176 		// Default is unarmed
177 		weapon_name = "unarmed";
178 	}
179 
180 	// Require anim parameter
181 	if (anim == NULL)
182 		Fatal_error("ChangeAnimPlaying() cannot set active animation to NULL!");
183 
184 	// Remake raj filename
185 	raj_name_hash = NULL_HASH;
186 
187 	sprintf(raj_name, "%s\\%s.raj", weapon_name, anim);
188 
189 	// Change animation to use
190 	anim_name = const_cast<char *>(anim);
191 
192 	// Set direction to run the anim
193 	if (forwards)
194 		auto_anim = 2;
195 	else
196 		auto_anim = 1;
197 
198 	pxanim = (PXanim *)rs_anims->Res_open(raj_name, raj_name_hash, cluster_name, cluster_name_hash);
199 
200 	// Set to the starting frame of this anim
201 	if (forwards)
202 		framenum = 0;
203 	else
204 		framenum = pxanim->frame_qty - 2;
205 
206 	g_repeats = repeats;
207 
208 	// Store initial offset coordinates
209 	av_x = ix;
210 	av_y = iy;
211 	av_z = iz;
212 
213 	ResetCamera();
214 }
215 
ActorViewDraw()216 int32 ActorViewDraw() {
217 	// Return value
218 	int32 returnStatus = MID_ANIMATION;
219 
220 	// Alters the light nicely
221 	AutoCycleLight();
222 	// Call this each cycle to update our light
223 	SetLight(500);
224 
225 	// This does most of the work
226 	DrawFrame(framenum);
227 
228 	// This code acts upon user input to alter the actor's rotation and anim frame
229 	// from the initialised defaults.
230 	if (g_av_userControlled) {
231 		// Increment in degrees
232 		int32 dang = 5;
233 
234 		// Actor rotation
235 		if (Read_DI_keys(Common::KEYCODE_LEFT)) {
236 			rot.vy = (int16)(rot.vy + 4096 * dang / 360);
237 		}
238 		if (Read_DI_keys(Common::KEYCODE_DOWN)) {
239 			rot.vy = (int16)(rot.vy - 4096 * dang / 360);
240 		}
241 		if (Read_DI_keys(Common::KEYCODE_UP)) {
242 			rot.vx = (int16)(rot.vx + 4096 * dang / 360);
243 		}
244 		if (Read_DI_keys(Common::KEYCODE_DOWN)) {
245 			rot.vx = (int16)(rot.vx - 4096 * dang / 360);
246 		}
247 		if (Read_DI_keys(Common::KEYCODE_PAGEUP)) {
248 			rot.vz = (int16)(rot.vz + 4096 * dang / 360);
249 		}
250 		if (Read_DI_keys(Common::KEYCODE_PAGEDOWN)) {
251 			rot.vz = (int16)(rot.vz - 4096 * dang / 360);
252 		}
253 
254 		if (rot.vx > 4096)
255 			rot.vx -= 4096;
256 		if (rot.vy > 4096)
257 			rot.vy -= 4096;
258 		if (rot.vz > 4096)
259 			rot.vz -= 4096;
260 
261 		if (rot.vx < -4096)
262 			rot.vx += 4096;
263 		if (rot.vy < -4096)
264 			rot.vy += 4096;
265 		if (rot.vz < -4096)
266 			rot.vz += 4096;
267 
268 		// Set animation playing forwards
269 		if (Read_DI_keys(Common::KEYCODE_1)) {
270 			auto_anim = 2;
271 		}
272 		// Set animation playing backwards
273 		if (Read_DI_keys(Common::KEYCODE_2)) {
274 			auto_anim = 1;
275 		}
276 		// Previous frame
277 		if (Read_DI_once_keys(Common::KEYCODE_MINUS)) {
278 			auto_anim = 0;
279 			framenum--;
280 		}
281 		// Next frame
282 		if (Read_DI_once_keys(Common::KEYCODE_EQUALS)) {
283 			auto_anim = 0;
284 			framenum++;
285 		}
286 	}
287 
288 	// Auto animating backward
289 	if (auto_anim == 1) {
290 		framenum--;
291 	}
292 	// Auto forwards
293 	else if (auto_anim) {
294 		framenum++;
295 	}
296 
297 	// Catch the loop for doing -1 in unsigned decimal
298 	if (framenum > pxanim->frame_qty)
299 		framenum = pxanim->frame_qty - 2;
300 	if (framenum < 0) {
301 		if (g_repeats > 0) {
302 			g_repeats--;
303 		} else {
304 			if (auto_anim == 1)
305 				returnStatus = ANIMATION_END;
306 		}
307 
308 		framenum = pxanim->frame_qty - 2;
309 	}
310 
311 	// Catch the loop for going past end of the animation
312 	if (framenum > (pxanim->frame_qty - 2)) {
313 		if (g_repeats > 0) {
314 			g_repeats--;
315 		} else {
316 			if (auto_anim == 2)
317 				returnStatus = ANIMATION_END;
318 		}
319 
320 		framenum = 0;
321 	}
322 
323 	// Catch illegal animations
324 	if (framenum < 0)
325 		framenum = 0;
326 
327 	// Draw the display list
328 	drawOTList();
329 
330 	// Now copy the sucker to the screen
331 	uint32 *address = (uint32 *)surface_manager->Lock_surface(working_buffer_id);
332 	uint32 pitch = surface_manager->Get_pitch(working_buffer_id);
333 	uint32 *source = (uint32 *)pRGB;
334 	uint16 *zActor = (uint16 *)pZa;
335 
336 	uint32 *safe_ad = address;
337 
338 	for (int32 y = SCREEN_DEPTH; y; y--) {
339 		uint32 *ad = safe_ad;
340 		for (int32 x = SCREEN_WIDTH; x; x--) {
341 			// If the z-map for this pixel is FFFF then this pixel doesn't contain actor
342 			if (*zActor != 0xFFFF) {
343 				*ad = *source;
344 				*zActor = 0xFFFF;
345 			}
346 
347 			++zActor;
348 			++ad;
349 			++source;
350 		}
351 		safe_ad += pitch / 4;
352 	}
353 
354 	// Unlock the surface
355 	surface_manager->Unlock_surface(working_buffer_id);
356 
357 	return returnStatus;
358 }
359 
DrawFrame(const int32 frame)360 void DrawFrame(const int32 frame) {
361 	// These structures are needed for the drawing code to accept our light
362 	PSXLampList the_lights;
363 	PSXShadeList the_shades;
364 	the_lights.n = 1;
365 	the_lights.states[0] = 0;
366 	the_lights.lamps[0] = g_av_Light;
367 	the_shades.n = 0;
368 
369 	// Open the animation file
370 	char bone_name[128];
371 	char pose_name[128];
372 	char mesh_name[128];
373 	char smesh_name[128];
374 	PSXrgb ambient;
375 	ambient.r = 128;
376 	ambient.g = 128;
377 	ambient.b = 128;
378 
379 	pxanim = (PXanim *)rs_anims->Res_open(raj_name, raj_name_hash, cluster_name, cluster_name_hash);
380 	PXFrameEnOfAnim(framenum, pxanim)->markers[ORG_POS];
381 
382 	// Make the actors orientation matrix
383 	g_av_actor->rot = rot;
384 	g_av_actor->rot.vy = (int16)(g_av_actor->rot.vy);
385 
386 	// Make the root local-world matrix
387 	RotMatrix_gte(&g_av_actor->rot, &g_av_actor->lw);
388 
389 	// Need to use marker to get correct actor height (making crouch look correct)
390 	PXframe *frm = PXFrameEnOfAnim(framenum, pxanim);
391 	PXmarker &marker = frm->markers[ORG_POS];
392 	float mposx, mposy, mposz;
393 	marker.GetXYZ(&mposx, &mposy, &mposz);
394 	int32 dy = (int32)mposy;
395 
396 	g_av_actor->lw.t[0] = 0;
397 	g_av_actor->lw.t[1] = dy - 112;
398 	g_av_actor->lw.t[2] = 0;
399 
400 	// Set the true rotation & position values from the ORG marker
401 	g_av_actor->truePos.x = 0;
402 	g_av_actor->truePos.y = dy - 112;
403 	g_av_actor->truePos.z = 0;
404 	g_av_actor->trueRot = g_av_actor->rot;
405 
406 	sprintf(pose_name, "%s\\pose.rap", weapon_name);
407 	sprintf(bone_name, "%s\\%s.rab", weapon_name, anim_name);
408 	sprintf(mesh_name, "mesh.rap");
409 	sprintf(smesh_name, "mesh_shadow.rap");
410 
411 	uint32 mesh_hash = HashString(mesh_name);
412 	rap_API *mesh = (rap_API *)rs_anims->Res_open(mesh_name, mesh_hash, cluster_name, cluster_name_hash);
413 	uint32 smesh_hash = HashString(smesh_name);
414 	rap_API *smesh = (rap_API *)rs_anims->Res_open(smesh_name, smesh_hash, cluster_name, cluster_name_hash);
415 	uint32 pose_hash = HashString(pose_name);
416 	rap_API *pose = (rap_API *)rs_anims->Res_open(pose_name, pose_hash, cluster_name, cluster_name_hash);
417 	uint32 bone_hash = HashString(bone_name);
418 	rab_API *rab = (rab_API *)rs_anims->Res_open(bone_name, bone_hash, cluster_name, cluster_name_hash);
419 
420 	ConvertRAP(pose);
421 	ConvertRAP(smesh);
422 	ConvertRAP(mesh);
423 
424 	// Some error checking
425 	if (*(int32 *)mesh->id != *(int32 *)const_cast<char *>(RAP_API_ID)) {
426 		Fatal_error("Wrong rap id value file %d api %d file:%s", mesh->id, RAP_API_ID, mesh_name);
427 	}
428 	if (mesh->schema != RAP_API_SCHEMA) {
429 		Fatal_error("Wrong rap schema value file %d rap_api %d file:%s", mesh->schema, RAP_API_SCHEMA, mesh_name);
430 	}
431 	if (*(int32 *)pose->id != *(int32 *)const_cast<char *>(RAP_API_ID)) {
432 		Fatal_error("Wrong rap id value file %d api %d file:%s", pose->id, RAP_API_ID, pose_name);
433 	}
434 	if (pose->schema != RAP_API_SCHEMA) {
435 		Fatal_error("Wrong rap schema value file %d rap_api %d file:%s", pose->schema, RAP_API_SCHEMA, pose_name);
436 	}
437 	if (*(int32 *)rab->id != *(int32 *)const_cast<char *>(RAB_API_ID)) {
438 		Fatal_error("Wrong rab id value file %d rab_api %d file:%s", rab->id, RAB_API_ID, bone_name);
439 	}
440 	if (rab->schema != RAB_API_SCHEMA) {
441 		Fatal_error("Wrong rab schema value file %d rab_api %d file:%s", rab->schema, RAB_API_SCHEMA, bone_name);
442 	}
443 	if (mesh->nBones != rab->nBones) {
444 		Fatal_error("mesh nBones != animation nBones %d != %d", mesh->nBones, rab->nBones);
445 	}
446 
447 	// Pass in the linkage file and the bones file
448 	Bone_Frame *bone_frame = rab->GetFrame(frame);
449 	int32 brightness;
450 
451 	int32 debug = 1;
452 
453 	BoneDeformation *myBones[MAX_DEFORMABLE_BONES];
454 
455 	for (int32 i = 0; i < MAX_DEFORMABLE_BONES; i++) {
456 		myBones[i] = NULL;
457 	}
458 
459 	// Shadow stuff to play with
460 	int32 nShadows = 0;
461 	SVECTORPC p_n[3];
462 	int32 p_d[3];
463 
464 	p_n[0].vx = 0;
465 	p_n[0].vy = -1;
466 	p_n[0].vz = 0;
467 	p_d[0] = -118;
468 
469 	MATRIXPC local2screen; // not really bothered about this...
470 
471 	// Drawing finally
472 	DrawActor4PC(g_av_actor, g_camera, bone_frame, mesh, pose, smesh, &ambient, &the_lights, &the_shades, nShadows, p_n, p_d, debug, uvframe, myBones, &brightness,
473 	             &local2screen);
474 
475 	uvframe++;
476 }
477 
MakeCameraView()478 void MakeCameraView() {
479 	RotMatrix_gte(&_crot, &g_camera->view);
480 
481 	// Include the x,y,z scalings
482 	g_camera->view.m[0][0] = (int16)(g_camera->view.m[0][0] * 1);
483 	g_camera->view.m[0][1] = (int16)(g_camera->view.m[0][1] * 1);
484 	g_camera->view.m[0][2] = (int16)(g_camera->view.m[0][2] * 1);
485 	g_camera->view.m[1][0] = (int16)(g_camera->view.m[1][0] * 1);
486 	g_camera->view.m[1][1] = (int16)(g_camera->view.m[1][1] * 1);
487 	g_camera->view.m[1][2] = (int16)(g_camera->view.m[1][2] * 1);
488 	g_camera->view.m[2][0] = (int16)(g_camera->view.m[2][0] * 4);
489 	g_camera->view.m[2][1] = (int16)(g_camera->view.m[2][1] * 4);
490 	g_camera->view.m[2][2] = (int16)(g_camera->view.m[2][2] * 4);
491 }
492 
ResetCamera()493 void ResetCamera() {
494 	_crot.vx = (4096 * 180) / 360;
495 	_crot.vy = (4096 * -30) / 360;
496 	_crot.vz = 0;
497 	g_camera->view.t[0] = 170 + av_x;
498 	g_camera->view.t[1] = 0 + av_y;
499 	g_camera->view.t[2] = 1800 + av_z;
500 	g_camera->focLen = 619 * 4;
501 	MakeCameraView();
502 }
503 
ResetActor()504 void ResetActor() {
505 	// Set up av_actor rotation
506 	rot.vx = 0;
507 	rot.vy = 0;
508 	rot.vz = 0;
509 }
510 
InitLight()511 void InitLight() {
512 	g_av_Light->nStates = 1;       // One state
513 	g_av_Light->w = 0;             // Zero width
514 	g_av_Light->b = 0;             // Zero bounce
515 	g_av_Light->anu = 0;           // Don't use it
516 	g_av_Light->type = OMNI_LIGHT; // OMNI
517 	g_av_Light->ba = 0;            // Means nothing for an OMNI
518 	g_av_Light->bs = 0;            // Means nothing for an OMNI
519 
520 	// Don't think these things are used...
521 	g_av_Light->states[0].ans2 = 0;
522 	g_av_Light->states[0].ane2 = (100 * 1) * (100 * 1);
523 
524 	// No shade...
525 	g_av_Light->states[0].m = 128;
526 
527 	// Direction doesn't matter; it's an OMNI light
528 	g_av_Light->states[0].vx = 4096; // Ignored for an OMNI light
529 	g_av_Light->states[0].vy = 0;    // Ignored for an OMNI light
530 	g_av_Light->states[0].vz = 0;    // Ignored for an OMNI light
531 
532 	// Initial angle
533 	av_LightA = 0;
534 	av_LightDir = TRUE8;
535 
536 	// Initial position
537 	av_LightX = 0;
538 	av_LightY = 0;
539 	av_LightZ = LIGHT_DISTANCE_FROM_ACTOR;
540 
541 	// Initial colour (RED)
542 	av_LightR = 255;
543 	av_LightG = 0;
544 	av_LightB = 0;
545 
546 	// Auto flags
547 	av_autoR = FALSE8;
548 	av_autoG = FALSE8;
549 	av_autoB = FALSE8;
550 }
551 
AutoCycleLight()552 void AutoCycleLight() {
553 	// Increase angle by 10 degrees
554 	av_LightA += 10;
555 	if (av_LightA >= 360)
556 		av_LightA = 0;
557 
558 	// Convert to radians
559 	double radians = (av_LightA * M_PI) / 180.0f;
560 
561 	// Now calculate z and x coordinates from this angle
562 	av_LightX = (int16)(sin(radians) * LIGHT_DISTANCE_FROM_ACTOR);
563 	av_LightZ = (int16)(cos(radians) * LIGHT_DISTANCE_FROM_ACTOR);
564 
565 	// Now bouce the light height between two fixed limits
566 	if (av_LightDir) {
567 		av_LightY = (int16)(av_LightY + 10);
568 
569 		if (av_LightY > LIGHT_HEIGHT_LIMIT) {
570 			av_LightY = LIGHT_HEIGHT_LIMIT;
571 			av_LightDir = FALSE8;
572 		}
573 	} else {
574 		av_LightY = (int16)(av_LightY - 10);
575 
576 		if (av_LightY < -LIGHT_HEIGHT_LIMIT) {
577 			av_LightY = -LIGHT_HEIGHT_LIMIT;
578 			av_LightDir = TRUE8;
579 		}
580 	}
581 
582 	// Red component
583 	if (av_autoR) {
584 		av_LightR += 3;
585 
586 		if (av_LightR > 255) {
587 			av_LightR = 255;
588 			av_autoR = FALSE8;
589 		}
590 	} else {
591 		av_LightR -= 2;
592 
593 		if (av_LightR < 0) {
594 			av_LightR = 0;
595 			av_autoR = TRUE8;
596 		}
597 	}
598 
599 	// Green component
600 	if (av_autoG) {
601 		av_LightG += 2;
602 
603 		if (av_LightG > 255) {
604 			av_LightG = 255;
605 			av_autoG = FALSE8;
606 		}
607 	} else {
608 		av_LightG -= 3;
609 
610 		if (av_LightG < 0) {
611 			av_LightG = 0;
612 			av_autoG = TRUE8;
613 		}
614 	}
615 
616 	// Blue component
617 	if (av_autoB) {
618 		av_LightB += 7;
619 
620 		if (av_LightB > 255) {
621 			av_LightB = 255;
622 			av_autoB = FALSE8;
623 		}
624 	} else {
625 		av_LightB -= 5;
626 
627 		if (av_LightB < 0) {
628 			av_LightB = 0;
629 			av_autoB = TRUE8;
630 		}
631 	}
632 }
633 
SetLight(int32 falloff)634 void SetLight(int32 falloff) {
635 	// Check colours are 0-255
636 	if ((av_LightR > 255) || (av_LightR < 0) || (av_LightG > 255) || (av_LightG < 0) || (av_LightB > 255) || (av_LightB < 0))
637 		Fatal_error("ActorView light rgb %d,%d,%d out of range (0-255)", av_LightR, av_LightG, av_LightB);
638 
639 	// Set colours (scale 0-255 to 0-4095)
640 	g_av_Light->states[0].c.r = (int16)((av_LightR * 4096) / 256);
641 	g_av_Light->states[0].c.g = (int16)((av_LightG * 4096) / 256);
642 	g_av_Light->states[0].c.b = (int16)((av_LightB * 4096) / 256);
643 
644 	// Set the v field of colour to be the maximum of r,g,b
645 	g_av_Light->states[0].c.v = g_av_Light->states[0].c.r;         // Start at red
646 	if (g_av_Light->states[0].c.g > g_av_Light->states[0].c.v)     // If green bigger
647 		g_av_Light->states[0].c.v = g_av_Light->states[0].c.g; // Set to green
648 	if (g_av_Light->states[0].c.b > g_av_Light->states[0].c.v)     // If blue bigger
649 		g_av_Light->states[0].c.v = g_av_Light->states[0].c.b; // Set to blue
650 
651 	g_av_Light->states[0].pos.vx = (int32)av_LightX;
652 	g_av_Light->states[0].pos.vy = (int32)av_LightY;
653 	g_av_Light->states[0].pos.vz = (int32)av_LightZ;
654 
655 	// And add the players position
656 	g_av_Light->states[0].pos.vx += (int32)g_av_actor->truePos.x;
657 	g_av_Light->states[0].pos.vy += (int32)g_av_actor->truePos.y;
658 	g_av_Light->states[0].pos.vz += (int32)g_av_actor->truePos.z;
659 
660 	// Falloff
661 	if (falloff == 0) {
662 		g_av_Light->afu = 0; // Don't use it
663 	} else {
664 		g_av_Light->states[0].afs2 = (falloff * falloff) / 100; // (d/10)^2     = (d*d)/100
665 		g_av_Light->states[0].afe2 = falloff * falloff;         // d^2          = (d*d)
666 		g_av_Light->afu = 1;                                    // Use it
667 	}
668 }
669 
my_sprintf(char * buf,const char * format...)670 int32 my_sprintf(char *buf, const char *format...) {
671 	char lbuf[256];
672 
673 	// Process the variable arguments
674 	va_list arglist;
675 	va_start(arglist, format);
676 
677 	int32 slen = vsnprintf(lbuf, 256, const_cast<char *>(format), arglist);
678 
679 	strncpy(buf, lbuf, slen);
680 	buf[slen] = '\0';
681 	return slen;
682 }
683 
684 } // End of namespace ICB
685