Enhance AI

This commit is contained in:
schmelczerandras 2020-04-19 16:45:58 +02:00
parent 26a0fd884f
commit 26e48fd997
28 changed files with 208 additions and 69 deletions

View file

@ -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++)

View file

@ -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();

View file

@ -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;
} }

View file

@ -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

View file

@ -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++;

View file

@ -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

View file

@ -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);
} }

View file

@ -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

View file

@ -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));

View file

@ -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();

View file

@ -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;

View file

@ -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,

View file

@ -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 &&

View file

@ -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;
} }

View file

@ -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)};

View file

@ -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,

View file

@ -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) {

View file

@ -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;

View file

@ -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;

View file

@ -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(

View file

@ -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);

View file

@ -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:

View file

@ -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++;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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();

View file

@ -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)};