2020-05-27 09:21:20 +02:00
|
|
|
#include "framework.h"
|
2018-08-19 09:46:58 +02:00
|
|
|
#include "lot.h"
|
2020-01-16 19:14:35 +01:00
|
|
|
#include "box.h"
|
2020-05-27 09:21:20 +02:00
|
|
|
#include "setup.h"
|
2020-04-12 06:50:43 +02:00
|
|
|
#include "camera.h"
|
2020-04-23 19:22:01 +02:00
|
|
|
#include "lara.h"
|
2020-05-27 09:21:20 +02:00
|
|
|
#include "level.h"
|
2018-08-19 09:46:58 +02:00
|
|
|
|
2019-12-13 19:00:59 +01:00
|
|
|
#define DEFAULT_FLY_UPDOWN_SPEED 16
|
|
|
|
#define DEFAULT_SWIM_UPDOWN_SPEED 32
|
|
|
|
|
2020-01-11 11:43:42 +01:00
|
|
|
int SlotsUsed;
|
2020-04-01 13:09:14 +02:00
|
|
|
CREATURE_INFO* BaddieSlots;
|
2020-01-11 11:43:42 +01:00
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void InitialiseLOTarray(int allocMem)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
|
|
|
if (allocMem)
|
2020-04-24 19:15:05 +02:00
|
|
|
BaddieSlots = (CREATURE_INFO*)game_malloc(sizeof(CREATURE_INFO) * NUM_SLOTS);
|
2018-08-19 09:46:58 +02:00
|
|
|
|
|
|
|
CREATURE_INFO* creature = BaddieSlots;
|
2019-11-27 15:12:35 +01:00
|
|
|
for (int i = 0; i < NUM_SLOTS; i++, creature++)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
|
|
|
creature->itemNum = NO_ITEM;
|
2020-04-24 19:15:05 +02:00
|
|
|
creature->LOT.node = (BOX_NODE*)game_malloc(sizeof(BOX_NODE) * NumberBoxes);
|
2018-08-19 09:46:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SlotsUsed = 0;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
int EnableBaddieAI(short itemNum, int always)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
|
|
|
ITEM_INFO* item = &Items[itemNum];
|
|
|
|
|
|
|
|
if (item->data != NULL)
|
|
|
|
return true;
|
2020-01-11 11:43:42 +01:00
|
|
|
|
|
|
|
if (SlotsUsed >= NUM_SLOTS)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
2019-11-27 15:12:35 +01:00
|
|
|
int cameraDistance = 0;
|
2018-08-19 09:46:58 +02:00
|
|
|
if (!always)
|
|
|
|
{
|
2019-11-27 15:12:35 +01:00
|
|
|
int deltaX = (item->pos.xPos - Camera.pos.x) >> 8;
|
|
|
|
int deltaY = (item->pos.yPos - Camera.pos.y) >> 8;
|
|
|
|
int deltaZ = (item->pos.zPos - Camera.pos.z) >> 8;
|
2018-08-19 09:46:58 +02:00
|
|
|
cameraDistance = SQUARE(deltaX) + SQUARE(deltaY) + SQUARE(deltaZ);
|
|
|
|
}
|
|
|
|
|
2019-11-27 15:12:35 +01:00
|
|
|
int slotToDisable = -1;
|
2018-08-19 09:46:58 +02:00
|
|
|
CREATURE_INFO* creature = BaddieSlots;
|
2019-11-27 15:12:35 +01:00
|
|
|
for (int slot = 0; slot < NUM_SLOTS; slot++, creature++)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
|
|
|
item = &Items[creature->itemNum];
|
|
|
|
|
2019-11-27 15:12:35 +01:00
|
|
|
int deltaX = (item->pos.xPos - Camera.pos.x) >> 8;
|
|
|
|
int deltaY = (item->pos.yPos - Camera.pos.y) >> 8;
|
|
|
|
int deltaZ = (item->pos.zPos - Camera.pos.z) >> 8;
|
|
|
|
int distance = SQUARE(deltaX) + SQUARE(deltaY) + SQUARE(deltaZ);
|
2018-08-19 09:46:58 +02:00
|
|
|
|
|
|
|
if (distance > cameraDistance)
|
|
|
|
{
|
|
|
|
cameraDistance = distance;
|
|
|
|
slotToDisable = slot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-11 11:43:42 +01:00
|
|
|
if (slotToDisable < 0 || slotToDisable > NUM_SLOTS)
|
2018-08-19 09:46:58 +02:00
|
|
|
return false;
|
|
|
|
|
2020-01-11 11:43:42 +01:00
|
|
|
ITEM_INFO* itemToDisable = &Items[BaddieSlots[slotToDisable].itemNum];
|
|
|
|
CREATURE_INFO* creatureToDisable = &BaddieSlots[slotToDisable];
|
2018-08-19 09:46:58 +02:00
|
|
|
|
2020-01-11 11:43:42 +01:00
|
|
|
itemToDisable->status = ITEM_INVISIBLE;
|
|
|
|
DisableBaddieAI(creatureToDisable->itemNum);
|
|
|
|
InitialiseSlot(itemNum, slotToDisable);
|
2018-08-19 09:46:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CREATURE_INFO* creature = BaddieSlots;
|
2019-11-27 15:12:35 +01:00
|
|
|
for (int slot = 0; slot < NUM_SLOTS; slot++, creature++)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
|
|
|
if (creature->itemNum == NO_ITEM)
|
|
|
|
{
|
|
|
|
InitialiseSlot(itemNum, slot);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void DisableBaddieAI(short itemNumber)
|
2019-10-29 20:05:58 +01:00
|
|
|
{
|
|
|
|
ITEM_INFO* item = &Items[itemNumber];
|
2019-11-03 08:05:48 +01:00
|
|
|
CREATURE_INFO* creature = (CREATURE_INFO*)item->data;
|
2019-10-29 20:05:58 +01:00
|
|
|
|
|
|
|
item->data = NULL;
|
|
|
|
if (creature)
|
|
|
|
{
|
|
|
|
creature->itemNum = NO_ITEM;
|
|
|
|
SlotsUsed--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void InitialiseSlot(short itemNum, short slot)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
|
|
|
ITEM_INFO* item = &Items[itemNum];
|
2020-04-27 15:28:54 +02:00
|
|
|
ObjectInfo* obj = &Objects[item->objectNumber];
|
2019-12-13 19:00:59 +01:00
|
|
|
CREATURE_INFO* creature = &BaddieSlots[slot];
|
2018-08-19 09:46:58 +02:00
|
|
|
|
|
|
|
item->data = creature;
|
|
|
|
creature->itemNum = itemNum;
|
|
|
|
creature->mood = BORED_MOOD;
|
|
|
|
creature->jointRotation[0] = 0;
|
2018-09-14 14:56:27 +02:00
|
|
|
creature->jointRotation[1] = 0;
|
2018-08-19 09:46:58 +02:00
|
|
|
creature->jointRotation[2] = 0;
|
2018-09-14 14:56:27 +02:00
|
|
|
creature->jointRotation[3] = 0;
|
2018-08-19 09:46:58 +02:00
|
|
|
creature->alerted = false;
|
|
|
|
creature->headLeft = false;
|
|
|
|
creature->headRight = false;
|
|
|
|
creature->reachedGoal = false;
|
|
|
|
creature->hurtByLara = false;
|
|
|
|
creature->patrol2 = false;
|
|
|
|
creature->jumpAhead = false;
|
|
|
|
creature->monkeyAhead = false;
|
|
|
|
creature->alerted = false;
|
|
|
|
creature->LOT.canJump = false;
|
|
|
|
creature->LOT.canMonkey = false;
|
2020-01-11 11:43:42 +01:00
|
|
|
creature->LOT.isAmphibious = false; // only the crocodile can go water and land. (default: true)
|
2018-08-19 09:46:58 +02:00
|
|
|
creature->LOT.isJumping = false;
|
|
|
|
creature->LOT.isMonkeying = false;
|
2019-11-24 17:01:13 +01:00
|
|
|
creature->maximumTurn = ANGLE(1);
|
2018-08-19 09:46:58 +02:00
|
|
|
creature->flags = 0;
|
|
|
|
creature->enemy = NULL;
|
2019-12-13 19:00:59 +01:00
|
|
|
creature->LOT.fly = NO_FLYING;
|
|
|
|
creature->LOT.blockMask = BLOCKED;
|
2019-12-04 16:12:53 +01:00
|
|
|
|
2019-12-13 19:00:59 +01:00
|
|
|
if (obj->intelligent)
|
2018-08-19 09:46:58 +02:00
|
|
|
{
|
2020-01-13 13:18:55 +01:00
|
|
|
// simple check to set hitEffect to blood or smoke by default if intelligent enabled and no value assigned to hitEffect and the entity have HP !
|
2019-12-13 19:00:59 +01:00
|
|
|
// undead have smoke instead of blood !
|
2020-01-11 11:43:42 +01:00
|
|
|
if (obj->hitEffect == HIT_NONE)
|
2019-12-13 19:00:59 +01:00
|
|
|
{
|
|
|
|
if (obj->undead)
|
|
|
|
obj->hitEffect = HIT_SMOKE;
|
|
|
|
else if (!obj->undead && obj->hitPoints)
|
|
|
|
obj->hitEffect = HIT_BLOOD;
|
|
|
|
}
|
2020-01-11 11:54:06 +01:00
|
|
|
|
2020-01-13 13:18:55 +01:00
|
|
|
// init the basic zone for creature.
|
|
|
|
// ignore if the zoneType is specified in the Objects[] already with other than ZONE_NULL.
|
|
|
|
if (obj->zoneType == ZONE_NULL)
|
|
|
|
obj->zoneType = ZONE_BASIC; // only entity that use CreatureActive() will reach InitialiseSlot() !
|
|
|
|
|
2020-01-11 11:54:06 +01:00
|
|
|
obj->nonLot = false; // change to use pathfinding
|
2018-08-19 09:46:58 +02:00
|
|
|
}
|
|
|
|
|
2019-12-13 19:00:59 +01:00
|
|
|
switch (obj->zoneType)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case ZONE_NULL:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_SKELLY:
|
|
|
|
// Can jump
|
|
|
|
creature->LOT.step = SECTOR(1) - CLICK(3);
|
|
|
|
creature->LOT.drop = -(SECTOR(1) - CLICK(3));
|
|
|
|
creature->LOT.canJump = true;
|
|
|
|
creature->LOT.zone = ZONE_SKELLY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_BASIC:
|
|
|
|
creature->LOT.step = SECTOR(1) - CLICK(3);
|
|
|
|
creature->LOT.drop = -(SECTOR(1) - CLICK(3));
|
|
|
|
creature->LOT.zone = ZONE_BASIC;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_FLYER:
|
|
|
|
// Can fly
|
|
|
|
creature->LOT.step = SECTOR(20);
|
|
|
|
creature->LOT.drop = -SECTOR(20);
|
|
|
|
creature->LOT.fly = DEFAULT_FLY_UPDOWN_SPEED;
|
|
|
|
creature->LOT.zone = ZONE_FLYER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_WATER:
|
|
|
|
// Can swim
|
|
|
|
creature->LOT.step = SECTOR(20);
|
|
|
|
creature->LOT.drop = -SECTOR(20);
|
|
|
|
if (item->objectNumber == ID_CROCODILE)
|
|
|
|
{
|
2020-01-11 11:43:42 +01:00
|
|
|
creature->LOT.fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // crocodile is more slower (up/down) than the other creature when swimming.
|
2019-12-13 19:00:59 +01:00
|
|
|
creature->LOT.isAmphibious = true; // crocodile can walk and swim.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
creature->LOT.fly = DEFAULT_SWIM_UPDOWN_SPEED;
|
|
|
|
}
|
|
|
|
creature->LOT.zone = ZONE_WATER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_HUMAN_CLASSIC:
|
|
|
|
// Can climb
|
|
|
|
creature->LOT.step = SECTOR(1);
|
|
|
|
creature->LOT.drop = -SECTOR(1);
|
|
|
|
creature->LOT.zone = ZONE_HUMAN_CLASSIC;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_HUMAN_JUMP:
|
|
|
|
// Can climb and jump
|
|
|
|
creature->LOT.step = SECTOR(1);
|
|
|
|
creature->LOT.drop = -SECTOR(1);
|
|
|
|
creature->LOT.canJump = true;
|
|
|
|
creature->LOT.zone = ZONE_HUMAN_CLASSIC;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_HUMAN_JUMP_AND_MONKEY:
|
|
|
|
// Can climb, jump, monkey
|
|
|
|
creature->LOT.step = SECTOR(1);
|
|
|
|
creature->LOT.drop = -SECTOR(1);
|
|
|
|
creature->LOT.canJump = true;
|
|
|
|
creature->LOT.canMonkey = true;
|
|
|
|
creature->LOT.zone = ZONE_HUMAN_CLASSIC;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_SPIDER:
|
|
|
|
creature->LOT.step = SECTOR(1) - CLICK(2);
|
|
|
|
creature->LOT.drop = -(SECTOR(1) - CLICK(2));
|
|
|
|
creature->LOT.zone = ZONE_HUMAN_CLASSIC;
|
|
|
|
break;
|
2020-01-13 13:18:55 +01:00
|
|
|
|
|
|
|
case ZONE_BLOCKABLE:
|
|
|
|
creature->LOT.blockMask = BLOCKABLE;
|
2020-05-28 15:17:34 +02:00
|
|
|
creature->LOT.zone = ZONE_BASIC;
|
2020-01-13 13:18:55 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ZONE_SOPHIALEE:
|
|
|
|
creature->LOT.step = CLICK(4);
|
|
|
|
creature->LOT.drop = -CLICK(3);
|
2020-05-28 15:17:34 +02:00
|
|
|
creature->LOT.zone = ZONE_HUMAN_CLASSIC;
|
2020-01-13 13:18:55 +01:00
|
|
|
break;
|
2019-12-13 19:00:59 +01:00
|
|
|
}
|
2018-08-19 09:46:58 +02:00
|
|
|
|
|
|
|
ClearLOT(&creature->LOT);
|
|
|
|
if (itemNum != Lara.itemNumber)
|
|
|
|
CreateZone(item);
|
|
|
|
|
|
|
|
SlotsUsed++;
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void ClearLOT(LOT_INFO* LOT)
|
2019-10-29 20:05:58 +01:00
|
|
|
{
|
|
|
|
LOT->head = NO_BOX;
|
|
|
|
LOT->tail = NO_BOX;
|
|
|
|
LOT->searchNumber = 0;
|
|
|
|
LOT->targetBox = NO_BOX;
|
|
|
|
LOT->requiredBox = NO_BOX;
|
|
|
|
|
|
|
|
BOX_NODE* node = LOT->node;
|
2019-11-27 15:12:35 +01:00
|
|
|
for (int i = 0; i < NumberBoxes; i++)
|
2019-10-29 20:05:58 +01:00
|
|
|
{
|
|
|
|
node->exitBox = NO_BOX;
|
|
|
|
node->nextExpansion = NO_BOX;
|
|
|
|
node->searchNumber = 0;
|
|
|
|
node++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:49:19 +01:00
|
|
|
void CreateZone(ITEM_INFO* item)
|
2019-10-29 20:05:58 +01:00
|
|
|
{
|
|
|
|
CREATURE_INFO* creature = (CREATURE_INFO*)item->data;
|
|
|
|
ROOM_INFO* r = &Rooms[item->roomNumber];
|
|
|
|
|
|
|
|
item->boxNumber = XZ_GET_SECTOR(r, item->pos.xPos - r->x, item->pos.zPos - r->z).box;
|
|
|
|
|
|
|
|
if (creature->LOT.fly)
|
|
|
|
{
|
|
|
|
BOX_NODE* node = creature->LOT.node;
|
|
|
|
creature->LOT.zoneCount = 0;
|
|
|
|
|
2019-11-27 15:12:35 +01:00
|
|
|
for (int i = 0; i < NumberBoxes; i++)
|
2019-10-29 20:05:58 +01:00
|
|
|
{
|
|
|
|
node->boxNumber = i;
|
|
|
|
node++;
|
|
|
|
creature->LOT.zoneCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-28 15:17:34 +02:00
|
|
|
short* zone = Zones[creature->LOT.zone][FALSE];
|
|
|
|
short* flippedZone = Zones[creature->LOT.zone][TRUE];
|
2019-10-29 20:05:58 +01:00
|
|
|
|
2019-11-27 15:12:35 +01:00
|
|
|
short zoneNumber = zone[item->boxNumber];
|
|
|
|
short flippedZoneNumber = flippedZone[item->boxNumber];
|
2019-10-29 20:05:58 +01:00
|
|
|
|
|
|
|
BOX_NODE* node = creature->LOT.node;
|
|
|
|
creature->LOT.zoneCount = 0;
|
|
|
|
|
2019-11-27 15:12:35 +01:00
|
|
|
for (int i = 0; i < NumberBoxes; i++)
|
2019-10-29 20:05:58 +01:00
|
|
|
{
|
|
|
|
if (*zone == zoneNumber || *flippedZone == flippedZoneNumber)
|
|
|
|
{
|
|
|
|
node->boxNumber = i;
|
|
|
|
node++;
|
|
|
|
creature->LOT.zoneCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
zone++;
|
|
|
|
flippedZone++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|