Add christmas edition

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

1
.gitignore vendored
View file

@ -5,4 +5,3 @@
**/.vs
**/bin
.DS_Store
christmas

19
christmas/AdAstra.atsln Normal file
View file

@ -0,0 +1,19 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Atmel Studio Solution File, Format Version 11.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "AdAstra", "AdAstra\AdAstra.cproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|AVR = Release|AVR
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.ActiveCfg = Release|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.Build.0 = Release|AVR
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

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

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,15 @@
# Sprite generator
## Install
- Install Python 3.5 or later
- Execute `pip install -r requirements.txt`
## Use
- Using [Piskel](https://www.piskelapp.com/)
- draw the sprites
- export them in `zip` format (it's a .zip containing .png files)
- Place the files inside the [spritesheets](spritesheets) folder
- Execute `python main.py`
- The output is automatically placed in the [output](output) folder

View file

@ -0,0 +1,14 @@
{
"spellright.language": [
"en",
"hu"
],
"spellright.documentTypes": [
"markdown",
"latex",
"plaintext"
],
"cSpell.enableFiletypes": [
"!plaintext"
]
}

View file

@ -0,0 +1,13 @@
Szia, Ádám!
Boldog karácsonyt kívánok!
Viszont mást is szeretnék mondani.
Most egy kicsit még távolabb fogunk kerülni egymástól.
Persze biztos vagyok benne, hogy ezután is tartani fogjuk a kapcsolatot.
Viszont jó pillanatnak érzem a mostanit arra, hogy köszönetet mondjak.
Köszönöm az együtt töltött időnket.
Köszönöm, hogy más ember lettem melletted.
Ebben neked is részed volt.
Összességében, nélküled kicsit szomorúbb lenne az élet.
Remélem ezt te is tudod magadról.
Várom, hogy mihamarabb találkozzunk.
Andris

View file

@ -0,0 +1,11 @@
Szia, Apa!
Boldog karácsonyt kívánok!
Viszont mást is szeretnék mondani.
Szeretném megköszönni azt a számtalan segítséget, amit tőled kaptam.
Köszönöm, hogy mindig támogattál és motiváltál.
Köszönöm, hogy mindig mögöttem álltál.
Nélküled most biztosan nem tartanék itt.
Ha ezt nehezen is fejeztem ki, de mindig fontos voltál nekem.
Összességében, nélküled kicsit szomorúbb lenne az élet.
Remélem ezt te is tudod magadról.
Andris

View file

@ -0,0 +1,11 @@
Szia, Balázs!
Boldog karácsonyt kívánok!
Viszont mást is szeretnék mondani.
Habár rövidebb, de annál sűrűbb volt az egynyári kalandunk.
Öröm volt téged megismerni.
A kreativitásod és összeszedettséged inspiráló.
Túlzás nélkül sokunk példaképe lehetnél.
Összességében, nélküled kicsit szomorúbb lenne az élet.
Remélem ezt te is tudod magadról.
Várom, hogy mihamarabb találkozzunk.
Andris

View file

@ -0,0 +1,12 @@
Szia, Olivér!
Boldog karácsonyt kívánok!
Viszont mást is szeretnék mondani.
Jó pillanatnak érzem a mostanit arra, hogy köszönetet mondjak.
A barátságod, segítséged és humorod meghatározta az elmúlt éveimet.
Ha nem is gondolod így, de nagy hatással voltál rám.
Az elhivatottságod és kitartásod mindig inspirált.
Te vagy az a barát, akit mindenki szeretne.
Összességében, nélküled kicsit szomorúbb lenne az élet.
Remélem ezt te is tudod magadról.
Várom, hogy mihamarabb találkozzunk.
Andris

View file

@ -0,0 +1,121 @@
import io
import os
import zipfile
from math import ceil
from sys import argv
from typing import Tuple
from PIL import Image
c_header = """#include "texts.h"
#include <avr/eeprom.h>
// AUTO-GENERATED
"""
h_header = """#ifndef TEXTS_H
#define TEXTS_H
#include <avr/io.h>
// AUTO-GENERATED
"""
h_footer = """
#endif
"""
letters = [
"a",
"á",
"b",
"c",
"d",
"e",
"é",
"f",
"g",
"h",
"i",
"í",
"j",
"k",
"l",
"m",
"n",
"o",
"ó",
"ö",
"ő",
"p",
"q",
"r",
"s",
"t",
"u",
"ú",
"ü",
"ű",
"v",
"w",
"x",
"y",
"z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
",",
".",
"!",
"?",
" ",
]
def generate(input_text: str) -> Tuple[str, str]:
indices = []
for line in input_text.split("\n"):
for char in line.strip().lower().replace(", ", ","):
indices.append(letters.index(char))
indices.append(255)
indices.append(255)
if len(indices) > 512:
print(f"Input is too long ({len(indices)} > 512)")
exit()
output_h = h_header + f"const uint8_t texts[{len(indices)}];\n" + h_footer
output_c = c_header + f"const uint8_t texts[{len(indices)}] EEMEM = {{"
output_c += ", ".join(str(i) for i in indices)
output_c += "};\n"
return output_h, output_c
if __name__ == "__main__":
if len(argv) == 1:
print("Specify an input file")
exit()
with open(argv[1], "r", encoding="utf-8") as f:
input_text = f.read()
dot_h, dot_c = generate(input_text)
with open("output/texts.h", "w+") as f:
f.write(dot_h)
with open("output/texts.c", "w+") as f:
f.write(dot_c)

View file

@ -0,0 +1 @@
Pillow==8.0.1