Add christmas edition

This commit is contained in:
Andras Schmelczer 2026-06-06 15:23:48 +01:00
commit b62af486a7
74 changed files with 3422 additions and 1 deletions

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement">
<ProjectComponents />
</Store>

View file

@ -0,0 +1,396 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>7.0</ProjectVersion>
<ToolchainName>com.Atmel.AVRGCC8.C</ToolchainName>
<ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid>
<avrdevice>ATtiny85</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>C</Language>
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>TestProject</AssemblyName>
<Name>TestProject</Name>
<RootNamespace>TestProject</RootNamespace>
<ToolchainFlavour>Native</ToolchainFlavour>
<KeepTimersRunning>false</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>
<CacheFlash>false</CacheFlash>
<ProgFlashFromRam>true</ProgFlashFromRam>
<RamSnippetAddress>0x20000000</RamSnippetAddress>
<UncachedRange />
<preserveEEPROM>false</preserveEEPROM>
<OverrideVtorValue>exception_table</OverrideVtorValue>
<BootSegment>2</BootSegment>
<ResetRule>0</ResetRule>
<eraseonlaunchrule>0</eraseonlaunchrule>
<EraseKey />
<AsfFrameworkConfig>
<framework-data>
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.47.0" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<avrtool>com.atmel.avrdbg.tool.ispmk2</avrtool>
<avrtoolserialnumber>000200212345</avrtoolserialnumber>
<avrdeviceexpectedsignature>0x1E930B</avrdeviceexpectedsignature>
<avrtoolinterface>ISP</avrtoolinterface>
<avrtoolinterfaceclock>125000</avrtoolinterfaceclock>
<com_atmel_avrdbg_tool_ispmk2>
<ToolOptions>
<InterfaceProperties>
<IspClock>125000</IspClock>
</InterfaceProperties>
<InterfaceName>ISP</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.ispmk2</ToolType>
<ToolNumber>000200212345</ToolNumber>
<ToolName>AVRISP mkII</ToolName>
</com_atmel_avrdbg_tool_ispmk2>
<com_atmel_avrdbg_tool_simulator>
<ToolOptions xmlns="">
<InterfaceProperties>
</InterfaceProperties>
<InterfaceName>
</InterfaceName>
</ToolOptions>
<ToolType xmlns="">com.atmel.avrdbg.tool.simulator</ToolType>
<ToolNumber xmlns="">
</ToolNumber>
<ToolName xmlns="">Simulator</ToolName>
</com_atmel_avrdbg_tool_simulator>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGcc>
<avrgcc.common.Device>-mmcu=attiny85 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\gcc\dev\attiny85"</avrgcc.common.Device>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.SubroutinesFunctionPrologues>True</avrgcc.compiler.general.SubroutinesFunctionPrologues>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\include</Value>
<Value>../src/macros</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.compiler.warnings.ExtraWarnings>True</avrgcc.compiler.warnings.ExtraWarnings>
<avrgcc.compiler.warnings.Undefined>True</avrgcc.compiler.warnings.Undefined>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
<avrgcc.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\include</Value>
</ListValues>
</avrgcc.assembler.general.IncludePaths>
</AvrGcc>
</ToolchainSettings>
<OutputFileName>ad_astra</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGcc>
<avrgcc.common.Device>-mmcu=attiny85 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\gcc\dev\attiny85"</avrgcc.common.Device>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>DEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
<avrgcc.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\include</Value>
</ListValues>
</avrgcc.assembler.general.IncludePaths>
<avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
</AvrGcc>
</ToolchainSettings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'WithUART' ">
<ToolchainSettings>
<AvrGcc>
<avrgcc.common.Device>-mmcu=attiny85 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\gcc\dev\attiny85"</avrgcc.common.Device>
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
<Value>UART_ENABLED</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcc.compiler.warnings.ExtraWarnings>True</avrgcc.compiler.warnings.ExtraWarnings>
<avrgcc.compiler.warnings.Undefined>True</avrgcc.compiler.warnings.Undefined>
<avrgcc.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcc.linker.libraries.Libraries>
<avrgcc.assembler.general.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.229\include</Value>
</ListValues>
</avrgcc.assembler.general.IncludePaths>
</AvrGcc>
</ToolchainSettings>
<OutputFileName>ad_astra</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputPath>bin\WithUART\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="src\driver\sleep\sleep.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\hardware_access.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\hardware_access.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\output_pins\output_pins.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\output_pins\output_pins.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\power_saving\power_saving.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\spi\spi.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\spi\spi.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\timing\timing.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\hardware_access\timing\timing.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\driver\display\display.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\driver\display\display.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\driver\infra\infra.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\driver\infra\infra.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\driver\sleep\sleep.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\macros\bitwise.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\macros\math.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\macros\null.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\main.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\mediator\mediator.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\mediator\mediator.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\ai\ai.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\ai\ai.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\commands\commands.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\commands\commands.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\event_generator\event_generator.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\event_generator\event_generator.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\object.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\object.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\object_container\object_container.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\object_container\object_container.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\prototype.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\asteroid\asteroid.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\asteroid\asteroid.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\astronaut\astronaut.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\astronaut\astronaut.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\background\background.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\background\background.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\bullet\bullet.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\bullet\bullet.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\spaceship\spaceship.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\spaceship\spaceship.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\sprites.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\sprites.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\text\text.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\text\text.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\text\texts.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\objects\types\text\texts.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\util\random\random.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\util\random\random.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\util\rectangle\rectangle.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\util\rectangle\rectangle.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\util\vec2\vec2.c">
<SubType>compile</SubType>
</Compile>
<Compile Include="src\util\vec2\vec2.h">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="src" />
<Folder Include="src\driver\" />
<Folder Include="src\driver\display\" />
<Folder Include="src\driver\infra\" />
<Folder Include="src\driver\sleep\" />
<Folder Include="src\hardware_access\" />
<Folder Include="src\hardware_access\output_pins" />
<Folder Include="src\hardware_access\power_saving\" />
<Folder Include="src\hardware_access\spi\" />
<Folder Include="src\hardware_access\timing\" />
<Folder Include="src\macros\" />
<Folder Include="src\mediator\" />
<Folder Include="src\objects\" />
<Folder Include="src\objects\ai\" />
<Folder Include="src\objects\commands\" />
<Folder Include="src\objects\event_generator\" />
<Folder Include="src\objects\object_container\" />
<Folder Include="src\objects\types\" />
<Folder Include="src\objects\types\asteroid\" />
<Folder Include="src\objects\types\astronaut\" />
<Folder Include="src\objects\types\background\" />
<Folder Include="src\objects\types\bullet\" />
<Folder Include="src\objects\types\text" />
<Folder Include="src\objects\types\spaceship\" />
<Folder Include="src\util\" />
<Folder Include="src\util\random\" />
<Folder Include="src\util\rectangle\" />
<Folder Include="src\util\vec2\" />
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>

View file

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement">
<ProjectComponents>
<ProjectComponent z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<CApiVersion></CApiVersion>
<CBundle></CBundle>
<CClass>Device</CClass>
<CGroup>Startup</CGroup>
<CSub></CSub>
<CVariant></CVariant>
<CVendor>Atmel</CVendor>
<CVersion>1.3.0</CVersion>
<DefaultRepoPath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs</DefaultRepoPath>
<DependentComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<Description></Description>
<Files xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.229\include</AbsolutePath>
<Attribute></Attribute>
<Category>include</Category>
<Condition>C</Condition>
<FileContentHash i:nil="true" />
<FileVersion></FileVersion>
<Name>include</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.229\include\avr\iotn85.h</AbsolutePath>
<Attribute></Attribute>
<Category>header</Category>
<Condition>C</Condition>
<FileContentHash>RcYmivGpgsCGGCzeWAIjcA==</FileContentHash>
<FileVersion></FileVersion>
<Name>include/avr/iotn85.h</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.229\templates\main.c</AbsolutePath>
<Attribute>template</Attribute>
<Category>source</Category>
<Condition>C Exe</Condition>
<FileContentHash>9ptzGqB00V1zM0TC00KMag==</FileContentHash>
<FileVersion></FileVersion>
<Name>templates/main.c</Name>
<SelectString>Main file (.c)</SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.229\templates\main.cpp</AbsolutePath>
<Attribute>template</Attribute>
<Category>source</Category>
<Condition>C Exe</Condition>
<FileContentHash>YXFphlh0CtZJU+ebktABgQ==</FileContentHash>
<FileVersion></FileVersion>
<Name>templates/main.cpp</Name>
<SelectString>Main file (.cpp)</SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
<d4p1:anyType i:type="FileInfo">
<AbsolutePath>C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.229\gcc\dev\attiny85</AbsolutePath>
<Attribute></Attribute>
<Category>libraryPrefix</Category>
<Condition>GCC</Condition>
<FileContentHash i:nil="true" />
<FileVersion></FileVersion>
<Name>gcc/dev/attiny85</Name>
<SelectString></SelectString>
<SourcePath></SourcePath>
</d4p1:anyType>
</Files>
<PackName>ATtiny_DFP</PackName>
<PackPath>C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATtiny_DFP/1.3.229/Atmel.ATtiny_DFP.pdsc</PackPath>
<PackVersion>1.3.229</PackVersion>
<PresentInProject>true</PresentInProject>
<ReferenceConditionId>ATtiny85</ReferenceConditionId>
<RteComponents xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:string></d4p1:string>
</RteComponents>
<Status>Resolved</Status>
<VersionMode>Fixed</VersionMode>
<IsComponentInAtProject>true</IsComponentInAtProject>
</ProjectComponent>
</ProjectComponents>
</Store>

View file

@ -0,0 +1,137 @@
#include "display.h"
#include <avr/pgmspace.h>
#include "bitwise.h"
#include "math.h"
#include "../../hardware_access/hardware_access.h"
static uint8_t const configuration[] PROGMEM = {
0xD5, 0xF0, // set clock frequency
0x8D, 0x14, // enable charge pump
0x20, 0x00, // horizontal addressing mode
0xD6, 0x01, // 2 times vertical zoom
0x22, 0x00, 0x03, // only draw to the top half of the screen
0xAF // display on
};
static struct {
uint8_t compositingBuffer[DISPLAY_WIDTH_IN_PIXELS];
Rectangle compositingWindow;
DrawFunction drawEverything;
bool wasIntersection;
} display;
static inline void commandMode() {
setOutputPin(DISPLAY_DC_OUTPUT_PIN, false);
}
static inline void dataMode() {
setOutputPin(DISPLAY_DC_OUTPUT_PIN, true);
}
void setDisplayContrast(uint8_t value) {
commandMode();
sendByteOnSPI(0x81);
sendByteOnSPI(value);
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
dataMode();
}
void initializeDisplay(DrawFunction drawEverything) {
setOutputPin(DISPLAY_RESET_OUTPUT_PIN, false);
for (volatile uint8_t i = 0; i != 255; i++)
;
// some time has to elapse before the next line gets called,
// otherwise the display wont turn on
setOutputPin(DISPLAY_RESET_OUTPUT_PIN, true);
for (uint8_t i = 0; i < sizeof(configuration); i++) {
sendByteOnSPI(pgm_read_byte(configuration + i));
}
display.drawEverything = drawEverything;
setDisplayContrast(255);
dataMode();
}
void startIntersectionTest(Rectangle compositingWindow) {
display.wasIntersection = false;
display.compositingWindow = compositingWindow;
}
bool endIntersectionTest() {
for (uint8_t x = 0; x < DISPLAY_WIDTH_IN_PIXELS; x++) {
display.compositingBuffer[x] = 0;
}
return display.wasIntersection;
}
void drawFrame() {
display.compositingWindow = DEFAULT_COMPOSITING_WINDOW;
for (display.compositingWindow.position.y = 0; display.compositingWindow.position.y < DISPLAY_HEIGHT_IN_PIXELS; display.compositingWindow.position.y += 8) {
display.drawEverything(display.compositingWindow);
for (uint8_t x = 0; x < DISPLAY_WIDTH_IN_PIXELS; x++) {
sendByteOnSPI(display.compositingBuffer[x]);
sendByteOnSPI(display.compositingBuffer[x]);
display.compositingBuffer[x] = 0;
}
}
}
static void compositePixelColumn(uint8_t x, uint8_t invertedMask, uint8_t fill) {
if (display.compositingBuffer[x] & fill) {
display.wasIntersection = true;
}
display.compositingBuffer[x] = (display.compositingBuffer[x] & (~invertedMask)) | fill;
}
void drawBitmapFromProgMem(Rectangle boundingBox, uint16_t const bitmap[boundingBox.size.x][(boundingBox.size.y + 7) / 8], bool isMirrored) {
boundingBox.position = substract(boundingBox.position, display.compositingWindow.position);
uint8_t spriteY = max(0, -boundingBox.position.y);
uint8_t spriteYByte = spriteY >> 3;
for (uint8_t x = max(0, -boundingBox.position.x); x < boundingBox.size.x && boundingBox.position.x + x < DISPLAY_WIDTH_IN_PIXELS; x++) {
uint8_t spriteX = isMirrored ? boundingBox.size.x - x - 1 : x;
uint16_t currentPixelColumn = pgm_read_word(&bitmap[spriteX][spriteYByte]);
uint8_t fill, invertedMask;
if (boundingBox.position.y >= 0) {
fill = (currentPixelColumn & 0x00FF) << boundingBox.position.y;
invertedMask = currentPixelColumn >> 8 << boundingBox.position.y;
} else {
uint16_t lowerPixelColumn = spriteYByte + 1 < (boundingBox.size.y + 7) / 8 ? pgm_read_word(&bitmap[spriteX][spriteYByte + 1]) : 0;
uint8_t shift = spriteY % 8;
uint8_t inverseShift = 8 - shift;
fill = ((currentPixelColumn & 0x00FF) >> shift) | ((lowerPixelColumn & 0x0FF) << inverseShift);
invertedMask = (currentPixelColumn >> 8 >> shift) | (lowerPixelColumn >> 8 << inverseShift);
}
compositePixelColumn(boundingBox.position.x + x, invertedMask, fill);
}
}
void drawFilledRectangle(Rectangle box, uint8_t invertedMask, uint8_t fill) {
box.position = substract(box.position, display.compositingWindow.position);
uint8_t upperGapHeight = min(8, max(0, box.position.y));
uint8_t lowerGapHeight = min(8, max(0, 8 - (box.position.y + box.size.y)));
uint8_t actualFill = (fill >> lowerGapHeight) & (fill << upperGapHeight);
uint8_t actualInvertedMask = (invertedMask >> lowerGapHeight) & (invertedMask << upperGapHeight);
for (uint8_t x = max(0, box.position.x); x < box.position.x + box.size.x && x < DISPLAY_WIDTH_IN_PIXELS; x++) {
compositePixelColumn(x, actualInvertedMask, actualFill);
}
}

View file

@ -0,0 +1,74 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <stdbool.h>
#include <avr/io.h>
#include "../../util/rectangle/rectangle.h"
/*
SPI driver for D096-12864
*/
#define DISPLAY_RESET_OUTPUT_PIN PB0
#define DISPLAY_DC_OUTPUT_PIN PB4
// A two-times downscaling is used for greater performance
#define DISPLAY_WIDTH_IN_PIXELS 64
#define DISPLAY_HEIGHT_IN_PIXELS 32
// To easily access the window size
#define WINDOW ((Rectangle){(Vec2){0, 0}, (Vec2){DISPLAY_WIDTH_IN_PIXELS, DISPLAY_HEIGHT_IN_PIXELS}})
// To conserve RAM, drawing is done in chunks of DEFAULT_COMPOSITING_WINDOW size
// instead of buffering the whole display and writing it out at once
#define DEFAULT_COMPOSITING_WINDOW ((Rectangle){(Vec2){0, 0}, (Vec2){DISPLAY_WIDTH_IN_PIXELS, 8}})
typedef void (*DrawFunction)(Rectangle);
// Call DrawFunction n times after a call to drawFrame has been made
void initializeDisplay(DrawFunction drawEverything);
// Set a the brightness of the display
// Value can be any number between 0 and 255.
void setDisplayContrast(uint8_t value);
// To conserve program memory, pixel based intersection test
// is implemented here.
// Calling draw functions after calling startIntersectionTest
// will set a wasIntersection bit appropriately.
// Clear buffer and wasIntersection bit
void startIntersectionTest();
// Make the display go to / return from sleep
void turnDisplayOnOff(bool shouldBeOn);
// Return wasIntersection bit
bool endIntersectionTest();
// Initiate a draw sequence
void drawFrame();
// Draw a sprite of size boundingBox.size at boundingBox.position from bitmap
// if isMirrored then mirror around the vertical axis
// Bitmap's data is interpreted the following way:
/*
Each 16 bit word corresponds to an 8 pixel high column.
(These columns are laid out horizontally from left to right. Unfortunately,
the display uses this addressing mode.) The higher 8 bits of the word is the
inverted mask and the lower 8 bits are the fill bits.
newPixelColumn = oldPixelColumn & ~invertedMask | fill;
This seemingly weird layout is used to take advantage of SIMD operations
and speed up the drawing process significantly.
*/
void drawBitmapFromProgMem(Rectangle boundingBox, uint16_t const bitmap[boundingBox.size.x][(boundingBox.size.y + 7) / 8], bool isMirrored);
// Draw a one byte repeated texture covering the parameter box
// for a white rectangle use these arguments: invertedMask: don't care, fill: 0xFF
// for a black rectangle use these arguments: invertedMask: 0xFF, fill: 0x00
void drawFilledRectangle(Rectangle box, uint8_t invertedMask, uint8_t fill);
#endif

View file

@ -0,0 +1,144 @@
#include "infra.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include "bitwise.h"
#include "../../hardware_access/hardware_access.h"
// (0.5625 + (0.5625 + 1.6875) / 2) / 1000 / timer interval
#define MEAN_OF_0_1_BIT_TIMES 53
// 9 / 2 / 1000 / timer interval
#define MAYBE_ONE_CHECK_TIME 141
// some large value
#define TIMEOUT 254
typedef enum {
idle, maybeLeadingOne, leadingOneConfirmed, activeFirstBit, active, timingOut
} ProtocolState;
typedef enum {
noMatch, foundStartingByte, significantByte, waitingForEndOfCommand, waitingForRepeat
} CommandState;
static struct {
uint8_t current;
uint8_t bitPosition;
ProtocolState protocolState;
CommandState commandState;
OnCommandReceived onCommandReceived;
} infra;
static void saveCurrentByte() {
uint8_t byte = infra.current;
infra.current = 0;
infra.bitPosition = 0;
if (byte == INFRA_ADDRESS) {
infra.commandState = foundStartingByte;
return;
}
switch (infra.commandState) {
case foundStartingByte:
infra.commandState = significantByte;
break;
case significantByte:
infra.onCommandReceived(byte);
infra.commandState = waitingForEndOfCommand;
break;
case waitingForEndOfCommand:
if (byte == 0) {
infra.commandState = waitingForRepeat;
}
break;
case waitingForRepeat:
if (byte == 0) {
infra.onCommandReceived(REPEAT_CODE);
}
break;
default:
break;
}
}
static void saveBit(uint8_t bit) {
infra.current <<= 1;
infra.current |= bit;
if (++infra.bitPosition == 8) {
saveCurrentByte();
}
}
static inline uint8_t isIrOff() {
return getBit(PINB, IR_PIN);
}
static inline uint8_t isIrOn() {
return !isIrOff();
}
ISR(PCINT0_vect) {
switch (infra.protocolState) {
case idle:
if (isIrOn()) {
enableTimerB(MAYBE_ONE_CHECK_TIME);
infra.protocolState = maybeLeadingOne;
}
break;
case maybeLeadingOne:
infra.protocolState = idle;
break;
case leadingOneConfirmed:
if (isIrOff()) {
infra.protocolState = activeFirstBit;
}
break;
case activeFirstBit:
case timingOut:
if (isIrOn()) {
infra.protocolState = active;
enableTimerB(MEAN_OF_0_1_BIT_TIMES);
}
break;
case active:
if (isIrOn()) {
saveBit(1);
enableTimerB(MEAN_OF_0_1_BIT_TIMES);
}
break;
}
}
ISR(TIM0_COMPB_vect) {
switch (infra.protocolState) {
case maybeLeadingOne:
if (isIrOn()) {
infra.protocolState = leadingOneConfirmed;
}
disableTimerB();
break;
case active:
saveBit(0);
infra.protocolState = timingOut;
enableTimerB(TIMEOUT);
break;
case timingOut:
infra.protocolState = idle;
saveCurrentByte();
disableTimerB();
break;
default:
disableTimerB();
break;
}
}
void initializeInfra(OnCommandReceived onCommandReceived) {
setBit(PORTB, IR_PIN); // enable pull-up
setBit(PCMSK, IR_PIN); // specific pin change interrupt enable
setBit(GIMSK, PCIE); // global on pin change interrupt enable;
infra.onCommandReceived = onCommandReceived;
}

View file

@ -0,0 +1,24 @@
#ifndef INFRA_H
#define INFRA_H
#include <stdbool.h>
#include <avr/io.h>
/*
Custom NEC implementation using a TSOP4838
*/
#define INFRA_ADDRESS 255
#define IR_PIN PB3
#define REPEAT_CODE 1
typedef void (*OnCommandReceived)(uint8_t);
typedef void (*OnReceiveStarted)();
// Initialize infra and call onCommandReceived with every received byte
// Call onCommandReceived with the argument REPEAT_CODE if a repeat code
// has been received.
void initializeInfra(OnCommandReceived onCommandReceived);
#endif

View file

@ -0,0 +1,120 @@
#include "redundant_storage.h"
#include "../../hardware_access/hardware_access.h"
#define offsetof(type, member) __builtin_offsetof (type, member)
#define OBJECT_VALIDITY_MASK (0b00111111)
#define IS_USING_PAGE_A_BIT 6
#define IS_VALID_BIT 7
#define METADATA_REDUNDANCY 4
typedef struct {
uint8_t redundantBufferA[REDUNDANT_BUFFER_SIZE];
uint8_t redundantBufferB[REDUNDANT_BUFFER_SIZE];
uint8_t metadata[METADATA_REDUNDANCY];
} MemoryLayout;
static volatile struct {
MemoryLayout buffer;
uint8_t saveIndex;
uint8_t* savePointer;
uint8_t loadIndex;
uint8_t* loadPointer;
uint8_t validMetadata;
bool isEEPROMWriting;
} storage;
static inline bool isUsingPageA() {
return getBit(storage.validMetadata, IS_USING_PAGE_A_BIT);
}
bool isValid() {
return getBit(storage.validMetadata, IS_VALID_BIT);
}
static inline void fixMetadata() {
uint8_t a = storage.buffer.metadata[0];
uint8_t b = storage.buffer.metadata[1];
uint8_t c = storage.buffer.metadata[2];
uint8_t d = storage.buffer.metadata[3];
if (a == b || a == c || a == d) {
storage.validMetadata = a;
} else if (b == c || b == d) {
storage.validMetadata = b;
} else {
storage.validMetadata = c;
}
}
void initializeRedundantStorage() {
for (uint8_t i = 0; i < sizeof(MemoryLayout); i++) {
((uint8_t*)&storage.buffer)[i] = loadSavedByteEEPROM(i);
}
fixMetadata();
}
void invalidateEEPROM() {
for (uint8_t i = 0; i < sizeof(MemoryLayout); i++) {
((uint8_t*)&storage.buffer)[i] = 0;
}
storage.validMetadata = 0;
}
void onEEPROMWriteFinished(__attribute__((unused)) uint8_t* _) {
storage.isEEPROMWriting = false;
}
bool startSchedulingObjectsForSaving() {
if (storage.isEEPROMWriting) {
return false;
}
toggleBit(storage.validMetadata, IS_USING_PAGE_A_BIT);
storage.validMetadata &= ~OBJECT_VALIDITY_MASK;
storage.saveIndex = 0;
storage.savePointer = isUsingPageA() ?
storage.buffer.redundantBufferA
: storage.buffer.redundantBufferB;
return true;
}
void scheduleNextObjectForSave(uint8_t* object, uint8_t size) {
setBit(storage.validMetadata, storage.saveIndex);
storage.saveIndex++;
for (uint8_t i = 0; i < size; i++) {
*storage.savePointer = object[i];
storage.savePointer++;
}
}
void saveScheduledObjects() {
setBit(storage.validMetadata, IS_VALID_BIT);
for (uint8_t i = 0; i < METADATA_REDUNDANCY; i++) {
storage.buffer.metadata[i] = storage.validMetadata;
}
storage.isEEPROMWriting = true;
asyncWriteEEPROM((uint8_t*)&storage.buffer, onEEPROMWriteFinished);
}
bool loadNextObject(uint8_t* holder, uint8_t size) {
if (storage.loadIndex++ == 0) {
storage.loadPointer = isUsingPageA() ?
storage.buffer.redundantBufferA
: storage.buffer.redundantBufferB;
}
if (getBit(storage.validMetadata, storage.loadIndex - 1)) {
for (uint8_t i = 0; i < size; i++) {
holder[i] = *storage.loadPointer++;
}
return true;
}
return false;
}

View file

@ -0,0 +1,22 @@
#ifndef REDUNDANT_STORAGE_H
#define REDUNDANT_STORAGE_H
#include <stdbool.h>
#include <avr/io.h>
#define REDUNDANT_BUFFER_SIZE 24
void initializeRedundantStorage();
bool isValid();
void invalidateEEPROM();
bool startSchedulingObjectsForSaving();
void scheduleNextObjectForSave(uint8_t* object, uint8_t size);
void saveScheduledObjects();
// Returns is loaded object valid
bool loadNextObject(uint8_t* holder, uint8_t size);
#endif

View file

@ -0,0 +1,39 @@
#include "sleep.h"
#include <stdbool.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "bitwise.h"
#include "../../hardware_access/hardware_access.h"
#define TICKS_IN_MILISECOND 31
volatile int8_t milisecondsSinceFrameStart;
void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds) {
sleep_enable();
uint8_t previousFrameTime = 0;
while (function(previousFrameTime)) {
previousFrameTime = milisecondsSinceFrameStart;
while (milisecondsSinceFrameStart < frameLengthInMilliseconds) {
clearBit(MCUCR, SM1); // idle mode
sleep_cpu();
}
milisecondsSinceFrameStart = 0;
}
}
void powerOff() {
setBit(MCUCR, SM1); // power-down mode
sleep_cpu();
}
ISR(TIM0_COMPA_vect) {
milisecondsSinceFrameStart++;
enableTimerA(TICKS_IN_MILISECOND);
}

View file

@ -0,0 +1,16 @@
#ifndef SLEEP_H
#define SLEEP_H
#include <stdbool.h>
#include <avr/io.h>
// FrameFunction gets previousFrameTime (in milliseconds) as argument
typedef bool (*FrameFunction)(uint8_t);
// Shut down the machine
void powerOff();
// Call function every frameLengthInMilliseconds while it returns true
void startFrameLoop(FrameFunction function, uint8_t frameLengthInMilliseconds);
#endif

View file

@ -0,0 +1,75 @@
#include "eeprom.h"
#include <stdbool.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include "bitwise.h"
uint8_t memory[STORAGE_SIZE] EEMEM;
static volatile struct {
uint8_t* buffer;
uint8_t positon;
bool isWriting;
OnEEPROMFinished onFinished;
} eeprom;
uint8_t loadByteEEPROM(uint8_t* address) {
while(getBit(EECR, EEPE)) {}
EEAR = (uint16_t)address;
setBit(EECR, EERE);
return EEDR;
}
uint8_t loadSavedByteEEPROM(uint8_t address) {
return loadByteEEPROM(memory + address);
}
uint16_t loadWordEEPROM(uint16_t const* address) {
uint16_t high = ((uint16_t)loadByteEEPROM((uint8_t*)address + 1)) << 8;
uint16_t low = (uint16_t)loadByteEEPROM((uint8_t*)address);
return high | low;
}
static inline void saveByteEEPROM(uint8_t* address, uint8_t byte) {
if (byte != loadByteEEPROM(address)) {
EEDR = byte;
setBit(EECR, EEMPE);
setBit(EECR, EEPE);
}
}
void enableWritingEEPROM() {
modifyBit(EECR, EERIE, eeprom.isWriting);
}
void disableWritingEEPROM() {
clearBit(EECR, EERIE); // clear eeprom ready interrupt enable
}
bool asyncWriteEEPROM(uint8_t* buffer, OnEEPROMFinished onFinished) {
if (eeprom.isWriting) {
return false;
}
eeprom.buffer = buffer;
eeprom.onFinished = onFinished;
eeprom.positon = 0;
eeprom.isWriting = true;
setBit(EECR, EERIE); // set eeprom ready interrupt
return true;
}
ISR(EE_RDY_vect) {
saveByteEEPROM(memory + eeprom.positon, eeprom.buffer[eeprom.positon]);
if (++eeprom.positon == STORAGE_SIZE) {
clearBit(EECR, EERIE);
eeprom.isWriting = false;
eeprom.onFinished(eeprom.buffer);
}
}

View file

@ -0,0 +1,15 @@
#ifndef EEPROM_H
#define EEPROM_H
#include <avr/io.h>
#define STORAGE_SIZE 52
typedef void (*OnEEPROMFinished)(uint8_t*);
inline void initializeEEPROM() {
EECR = 0; // atomic write
}
#endif

View file

@ -0,0 +1,12 @@
#include "hardware_access.h"
#include <avr/interrupt.h>
void initializeHardwareAccess() {
sei();
initializePowerSaving();
initializeSPI();
initializeTiming();
initializeOutputPins();
}

View file

@ -0,0 +1,39 @@
#ifndef HARDWARE_ACCESS_H
#define HARDWARE_ACCESS_H
#include <stdbool.h>
#include <avr/io.h>
#include "bitwise.h"
#include "power_saving/power_saving.h"
#include "spi/spi.h"
#include "timing/timing.h"
#include "output_pins/output_pins.h"
/*
This module contains the lowest level functions to manipulate the hardware.
You only have to include this header file which serves as a facade.
The sub-modules' implementation can be freely changed as long as they
still implement these functions.
*/
// Initialize every hardware element at once
void initializeHardwareAccess();
// Enable interrupt OCCRA for TIMER0 with a modulo of triggerInterruptInXTicks
void enableTimerA(uint8_t triggerInterruptInXTicks);
// Enable interrupt OCCRB for TIMER0B with a modulo of triggerInterruptInXTicks
void enableTimerB(uint8_t triggerInterruptInXTicks);
void disableTimerB();
// Send a single byte on the built-in SPI interface
// The transfer is done in a serial manner to achieve
// greater throughput.
void sendByteOnSPI(uint8_t byte);
// Set the value of an output pin
void setOutputPin(uint8_t id, bool value);
#endif

View file

@ -0,0 +1,11 @@
#include "output_pins.h"
#include "bitwise.h"
#include <avr/io.h>
#include <stdbool.h>
void setOutputPin(uint8_t id, bool value) {
setBit(DDRB, id);
modifyBit(PORTB, id, value);
}

View file

@ -0,0 +1,7 @@
#ifndef GPIO_OUTPUT_PINS_H
#define GPIO_OUTPUT_PINS_H
inline void initializeOutputPins() {}
#endif

View file

@ -0,0 +1,13 @@
#ifndef POWER_SAVING_H
#define POWER_SAVING_H
#include <avr/io.h>
#include "bitwise.h"
inline void initializePowerSaving() {
setBit(ACSR, ACD); // disable ADC to save power
PRR = BV(PRTIM1) | BV(PRADC); // disable power to timer1 and ADC
}
#endif

View file

@ -0,0 +1,30 @@
#include "spi.h"
#include <avr/interrupt.h>
#define SWAP_BIT (BV(USIWM0) | BV(USICLK) | BV(USITC) | BV(USICS1))
void sendByteOnSPI(uint8_t byte) {
USIDR = byte;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
USICR = SWAP_BIT;
}

View file

@ -0,0 +1,15 @@
#ifndef SPI_H
#define SPI_H
#include <avr/io.h>
#include "bitwise.h"
#define DO_PIN PB1
#define USCK_PIN PB2
inline void initializeSPI() {
DDRB |= BV(DO_PIN) | BV(USCK_PIN); // set pin directions for MOSI and SCK
}
#endif

View file

@ -0,0 +1,17 @@
#include "timing.h"
#include "bitwise.h"
void enableTimerA(uint8_t triggerInterruptInXTicks) {
OCR0A = TCNT0 + triggerInterruptInXTicks;
}
void enableTimerB(uint8_t triggerInterruptInXTicks) {
setBit(TIFR, OCF0B);
OCR0B = TCNT0 + triggerInterruptInXTicks;
setBit(TIMSK, OCIE0B);
}
void disableTimerB() {
clearBit(TIMSK, OCIE0B);
}

View file

@ -0,0 +1,13 @@
#ifndef TIMING_H
#define TIMING_H
#include <avr/io.h>
#include "bitwise.h"
inline void initializeTiming() {
TCCR0B = BV(CS02); // CLK / 256
setBit(TIMSK, OCIE0A);
}
#endif

View file

@ -0,0 +1,11 @@
#ifndef BITWISE_H
#define BITWISE_H
#define BV(x) (1 << (x))
#define modifyBit(P, B, V) ((P) = ((P) & ~BV(B)) | ((V) << B))
#define setBit(P, B) ((P) |= BV(B))
#define clearBit(P, B) ((P) &= ~BV(B))
#define toggleBit(P, B) ((P) ^= BV(B))
#define getBit(P, B) (((P) & BV(B)) >> (B))
#endif

View file

@ -0,0 +1,9 @@
#ifndef MATH_H
#define MATH_H
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) > (b) ? (b) : (a))
#define abs(a, b) ((a) > 0 ? (a) : (-a))
#endif

View file

@ -0,0 +1,3 @@
#ifndef NULL
#define NULL ((void*)0)
#endif

View file

@ -0,0 +1,14 @@
#include "mediator/mediator.h"
// Stemming from the module oriented nature of the project
// there is a module responsible for setting up and orchestrating
// the other modules.
//
// From the main function we only have to instruct the mediator to
// do its job.
int main(void) {
setupConnections();
startGame();
}

View file

@ -0,0 +1,74 @@
#include "mediator.h"
#include <stdbool.h>
#include "../hardware_access/hardware_access.h"
#include "../objects/object_container/object_container.h"
#include "../objects/event_generator/event_generator.h"
#include "../objects/commands/commands.h"
#include "../objects/ai/ai.h"
#include "../driver/display/display.h"
#include "../driver/infra/infra.h"
#include "../driver/sleep/sleep.h"
#define TARGET_FRAME_DURATION 20 // ms
#define DEATH_SCREEN_LENGTH 50 // frames
#define SAVE_INTERVAL 50 // every x frames
static struct {
uint8_t contrast;
uint8_t framesSinceLastSave;
uint8_t deathDownCounter;
uint8_t receivedWakeUpBitCount;
} state = {
.contrast = 255
};
static inline bool handleDeathAnimation() {
if (isSpaceshipDestroyed()) {
setDisplayContrast(state.contrast * (state.deathDownCounter / DEATH_SCREEN_LENGTH));
if (state.deathDownCounter-- == 0) {
return false;
};
}
return true;
}
static bool frameFunction(uint8_t previousFrameTime) {
tickObjects(previousFrameTime);
handleCommands();
handleAI();
generateEvents();
drawFrame();
return handleDeathAnimation();
}
static inline void initializeGame() {
state.deathDownCounter = DEATH_SCREEN_LENGTH;
setDisplayContrast(state.contrast);
initializeBackground();
initializeObjectContainer();
}
void setupConnections() {
initializeHardwareAccess();
initializeInfra(addCommand);
initializeDisplay(drawObjects);
}
void startGame() {
while (true) {
initializeGame();
startFrameLoop(frameFunction, TARGET_FRAME_DURATION);
}
}
void changeDisplayContrast(int8_t by) {
if (by < 0) {
state.contrast = (state.contrast < -by) ? 0 : (state.contrast + by);
} else {
state.contrast = (state.contrast > 255 - by) ? 255 : (state.contrast + by);
}
setDisplayContrast(state.contrast);
}

View file

@ -0,0 +1,23 @@
#ifndef MEDIATOR_H
#define MEDIATOR_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();
// Start drawing frames and ticking objects
void startGame();
// Make the machine go to sleep
void handleOff();
// 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);
#endif

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

View 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

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

View 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

View file

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

View file

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

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

View 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

View file

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

View file

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

View 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);*/
}

View 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

View 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,
};

View 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

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

View 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

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

View 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

View 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,
};

View 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

View 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,
};

View 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

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

View 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

View 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,
};

View 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

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

View file

@ -0,0 +1,11 @@
#ifndef TEXTS_H
#define TEXTS_H
#include <avr/io.h>
// AUTO-GENERATED
const uint8_t texts[385];
#endif

View file

@ -0,0 +1,14 @@
#include "random.h"
static uint16_t currentValue = SEED;
static uint16_t getRandom16bitNumberModuloPrime() {
uint16_t bit = currentValue ^ (currentValue >> 2) ^ (currentValue >> 3) ^ (currentValue >> 5);
currentValue = (currentValue >> 1) | (bit << 15);
return currentValue % 1031;
}
uint8_t getRandomNumber() {
return (uint8_t)(getRandom16bitNumberModuloPrime() ^ getRandom16bitNumberModuloPrime());
}

View file

@ -0,0 +1,14 @@
#ifndef RANDOM_H
#define RANDOM_H
#include <avr/io.h>
// Mustn't be zero, should be lower than 65535
#define SEED 42
// Simple LFSR with some improvements to enhance distribution
// while maintaining short execution time
uint8_t getRandomNumber();
#endif

View file

@ -0,0 +1,34 @@
#include "rectangle.h"
bool areIntersecting(Rectangle r1, Rectangle r2) {
return (
r1.position.x < r2.position.x + r2.size.x &&
r1.position.x + r1.size.x > r2.position.x &&
r1.position.y < r2.position.y + r2.size.y &&
r1.position.y + r1.size.y > r2.position.y
);
}
bool isInside(Rectangle inner, Rectangle outer) {
return (
outer.position.x <= inner.position.x &&
inner.position.x + inner.size.x <= outer.position.x + outer.size.x &&
outer.position.y <= inner.position.y &&
inner.position.y + inner.size.y <= outer.position.y + outer.size.y
);
}
Vec2 getCenter(Rectangle r) {
return (Vec2) {
r.position.x + r.size.x / 2,
r.position.y + r.size.y / 2
};
}
Rectangle translateRectangle(Rectangle r, Vec2 translate) {
return (Rectangle) {
add(r.position, translate),
r.size
};
}

View file

@ -0,0 +1,26 @@
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include <stdbool.h>
#include "../vec2/vec2.h"
typedef struct {
Vec2 position;
Vec2 size;
} Rectangle;
bool areIntersecting(Rectangle r1, Rectangle r2);
// Return true only if inner is fully inside of outer
bool isInside(Rectangle inner, Rectangle outer);
// Return the geometrical middle point of the given rectangle
Vec2 getCenter(Rectangle r);
// Return a new rectangle shifted by vector translate
Rectangle translateRectangle(Rectangle r, Vec2 translate);
#endif

View file

@ -0,0 +1,24 @@
#include "vec2.h"
const Vec2 directions[] = {
[north] = (Vec2){0, -1},
[west] = (Vec2){-1, 0},
[south] = (Vec2){0, 1},
[east] = (Vec2){1, 0}
};
Vec2 add(Vec2 v1, Vec2 v2) {
return (Vec2){v1.x + v2.x, v1.y + v2.y};
}
Vec2 substract(Vec2 v1, Vec2 v2) {
return (Vec2){v1.x - v2.x, v1.y - v2.y};
}
Vec2 clampVec2(Vec2 v) {
return (Vec2){
v.x == 0 ? 0 : (v.x > 0 ? 1 : -1),
v.y == 0 ? 0 : (v.y > 0 ? 1 : -1)
};
}

View file

@ -0,0 +1,26 @@
#ifndef VEC2_H
#define VEC2_H
#include <avr/io.h>
typedef struct {
int8_t x;
int8_t y;
} Vec2;
typedef enum {
north, west, south, east
} Direction;
// The array directions can be indexed by a Direction and
// it contains vectors pointing into that direction.
const Vec2 directions[4];
Vec2 add(Vec2 v1, Vec2 v2);
Vec2 substract(Vec2 v1, Vec2 v2);
// Return new vector with ll components between -1 and 1 (inclusive)
Vec2 clampVec2(Vec2 vector);
#endif