Enhance AI
This commit is contained in:
parent
26a0fd884f
commit
26e48fd997
28 changed files with 208 additions and 69 deletions
|
|
@ -38,6 +38,13 @@ void setDisplayContrast(uint8_t value) {
|
||||||
dataMode();
|
dataMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void turnDisplayOnOff(bool shouldBeOn) {
|
||||||
|
commandMode();
|
||||||
|
sendByteOnSPI(0x8D | shouldBeOn); // set charge pump on/off
|
||||||
|
sendByteOnSPI(0x10 | shouldBeOn << 2); // set charge pump on/off
|
||||||
|
sendByteOnSPI(0xAE | shouldBeOn); // turn display sleep on/off
|
||||||
|
}
|
||||||
|
|
||||||
void initializeDisplay(DrawFunction drawEverything) {
|
void initializeDisplay(DrawFunction drawEverything) {
|
||||||
setOutputPin(DISPLAY_RESET_OUTPUT_PIN, false);
|
setOutputPin(DISPLAY_RESET_OUTPUT_PIN, false);
|
||||||
for (volatile uint8_t i = 0; i != 255; i++)
|
for (volatile uint8_t i = 0; i != 255; i++)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ typedef void (*DrawFunction)(Rectangle);
|
||||||
void initializeDisplay(DrawFunction drawEverything);
|
void initializeDisplay(DrawFunction drawEverything);
|
||||||
void setDisplayContrast(uint8_t value);
|
void setDisplayContrast(uint8_t value);
|
||||||
|
|
||||||
|
void turnDisplayOnOff(bool shouldBeOn);
|
||||||
|
|
||||||
void startIntersectionTest();
|
void startIntersectionTest();
|
||||||
bool endIntersectionTest();
|
bool endIntersectionTest();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ static struct {
|
||||||
ProtocolState protocolState;
|
ProtocolState protocolState;
|
||||||
CommandState commandState;
|
CommandState commandState;
|
||||||
OnCommandReceived onCommandReceived;
|
OnCommandReceived onCommandReceived;
|
||||||
|
OnReceiveStarted onReceiveStarted;
|
||||||
} infra;
|
} infra;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -81,6 +82,8 @@ static inline uint8_t isIrOn() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ISR(PCINT0_vect) {
|
ISR(PCINT0_vect) {
|
||||||
|
infra.onReceiveStarted();
|
||||||
|
|
||||||
switch (infra.protocolState) {
|
switch (infra.protocolState) {
|
||||||
case idle:
|
case idle:
|
||||||
if (isIrOn()) {
|
if (isIrOn()) {
|
||||||
|
|
@ -136,9 +139,10 @@ ISR(TIM0_COMPB_vect) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeInfra(OnCommandReceived onCommandReceived) {
|
void initializeInfra(OnCommandReceived onCommandReceived, OnReceiveStarted onReceiveStarted) {
|
||||||
setBit(PORTB, IR_PIN); // enable pull-up
|
setBit(PORTB, IR_PIN); // enable pull-up
|
||||||
setBit(PCMSK, IR_PIN); // specific pin change interrupt enable
|
setBit(PCMSK, IR_PIN); // specific pin change interrupt enable
|
||||||
setBit(GIMSK, PCIE); // global on pin change interrupt enable;
|
setBit(GIMSK, PCIE); // global on pin change interrupt enable;
|
||||||
infra.onCommandReceived = onCommandReceived;
|
infra.onCommandReceived = onCommandReceived;
|
||||||
|
infra.onReceiveStarted = onReceiveStarted;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
#define REPEAT_CODE 1
|
#define REPEAT_CODE 1
|
||||||
|
|
||||||
typedef void (*OnCommandReceived)(uint8_t);
|
typedef void (*OnCommandReceived)(uint8_t);
|
||||||
|
typedef void (*OnReceiveStarted)();
|
||||||
|
|
||||||
void initializeInfra(OnCommandReceived onCommandReceived);
|
void initializeInfra(OnCommandReceived onCommandReceived, OnReceiveStarted onReceiveStarted);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds) {
|
||||||
previousFrameTime = milisecondsSinceFrameStart;
|
previousFrameTime = milisecondsSinceFrameStart;
|
||||||
|
|
||||||
while (milisecondsSinceFrameStart < frameLengthInMilliseconds) {
|
while (milisecondsSinceFrameStart < frameLengthInMilliseconds) {
|
||||||
|
clearBit(MCUCR, SM1); // idle mode
|
||||||
sleep_cpu();
|
sleep_cpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +28,10 @@ void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void powerOff() {
|
||||||
|
setBit(MCUCR, SM1); // power-down mode
|
||||||
|
sleep_cpu();
|
||||||
|
}
|
||||||
|
|
||||||
ISR(TIM0_COMPA_vect) {
|
ISR(TIM0_COMPA_vect) {
|
||||||
milisecondsSinceFrameStart++;
|
milisecondsSinceFrameStart++;
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,6 @@ typedef bool (*FrameFunction)(uint8_t);
|
||||||
// frameFunction gets previousFrameTime (in milliseconds) as argument
|
// frameFunction gets previousFrameTime (in milliseconds) as argument
|
||||||
void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds);
|
void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds);
|
||||||
|
|
||||||
|
void powerOff();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ static struct {
|
||||||
uint8_t contrast;
|
uint8_t contrast;
|
||||||
uint8_t framesSinceLastSave;
|
uint8_t framesSinceLastSave;
|
||||||
uint8_t deathDownCounter;
|
uint8_t deathDownCounter;
|
||||||
|
bool isSleeping;
|
||||||
|
uint8_t receivedWakeUpBitCount;
|
||||||
} state = {
|
} state = {
|
||||||
.contrast = 255
|
.contrast = 255
|
||||||
};
|
};
|
||||||
|
|
@ -34,24 +36,45 @@ static inline void saveGame() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool frameFunction(uint8_t previousFrameTime) {
|
static inline bool handleDeathAnimation() {
|
||||||
disableWritingEEPROM();
|
if (isSpaceshipDestroyed()) {
|
||||||
|
|
||||||
if (spaceshipObject->as.spaceship.healthLoss >= MAX_HEALTH) {
|
|
||||||
setDisplayContrast(state.contrast * (state.deathDownCounter / DEATH_SCREEN_LENGTH));
|
setDisplayContrast(state.contrast * (state.deathDownCounter / DEATH_SCREEN_LENGTH));
|
||||||
if (state.deathDownCounter-- == 0) {
|
if (state.deathDownCounter-- == 0) {
|
||||||
invalidateEEPROM();
|
invalidateEEPROM();
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleOff() {
|
||||||
|
turnDisplayOnOff(false);
|
||||||
|
state.isSleeping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleOn() {
|
||||||
|
if (state.isSleeping && ++state.receivedWakeUpBitCount > 50) {
|
||||||
|
turnDisplayOnOff(true);
|
||||||
|
state.isSleeping = false;
|
||||||
|
state.receivedWakeUpBitCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool frameFunction(uint8_t previousFrameTime) {
|
||||||
|
if (state.isSleeping) {
|
||||||
|
powerOff();
|
||||||
|
} else {
|
||||||
|
disableWritingEEPROM();
|
||||||
|
tickObjects(previousFrameTime);
|
||||||
handleCommands();
|
handleCommands();
|
||||||
handleAI();
|
handleAI();
|
||||||
generateEvents();
|
generateEvents();
|
||||||
tickObjects(previousFrameTime);
|
|
||||||
drawFrame();
|
drawFrame();
|
||||||
enableWritingEEPROM();
|
enableWritingEEPROM();
|
||||||
saveGame();
|
saveGame();
|
||||||
|
return handleDeathAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +94,7 @@ static inline void createObjects() {
|
||||||
void setupConnections() {
|
void setupConnections() {
|
||||||
initializeHardwareAccess();
|
initializeHardwareAccess();
|
||||||
initializeRedundantStorage();
|
initializeRedundantStorage();
|
||||||
initializeInfra(addCommand);
|
initializeInfra(addCommand, handleOn);
|
||||||
initializeDisplay(drawObjects);
|
initializeDisplay(drawObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
void startGame();
|
void startGame();
|
||||||
|
void handleOff();
|
||||||
void changeDisplayContrast(int8_t by);
|
void changeDisplayContrast(int8_t by);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,15 @@
|
||||||
#include "../../util/random/random.h"
|
#include "../../util/random/random.h"
|
||||||
#include "../../driver/display/display.h"
|
#include "../../driver/display/display.h"
|
||||||
|
|
||||||
|
|
||||||
#define AI_ACTION_COUNT 5
|
#define AI_ACTION_COUNT 5
|
||||||
|
|
||||||
|
typedef bool (*Predicate)(Rectangle*, Object*, uint8_t);
|
||||||
typedef bool (*Predicate)(Rectangle*, Object*);
|
|
||||||
typedef void (*Execution)(Object*);
|
typedef void (*Execution)(Object*);
|
||||||
|
|
||||||
|
static uint8_t timeSinceLastAction;
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Predicate predicate;
|
Predicate predicate;
|
||||||
Execution execution;
|
Execution execution;
|
||||||
|
|
@ -75,24 +78,41 @@ static void carefullyMoveSpaceship(Object* astronaut, Vec2 target) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldShootTurret(
|
static void makeAiAstronautDoAction(Object* astronaut) {
|
||||||
|
if (timeSinceLastAction > AI_ACTION_INTERVAL) {
|
||||||
|
makeAstronautDoAction(astronaut);
|
||||||
|
timeSinceLastAction = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool shouldControlTurret(
|
||||||
__attribute__((unused)) Rectangle* boundingBox,
|
__attribute__((unused)) Rectangle* boundingBox,
|
||||||
__attribute__((unused)) Object* astronaut
|
__attribute__((unused)) Object* astronaut,
|
||||||
|
__attribute__((unused)) uint8_t astronautId
|
||||||
) {
|
) {
|
||||||
return getIntersectingObjectOfType(
|
return getCountOf(&Asteroid) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void executeControlTurret(Object* astronaut) {
|
||||||
|
if (getIntersectingObjectOfType(
|
||||||
(Rectangle){
|
(Rectangle){
|
||||||
add(TURRET_POSITION, spaceshipObject->position),
|
add(TURRET_POSITION, spaceshipObject->position),
|
||||||
(Vec2){63, 1}
|
(Vec2){63, 1}
|
||||||
},
|
},
|
||||||
&Asteroid
|
&Asteroid
|
||||||
);
|
)) {
|
||||||
|
makeAiAstronautDoAction(astronaut);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldControlSpaceship(
|
static bool shouldControlSpaceship(
|
||||||
__attribute__((unused)) Rectangle* boundingBox,
|
__attribute__((unused)) Rectangle* boundingBox,
|
||||||
__attribute__((unused)) Object* astronaut
|
__attribute__((unused)) Object* astronaut,
|
||||||
|
uint8_t astronautId
|
||||||
) {
|
) {
|
||||||
return getCountOf(&Asteroid) > 0;
|
return getCountOf(&Asteroid) > 0
|
||||||
|
&& astronautId == 1
|
||||||
|
&& spaceshipObject->as.spaceship.healthLoss < MAX_HEALTH / 4 * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void executeControlSpaceship(Object* astronaut) {
|
static void executeControlSpaceship(Object* astronaut) {
|
||||||
|
|
@ -105,7 +125,7 @@ static void executeControlSpaceship(Object* astronaut) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldRepairSpaceship(Rectangle* boundingBox, Object* astronaut) {
|
static bool shouldRepairSpaceship(Rectangle* boundingBox, Object* astronaut, __attribute__((unused)) uint8_t astronautId) {
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
areIntersecting(*boundingBox, getBoundingBox(astronaut)) &&
|
areIntersecting(*boundingBox, getBoundingBox(astronaut)) &&
|
||||||
|
|
@ -115,9 +135,9 @@ static bool shouldRepairSpaceship(Rectangle* boundingBox, Object* astronaut) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldCenterSpaceship(Rectangle* boundingBox, Object* astronaut) {
|
static bool shouldCenterSpaceship(Rectangle* boundingBox, Object* astronaut, __attribute__((unused)) uint8_t astronautId) {
|
||||||
return (
|
return (
|
||||||
!actions[0].isSomeoneDoingThis &&
|
!actions[1].isSomeoneDoingThis &&
|
||||||
(
|
(
|
||||||
areIntersecting(*boundingBox, getBoundingBox(astronaut)) ||
|
areIntersecting(*boundingBox, getBoundingBox(astronaut)) ||
|
||||||
getCenter(getBoundingBox(spaceshipObject)).x != getCenter(WINDOW).x ||
|
getCenter(getBoundingBox(spaceshipObject)).x != getCenter(WINDOW).x ||
|
||||||
|
|
@ -135,7 +155,8 @@ static void executeCenterSpaceship(Object* astronaut) {
|
||||||
|
|
||||||
static bool shouldSocialize(
|
static bool shouldSocialize(
|
||||||
__attribute__((unused)) Rectangle* boundingBox,
|
__attribute__((unused)) Rectangle* boundingBox,
|
||||||
__attribute__((unused)) Object* astronaut
|
__attribute__((unused)) Object* astronaut,
|
||||||
|
__attribute__((unused)) uint8_t astronautId
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +168,13 @@ static void executeSocialize(Object* astronaut) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static AIAction actions[AI_ACTION_COUNT] = {
|
static AIAction actions[AI_ACTION_COUNT] = {
|
||||||
|
(AIAction) {
|
||||||
|
.predicate = shouldRepairSpaceship,
|
||||||
|
.execution = makeAiAstronautDoAction,
|
||||||
|
.spaceshipPart = spaceshipParts + BEDS_INDEX,
|
||||||
|
.onlyOneAstronautCanDoIt = true,
|
||||||
|
.deltaCenter = {2, 0}
|
||||||
|
},
|
||||||
(AIAction) {
|
(AIAction) {
|
||||||
.predicate = shouldControlSpaceship,
|
.predicate = shouldControlSpaceship,
|
||||||
.execution = executeControlSpaceship,
|
.execution = executeControlSpaceship,
|
||||||
|
|
@ -155,15 +183,8 @@ static AIAction actions[AI_ACTION_COUNT] = {
|
||||||
.deltaCenter = {-3, 0}
|
.deltaCenter = {-3, 0}
|
||||||
},
|
},
|
||||||
(AIAction) {
|
(AIAction) {
|
||||||
.predicate = shouldRepairSpaceship,
|
.predicate = shouldControlTurret,
|
||||||
.execution = makeAstronautDoAction,
|
.execution = executeControlTurret,
|
||||||
.spaceshipPart = spaceshipParts + BEDS_INDEX,
|
|
||||||
.onlyOneAstronautCanDoIt = true,
|
|
||||||
.deltaCenter = {2, 0}
|
|
||||||
},
|
|
||||||
(AIAction) {
|
|
||||||
.predicate = shouldShootTurret,
|
|
||||||
.execution = makeAstronautDoAction,
|
|
||||||
.spaceshipPart = spaceshipParts + TURRET_CONTROLLER_INDEX,
|
.spaceshipPart = spaceshipParts + TURRET_CONTROLLER_INDEX,
|
||||||
.onlyOneAstronautCanDoIt = true,
|
.onlyOneAstronautCanDoIt = true,
|
||||||
.deltaCenter = {-3, 0}
|
.deltaCenter = {-3, 0}
|
||||||
|
|
@ -185,20 +206,24 @@ static AIAction actions[AI_ACTION_COUNT] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void handleAI() {
|
void handleAI() {
|
||||||
|
timeSinceLastAction++;
|
||||||
|
uint8_t astronautCount = 0;
|
||||||
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
||||||
actions[j].isSomeoneDoingThis = false;
|
actions[j].isSomeoneDoingThis = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||||
if (objects[i].prototype == &Astronaut && objects + i != character) {
|
if (objects[i].prototype == &Astronaut && objects + i != character) {
|
||||||
|
astronautCount++;
|
||||||
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
||||||
AIAction* currentAction = actions + j;
|
AIAction* currentAction = actions + j;
|
||||||
Rectangle boundingBox = getBoundingBoxOfSpaceshipPart(currentAction->spaceshipPart);
|
Rectangle boundingBox = getBoundingBoxOfSpaceshipPart(currentAction->spaceshipPart);
|
||||||
|
|
||||||
Object* astronautIntersectingBoundingBox = getIntersectingObjectOfType(boundingBox, &Astronaut);
|
Object* astronautIntersectingBoundingBox = getIntersectingObjectOfType(boundingBox, &Astronaut);
|
||||||
if (
|
if (
|
||||||
isSpaceshipPartActivated(currentAction->spaceshipPart) &&
|
isSpaceshipPartActivated(currentAction->spaceshipPart) &&
|
||||||
(!currentAction->onlyOneAstronautCanDoIt || (!currentAction->isSomeoneDoingThis && astronautIntersectingBoundingBox != character)) &&
|
(!currentAction->onlyOneAstronautCanDoIt || (!currentAction->isSomeoneDoingThis && astronautIntersectingBoundingBox != character)) &&
|
||||||
currentAction->predicate(&boundingBox, objects + i)
|
currentAction->predicate(&boundingBox, objects + i, astronautCount)
|
||||||
) {
|
) {
|
||||||
if (!areIntersecting(boundingBox, getBoundingBox(objects + i))) {
|
if (!areIntersecting(boundingBox, getBoundingBox(objects + i))) {
|
||||||
carefullyMoveAstronaut(objects + i, add(getCenter(boundingBox), currentAction->deltaCenter));
|
carefullyMoveAstronaut(objects + i, add(getCenter(boundingBox), currentAction->deltaCenter));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef AI_H
|
#ifndef AI_H
|
||||||
#define AI_H
|
#define AI_H
|
||||||
|
|
||||||
|
// Between AI astronauts do actions
|
||||||
|
// there has to be at least this many frames
|
||||||
|
#define AI_ACTION_INTERVAL 30
|
||||||
|
|
||||||
// If there are non player controlled astronauts
|
// If there are non player controlled astronauts
|
||||||
// control them according to some basic rule set
|
// control them according to some basic rule set
|
||||||
void handleAI();
|
void handleAI();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
|
|
||||||
#include "../../objects/object_container/object_container.h"
|
#include "../../objects/object_container/object_container.h"
|
||||||
|
#include "../../objects/types/spaceship/spaceship.h"
|
||||||
#include "../../mediator/mediator.h"
|
#include "../../mediator/mediator.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -55,6 +56,14 @@ void handleCommands() {
|
||||||
case moveDown:
|
case moveDown:
|
||||||
moveAstronaut(character, directions[south]);
|
moveAstronaut(character, directions[south]);
|
||||||
break;
|
break;
|
||||||
|
case reset:
|
||||||
|
destroySpaceship();
|
||||||
|
commands.previous = noAction;
|
||||||
|
break;
|
||||||
|
case turnOff:
|
||||||
|
handleOff();
|
||||||
|
commands.previous = noAction;
|
||||||
|
break;
|
||||||
case action:
|
case action:
|
||||||
makeAstronautDoAction(character);
|
makeAstronautDoAction(character);
|
||||||
commands.previous = noAction;
|
commands.previous = noAction;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
#ifndef COMMANDS_H
|
#ifndef COMMANDS_H
|
||||||
#define COMMANDS_H
|
#define COMMANDS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
// There can be no more than COMMAND_BUFFER_SIZE commands
|
// There can be no more than COMMAND_BUFFER_SIZE commands
|
||||||
// waiting for processing simultaneously
|
// waiting for processing simultaneously
|
||||||
#define COMMAND_BUFFER_SIZE 8
|
#define COMMAND_BUFFER_SIZE 8
|
||||||
|
|
@ -15,6 +18,8 @@
|
||||||
typedef enum {
|
typedef enum {
|
||||||
noCommand = 0,
|
noCommand = 0,
|
||||||
repeat = 1,
|
repeat = 1,
|
||||||
|
turnOff = 61,
|
||||||
|
reset = 157,
|
||||||
increaseContrast = 87,
|
increaseContrast = 87,
|
||||||
decreaseContrast = 31,
|
decreaseContrast = 31,
|
||||||
moveUp = 231,
|
moveUp = 231,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ static void generate(Prototype const* type, Predicate predicate) {
|
||||||
bool generateAstronautPredicate(Rectangle* proposedBoundingBox) {
|
bool generateAstronautPredicate(Rectangle* proposedBoundingBox) {
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
(getCountOf(&Astronaut) == 1 && spaceshipObject->as.spaceship.progress >= hasTable) ||
|
(getCountOf(&Astronaut) == 1 && spaceshipObject->as.spaceship.progress >= hasHalfCrew) ||
|
||||||
(getCountOf(&Astronaut) == 2 && spaceshipObject->as.spaceship.progress >= hasFullCrew)
|
(getCountOf(&Astronaut) == 2 && spaceshipObject->as.spaceship.progress >= hasFullCrew)
|
||||||
) &&
|
) &&
|
||||||
getIntersectingObjectOfType(*proposedBoundingBox, &Astronaut) == NULL &&
|
getIntersectingObjectOfType(*proposedBoundingBox, &Astronaut) == NULL &&
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ bool mineAsteroid(Object* asteroid) {
|
||||||
return ++asteroid->as.asteroid.animationFrame == IDLE_FRAME_COUNT;
|
return ++asteroid->as.asteroid.animationFrame == IDLE_FRAME_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tick(Object* asteroid, uint8_t previousFrameTime) {
|
static void tick(Object* asteroid, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||||
if (asteroid->as.asteroid.animationFrame < IDLE_FRAME_COUNT) {
|
if (asteroid->as.asteroid.animationFrame < IDLE_FRAME_COUNT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,8 @@ void moveAstronaut(Object* astronaut, Vec2 direction) {
|
||||||
setWasDoingAction(astronaut, false);
|
setWasDoingAction(astronaut, false);
|
||||||
|
|
||||||
if (getIsControllingSpaceship(astronaut)) {
|
if (getIsControllingSpaceship(astronaut)) {
|
||||||
moveSpaceship(direction);
|
moveSpaceship((Vec2){direction.x, 0});
|
||||||
|
moveSpaceship((Vec2){0, direction.y});
|
||||||
} else {
|
} else {
|
||||||
Vec2 proposedPosition = add(astronaut->position, direction);
|
Vec2 proposedPosition = add(astronaut->position, direction);
|
||||||
Rectangle proposedBoundingBox = (Rectangle){proposedPosition, getSize(astronaut)};
|
Rectangle proposedBoundingBox = (Rectangle){proposedPosition, getSize(astronaut)};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
// Between two consecutive actions (or movements)
|
// Between two consecutive actions (or movements)
|
||||||
// there has to be at least this many milliseconds
|
// there has to be at least this many milliseconds
|
||||||
#define TIME_BETWEEN_ACTION_CHANGE 40
|
#define TIME_BETWEEN_ACTION_CHANGE 50
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
noAction = 0,
|
noAction = 0,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ static Star createStarOnTheRight() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tick(Object* background, uint8_t previousFrameTime) {
|
static void tick(Object* background, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||||
background->as.background.movementState++;
|
background->as.background.movementState++;
|
||||||
for (uint8_t i = 0; i < STAR_COUNT; i++) {
|
for (uint8_t i = 0; i < STAR_COUNT; i++) {
|
||||||
switch (i % 3) {
|
switch (i % 3) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void tick(Object* bullet, uint8_t previousFrameTime) {
|
static void tick(Object* bullet, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||||
if (bullet->as.bullet.wereIntersectingInThePreviousFrame) {
|
if (bullet->as.bullet.wereIntersectingInThePreviousFrame) {
|
||||||
clearObject(bullet);
|
clearObject(bullet);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include "../../object_container/object_container.h"
|
#include "../../object_container/object_container.h"
|
||||||
|
|
||||||
|
|
||||||
static void tick(Object* heart, uint8_t previousFrameTime) {
|
static void tick(Object* heart, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||||
if (++heart->as.heart.timeSinceLastChange == HEART_ANIMATION_INTERVAL) {
|
if (++heart->as.heart.timeSinceLastChange == HEART_ANIMATION_INTERVAL) {
|
||||||
heart->as.heart.animationFrame = (heart->as.heart.animationFrame + 1) % HEART_FRAME_COUNT;
|
heart->as.heart.animationFrame = (heart->as.heart.animationFrame + 1) % HEART_FRAME_COUNT;
|
||||||
heart->as.heart.timeSinceLastChange = 0;
|
heart->as.heart.timeSinceLastChange = 0;
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,14 @@ bool isSpaceshipPartActivated(SpaceshipPart* part) {
|
||||||
return part->alwaysActiveDoNotDraw || ((spaceshipObject->as.spaceship.activatedParts >> (part - spaceshipParts)) & 1);
|
return part->alwaysActiveDoNotDraw || ((spaceshipObject->as.spaceship.activatedParts >> (part - spaceshipParts)) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isSpaceshipDestroyed() {
|
||||||
|
return spaceshipObject->as.spaceship.healthLoss >= MAX_HEALTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroySpaceship() {
|
||||||
|
spaceshipObject->as.spaceship.healthLoss = MAX_HEALTH;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void drawSpaceshipHealthBar() {
|
static inline void drawSpaceshipHealthBar() {
|
||||||
uint8_t actualBarLength = spaceshipObject->as.spaceship.healthLoss * BAR_LENGTH / MAX_HEALTH;
|
uint8_t actualBarLength = spaceshipObject->as.spaceship.healthLoss * BAR_LENGTH / MAX_HEALTH;
|
||||||
drawFilledRectangle(
|
drawFilledRectangle(
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,11 @@ SpaceshipPart spaceshipParts[SPACESHIP_PART_COUNT];
|
||||||
const Prototype Spaceship;
|
const Prototype Spaceship;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
hasBeds = 1,
|
hasBeds = 2,
|
||||||
hasTurret = 3,
|
hasTurret = 5,
|
||||||
hasTable = 5,
|
hasHalfCrew = 8,
|
||||||
hasFullCrew = 9
|
hasFullCrew = 15,
|
||||||
|
hasTable = 25,
|
||||||
} Progress;
|
} Progress;
|
||||||
|
|
||||||
struct _spaceship_t {
|
struct _spaceship_t {
|
||||||
|
|
@ -66,6 +67,9 @@ bool isOnLadder(Rectangle boundingBox);
|
||||||
void onAsteroidMined();
|
void onAsteroidMined();
|
||||||
bool isSpaceshipPartActivated(SpaceshipPart* part);
|
bool isSpaceshipPartActivated(SpaceshipPart* part);
|
||||||
|
|
||||||
|
bool isSpaceshipDestroyed();
|
||||||
|
void destroySpaceship();
|
||||||
|
|
||||||
void shootTurretOfSpaceship();
|
void shootTurretOfSpaceship();
|
||||||
Action getPossibleActionFromSpaceship(Object* astronaut);
|
Action getPossibleActionFromSpaceship(Object* astronaut);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include "bitwise.h"
|
#include "bitwise.h"
|
||||||
#include "../../hardware_access/hardware_access.h"
|
#include "../../hardware_access/hardware_access.h"
|
||||||
#include "../uart/receive.h"
|
#include "../uart/receive.h"
|
||||||
|
#include "../uart/transmit.h"
|
||||||
|
|
||||||
|
|
||||||
// (0.5625 + (0.5625 + 1.6875) / 2) / 1000 / timer interval
|
// (0.5625 + (0.5625 + 1.6875) / 2) / 1000 / timer interval
|
||||||
|
|
@ -48,6 +49,7 @@ static void saveCurrentByte() {
|
||||||
break;
|
break;
|
||||||
case significantByte:
|
case significantByte:
|
||||||
infra.onCommandReceived(byte);
|
infra.onCommandReceived(byte);
|
||||||
|
sendUintOnUartAsync(byte);
|
||||||
infra.commandState = waitingForEndOfCommand;
|
infra.commandState = waitingForEndOfCommand;
|
||||||
break;
|
break;
|
||||||
case waitingForEndOfCommand:
|
case waitingForEndOfCommand:
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds) {
|
||||||
previousFrameTime = milisecondsSinceFrameStart;
|
previousFrameTime = milisecondsSinceFrameStart;
|
||||||
|
|
||||||
while (milisecondsSinceFrameStart < frameLengthInMilliseconds) {
|
while (milisecondsSinceFrameStart < frameLengthInMilliseconds) {
|
||||||
|
clearBit(MCUCR, SM1); // idle mode
|
||||||
sleep_cpu();
|
sleep_cpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +28,10 @@ void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void powerOff() {
|
||||||
|
setBit(MCUCR, SM1); // power-down mode
|
||||||
|
sleep_cpu();
|
||||||
|
}
|
||||||
|
|
||||||
ISR(TIM0_COMPA_vect) {
|
ISR(TIM0_COMPA_vect) {
|
||||||
milisecondsSinceFrameStart++;
|
milisecondsSinceFrameStart++;
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,10 @@ static bool frameFunction(uint8_t previousFrameTime) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tickObjects(previousFrameTime);
|
||||||
handleCommands();
|
handleCommands();
|
||||||
handleAI();
|
handleAI();
|
||||||
generateEvents();
|
generateEvents();
|
||||||
tickObjects(previousFrameTime);
|
|
||||||
drawFrame();
|
drawFrame();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,17 @@
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Setup the drivers, and business layer objects and their relations
|
||||||
|
// It is kind of a very basic dependency injection.
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
|
|
||||||
|
// Start drawing frames and ticking objects
|
||||||
void startGame();
|
void startGame();
|
||||||
|
|
||||||
|
// Increase or decrease the contrast (brightness) of the display
|
||||||
|
// by the given value
|
||||||
|
// The contrast can be any number between 0 and 255.
|
||||||
|
// The function automatically clamps the contrast.
|
||||||
void changeDisplayContrast(int8_t by);
|
void changeDisplayContrast(int8_t by);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,15 @@
|
||||||
#include "../../util/random/random.h"
|
#include "../../util/random/random.h"
|
||||||
#include "../../driver/display/display.h"
|
#include "../../driver/display/display.h"
|
||||||
|
|
||||||
#define AI_ACTION_COUNT 5
|
|
||||||
|
|
||||||
|
#define AI_ACTION_COUNT 5
|
||||||
|
|
||||||
typedef bool (*Predicate)(Rectangle*, Object*);
|
typedef bool (*Predicate)(Rectangle*, Object*);
|
||||||
typedef void (*Execution)(Object*);
|
typedef void (*Execution)(Object*);
|
||||||
|
|
||||||
|
static uint8_t timeSinceLastAction;
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Predicate predicate;
|
Predicate predicate;
|
||||||
Execution execution;
|
Execution execution;
|
||||||
|
|
@ -70,29 +73,41 @@ static void carefullyMoveSpaceship(Object* astronaut, Vec2 target) {
|
||||||
getCenter(getBoundingBox(spaceshipObject))
|
getCenter(getBoundingBox(spaceshipObject))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
moveAstronaut(astronaut, (Vec2){direction.x, 0});
|
|
||||||
moveAstronaut(astronaut, (Vec2){0, direction.y});
|
moveAstronaut(astronaut, direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldShootTurret(
|
static void makeAiAstronautDoAction(Object* astronaut) {
|
||||||
|
timeSinceLastAction = 0;
|
||||||
|
makeAstronautDoAction(astronaut);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool shouldControlTurret(
|
||||||
__attribute__((unused)) Rectangle* boundingBox,
|
__attribute__((unused)) Rectangle* boundingBox,
|
||||||
__attribute__((unused)) Object* astronaut
|
__attribute__((unused)) Object* astronaut
|
||||||
) {
|
) {
|
||||||
return getIntersectingObjectOfType(
|
return getCountOf(&Asteroid) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void executeControlTurret(Object* astronaut) {
|
||||||
|
if (getIntersectingObjectOfType(
|
||||||
(Rectangle){
|
(Rectangle){
|
||||||
add(TURRET_POSITION, spaceshipObject->position),
|
add(TURRET_POSITION, spaceshipObject->position),
|
||||||
(Vec2){63, 1}
|
(Vec2){63, 1}
|
||||||
},
|
},
|
||||||
&Asteroid
|
&Asteroid
|
||||||
);
|
)) {
|
||||||
|
makeAiAstronautDoAction(astronaut);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldControlSpaceship(
|
static bool shouldControlSpaceship(
|
||||||
__attribute__((unused)) Rectangle* boundingBox,
|
__attribute__((unused)) Rectangle* boundingBox,
|
||||||
__attribute__((unused)) Object* astronaut
|
__attribute__((unused)) Object* astronaut
|
||||||
) {
|
) {
|
||||||
return getCountOf(&Asteroid) > 0;
|
return getCountOf(&Asteroid) > 0
|
||||||
|
&& spaceshipObject->as.spaceship.healthLoss < MAX_HEALTH / 4 * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void executeControlSpaceship(Object* astronaut) {
|
static void executeControlSpaceship(Object* astronaut) {
|
||||||
|
|
@ -156,14 +171,14 @@ static AIAction actions[AI_ACTION_COUNT] = {
|
||||||
},
|
},
|
||||||
(AIAction) {
|
(AIAction) {
|
||||||
.predicate = shouldRepairSpaceship,
|
.predicate = shouldRepairSpaceship,
|
||||||
.execution = makeAstronautDoAction,
|
.execution = makeAiAstronautDoAction,
|
||||||
.spaceshipPart = spaceshipParts + BEDS_INDEX,
|
.spaceshipPart = spaceshipParts + BEDS_INDEX,
|
||||||
.onlyOneAstronautCanDoIt = true,
|
.onlyOneAstronautCanDoIt = true,
|
||||||
.deltaCenter = {2, 0}
|
.deltaCenter = {2, 0}
|
||||||
},
|
},
|
||||||
(AIAction) {
|
(AIAction) {
|
||||||
.predicate = shouldShootTurret,
|
.predicate = shouldControlTurret,
|
||||||
.execution = makeAstronautDoAction,
|
.execution = executeControlTurret,
|
||||||
.spaceshipPart = spaceshipParts + TURRET_CONTROLLER_INDEX,
|
.spaceshipPart = spaceshipParts + TURRET_CONTROLLER_INDEX,
|
||||||
.onlyOneAstronautCanDoIt = true,
|
.onlyOneAstronautCanDoIt = true,
|
||||||
.deltaCenter = {-3, 0}
|
.deltaCenter = {-3, 0}
|
||||||
|
|
@ -185,6 +200,8 @@ static AIAction actions[AI_ACTION_COUNT] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void handleAI() {
|
void handleAI() {
|
||||||
|
timeSinceLastAction++;
|
||||||
|
|
||||||
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
||||||
actions[j].isSomeoneDoingThis = false;
|
actions[j].isSomeoneDoingThis = false;
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +219,7 @@ void handleAI() {
|
||||||
) {
|
) {
|
||||||
if (!areIntersecting(boundingBox, getBoundingBox(objects + i))) {
|
if (!areIntersecting(boundingBox, getBoundingBox(objects + i))) {
|
||||||
carefullyMoveAstronaut(objects + i, add(getCenter(boundingBox), currentAction->deltaCenter));
|
carefullyMoveAstronaut(objects + i, add(getCenter(boundingBox), currentAction->deltaCenter));
|
||||||
} else {
|
} else if (timeSinceLastAction > AI_ACTION_INTERVAL) {
|
||||||
currentAction->execution(objects + i);
|
currentAction->execution(objects + i);
|
||||||
}
|
}
|
||||||
currentAction->isSomeoneDoingThis = true;
|
currentAction->isSomeoneDoingThis = true;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef AI_H
|
#ifndef AI_H
|
||||||
#define AI_H
|
#define AI_H
|
||||||
|
|
||||||
|
// Between AI astronauts do actions
|
||||||
|
// there has to be at least this many frames
|
||||||
|
#define AI_ACTION_INTERVAL 15
|
||||||
|
|
||||||
// If there are non player controlled astronauts
|
// If there are non player controlled astronauts
|
||||||
// control them according to some basic rule set
|
// control them according to some basic rule set
|
||||||
void handleAI();
|
void handleAI();
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,8 @@ void moveAstronaut(Object* astronaut, Vec2 direction) {
|
||||||
setWasDoingAction(astronaut, false);
|
setWasDoingAction(astronaut, false);
|
||||||
|
|
||||||
if (getIsControllingSpaceship(astronaut)) {
|
if (getIsControllingSpaceship(astronaut)) {
|
||||||
moveSpaceship(direction);
|
moveSpaceship((Vec2){direction.x, 0});
|
||||||
|
moveSpaceship((Vec2){0, direction.y});
|
||||||
} else {
|
} else {
|
||||||
Vec2 proposedPosition = add(astronaut->position, direction);
|
Vec2 proposedPosition = add(astronaut->position, direction);
|
||||||
Rectangle proposedBoundingBox = (Rectangle){proposedPosition, getSize(astronaut)};
|
Rectangle proposedBoundingBox = (Rectangle){proposedPosition, getSize(astronaut)};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue