Add christmas edition
This commit is contained in:
parent
2f1c7d9e82
commit
b62af486a7
74 changed files with 3422 additions and 1 deletions
235
christmas/AdAstra/src/objects/ai/ai.c
Normal file
235
christmas/AdAstra/src/objects/ai/ai.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
#include "ai.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/io.h>
|
||||
|
||||
#include "../object_container/object_container.h"
|
||||
#include "../types/astronaut/astronaut.h"
|
||||
#include "../types/spaceship/spaceship.h"
|
||||
#include "../../util/rectangle/rectangle.h"
|
||||
#include "../../util/random/random.h"
|
||||
#include "../../driver/display/display.h"
|
||||
|
||||
|
||||
#define AI_ACTION_COUNT 5
|
||||
|
||||
typedef bool (*Predicate)(Rectangle*, Object*, uint8_t);
|
||||
typedef void (*Execution)(Object*);
|
||||
|
||||
static uint8_t timeSinceLastAction;
|
||||
|
||||
|
||||
typedef struct {
|
||||
Predicate predicate;
|
||||
Execution execution;
|
||||
SpaceshipPart* spaceshipPart;
|
||||
bool onlyOneAstronautCanDoIt;
|
||||
Vec2 deltaCenter;
|
||||
bool isSomeoneDoingThis;
|
||||
} AIAction;
|
||||
|
||||
static AIAction actions[AI_ACTION_COUNT];
|
||||
|
||||
|
||||
static Vec2 whichDirectionToMove(Object* astronaut, Vec2 position) {
|
||||
bool const isTargetOnUpperFloor = isOnUpperFloor((Rectangle){position, (Vec2){0, 0}}); // else it's on the lower floor
|
||||
|
||||
Vec2 const ladder = add(LADDER_BOUNDING_BOX.position, spaceshipObject->position);
|
||||
|
||||
Vec2 astronautCenter = getCenter(getBoundingBox(astronaut));
|
||||
Vec2 target = astronautCenter;
|
||||
if ((
|
||||
isOnUpperFloor(getBoundingBox(astronaut)) && isTargetOnUpperFloor
|
||||
) || (
|
||||
isOnLowerFloor(getBoundingBox(astronaut)) && !isTargetOnUpperFloor
|
||||
)) {
|
||||
target.x = position.x;
|
||||
} else if (isOnLadder(getBoundingBox(astronaut))){
|
||||
target.y = add(position, (Vec2){0, isTargetOnUpperFloor ? -10 : 10}).y;
|
||||
} else {
|
||||
target.x = ladder.x;
|
||||
}
|
||||
|
||||
return clampVec2(substract(target, astronautCenter));
|
||||
}
|
||||
|
||||
static void carefullyMoveAstronaut(Object* astronaut, Vec2 target) {
|
||||
if (getIsControllingSpaceship(astronaut)) {
|
||||
makeAstronautDoAction(astronaut);
|
||||
}
|
||||
if (!getIsControllingSpaceship(astronaut)) {
|
||||
moveAstronaut(astronaut, whichDirectionToMove(astronaut, target));
|
||||
}
|
||||
}
|
||||
|
||||
static void carefullyMoveSpaceship(Object* astronaut, Vec2 target) {
|
||||
if (!getIsControllingSpaceship(astronaut)) {
|
||||
makeAstronautDoAction(astronaut);
|
||||
}
|
||||
if (getIsControllingSpaceship(astronaut)) {
|
||||
Vec2 direction = clampVec2(
|
||||
substract(
|
||||
target,
|
||||
getCenter(getBoundingBox(spaceshipObject))
|
||||
)
|
||||
);
|
||||
|
||||
moveAstronaut(astronaut, direction);
|
||||
}
|
||||
}
|
||||
|
||||
static void makeAiAstronautDoAction(Object* astronaut) {
|
||||
if (timeSinceLastAction > AI_ACTION_INTERVAL) {
|
||||
makeAstronautDoAction(astronaut);
|
||||
timeSinceLastAction = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool shouldControlTurret(
|
||||
__attribute__((unused)) Rectangle* boundingBox,
|
||||
__attribute__((unused)) Object* astronaut,
|
||||
__attribute__((unused)) uint8_t astronautId
|
||||
) {
|
||||
return getCountOf(&Asteroid) > 0;
|
||||
}
|
||||
|
||||
static void executeControlTurret(Object* astronaut) {
|
||||
if (getIntersectingObjectOfType(
|
||||
(Rectangle){
|
||||
add(TURRET_POSITION, spaceshipObject->position),
|
||||
(Vec2){63, 1}
|
||||
},
|
||||
&Asteroid
|
||||
)) {
|
||||
makeAiAstronautDoAction(astronaut);
|
||||
};
|
||||
}
|
||||
|
||||
static bool shouldControlSpaceship(
|
||||
__attribute__((unused)) Rectangle* boundingBox,
|
||||
__attribute__((unused)) Object* astronaut,
|
||||
uint8_t astronautId
|
||||
) {
|
||||
return getCountOf(&Asteroid) > 0
|
||||
&& astronautId == 1
|
||||
&& spaceshipObject->as.spaceship.healthLoss < MAX_HEALTH / 4 * 3;
|
||||
}
|
||||
|
||||
static void executeControlSpaceship(Object* astronaut) {
|
||||
carefullyMoveSpaceship(
|
||||
astronaut,
|
||||
add(
|
||||
getCenter(getBoundingBox(getFirstOfType(&Asteroid))),
|
||||
(Vec2){-30, 0}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static bool shouldRepairSpaceship(Rectangle* boundingBox, Object* astronaut, __attribute__((unused)) uint8_t astronautId) {
|
||||
return (
|
||||
(
|
||||
areIntersecting(*boundingBox, getBoundingBox(astronaut)) &&
|
||||
spaceshipObject->as.spaceship.healthLoss > 0
|
||||
) ||
|
||||
spaceshipObject->as.spaceship.healthLoss >= MAX_HEALTH / 2
|
||||
);
|
||||
}
|
||||
|
||||
static bool shouldCenterSpaceship(Rectangle* boundingBox, Object* astronaut, __attribute__((unused)) uint8_t astronautId) {
|
||||
return (
|
||||
!actions[1].isSomeoneDoingThis &&
|
||||
(
|
||||
areIntersecting(*boundingBox, getBoundingBox(astronaut)) ||
|
||||
getCenter(getBoundingBox(spaceshipObject)).x != getCenter(WINDOW).x ||
|
||||
getCenter(getBoundingBox(spaceshipObject)).y != getCenter(WINDOW).y
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void executeCenterSpaceship(Object* astronaut) {
|
||||
carefullyMoveSpaceship(
|
||||
astronaut,
|
||||
getCenter(WINDOW)
|
||||
);
|
||||
}
|
||||
|
||||
static bool shouldSocialize(
|
||||
__attribute__((unused)) Rectangle* boundingBox,
|
||||
__attribute__((unused)) Object* astronaut,
|
||||
__attribute__((unused)) uint8_t astronautId
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void executeSocialize(Object* astronaut) {}
|
||||
|
||||
static AIAction actions[AI_ACTION_COUNT] = {
|
||||
(AIAction) {
|
||||
.predicate = shouldRepairSpaceship,
|
||||
.execution = makeAiAstronautDoAction,
|
||||
.spaceshipPart = spaceshipParts + BEDS_INDEX,
|
||||
.onlyOneAstronautCanDoIt = true,
|
||||
.deltaCenter = {2, 0}
|
||||
},
|
||||
(AIAction) {
|
||||
.predicate = shouldControlSpaceship,
|
||||
.execution = executeControlSpaceship,
|
||||
.spaceshipPart = spaceshipParts + COMMAND_PANEL_INDEX,
|
||||
.onlyOneAstronautCanDoIt = true,
|
||||
.deltaCenter = {-3, 0}
|
||||
},
|
||||
(AIAction) {
|
||||
.predicate = shouldControlTurret,
|
||||
.execution = executeControlTurret,
|
||||
.spaceshipPart = spaceshipParts + TURRET_CONTROLLER_INDEX,
|
||||
.onlyOneAstronautCanDoIt = true,
|
||||
.deltaCenter = {-3, 0}
|
||||
},
|
||||
(AIAction) {
|
||||
.predicate = shouldCenterSpaceship,
|
||||
.execution = executeCenterSpaceship,
|
||||
.spaceshipPart = spaceshipParts + COMMAND_PANEL_INDEX,
|
||||
.onlyOneAstronautCanDoIt = true,
|
||||
.deltaCenter = {-3, 0}
|
||||
},
|
||||
(AIAction) {
|
||||
.predicate = shouldSocialize,
|
||||
.execution = executeSocialize,
|
||||
.spaceshipPart = spaceshipParts + TABLE_INDEX,
|
||||
.onlyOneAstronautCanDoIt = false,
|
||||
.deltaCenter = {2, 0}
|
||||
},
|
||||
};
|
||||
|
||||
void handleAI() {
|
||||
timeSinceLastAction++;
|
||||
uint8_t astronautCount = 0;
|
||||
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
||||
actions[j].isSomeoneDoingThis = false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
if (objects[i].prototype == &Astronaut && objects + i != character) {
|
||||
astronautCount++;
|
||||
for (uint8_t j = 0; j < ACTION_COUNT; j++) {
|
||||
AIAction* currentAction = actions + j;
|
||||
Rectangle boundingBox = getBoundingBoxOfSpaceshipPart(currentAction->spaceshipPart);
|
||||
|
||||
Object* astronautIntersectingBoundingBox = getIntersectingObjectOfType(boundingBox, &Astronaut);
|
||||
if (
|
||||
isSpaceshipPartActivated(currentAction->spaceshipPart) &&
|
||||
(!currentAction->onlyOneAstronautCanDoIt || (!currentAction->isSomeoneDoingThis && astronautIntersectingBoundingBox != character)) &&
|
||||
currentAction->predicate(&boundingBox, objects + i, astronautCount)
|
||||
) {
|
||||
if (!areIntersecting(boundingBox, getBoundingBox(objects + i))) {
|
||||
carefullyMoveAstronaut(objects + i, add(getCenter(boundingBox), currentAction->deltaCenter));
|
||||
} else {
|
||||
currentAction->execution(objects + i);
|
||||
}
|
||||
currentAction->isSomeoneDoingThis = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
christmas/AdAstra/src/objects/ai/ai.h
Normal file
12
christmas/AdAstra/src/objects/ai/ai.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef 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
|
||||
// control them according to some basic rule set
|
||||
void handleAI();
|
||||
|
||||
#endif
|
||||
71
christmas/AdAstra/src/objects/commands/commands.c
Normal file
71
christmas/AdAstra/src/objects/commands/commands.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#include "commands.h"
|
||||
|
||||
#include "../../objects/object_container/object_container.h"
|
||||
#include "../../objects/types/spaceship/spaceship.h"
|
||||
#include "../../mediator/mediator.h"
|
||||
|
||||
|
||||
static struct {
|
||||
Command received[COMMAND_BUFFER_SIZE];
|
||||
uint8_t start;
|
||||
uint8_t end;
|
||||
Command previous;
|
||||
} commands;
|
||||
|
||||
|
||||
static inline bool areThereAnyCommandsLeft() {
|
||||
return commands.start != commands.end;
|
||||
}
|
||||
|
||||
static inline Command getNextCommand() {
|
||||
Command top = commands.received[commands.start++];
|
||||
commands.start %= COMMAND_BUFFER_SIZE;
|
||||
return top;
|
||||
}
|
||||
|
||||
void addCommand(Command command) {
|
||||
commands.received[commands.end++] = command;
|
||||
commands.end %= COMMAND_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
void handleCommands() {
|
||||
while(areThereAnyCommandsLeft()) {
|
||||
Command next = getNextCommand();
|
||||
if (next == repeat) {
|
||||
next = commands.previous;
|
||||
} else {
|
||||
commands.previous = next;
|
||||
}
|
||||
|
||||
switch(next) {
|
||||
case increaseContrast:
|
||||
changeDisplayContrast(CONTRAST_STEP);
|
||||
break;
|
||||
case decreaseContrast:
|
||||
changeDisplayContrast(-CONTRAST_STEP);
|
||||
break;
|
||||
case moveLeft:
|
||||
moveAstronaut(character, directions[west]);
|
||||
break;
|
||||
case moveRight:
|
||||
moveAstronaut(character, directions[east]);
|
||||
break;
|
||||
case moveUp:
|
||||
moveAstronaut(character, directions[north]);
|
||||
break;
|
||||
case moveDown:
|
||||
moveAstronaut(character, directions[south]);
|
||||
break;
|
||||
case reset:
|
||||
destroySpaceship();
|
||||
commands.previous = noAction;
|
||||
break;
|
||||
case action:
|
||||
makeAstronautDoAction(character);
|
||||
commands.previous = noAction;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
christmas/AdAstra/src/objects/commands/commands.h
Normal file
38
christmas/AdAstra/src/objects/commands/commands.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef COMMANDS_H
|
||||
#define COMMANDS_H
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// There can be no more than COMMAND_BUFFER_SIZE commands
|
||||
// waiting for processing simultaneously
|
||||
#define COMMAND_BUFFER_SIZE 8
|
||||
|
||||
// increaseContrast and decreaseContrast changes the contrast
|
||||
// with this value
|
||||
#define CONTRAST_STEP 15
|
||||
|
||||
// The possible inputs of the system
|
||||
// Coincidentally these are the codes of the IR remote
|
||||
// controller's buttons.
|
||||
typedef enum {
|
||||
noCommand = 0,
|
||||
repeat = 1,
|
||||
reset = 157,
|
||||
increaseContrast = 87,
|
||||
decreaseContrast = 31,
|
||||
moveUp = 231,
|
||||
moveDown = 181,
|
||||
moveLeft = 239,
|
||||
moveRight = 165,
|
||||
action = 199,
|
||||
} Command;
|
||||
|
||||
// Add a new command to the buffer
|
||||
// It will not be processed immediately.
|
||||
void addCommand(Command command);
|
||||
|
||||
// Process every command in the buffer at once in a FIFO manner
|
||||
void handleCommands();
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include "event_generator.h"
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "null.h"
|
||||
|
||||
#include "../object.h"
|
||||
#include "../object_container/object_container.h"
|
||||
#include "../types/spaceship/spaceship.h"
|
||||
#include "../types/background/background.h"
|
||||
#include "../types/astronaut/astronaut.h"
|
||||
#include "../types/asteroid/asteroid.h"
|
||||
#include "../types/text/text.h"
|
||||
#include "../../util/random/random.h"
|
||||
#include "../../driver/display/display.h"
|
||||
|
||||
|
||||
typedef bool (*Predicate)(Rectangle*);
|
||||
|
||||
|
||||
static inline Vec2 getRandomPosition() {
|
||||
return (Vec2) {
|
||||
getRandomNumber() % DISPLAY_WIDTH_IN_PIXELS,
|
||||
getRandomNumber() % DISPLAY_HEIGHT_IN_PIXELS
|
||||
};
|
||||
}
|
||||
|
||||
static void generate(Prototype const* type, Predicate predicate) {
|
||||
Object* emptySpace = getEmptyObjectSpace();
|
||||
if (emptySpace == NULL || getRandomNumber() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t tryCount = 0; tryCount < TRY_COUNT; tryCount++) {
|
||||
Rectangle proposedBoundingBox = (Rectangle){getRandomPosition(), getSizeFromPrototype(type)};
|
||||
|
||||
if (predicate(&proposedBoundingBox)) {
|
||||
createObject(type, emptySpace);
|
||||
emptySpace->position = proposedBoundingBox.position;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool generateAstronautPredicate(Rectangle* proposedBoundingBox) {
|
||||
return (
|
||||
(
|
||||
(getCountOf(&Astronaut) == 1 && spaceshipObject->as.spaceship.progress >= hasHalfCrew) ||
|
||||
(getCountOf(&Astronaut) == 2 && spaceshipObject->as.spaceship.progress >= hasTable)
|
||||
) &&
|
||||
getIntersectingObjectOfType(*proposedBoundingBox, &Astronaut) == NULL &&
|
||||
isOnboard(*proposedBoundingBox)
|
||||
);
|
||||
}
|
||||
|
||||
bool generateAsteroidPredicate(Rectangle* proposedBoundingBox) {
|
||||
return (
|
||||
getCountOf(&Asteroid) < MAX_ASTEROID_COUNT &&
|
||||
isInside(*proposedBoundingBox, WINDOW) &&
|
||||
getIntersectingObjectOfType(*proposedBoundingBox, &Spaceship) == NULL &&
|
||||
getIntersectingObjectOfType(*proposedBoundingBox, &Asteroid) == NULL
|
||||
);
|
||||
}
|
||||
|
||||
void createObjects() {
|
||||
createObject(&Background, getEmptyObjectSpace());
|
||||
|
||||
createObject(&Spaceship, spaceshipObject);
|
||||
spaceshipObject->position = (Vec2){EXHAUST_BOUNDING_BOX.size.x, DISPLAY_HEIGHT_IN_PIXELS / 2 - getSize(spaceshipObject).y / 2};
|
||||
|
||||
createObject(&Astronaut, character);
|
||||
Rectangle upperFloor = translateRectangle(UPPER_FLOOR_BOUNDING_BOX, spaceshipObject->position);
|
||||
character->position = add(upperFloor.position, (Vec2){10, 1});
|
||||
}
|
||||
|
||||
void generateEvents() {
|
||||
generate(&Astronaut, generateAstronautPredicate);
|
||||
generate(&Asteroid, generateAsteroidPredicate);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef EVENT_GENERATOR_H
|
||||
#define EVENT_GENERATOR_H
|
||||
|
||||
#define MAX_ASTEROID_COUNT 2
|
||||
|
||||
// For minimizing code size the position of generated objects is decided randomly.
|
||||
// If it fits then it stays. For each generated object can be a maximum of
|
||||
// TRY_COUNT tries.
|
||||
#define TRY_COUNT 16
|
||||
|
||||
void createObjects();
|
||||
|
||||
// Generate asteroids and astronaut randomly based on a set of conditions
|
||||
void generateEvents();
|
||||
|
||||
#endif
|
||||
41
christmas/AdAstra/src/objects/object.c
Normal file
41
christmas/AdAstra/src/objects/object.c
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include "object.h"
|
||||
#include "null.h"
|
||||
|
||||
|
||||
Object* createObject(Prototype const* prototype, Object* holder) {
|
||||
Object empty = {0};
|
||||
*holder = empty;
|
||||
holder->prototype = prototype;
|
||||
return holder;
|
||||
}
|
||||
|
||||
void tickObject(Object* object, uint8_t previousFrameTime) {
|
||||
if (object->prototype != NULL) {
|
||||
((TickMethod)pgm_read_word(&object->prototype->tick))(object, previousFrameTime);
|
||||
}
|
||||
}
|
||||
|
||||
void drawObject(Object* object, Rectangle compositingWindow) {
|
||||
if (object->prototype != NULL && areIntersecting(getBoundingBox(object), compositingWindow)) {
|
||||
((DrawMethod)pgm_read_word(&object->prototype->draw))(object, compositingWindow);
|
||||
}
|
||||
}
|
||||
|
||||
Vec2 getSizeFromPrototype(Prototype const* prototype) {
|
||||
// required for casting
|
||||
uint16_t read = pgm_read_word(&prototype->size);
|
||||
Vec2* v = (Vec2*) &read;
|
||||
return *v;
|
||||
}
|
||||
|
||||
Vec2 getSize(Object const* object) {
|
||||
return getSizeFromPrototype(object->prototype);
|
||||
}
|
||||
|
||||
void move(Object* object, Vec2 value) {
|
||||
object->position = add(object->position, value);
|
||||
}
|
||||
|
||||
Rectangle getBoundingBox(Object const* object) {
|
||||
return (Rectangle){object->position, getSize(object)};
|
||||
}
|
||||
78
christmas/AdAstra/src/objects/object.h
Normal file
78
christmas/AdAstra/src/objects/object.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#ifndef OBJECT_H
|
||||
#define OBJECT_H
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "../util/rectangle/rectangle.h"
|
||||
|
||||
#include "types/asteroid/asteroid.h"
|
||||
#include "types/astronaut/astronaut.h"
|
||||
#include "types/background/background.h"
|
||||
#include "types/spaceship/spaceship.h"
|
||||
#include "types/bullet/bullet.h"
|
||||
#include "types/text/text.h"
|
||||
|
||||
#include "prototype.h"
|
||||
|
||||
|
||||
// Objects (they could have been called GameObjects) have a simple
|
||||
// hierarchy. A prototype/flyweight motivated system is used.
|
||||
// Each type has some common data and methods which are stored
|
||||
// in their respective prototype.
|
||||
// This module provides us with the methods to easily and mostly
|
||||
// transparently access an object's prototype.
|
||||
|
||||
|
||||
typedef union {
|
||||
struct _background_t background;
|
||||
struct _spaceship_t spaceship;
|
||||
struct _astronaut_t astronaut;
|
||||
struct _asteroid_t asteroid;
|
||||
struct _bullet_t bullet;
|
||||
struct _text_t text;
|
||||
} object_specific_data_t;
|
||||
|
||||
|
||||
struct _object_t {
|
||||
Prototype const* prototype;
|
||||
Vec2 position;
|
||||
object_specific_data_t as;
|
||||
};
|
||||
|
||||
|
||||
// A simplified object intended for persisting its data
|
||||
// without saving its 16 bit long reference to its prototype
|
||||
typedef struct {
|
||||
Vec2 position;
|
||||
object_specific_data_t as;
|
||||
} DTO;
|
||||
|
||||
|
||||
// Set the prototype of the holder and initialize all the holder's
|
||||
// vale to zero. Return the freshly updated holder
|
||||
Object* createObject(Prototype const* prototype, Object* holder);
|
||||
|
||||
// Call the tick function referenced in the object's prototype
|
||||
// on the object itself
|
||||
// Object might react to the elapsed time.
|
||||
// Does nothing when called with NULL.
|
||||
void tickObject(Object* object, uint8_t previousFrameTime);
|
||||
|
||||
// Call the draw function referenced in the object's prototype
|
||||
// on the object itself
|
||||
// Does nothing when called with NULL.
|
||||
void drawObject(Object* object, Rectangle compositingWindow);
|
||||
|
||||
// Find out the prototype of the object and return the size of that
|
||||
Vec2 getSize(Object const* object);
|
||||
Vec2 getSizeFromPrototype(Prototype const* prototype);
|
||||
|
||||
// Move the position of the object by a vector
|
||||
void move(Object* object, Vec2 value);
|
||||
|
||||
// Get a new rectangle from the objects position and its
|
||||
// prototype's size
|
||||
Rectangle getBoundingBox(Object const* object);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#include "object_container.h"
|
||||
|
||||
#include "../../driver/redundant_storage/redundant_storage.h"
|
||||
#include "../event_generator/event_generator.h"
|
||||
|
||||
|
||||
Object* getFirstOfType(Prototype const* type) {
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
if (objects[i].prototype == type) {
|
||||
return objects + i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t getCountOf(Prototype const* type) {
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
if (objects[i].prototype == type) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
Object* getIntersectingObjectOfType(Rectangle boundingBox, Prototype const* type) {
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
if (objects[i].prototype == type && areIntersecting(boundingBox, getBoundingBox(objects + i))) {
|
||||
return objects + i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void clearObject(Object* object) {
|
||||
createObject(NULL, object);
|
||||
}
|
||||
|
||||
void drawObjects(Rectangle window) {
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
drawObject(objects + i, window);
|
||||
}
|
||||
}
|
||||
|
||||
void tickObjects(uint8_t previousFrameTime) {
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
tickObject(objects + i, previousFrameTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void initializeObjectContainer() {
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
clearObject(objects + i);
|
||||
}
|
||||
|
||||
createObjects();
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef OBJECT_HANDLER_H
|
||||
#define OBJECT_HANDLER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/io.h>
|
||||
|
||||
#include "null.h"
|
||||
|
||||
#include "../object.h"
|
||||
#include "../../util/rectangle/rectangle.h"
|
||||
#include "../../util/vec2/vec2.h"
|
||||
|
||||
|
||||
// Contain up to OBJECT_COUNT objects.
|
||||
// Provide the basic functionality to access, search and modify
|
||||
// these objects.
|
||||
// For ease of use, there are some convenience global variables for accessing
|
||||
// objects that are very commonly accessed.
|
||||
|
||||
#define OBJECT_COUNT 10
|
||||
#define BACKGROUND_INDEX 0
|
||||
#define SPACESHIP_INDEX 1
|
||||
#define CHARACTER_INDEX 2
|
||||
|
||||
#define spaceshipObject (objects + SPACESHIP_INDEX)
|
||||
#define character (objects + CHARACTER_INDEX)
|
||||
|
||||
// The actual container
|
||||
Object objects[OBJECT_COUNT];
|
||||
|
||||
// may return NULL
|
||||
Object* getFirstOfType(Prototype const* type);
|
||||
// may return NULL
|
||||
#define getEmptyObjectSpace() getFirstOfType(NULL)
|
||||
|
||||
// Return the number of objects with a prototype being type
|
||||
uint8_t getCountOf(Prototype const* type);
|
||||
|
||||
// Return a reference to a random object intersecting boundingBox and having
|
||||
// a prototype of type
|
||||
Object* getIntersectingObjectOfType(Rectangle boundingBox, Prototype const* type);
|
||||
|
||||
// Call the tick method of every object
|
||||
// objects might respond to the elapsed time
|
||||
void tickObjects(uint8_t previousFrameTime);
|
||||
|
||||
// Call the draw method of every object
|
||||
void drawObjects(Rectangle window);
|
||||
|
||||
// Delete the object given by its address from objects
|
||||
// It achieves this by setting the object's prototype to NULL.
|
||||
void clearObject(Object* object);
|
||||
|
||||
// Delete every object inside of objects
|
||||
// and create (or load) the starting objects
|
||||
void initializeObjectContainer();
|
||||
|
||||
#endif
|
||||
22
christmas/AdAstra/src/objects/prototype.c
Normal file
22
christmas/AdAstra/src/objects/prototype.c
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "prototype.h"
|
||||
|
||||
|
||||
Prototype temp;
|
||||
|
||||
static void loadPrototype(Prototype* prototype) {
|
||||
/*for (uint8_t i = 0; i < sizeof(prototype); i++) {
|
||||
((uint8_t*)(&temp))[i] = pgm_read_byte(prototype + i);
|
||||
}*/
|
||||
|
||||
temp = *prototype;
|
||||
}
|
||||
|
||||
void tickObjectFromPrototype(Object* object) {
|
||||
loadPrototype(object->prototype);
|
||||
temp.tick(object);
|
||||
}
|
||||
|
||||
void drawObjectFromPrototype(Object* object) {
|
||||
/*loadPrototype(object->prototype);
|
||||
temp.draw(object);*/
|
||||
}
|
||||
28
christmas/AdAstra/src/objects/prototype.h
Normal file
28
christmas/AdAstra/src/objects/prototype.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef PROTOTYPE_H
|
||||
#define PROTOTYPE_H
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
#include "../util/rectangle/rectangle.h"
|
||||
|
||||
|
||||
// See more information in object.h
|
||||
|
||||
struct _object_t;
|
||||
typedef struct _object_t Object;
|
||||
|
||||
// Update the inner state of the given object
|
||||
// The first argument is the object itself, the second is the elapsed
|
||||
// time in milliseconds.
|
||||
typedef void (*TickMethod)(Object*, uint8_t);
|
||||
|
||||
// Draw the given object if its overlapping with the given rectangle
|
||||
typedef void (*DrawMethod)(Object*, Rectangle);
|
||||
|
||||
typedef struct {
|
||||
TickMethod tick;
|
||||
DrawMethod draw;
|
||||
Vec2 size;
|
||||
} Prototype;
|
||||
|
||||
#endif
|
||||
51
christmas/AdAstra/src/objects/types/asteroid/asteroid.c
Normal file
51
christmas/AdAstra/src/objects/types/asteroid/asteroid.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include "asteroid.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "../sprites.h"
|
||||
#include "../../object.h"
|
||||
#include "../../object_container/object_container.h"
|
||||
#include "../../../util/vec2/vec2.h"
|
||||
#include "../../../util/random/random.h"
|
||||
#include "../../../driver/display/display.h"
|
||||
|
||||
|
||||
bool mineAsteroid(Object* asteroid) {
|
||||
return ++asteroid->as.asteroid.animationFrame == IDLE_FRAME_COUNT;
|
||||
}
|
||||
|
||||
static void tick(Object* asteroid, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||
if (asteroid->as.asteroid.animationFrame < IDLE_FRAME_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (++asteroid->as.asteroid.timeSinceLastFrameChange == EXPLODING_FRAME_CHANGE_INTERVAL) {
|
||||
if (++asteroid->as.asteroid.animationFrame >= IDLE_FRAME_COUNT + EXPLODING_FRAME_COUNT) {
|
||||
clearObject(asteroid);
|
||||
} else {
|
||||
asteroid->as.asteroid.timeSinceLastFrameChange = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isAsteroidIntersectingWithSpaceship(Object* asteroid, Object* spaceship) {
|
||||
Rectangle bb = getBoundingBox(asteroid);
|
||||
startIntersectionTest(bb);
|
||||
drawObject(asteroid, bb);
|
||||
drawObject(spaceship, bb);
|
||||
return endIntersectionTest();
|
||||
}
|
||||
|
||||
static void draw(Object* asteroid, Rectangle __attribute__((unused)) compositingWindow) {
|
||||
drawBitmapFromProgMem(
|
||||
getBoundingBox(asteroid),
|
||||
small_asteroid[asteroid->as.asteroid.animationFrame],
|
||||
asteroid->position.x % 2
|
||||
);
|
||||
}
|
||||
|
||||
const Prototype Asteroid PROGMEM = {
|
||||
.tick = tick,
|
||||
.draw = draw,
|
||||
.size = ASTEROID_SIZE,
|
||||
};
|
||||
25
christmas/AdAstra/src/objects/types/asteroid/asteroid.h
Normal file
25
christmas/AdAstra/src/objects/types/asteroid/asteroid.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef ASTEROID_H
|
||||
#define ASTEROID_H
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "../../prototype.h"
|
||||
|
||||
#define ASTEROID_SIZE ((Vec2){8, 8})
|
||||
#define IDLE_FRAME_COUNT 4
|
||||
#define EXPLODING_FRAME_COUNT 3
|
||||
#define EXPLODING_FRAME_CHANGE_INTERVAL 3
|
||||
|
||||
const Prototype Asteroid;
|
||||
|
||||
bool mineAsteroid(Object* asteroid);
|
||||
bool isAsteroidIntersectingWithSpaceship(Object* asteroid, Object* spaceship);
|
||||
|
||||
struct _asteroid_t {
|
||||
bool isMirrored;
|
||||
uint8_t timeSinceLastFrameChange;
|
||||
uint8_t animationFrame;
|
||||
};
|
||||
|
||||
#endif
|
||||
137
christmas/AdAstra/src/objects/types/astronaut/astronaut.c
Normal file
137
christmas/AdAstra/src/objects/types/astronaut/astronaut.c
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "astronaut.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "bitwise.h"
|
||||
#include "../../../driver/display/display.h"
|
||||
#include "../../object_container/object_container.h"
|
||||
#include "../sprites.h"
|
||||
#include "../../../util/vec2/vec2.h"
|
||||
#include "../../../util/random/random.h"
|
||||
#include "../../object.h"
|
||||
|
||||
|
||||
#define IS_MIRRORED_BIT 0
|
||||
#define IS_CONTROLLING_SPACESHIP_BIT 1
|
||||
#define WAS_DOING_ACTION_BIT 2
|
||||
|
||||
static inline bool getIsMirrored(Object* astronaut) {
|
||||
return getBit(astronaut->as.astronaut.flags, IS_MIRRORED_BIT);
|
||||
}
|
||||
|
||||
static inline void setIsMirrored(Object* astronaut, bool value) {
|
||||
modifyBit(astronaut->as.astronaut.flags, IS_MIRRORED_BIT, value);
|
||||
}
|
||||
|
||||
static inline bool getWasDoingAction(Object* astronaut) {
|
||||
return getBit(astronaut->as.astronaut.flags, WAS_DOING_ACTION_BIT);
|
||||
}
|
||||
|
||||
static inline void setWasDoingAction(Object* astronaut, bool value) {
|
||||
modifyBit(astronaut->as.astronaut.flags, WAS_DOING_ACTION_BIT, value);
|
||||
}
|
||||
|
||||
|
||||
bool getIsControllingSpaceship(Object* astronaut) {
|
||||
return getBit(astronaut->as.astronaut.flags, IS_CONTROLLING_SPACESHIP_BIT);
|
||||
}
|
||||
|
||||
static void setIsControllingSpaceship(Object* astronaut, bool value) {
|
||||
modifyBit(astronaut->as.astronaut.flags, IS_CONTROLLING_SPACESHIP_BIT, value);
|
||||
}
|
||||
|
||||
static inline void applyGravity(Object* astronaut) {
|
||||
if (!isOnLadder(getBoundingBox(astronaut)) && !isBottomOnFloor(getBoundingBox(astronaut))) {
|
||||
move(astronaut, directions[south]);
|
||||
}
|
||||
}
|
||||
|
||||
static void tick(Object* astronaut, uint8_t previousFrameTime) {
|
||||
if (astronaut->as.astronaut.timeSinceLastAction < TIME_BETWEEN_ACTION_CHANGE) {
|
||||
astronaut->as.astronaut.timeSinceLastAction += previousFrameTime;
|
||||
}
|
||||
|
||||
applyGravity(astronaut);
|
||||
}
|
||||
|
||||
void moveAstronaut(Object* astronaut, Vec2 direction) {
|
||||
if (
|
||||
astronaut->as.astronaut.timeSinceLastAction < TIME_BETWEEN_ACTION_CHANGE
|
||||
&& !getWasDoingAction(astronaut)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
astronaut->as.astronaut.timeSinceLastAction = 0;
|
||||
setWasDoingAction(astronaut, false);
|
||||
|
||||
if (getIsControllingSpaceship(astronaut)) {
|
||||
moveSpaceship((Vec2){direction.x, 0});
|
||||
moveSpaceship((Vec2){0, direction.y});
|
||||
} else {
|
||||
Vec2 proposedPosition = add(astronaut->position, direction);
|
||||
Rectangle proposedBoundingBox = (Rectangle){proposedPosition, getSize(astronaut)};
|
||||
if (isOnboard(proposedBoundingBox)) {
|
||||
astronaut->position = proposedPosition;
|
||||
astronaut->as.astronaut.animationFrame = (astronaut->as.astronaut.animationFrame + 1) % MOVE_FRAME_COUNT;
|
||||
}
|
||||
|
||||
if (direction.x == 0) {
|
||||
astronaut->as.astronaut.animationFrame = 0;
|
||||
}
|
||||
setIsMirrored(astronaut, direction.x < 0);
|
||||
}
|
||||
}
|
||||
|
||||
void makeAstronautDoAction(Object* astronaut) {
|
||||
if (
|
||||
astronaut->as.astronaut.timeSinceLastAction < TIME_BETWEEN_ACTION_CHANGE
|
||||
&& getWasDoingAction(astronaut)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
astronaut->as.astronaut.timeSinceLastAction = 0;
|
||||
setWasDoingAction(astronaut, true);
|
||||
|
||||
if (getIsControllingSpaceship(astronaut)) {
|
||||
setIsControllingSpaceship(astronaut, false);
|
||||
} else {
|
||||
Object* text;
|
||||
switch (getPossibleActionFromSpaceship(astronaut)) {
|
||||
case shootTurret:
|
||||
shootTurretOfSpaceship();
|
||||
break;
|
||||
case showLove:
|
||||
text = getEmptyObjectSpace();
|
||||
if (text != NULL) {
|
||||
if (!createText(text)) {
|
||||
speedUpText();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case repairingSpaceship:
|
||||
if (spaceshipObject->as.spaceship.healthLoss > 0) {
|
||||
spaceshipObject->as.spaceship.healthLoss--;
|
||||
}
|
||||
break;
|
||||
case controllingSpaceship:
|
||||
setIsControllingSpaceship(astronaut, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(Object* astronaut, __attribute__((unused)) Rectangle compositingWindow) {
|
||||
drawBitmapFromProgMem(
|
||||
getBoundingBox(astronaut),
|
||||
small_character_moving[astronaut->as.astronaut.animationFrame],
|
||||
getIsMirrored(astronaut)
|
||||
);
|
||||
}
|
||||
|
||||
const Prototype Astronaut PROGMEM = {
|
||||
.tick = tick,
|
||||
.draw = draw,
|
||||
.size = ASTRONAUT_SIZE
|
||||
};
|
||||
37
christmas/AdAstra/src/objects/types/astronaut/astronaut.h
Normal file
37
christmas/AdAstra/src/objects/types/astronaut/astronaut.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef ASTRONAUT_H
|
||||
#define ASTRONAUT_H
|
||||
|
||||
#include "../../prototype.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#define ASTRONAUT_SIZE ((Vec2){5, 5})
|
||||
#define MOVE_FRAME_COUNT 4
|
||||
|
||||
// Between two consecutive actions (or movements)
|
||||
// there has to be at least this many milliseconds
|
||||
#define TIME_BETWEEN_ACTION_CHANGE 50
|
||||
|
||||
typedef enum {
|
||||
noAction = 0,
|
||||
controllingSpaceship,
|
||||
shootTurret,
|
||||
showLove,
|
||||
repairingSpaceship,
|
||||
ACTION_COUNT
|
||||
} Action;
|
||||
|
||||
|
||||
const Prototype Astronaut;
|
||||
|
||||
struct _astronaut_t {
|
||||
uint8_t flags;
|
||||
uint8_t animationFrame;
|
||||
uint8_t timeSinceLastAction;
|
||||
};
|
||||
|
||||
void moveAstronaut(Object* astronaut, Vec2 unitVector);
|
||||
void makeAstronautDoAction(Object* astronaut);
|
||||
bool getIsControllingSpaceship(Object* astronaut);
|
||||
|
||||
#endif
|
||||
71
christmas/AdAstra/src/objects/types/background/background.c
Normal file
71
christmas/AdAstra/src/objects/types/background/background.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#include "background.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "../../object.h"
|
||||
#include "../../../util/rectangle/rectangle.h"
|
||||
#include "../../../util/random/random.h"
|
||||
#include "../sprites.h"
|
||||
#include "../../../driver/display/display.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
Vec2 position;
|
||||
uint8_t type;
|
||||
} Star;
|
||||
|
||||
static Star backgroundStars[STAR_COUNT];
|
||||
|
||||
static Star createStarOnTheRight() {
|
||||
return (Star){
|
||||
(Vec2){DISPLAY_WIDTH_IN_PIXELS, getRandomNumber() % (DISPLAY_HEIGHT_IN_PIXELS - STAR_SIZE)},
|
||||
getRandomNumber() % STAR_SHAPE_COUNT
|
||||
};
|
||||
}
|
||||
|
||||
static void tick(Object* background, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||
background->as.background.movementState++;
|
||||
for (uint8_t i = 0; i < STAR_COUNT; i++) {
|
||||
switch (i % 3) {
|
||||
case 0:
|
||||
backgroundStars[i].position.x -= 1;
|
||||
break;
|
||||
case 1:
|
||||
backgroundStars[i].position.x -= background->as.background.movementState & 1;
|
||||
break;
|
||||
case 2:
|
||||
backgroundStars[i].position.x -= ~background->as.background.movementState & 1;
|
||||
break;
|
||||
}
|
||||
if (backgroundStars[i].position.x == -STAR_SIZE) {
|
||||
backgroundStars[i] = createStarOnTheRight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(__attribute__((unused)) Object* background, Rectangle compositingWindow) {
|
||||
for (uint8_t i = 0; i < STAR_COUNT; i++) {
|
||||
Rectangle starBoundingBox = (Rectangle){backgroundStars[i].position, (Vec2){STAR_SIZE, STAR_SIZE}};
|
||||
if (areIntersecting(compositingWindow, starBoundingBox)) {
|
||||
drawBitmapFromProgMem(
|
||||
starBoundingBox,
|
||||
stars[backgroundStars[i].type],
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initializeBackground() {
|
||||
for (uint8_t i = 0; i < STAR_COUNT; i++) {
|
||||
backgroundStars[i] = createStarOnTheRight();
|
||||
backgroundStars[i].position.x = getRandomNumber();
|
||||
}
|
||||
}
|
||||
|
||||
const Prototype Background PROGMEM = {
|
||||
.tick = tick,
|
||||
.draw = draw,
|
||||
.size = (Vec2){DISPLAY_WIDTH_IN_PIXELS, DISPLAY_HEIGHT_IN_PIXELS} // == WINDOW.size
|
||||
};
|
||||
18
christmas/AdAstra/src/objects/types/background/background.h
Normal file
18
christmas/AdAstra/src/objects/types/background/background.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef BACKGROUND_H
|
||||
#define BACKGROUND_H
|
||||
|
||||
#include "../../prototype.h"
|
||||
|
||||
|
||||
#define STAR_COUNT 8
|
||||
#define STAR_SIZE 3
|
||||
#define STAR_SHAPE_COUNT 3
|
||||
|
||||
const Prototype Background;
|
||||
struct _background_t {
|
||||
uint8_t movementState;
|
||||
};
|
||||
|
||||
void initializeBackground();
|
||||
|
||||
#endif
|
||||
40
christmas/AdAstra/src/objects/types/bullet/bullet.c
Normal file
40
christmas/AdAstra/src/objects/types/bullet/bullet.c
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#include "bullet.h"
|
||||
|
||||
#include "../asteroid/asteroid.h"
|
||||
#include "../spaceship/spaceship.h"
|
||||
#include "../../object.h"
|
||||
#include "../../object_container/object_container.h"
|
||||
#include "null.h"
|
||||
#include "../../../driver/display/display.h"
|
||||
|
||||
|
||||
static void tick(Object* bullet, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||
if (bullet->as.bullet.wereIntersectingInThePreviousFrame) {
|
||||
clearObject(bullet);
|
||||
return;
|
||||
}
|
||||
|
||||
move(bullet, directions[east]);
|
||||
|
||||
Object* asteroid = getIntersectingObjectOfType(getBoundingBox(bullet), &Asteroid);
|
||||
if (asteroid != NULL) {
|
||||
if (mineAsteroid(asteroid) || mineAsteroid(asteroid)) {
|
||||
onAsteroidMined();
|
||||
}
|
||||
bullet->as.bullet.wereIntersectingInThePreviousFrame = true;
|
||||
}
|
||||
|
||||
if (!areIntersecting(getBoundingBox(bullet), WINDOW)) {
|
||||
bullet->as.bullet.wereIntersectingInThePreviousFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(Object* bullet, Rectangle __attribute__((unused)) compositingWindow) {
|
||||
drawFilledRectangle((Rectangle){bullet->position, BULLET_SIZE}, 0, 0xFF);
|
||||
}
|
||||
|
||||
const Prototype Bullet PROGMEM = {
|
||||
.tick = tick,
|
||||
.draw = draw,
|
||||
.size = BULLET_SIZE,
|
||||
};
|
||||
16
christmas/AdAstra/src/objects/types/bullet/bullet.h
Normal file
16
christmas/AdAstra/src/objects/types/bullet/bullet.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef BULLET_H
|
||||
#define BULLET_H
|
||||
|
||||
#include "../../prototype.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#define BULLET_SIZE ((Vec2){5, 1})
|
||||
|
||||
|
||||
const Prototype Bullet;
|
||||
struct _bullet_t {
|
||||
bool wereIntersectingInThePreviousFrame;
|
||||
};
|
||||
|
||||
#endif
|
||||
225
christmas/AdAstra/src/objects/types/spaceship/spaceship.c
Normal file
225
christmas/AdAstra/src/objects/types/spaceship/spaceship.c
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include "spaceship.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "../../object.h"
|
||||
#include "../../../driver/display/display.h"
|
||||
#include "../astronaut/astronaut.h"
|
||||
#include "../asteroid/asteroid.h"
|
||||
#include "../../object_container/object_container.h"
|
||||
#include "../sprites.h"
|
||||
#include "../../../util/vec2/vec2.h"
|
||||
#include "../../../util/random/random.h"
|
||||
#include "bitwise.h"
|
||||
|
||||
|
||||
static uint8_t flickerState;
|
||||
|
||||
SpaceshipPart spaceshipParts[SPACESHIP_PART_COUNT] = {
|
||||
[TABLE_INDEX] = (SpaceshipPart) {
|
||||
{{7, 6}, {5, 5}},
|
||||
tree[0],
|
||||
showLove,
|
||||
false
|
||||
},
|
||||
[BEDS_INDEX] = (SpaceshipPart) {
|
||||
{{3, 12}, {8, 6}},
|
||||
beds[0],
|
||||
repairingSpaceship,
|
||||
false
|
||||
},
|
||||
[COMMAND_PANEL_INDEX] = (SpaceshipPart) {
|
||||
{{26, 7}, {7, 4}},
|
||||
NULL,
|
||||
controllingSpaceship,
|
||||
true,
|
||||
},
|
||||
[TURRET_CONTROLLER_INDEX] = (SpaceshipPart) {
|
||||
{{26, 12}, {7, 6}},
|
||||
turret_controller[0],
|
||||
shootTurret,
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool isOnUpperFloor(Rectangle boundingBox) {
|
||||
return isInside(boundingBox, translateRectangle(UPPER_FLOOR_BOUNDING_BOX, spaceshipObject->position));
|
||||
}
|
||||
|
||||
bool isOnLowerFloor(Rectangle boundingBox) {
|
||||
return isInside(boundingBox, translateRectangle(LOWER_FLOOR_BOUNDING_BOX, spaceshipObject->position));
|
||||
}
|
||||
|
||||
bool isBottomOnFloor(Rectangle boundingBox) {
|
||||
return (
|
||||
add(spaceshipObject->position, UPPER_FLOOR_BOUNDING_BOX.position).y + UPPER_FLOOR_BOUNDING_BOX.size.y == boundingBox.position.y + boundingBox.size.y ||
|
||||
add(spaceshipObject->position, LOWER_FLOOR_BOUNDING_BOX.position).y + LOWER_FLOOR_BOUNDING_BOX.size.y == boundingBox.position.y + boundingBox.size.y
|
||||
);
|
||||
}
|
||||
|
||||
bool isOnLadder(Rectangle boundingBox) {
|
||||
return areIntersecting(boundingBox, translateRectangle(LADDER_BOUNDING_BOX, spaceshipObject->position));
|
||||
}
|
||||
|
||||
bool isOnboard(Rectangle boundingBox) {
|
||||
return isOnLowerFloor(boundingBox) || isOnUpperFloor(boundingBox) || isOnLadder(boundingBox);
|
||||
}
|
||||
|
||||
Rectangle getBoundingBoxOfSpaceshipPart(SpaceshipPart* part) {
|
||||
return translateRectangle(part->boundingBox, spaceshipObject->position);
|
||||
}
|
||||
|
||||
void shootTurretOfSpaceship() {
|
||||
Object* bullet = getEmptyObjectSpace();
|
||||
if (getEmptyObjectSpace() != NULL && spaceshipObject->as.spaceship.healthLoss < MAX_HEALTH - 1) {
|
||||
createObject(&Bullet, bullet);
|
||||
bullet->position = add(TURRET_POSITION, spaceshipObject->position);
|
||||
spaceshipObject->as.spaceship.healthLoss++;
|
||||
}
|
||||
}
|
||||
|
||||
void onAsteroidMined() {
|
||||
switch (++spaceshipObject->as.spaceship.progress) {
|
||||
case hasBeds:
|
||||
setBit(spaceshipObject->as.spaceship.activatedParts, BEDS_INDEX);
|
||||
break;
|
||||
case hasTurret:
|
||||
setBit(spaceshipObject->as.spaceship.activatedParts, TURRET_CONTROLLER_INDEX);
|
||||
break;
|
||||
case hasTable:
|
||||
setBit(spaceshipObject->as.spaceship.activatedParts, TABLE_INDEX);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void moveSpaceship(Vec2 direction) {
|
||||
Vec2 proposedPosition = add(spaceshipObject->position, direction);
|
||||
|
||||
if (!isInside(translateRectangle(IN_VIEW_BOUNDING_BOX, proposedPosition), WINDOW)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
if (objects[i].prototype == &Astronaut) {
|
||||
move(objects + i, direction);
|
||||
}
|
||||
}
|
||||
|
||||
move(spaceshipObject, direction);
|
||||
|
||||
spaceshipObject->position = proposedPosition;
|
||||
|
||||
for (uint8_t i = 0; i < OBJECT_COUNT; i++) {
|
||||
if (objects[i].prototype == &Asteroid && isAsteroidIntersectingWithSpaceship(objects + i, spaceshipObject)) {
|
||||
if (mineAsteroid(objects + i)) {
|
||||
spaceshipObject->as.spaceship.healthLoss += 2;
|
||||
onAsteroidMined();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Action getPossibleActionFromSpaceship(Object* astronaut) {
|
||||
for (uint8_t i = 0; i < SPACESHIP_PART_COUNT; i++) {
|
||||
SpaceshipPart* part = spaceshipParts + i;
|
||||
if (
|
||||
isSpaceshipPartActivated(part) && areIntersecting(getBoundingBoxOfSpaceshipPart(part), getBoundingBox(astronaut))) {
|
||||
return part->possibleAction;
|
||||
}
|
||||
}
|
||||
|
||||
return noAction;
|
||||
}
|
||||
|
||||
void tick(Object* spaceship, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||
flickerState = !flickerState;
|
||||
if (spaceship->as.spaceship.healthLoss >= MAX_HEALTH) {
|
||||
spaceship->as.spaceship.healthLoss++;
|
||||
}
|
||||
}
|
||||
|
||||
bool isSpaceshipPartActivated(SpaceshipPart* part) {
|
||||
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() {
|
||||
uint8_t actualBarLength = spaceshipObject->as.spaceship.healthLoss * BAR_LENGTH / MAX_HEALTH;
|
||||
drawFilledRectangle(
|
||||
(Rectangle){add(
|
||||
spaceshipObject->position,
|
||||
(Vec2){BAR_END_POSITION.x - actualBarLength, BAR_END_POSITION.y}
|
||||
), (Vec2){actualBarLength, 1}}, 0xFF, 0x00
|
||||
);
|
||||
}
|
||||
|
||||
static inline void drawSpaceshipParts(Rectangle compositingWindow) {
|
||||
for (uint8_t i = 0; i < SPACESHIP_PART_COUNT; i++) {
|
||||
if (
|
||||
!(spaceshipParts + i)->alwaysActiveDoNotDraw &&
|
||||
isSpaceshipPartActivated(spaceshipParts + i) &&
|
||||
areIntersecting(compositingWindow, getBoundingBoxOfSpaceshipPart(spaceshipParts + i))
|
||||
) {
|
||||
drawBitmapFromProgMem(
|
||||
getBoundingBoxOfSpaceshipPart(spaceshipParts + i),
|
||||
spaceshipParts[i].sprite,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void drawExhaust(Rectangle compositingWindow) {
|
||||
Rectangle exhaustRectangle = translateRectangle(EXHAUST_BOUNDING_BOX, spaceshipObject->position);
|
||||
if (
|
||||
areIntersecting(compositingWindow, exhaustRectangle) &&
|
||||
flickerState
|
||||
) {
|
||||
drawBitmapFromProgMem(exhaustRectangle, exhaust[0], false);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void drawGlitches() {
|
||||
for (uint8_t i = 0; i < spaceshipObject->as.spaceship.healthLoss - MAX_HEALTH; i++) {
|
||||
Rectangle r = translateRectangle(
|
||||
(Rectangle){(Vec2){getRandomNumber() % SPACESHIP_SIZE.x, getRandomNumber() % SPACESHIP_SIZE.y}, (Vec2){8, 8}},
|
||||
spaceshipObject->position
|
||||
);
|
||||
if (areIntersecting(r, WINDOW)) {
|
||||
drawFilledRectangle(r, 0xFF, 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(Object* spaceship, Rectangle compositingWindow) {
|
||||
drawBitmapFromProgMem(
|
||||
getBoundingBox(spaceship),
|
||||
spaceship_idle[0],
|
||||
false
|
||||
);
|
||||
|
||||
drawSpaceshipParts(compositingWindow);
|
||||
drawSpaceshipHealthBar();
|
||||
|
||||
if (spaceship->as.spaceship.healthLoss > MAX_HEALTH) {
|
||||
drawGlitches();
|
||||
} else {
|
||||
drawExhaust(compositingWindow);
|
||||
}
|
||||
}
|
||||
|
||||
const Prototype Spaceship PROGMEM = {
|
||||
.tick = tick,
|
||||
.draw = draw,
|
||||
.size = SPACESHIP_SIZE,
|
||||
};
|
||||
75
christmas/AdAstra/src/objects/types/spaceship/spaceship.h
Normal file
75
christmas/AdAstra/src/objects/types/spaceship/spaceship.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef SPACESHIP_H
|
||||
#define SPACESHIP_H
|
||||
|
||||
|
||||
#include "../../prototype.h"
|
||||
#include "../../../util/rectangle/rectangle.h"
|
||||
#include "../astronaut/astronaut.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SPACESHIP_SIZE ((Vec2){36, 23})
|
||||
|
||||
#define IN_VIEW_BOUNDING_BOX ((Rectangle){(Vec2){7, 4}, (Vec2){22, 15}})
|
||||
#define UPPER_FLOOR_BOUNDING_BOX ((Rectangle){(Vec2){8, 5}, (Vec2){19, 6}})
|
||||
#define LOWER_FLOOR_BOUNDING_BOX ((Rectangle){(Vec2){5, 12}, (Vec2){23, 6}})
|
||||
#define EXHAUST_BOUNDING_BOX ((Rectangle){(Vec2){-4, 9}, (Vec2){5, 5}})
|
||||
|
||||
#define TURRET_POSITION ((Vec2){35, 11})
|
||||
|
||||
#define LADDER_BOUNDING_BOX ((Rectangle){(Vec2){12, 10}, (Vec2){1, 4}})
|
||||
|
||||
#define BOBBING_INTERVAL 130
|
||||
#define SPACESHIP_PART_COUNT 4
|
||||
|
||||
#define TABLE_INDEX 3
|
||||
#define BEDS_INDEX 2
|
||||
#define COMMAND_PANEL_INDEX 1
|
||||
#define TURRET_CONTROLLER_INDEX 0
|
||||
|
||||
#define BAR_END_POSITION ((Vec2){33, 11})
|
||||
#define BAR_LENGTH 4
|
||||
#define MAX_HEALTH 8
|
||||
|
||||
typedef struct {
|
||||
Rectangle boundingBox;
|
||||
uint16_t** sprite;
|
||||
Action possibleAction;
|
||||
bool alwaysActiveDoNotDraw;
|
||||
} SpaceshipPart;
|
||||
|
||||
SpaceshipPart spaceshipParts[SPACESHIP_PART_COUNT];
|
||||
|
||||
const Prototype Spaceship;
|
||||
|
||||
typedef enum {
|
||||
hasBeds = 2,
|
||||
hasTurret = 5,
|
||||
hasHalfCrew = 8,
|
||||
hasTable = 12
|
||||
} Progress;
|
||||
|
||||
struct _spaceship_t {
|
||||
uint8_t healthLoss;
|
||||
uint8_t progress;
|
||||
uint8_t activatedParts;
|
||||
};
|
||||
|
||||
bool isOnboard(Rectangle boundingBox);
|
||||
void moveSpaceship(Vec2 direction);
|
||||
Rectangle getBoundingBoxOfSpaceshipPart(SpaceshipPart* part);
|
||||
|
||||
bool isBottomOnFloor(Rectangle boundingBox);
|
||||
bool isOnUpperFloor(Rectangle boundingBox);
|
||||
bool isOnLowerFloor(Rectangle boundingBox);
|
||||
bool isOnLadder(Rectangle boundingBox);
|
||||
void onAsteroidMined();
|
||||
bool isSpaceshipPartActivated(SpaceshipPart* part);
|
||||
|
||||
bool isSpaceshipDestroyed();
|
||||
void destroySpaceship();
|
||||
|
||||
void shootTurretOfSpaceship();
|
||||
Action getPossibleActionFromSpaceship(Object* astronaut);
|
||||
|
||||
#endif
|
||||
16
christmas/AdAstra/src/objects/types/sprites.c
Normal file
16
christmas/AdAstra/src/objects/types/sprites.c
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#include "sprites.h"
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
|
||||
// AUTO-GENERATED
|
||||
|
||||
const uint16_t beds[1][8][1] PROGMEM = {{{0x707},{0xc0c},{0x2424},{0x2424},{0x2424},{0x2424},{0x2424},{0x2424}}};
|
||||
const uint16_t exhaust[1][5][1] PROGMEM = {{{0x404},{0xe0e},{0xe0e},{0x1f1f},{0x404}}};
|
||||
const uint16_t font[50][4][1] PROGMEM = {{{0xff78},{0xff14},{0xff14},{0xff78}},{{0xff78},{0xff14},{0xff14},{0xff79}},{{0xff7c},{0xff54},{0xff54},{0xff28}},{{0xff38},{0xff44},{0xff44},{0xff28}},{{0xff7c},{0xff44},{0xff44},{0xff38}},{{0xff7c},{0xff54},{0xff54},{0xff44}},{{0xff7c},{0xff54},{0xff55},{0xff44}},{{0xff7c},{0xff14},{0xff14},{0xff04}},{{0xff38},{0xff44},{0xff54},{0xff30}},{{0xff7c},{0xff10},{0xff10},{0xff7c}},{{0xff44},{0xff7c},{0xff44},{0xff44}},{{0xff44},{0xff7c},{0xff45},{0xff44}},{{0xff20},{0xff40},{0xff40},{0xff3c}},{{0xff7c},{0xff10},{0xff28},{0xff44}},{{0xff7c},{0xff40},{0xff40},{0xff40}},{{0xff7c},{0xff0c},{0xff1c},{0xff7c}},{{0xff7c},{0xff08},{0xff30},{0xff7c}},{{0xff38},{0xff44},{0xff44},{0xff38}},{{0xff38},{0xff44},{0xff45},{0xff38}},{{0xff39},{0xff44},{0xff44},{0xff39}},{{0xff39},{0xff45},{0xff45},{0xff39}},{{0xff7c},{0xff14},{0xff14},{0xff08}},{{0xff18},{0xff24},{0xff24},{0xff58}},{{0xff7c},{0xff14},{0xff14},{0xff68}},{{0xff48},{0xff54},{0xff54},{0xff24}},{{0xff04},{0xff7c},{0xff04},{0xff04}},{{0xff7c},{0xff40},{0xff40},{0xff7c}},{{0xff7c},{0xff40},{0xff41},{0xff7c}},{{0xff7d},{0xff40},{0xff40},{0xff7d}},{{0xff7d},{0xff41},{0xff41},{0xff7d}},{{0xff3c},{0xff40},{0xff40},{0xff3c}},{{0xff7c},{0xff60},{0xff70},{0xff7c}},{{0xff44},{0xff38},{0xff38},{0xff44}},{{0xff1c},{0xff70},{0xff10},{0xff1c}},{{0xff64},{0xff54},{0xff54},{0xff4c}},{{0xff7c},{0xff44},{0xff44},{0xff7c}},{{0xff08},{0xff44},{0xff7c},{0xff40}},{{0xff4c},{0xff64},{0xff54},{0xff4c}},{{0xff44},{0xff54},{0xff54},{0xff7c}},{{0xff3c},{0xff20},{0xff70},{0xff20}},{{0xff5c},{0xff54},{0xff54},{0xff24}},{{0xff7c},{0xff54},{0xff54},{0xff74}},{{0xff04},{0xff64},{0xff14},{0xff0c}},{{0xff7c},{0xff54},{0xff54},{0xff7c}},{{0xff5c},{0xff54},{0xff54},{0xff7c}},{{0xff00},{0xffc0},{0xff00},{0xff00}},{{0xff40},{0xff00},{0xff00},{0xff00}},{{0xff5c},{0xff00},{0xff00},{0xff00}},{{0xff08},{0xffa4},{0xff14},{0xff08}},{{0xff00},{0xff00},{0xff00},{0xff00}}};
|
||||
const uint16_t small_asteroid[7][8][1] PROGMEM = {{{0x1c1c},{0x7e7e},{0xfef2},{0xfffb},{0xffff},{0xffdf},{0x7e7e},{0x3c3c}},{{0x0},{0x3c3c},{0x7e72},{0x7e7a},{0x7e7e},{0x7e5e},{0x3c3c},{0x0}},{{0x0},{0x0},{0x3030},{0x7878},{0x7c7c},{0x7c5c},{0x3838},{0x0}},{{0x0},{0x0},{0x3030},{0x3838},{0x3838},{0x1010},{0x0},{0x0}},{{0x0},{0x3030},{0x4848},{0x4444},{0x4444},{0x2828},{0x1010},{0x0}},{{0x2828},{0x8484},{0x8080},{0x0},{0x8282},{0x4444},{0x2828},{0x0}},{{0x202},{0x0},{0x0},{0x0},{0x0},{0x0},{0x8181},{0x4242}}};
|
||||
const uint16_t small_character_moving[4][5][1] PROGMEM = {{{0x606},{0x1f1d},{0xf0b},{0x1f1d},{0x606}},{{0x606},{0x1f1d},{0xf0b},{0xf0d},{0x1616}},{{0x606},{0x1f1d},{0xf0b},{0x1f1d},{0x606}},{{0x606},{0xf0d},{0x1f1b},{0x1f1d},{0x606}}};
|
||||
const uint16_t spaceship_idle[1][36][3] PROGMEM = {{{0x404,0x1414,0x1010},{0x404,0x3e3e,0x1010},{0xceca,0xffff,0x3828},{0xee6a,0xff8a,0x3929},{0xff3b,0xff09,0x7f6f},{0xff91,0xff08,0x7f44},{0xff51,0xff08,0x7f44},{0xff31,0xff08,0x7f44},{0xff1f,0xff08,0x7f7c},{0xf111,0xff08,0x4744},{0xf010,0xff08,0x704},{0xf010,0xfff8,0x707},{0xf010,0xff28,0x705},{0xf010,0xfff8,0x707},{0xf010,0xff08,0x704},{0xf010,0xff08,0x704},{0xf010,0xff08,0x704},{0xf010,0xff08,0x704},{0xf414,0xff08,0x1714},{0xf414,0xff08,0x1714},{0xfc1c,0xff08,0x1f1c},{0xfc1c,0xff08,0x1f1c},{0xf818,0xff08,0xf0c},{0xf010,0xff08,0x704},{0xf010,0xff08,0x704},{0xf010,0xff08,0x704},{0xf010,0xff0c,0x704},{0xf010,0xff0a,0x704},{0xf0b0,0xff1f,0x706},{0xe020,0xff1c,0x302},{0xe060,0xff1c,0x303},{0xc0c0,0xff9c,0x101},{0x8080,0xffdd,0x0},{0x0,0x7f7f,0x0},{0x0,0x1c1c,0x0},{0x0,0x808,0x0}}};
|
||||
const uint16_t stars[3][3][1] PROGMEM = {{{0x202},{0x707},{0x202}},{{0x505},{0x202},{0x505}},{{0x0},{0x303},{0x303}}};
|
||||
const uint16_t tree[1][5][1] PROGMEM = {{{0x808},{0xa0a},{0x1515},{0xa0a},{0x808}}};
|
||||
const uint16_t turret_controller[1][7][1] PROGMEM = {{{0x3030},{0x808},{0xf0f},{0x101},{0x101},{0x101},{0x101}}};
|
||||
19
christmas/AdAstra/src/objects/types/sprites.h
Normal file
19
christmas/AdAstra/src/objects/types/sprites.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef SPRITES_H
|
||||
#define SPRITES_H
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
|
||||
// AUTO-GENERATED
|
||||
|
||||
const uint16_t beds[1][8][1];
|
||||
const uint16_t exhaust[1][5][1];
|
||||
const uint16_t font[50][4][1];
|
||||
const uint16_t small_asteroid[7][8][1];
|
||||
const uint16_t small_character_moving[4][5][1];
|
||||
const uint16_t spaceship_idle[1][36][3];
|
||||
const uint16_t stars[3][3][1];
|
||||
const uint16_t tree[1][5][1];
|
||||
const uint16_t turret_controller[1][7][1];
|
||||
|
||||
#endif
|
||||
114
christmas/AdAstra/src/objects/types/text/text.c
Normal file
114
christmas/AdAstra/src/objects/types/text/text.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include "text.h"
|
||||
|
||||
#include <avr/eeprom.h>
|
||||
|
||||
#include "../asteroid/asteroid.h"
|
||||
#include "../spaceship/spaceship.h"
|
||||
#include "../sprites.h"
|
||||
#include "../../object.h"
|
||||
#include "../../object_container/object_container.h"
|
||||
#include "null.h"
|
||||
#include "math.h"
|
||||
#include "../../../driver/display/display.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#define STRING_TERMINATOR 255
|
||||
#define CHARACTER_WIDTH 4
|
||||
|
||||
static bool canCreateNext = true;
|
||||
static int8_t previousTextIndex = -1;
|
||||
static bool isSpedUp = false;
|
||||
|
||||
bool createText(Object* object) {
|
||||
if (!canCreateNext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
createObject(&Text, object);
|
||||
object->as.text.xPosition = DISPLAY_WIDTH_IN_PIXELS;
|
||||
object->as.text.hasSetCanCreate = false;
|
||||
object->position = (Vec2){0, 0};
|
||||
previousTextIndex++;
|
||||
isSpedUp = false;
|
||||
uint8_t textCount = 0;
|
||||
for (uint16_t i = 0; i < 512; i++) {
|
||||
if (textCount == previousTextIndex) {
|
||||
if (eeprom_read_byte(i) == STRING_TERMINATOR) {
|
||||
previousTextIndex = 0;
|
||||
object->as.text.textStartIndex = 0;
|
||||
} else {
|
||||
object->as.text.textStartIndex = i;
|
||||
}
|
||||
canCreateNext = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (eeprom_read_byte(i) == STRING_TERMINATOR) {
|
||||
textCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void speedUpText() {
|
||||
isSpedUp = true;
|
||||
}
|
||||
|
||||
static uint8_t getTextLength(Object* text) {
|
||||
for (uint8_t i = 0; i < 255; i++) {
|
||||
if (eeprom_read_byte(text->as.text.textStartIndex + i) == STRING_TERMINATOR) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tick(Object* text, __attribute__((unused)) uint8_t previousFrameTime) {
|
||||
if (++text->as.text.timeSinceLastShift > 2 || isSpedUp) {
|
||||
text->as.text.xPosition--;
|
||||
text->as.text.timeSinceLastShift = 0;
|
||||
}
|
||||
|
||||
if (
|
||||
!text->as.text.hasSetCanCreate
|
||||
&& getTextLength(text) * (CHARACTER_WIDTH + 1) + text->as.text.xPosition < DISPLAY_WIDTH_IN_PIXELS - CHARACTER_WIDTH - 1
|
||||
) {
|
||||
canCreateNext = true;
|
||||
text->as.text.hasSetCanCreate = true;
|
||||
}
|
||||
|
||||
if (getTextLength(text) * (CHARACTER_WIDTH + 1) < -text->as.text.xPosition) {
|
||||
clearObject(text);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(Object* text, Rectangle __attribute__((unused)) compositingWindow) {
|
||||
uint8_t indexOfFirstVisible = max(0, -text->as.text.xPosition / (CHARACTER_WIDTH + 1));
|
||||
for (uint8_t i = indexOfFirstVisible; i < indexOfFirstVisible + DISPLAY_WIDTH_IN_PIXELS / (CHARACTER_WIDTH + 1) + 2; i++) {
|
||||
uint8_t letter = eeprom_read_byte(text->as.text.textStartIndex + i);
|
||||
if (letter == STRING_TERMINATOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawBitmapFromProgMem(
|
||||
(Rectangle){(Vec2){text->as.text.xPosition + i * (CHARACTER_WIDTH + 1), 0}, (Vec2){4, 8}},
|
||||
font[letter],
|
||||
false
|
||||
);
|
||||
|
||||
drawFilledRectangle(
|
||||
(Rectangle){(Vec2){text->as.text.xPosition + i * (CHARACTER_WIDTH + 1) + CHARACTER_WIDTH, 0},
|
||||
(Vec2){1, 8}},
|
||||
0xFF,
|
||||
0x00
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Prototype Text PROGMEM = {
|
||||
.tick = tick,
|
||||
.draw = draw,
|
||||
.size = TEXT_SIZE,
|
||||
};
|
||||
21
christmas/AdAstra/src/objects/types/text/text.h
Normal file
21
christmas/AdAstra/src/objects/types/text/text.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef TEXT_H
|
||||
#define TEXT_H
|
||||
|
||||
#include "../../prototype.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#define TEXT_SIZE ((Vec2){64, 8})
|
||||
|
||||
const Prototype Text;
|
||||
struct _text_t {
|
||||
int16_t xPosition;
|
||||
uint16_t textStartIndex;
|
||||
uint8_t timeSinceLastShift;
|
||||
bool hasSetCanCreate;
|
||||
};
|
||||
|
||||
bool createText(Object* position);
|
||||
void speedUpText();
|
||||
|
||||
#endif
|
||||
8
christmas/AdAstra/src/objects/types/text/texts.c
Normal file
8
christmas/AdAstra/src/objects/types/text/texts.c
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#include "texts.h"
|
||||
|
||||
#include <avr/eeprom.h>
|
||||
|
||||
|
||||
// AUTO-GENERATED
|
||||
|
||||
const uint8_t texts[385] EEMEM = {24, 34, 10, 0, 45, 2, 0, 14, 1, 34, 24, 47, 255, 2, 17, 14, 4, 17, 8, 49, 13, 0, 23, 1, 3, 24, 17, 16, 33, 25, 49, 13, 11, 30, 1, 16, 17, 13, 47, 255, 30, 10, 24, 34, 17, 16, 25, 49, 15, 1, 24, 25, 49, 10, 24, 49, 24, 34, 5, 23, 5, 25, 16, 6, 13, 49, 15, 17, 16, 4, 0, 16, 10, 46, 255, 9, 0, 2, 1, 23, 49, 23, 19, 30, 10, 4, 5, 2, 2, 45, 4, 5, 49, 0, 16, 16, 1, 14, 49, 24, 29, 23, 29, 2, 2, 49, 30, 17, 14, 25, 49, 0, 34, 49, 5, 8, 33, 16, 33, 1, 23, 10, 49, 13, 0, 14, 0, 16, 4, 26, 16, 13, 46, 255, 19, 23, 19, 15, 49, 30, 17, 14, 25, 49, 25, 6, 8, 5, 4, 49, 15, 5, 8, 10, 24, 15, 5, 23, 16, 10, 46, 255, 0, 49, 13, 23, 5, 0, 25, 10, 30, 10, 25, 1, 24, 17, 4, 49, 6, 24, 49, 19, 24, 24, 34, 5, 24, 34, 5, 4, 5, 25, 25, 24, 6, 8, 5, 4, 49, 10, 16, 24, 21, 10, 23, 1, 14, 18, 46, 255, 25, 27, 14, 34, 1, 24, 49, 16, 6, 14, 13, 28, 14, 49, 24, 17, 13, 26, 16, 13, 49, 21, 6, 14, 4, 0, 13, 6, 21, 5, 49, 14, 5, 9, 5, 25, 16, 6, 14, 46, 255, 19, 24, 24, 34, 5, 24, 24, 6, 8, 6, 2, 5, 16, 45, 16, 6, 14, 13, 28, 14, 5, 4, 49, 13, 10, 3, 24, 10, 25, 49, 24, 34, 17, 15, 17, 23, 27, 2, 2, 49, 14, 5, 16, 16, 5, 49, 0, 34, 49, 6, 14, 5, 25, 46, 255, 23, 5, 15, 6, 14, 5, 15, 49, 5, 34, 25, 49, 25, 5, 49, 10, 24, 49, 25, 26, 4, 17, 4, 49, 15, 0, 8, 0, 4, 23, 18, 14, 46, 255, 30, 1, 23, 17, 15, 45, 9, 17, 8, 33, 49, 15, 10, 9, 0, 15, 0, 23, 0, 2, 2, 49, 25, 0, 14, 1, 14, 13, 17, 34, 34, 26, 16, 13, 46, 255, 0, 16, 4, 23, 10, 24, 255, 255, 255};
|
||||
11
christmas/AdAstra/src/objects/types/text/texts.h
Normal file
11
christmas/AdAstra/src/objects/types/text/texts.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef TEXTS_H
|
||||
#define TEXTS_H
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
|
||||
// AUTO-GENERATED
|
||||
|
||||
const uint8_t texts[385];
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue