fork from Radiance
This commit is contained in:
parent
aa18edc2f1
commit
d3d1dd9be9
68 changed files with 4948 additions and 2 deletions
15
CREDITS.md
Normal file
15
CREDITS.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Sonic Radiance Credits
|
||||||
|
|
||||||
|
## GameDesign and Programming
|
||||||
|
|
||||||
|
- Kazhnuz
|
||||||
|
|
||||||
|
## Engine used
|
||||||
|
|
||||||
|
- [Love2D](https://love2d.org/)
|
||||||
|
|
||||||
|
## Visuals
|
||||||
|
|
||||||
|
- Background and Tiles from [Shadow Shoot](https://www.spriters-resource.com/mobile/shadowshoot/) ripped by Kazhnuz and Mr. C.
|
||||||
|
|
||||||
|
- HUD elements from [FieryExplosion](https://www.spriters-resource.com/submitter/FieryExplosion/)
|
18
README.md
18
README.md
|
@ -1,3 +1,17 @@
|
||||||
# sonic-boost
|
# Sonic Radiance
|
||||||
|
|
||||||
Un fangame basé sur Shadow Shoot
|
Sonic Radiance est un projet de fangame Sonic the Hedgehog en style (J)RPG avec des inspirations de Zelda-like et de T-RPG. Le but est de mélanger l'ambiance des Sonic Adventure avec le style des Sonic Advance, des Sonic Battle dans un enrobage RPG.
|
||||||
|
|
||||||
|
Le but est également de garder certains éléments "centraux" des jeux Sonic, tels que les ranks, la vitesse, l'aspect action. Cela sera fait grace à un système de combat spécial offrant au joueur des capacités d'agir plusieurs fois et évitant les trop long "temps d'attente" où le joueur ne fait que regarder le combat se dérouler. L'overworld quant à lui sera en 2D vue de haut, avec des capacités sur le terrain gardant les origines "plateforme" de la série. De plus, des mini-jeux seront présent, visant à rendre le jeu plus dynamique.
|
||||||
|
|
||||||
|
Ah, et il y aura des chao aussi.
|
||||||
|
|
||||||
|
## Synopsis
|
||||||
|
|
||||||
|
Sunlit Island est une station balnéaire populaire sur une petite île ensoleillée et agréable. Doté d’une histoire riche, berceau de l’ancienne civilisation Sidh et sujet de projets de la compagnie Rimlight il y a 15 ans, elle peut aussi bien intéresser ceux qui ne viennent que se baigner que ceux qui cherchent à étancher leur soif de découvertes. Cependant, Sonic, Tails et Amy ont reçu un appel terrifié du maire de la ville : des badnics sont apparus partout sur l’île du jour au lendemain, et sement la terreur ! Face à ce qui semble être très certainement un coup d’Eggman, nos héros se sont précipités sur l’île.
|
||||||
|
|
||||||
|
Seront-ils seuls dans cet aventure ? Quel est le plan d’Eggman ? Quels secrets renferme l'île de Sunlit Island ? Sonic ne pourra pas seul affronter une telle menace ! Il lui faudra l'aide de ses camarades pour vaincre le savant fou et éclaircir les mystères de Sunlit Island.
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
Le code du jeu est sous licence MIT. Les assets originaux sont © SEGA, et les assets et histoire sont propriété de leurs créateurs respectifs.
|
||||||
|
|
61
game-design/characters/amy-rose.md
Normal file
61
game-design/characters/amy-rose.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Amy Rose
|
||||||
|
|
||||||
|
Plus grande fan autoproclamée de Sonic devenue une ami fidèle du hérisson, Amy Rose est une force de la nature en plus d'être une personne pleine de compassion et d'énergie. Cette jeune hérissonne ne désespère pas de réussir à séduire Sonic, mais est également capable de penser au plus important.
|
||||||
|
|
||||||
|
Elle est présente avec Tails et Sonic pour élucidé le mystère de l'ile de Sunlit Island
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type power
|
||||||
|
|
||||||
|
- Agit 2× par tour
|
||||||
|
|
||||||
|
- Moins forte que Knuckles, mais plus de technique et quelques capacités de soin
|
||||||
|
|
||||||
|
- Peut passer en super state? (j'ai les sprites pour, donc pourquoi pas ?)
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Homming Dash
|
||||||
|
|
||||||
|
- Punch (détruit des éléments du décors)
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank A
|
||||||
|
|
||||||
|
- **PP** :: Rank C
|
||||||
|
|
||||||
|
- **Attack** :: Rank A
|
||||||
|
|
||||||
|
- **Defense** :: Rank B
|
||||||
|
|
||||||
|
- **Technique** :: Rank C
|
||||||
|
|
||||||
|
- **Power** :: Rank B
|
||||||
|
|
||||||
|
- **Mind** :: Rank C
|
||||||
|
|
||||||
|
- **Speed** :: Rank B
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
- Rose Heal (soigne un personnage)
|
||||||
|
|
||||||
|
- Tarot Curse (maudit un ennemi)
|
||||||
|
|
||||||
|
- Amy Flash (affecte tout les ennemis avec un effet)
|
||||||
|
|
||||||
|
- Piko Tornado (G.Tornado de S. Battle)
|
||||||
|
|
||||||
|
- Gift Trap
|
||||||
|
|
||||||
|
- Hammer Attack
|
||||||
|
|
||||||
|
- Pink Tornado
|
||||||
|
|
||||||
|
- Rose Typhon
|
||||||
|
|
||||||
|
- Spin Hammer Attack
|
||||||
|
|
||||||
|
- Storming Heart (comme le Amy Flash, mais sur un ennemi + quelques dégats)
|
61
game-design/characters/cream-the-rabbit.md
Normal file
61
game-design/characters/cream-the-rabbit.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Cream the Rabbit
|
||||||
|
|
||||||
|
Cream est la fille de Vanilla the Rabbit, une petite lapine calme et polie qui n'aime pas se battre. Cependant courageuse, elle n'hésite pas à venir à ses ennemis en aide. Ce n'est pas le danger qui lui fait peur, mais le fait de ne pas aimer la violence.
|
||||||
|
|
||||||
|
Elle rejoindra l'équipe après avoir été lors d'une quête annexe.
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type Technique
|
||||||
|
|
||||||
|
- Agit 2× par tour
|
||||||
|
|
||||||
|
- La plus faible en force et en pouvoir, Cream est cependant la meilleurs healeuse du jeu.
|
||||||
|
|
||||||
|
- Ne peut pas passer en super state.
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Vol
|
||||||
|
|
||||||
|
- Attaque à distance (Chao)
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank A
|
||||||
|
|
||||||
|
- **PP** :: Rank B
|
||||||
|
|
||||||
|
- **Attack** :: Rank D
|
||||||
|
|
||||||
|
- **Defense** :: Rank B
|
||||||
|
|
||||||
|
- **Technique** :: Rank A
|
||||||
|
|
||||||
|
- **Power** :: Rank C
|
||||||
|
|
||||||
|
- **Mind** :: Rank A
|
||||||
|
|
||||||
|
- **Speed** :: Rank C
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
- Chao Attack
|
||||||
|
|
||||||
|
- Heal Cheer
|
||||||
|
|
||||||
|
- Revive Cheer
|
||||||
|
|
||||||
|
- Thoughness Cheer
|
||||||
|
|
||||||
|
- Refresh Cheer
|
||||||
|
|
||||||
|
- Cure Cheer
|
||||||
|
|
||||||
|
- Demoralize
|
||||||
|
|
||||||
|
- Omochao Trap
|
||||||
|
|
||||||
|
- Chao Tornado (Chao Rush de S. Battle)
|
||||||
|
|
||||||
|
- Chao Canon
|
62
game-design/characters/espio-the-chamelon.md
Normal file
62
game-design/characters/espio-the-chamelon.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Espio the Hedgehog
|
||||||
|
|
||||||
|
Ninja des Chaotix, Espio est sérieux et concentré, et se laisse peu distraire par le divertissement et autres choses du genre. Il est présent sur l'île pour enquêter sur la société Rimlight, et sur ses rapports avec GUN. Il s'intéressera particulièrement à l'affaire de la Gosth Central.
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type Speed
|
||||||
|
|
||||||
|
- Agit 2× par tour
|
||||||
|
|
||||||
|
- A pas mal de capacité pour esquiver. Basé pas mal sur les critiques.
|
||||||
|
|
||||||
|
- Ne peut pas passer en super state.
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Homming Dash
|
||||||
|
|
||||||
|
- Attaque à distance (Kunai)
|
||||||
|
|
||||||
|
- Light Dash
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank B
|
||||||
|
|
||||||
|
- **PP** :: Rank C
|
||||||
|
|
||||||
|
- **Attack** :: Rank A
|
||||||
|
|
||||||
|
- **Defense** :: Rank C
|
||||||
|
|
||||||
|
- **Technique** :: Rank B
|
||||||
|
|
||||||
|
- **Power** :: Rank C
|
||||||
|
|
||||||
|
- **Mind** :: Rank A
|
||||||
|
|
||||||
|
- **Speed** :: Rank A
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
|
||||||
|
- Shuriken
|
||||||
|
|
||||||
|
- Expl. Kunai
|
||||||
|
|
||||||
|
- Chroma Camo (augmente esquive pd 3 tours, passe Espio dans son set de Sprite "invisible")
|
||||||
|
|
||||||
|
- Leaf Swirl (tornade qui touche tout les ennemis autour et rend espio invisible 1 tour)
|
||||||
|
|
||||||
|
- Chaotic Tornado (Espio tournoi et provoque des dégats tout autour)
|
||||||
|
|
||||||
|
- Ninja Trap (Espio fait apparaitre un explosif dans le sol)
|
||||||
|
|
||||||
|
- Tornado Dash (Espio tournoi et attaque en avant sur qq cases)
|
||||||
|
|
||||||
|
- Poison Kunai
|
||||||
|
|
||||||
|
- Ninja Trick (produit un effet faibless aux ennemis autour)
|
||||||
|
|
||||||
|
- Ninja Focus (le personnage reçoit le status "FOCUS")
|
69
game-design/characters/knuckles-the-echidna.md
Normal file
69
game-design/characters/knuckles-the-echidna.md
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# Knuckles the Echidna
|
||||||
|
|
||||||
|
Knuckles est le gardien de la Master Emerald, ayant vécu en ermite depuis toujours sur Angel Island, l'ile flottante où la gemme mystique repose. Cet echidné prend son rôle au sérieux, et ne supporte pas que quiconque pose la main dessus. Dupé mainte fois par le passé, il est désormais plus prudent.
|
||||||
|
|
||||||
|
Cependant, un mal mystérieux s'est attaqué à la Master Emerald, la corrompant et la rendant instable. Guidée par un esprit, "Morrigan", le gardien a localisé la source de ce mal sur Sunlit Island. Serait-ce les expérimentation d'Eggman qui est la source de ce mal ? Knuckles est déterminé à élucidé ce mystère, et à sauver la Master Emerald.
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type power
|
||||||
|
|
||||||
|
- Agit 2× par tour
|
||||||
|
|
||||||
|
- Personnage le plus fort, assez tanky, mais peu resistant sur le mental
|
||||||
|
|
||||||
|
- Peut passer en super state
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Plane & grimpe
|
||||||
|
|
||||||
|
- Punch (détruit des éléments du décors)
|
||||||
|
|
||||||
|
- L'avoir dans l'équipe offre un détecteur à objet caché.
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank A
|
||||||
|
|
||||||
|
- **PP** :: Rank D
|
||||||
|
|
||||||
|
- **Attack** :: Rank S
|
||||||
|
|
||||||
|
- **Defense** :: Rank A
|
||||||
|
|
||||||
|
- **Technique** :: Rank C
|
||||||
|
|
||||||
|
- **Power** :: Rank C
|
||||||
|
|
||||||
|
- **Mind** :: Rank D
|
||||||
|
|
||||||
|
- **Speed** :: Rank C
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
- Spin Attack
|
||||||
|
|
||||||
|
- Spin Dash
|
||||||
|
|
||||||
|
- Deep Impact
|
||||||
|
|
||||||
|
- Spin Drill
|
||||||
|
|
||||||
|
- Enrage
|
||||||
|
|
||||||
|
- Ground Shaker / Hammer Punch (touche tout les ennemis sur une grande zone, utilise le Knux. Chop comme sprite)
|
||||||
|
|
||||||
|
- Uppercut
|
||||||
|
|
||||||
|
- Maximum Heat Knuckles
|
||||||
|
|
||||||
|
- Mole Bomb
|
||||||
|
|
||||||
|
- Meteor Crush
|
||||||
|
|
||||||
|
- Thunder Arrown
|
||||||
|
|
||||||
|
- Volcanic Dunk
|
||||||
|
|
||||||
|
- Guardian Heal
|
67
game-design/characters/miles-tails-prower.md
Normal file
67
game-design/characters/miles-tails-prower.md
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Sonic the Hedgehog
|
||||||
|
|
||||||
|
Le renardeau suivant Sonic depuis (presque) toujours. Inventif et intelligent, Tails n'est pas autant un combattant que ses amis mais à appris à se débrouiller en combat. Il souffre d'un manque de confiance en lui, qu'il a commencé à apprendre à dépasser, mais Rome ne s'est pas faite en un jour.
|
||||||
|
|
||||||
|
Usant de la mécanique et de son intelligence, c'est un
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type Technique
|
||||||
|
|
||||||
|
- Agit 2× par tour
|
||||||
|
|
||||||
|
- Est le deuxième plus faible des perso principaux, mais peut faire pas mal d'effets secondaire et est bon avec les objets. Personnage équilibré de soutiens.
|
||||||
|
|
||||||
|
- Peut passer en super state.
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Vol
|
||||||
|
|
||||||
|
- Arm Cannon (permet d'activer à distances des dispositif)
|
||||||
|
|
||||||
|
- Peut actionner les machines.
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank C
|
||||||
|
|
||||||
|
- **PP** :: Rank A
|
||||||
|
|
||||||
|
- **Attack** :: Rank D
|
||||||
|
|
||||||
|
- **Defense** :: Rank C
|
||||||
|
|
||||||
|
- **Technique** :: Rank S
|
||||||
|
|
||||||
|
- **Power** :: Rank C
|
||||||
|
|
||||||
|
- **Mind** :: Rank A
|
||||||
|
|
||||||
|
- **Speed** :: Rank B
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
- Spin Attack :: une course tourbillonante attaquant un ennemi au sol sur trois case devant le personnage.
|
||||||
|
|
||||||
|
- Spin Dash :: une course tourbillonante chargée attaquant un ennemi au sol sur cinq case devant le personnage. Le personnage se défend automatiquement pendant le tour de charge et inflige des dégats sur les techniques direct.
|
||||||
|
|
||||||
|
- Adrénaline Rush :: Le personnage peut activer un mode Hyper sur un personnage au choix.
|
||||||
|
|
||||||
|
- Chu² Bomb :: Un piège qui traverse le terrain jusqu'à un ennemi (1 case/tour)
|
||||||
|
|
||||||
|
- Dummy Ring Bomb :: Lance trois anneau paralysant devant le personnage.
|
||||||
|
|
||||||
|
- Energy Shot :: Attaque à distance paralysante sur un ennemi en avant (5 case)
|
||||||
|
|
||||||
|
- Mecha Hook :: Lance un coup de poing mécanique tout autour de lui. Peut étourdir
|
||||||
|
|
||||||
|
- Magic Upper :: Lance un coup de poing concentré puissant sur une case devant lui.
|
||||||
|
|
||||||
|
- Tail Attack :: Un coup de queue qui attaque sur trois case devant le héros.
|
||||||
|
|
||||||
|
- Rapid Tail Attack :: Un coup de queue tournoyant attanquant tout autour plusieurs fois.
|
||||||
|
|
||||||
|
- Scan :: Permet d'obtenir les informations sur un ennemi
|
||||||
|
|
||||||
|
- Energy Laser :: Tir perforant touchant tous les ennemis en avant.
|
67
game-design/characters/rouge-the-bat.md
Normal file
67
game-design/characters/rouge-the-bat.md
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Rouge the Bat
|
||||||
|
|
||||||
|
Espionne du gouvernement, intelligente, voleuse et aimant bien se jouer des autres. Aimant particulièrement taquiner Knuckles et tenter de lui voler la master emerald - bien qu'on peut se demander si le fait que ça énerve l'échidné n'est pas en soi une raison suffisante.
|
||||||
|
|
||||||
|
Elle est présente sur l'île pour faire des recherche sur le projet Gosth Island et la manière dont Eggman à obtenu des informations dessus. Elle rejoindra l'équipe avec des informations sur le projet Gosth Island, et sur le rôle de Rimlight dans toute cette histoire.
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type technique
|
||||||
|
|
||||||
|
- Agit 2× par tour
|
||||||
|
|
||||||
|
- Personnage technique assez puissant en combat, avec pas mal de capacité pour mettre des altération d'état
|
||||||
|
|
||||||
|
- Ne peut pas passer en super state
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Vole
|
||||||
|
|
||||||
|
- Punch (détruit des éléments du décors)
|
||||||
|
|
||||||
|
- L'avoir dans l'équipe offre un détecteur à objet caché.
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank C
|
||||||
|
|
||||||
|
- **PP** :: Rank A
|
||||||
|
|
||||||
|
- **Attack** :: Rank D
|
||||||
|
|
||||||
|
- **Defense** :: Rank C
|
||||||
|
|
||||||
|
- **Technique** :: Rank A
|
||||||
|
|
||||||
|
- **Power** :: Rank B
|
||||||
|
|
||||||
|
- **Mind** :: Rank C
|
||||||
|
|
||||||
|
- **Speed** :: Rank D
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
- Spin Drill
|
||||||
|
|
||||||
|
- Upper Attack (Secret Kick)
|
||||||
|
|
||||||
|
- Black Wave
|
||||||
|
|
||||||
|
- Bat Kick
|
||||||
|
|
||||||
|
- Bat Bomb
|
||||||
|
|
||||||
|
- Beauty Choc
|
||||||
|
|
||||||
|
- Charly Kick (coup de pied qui attaque tout autour)
|
||||||
|
|
||||||
|
- Distract (Generation / Chronicles)
|
||||||
|
|
||||||
|
- Jewel Storm ?
|
||||||
|
|
||||||
|
- Shriek
|
||||||
|
|
||||||
|
- Plunder
|
||||||
|
|
||||||
|
- Silent Size
|
65
game-design/characters/shadow-the-hedgehog.md
Normal file
65
game-design/characters/shadow-the-hedgehog.md
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# Shadow the Hedgehog
|
||||||
|
|
||||||
|
L'espèce vivante suprême, un hérisson sombre et peu loquace. Rarement la personne la plus sympathique avec qui parler, et plus souvent concentré sur sa mission que sur aider chaque personnage sur son passage, Shadow n'en est pas moins quelqu'un de dévoué pour protéger la planète. Il est ici afin de découvrir qui est le mystérieux "traitre" qu'il y aurait sur l'île.
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type Speed
|
||||||
|
|
||||||
|
- Agit 3× par tour
|
||||||
|
|
||||||
|
- Personnage le plus puissant à distance, et bon en vitesse. Peu de PV et
|
||||||
|
|
||||||
|
- Peut passer en super state
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Homming Dash
|
||||||
|
|
||||||
|
- Course
|
||||||
|
|
||||||
|
- Light Dash
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank D
|
||||||
|
|
||||||
|
- **PP** :: Rank B
|
||||||
|
|
||||||
|
- **Attack** :: Rank A
|
||||||
|
|
||||||
|
- **Defense** :: Rank D
|
||||||
|
|
||||||
|
- **Technique** :: Rank C
|
||||||
|
|
||||||
|
- **Power** :: Rank S
|
||||||
|
|
||||||
|
- **Mind** :: Rank D
|
||||||
|
|
||||||
|
- **Speed** :: Rank A
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
- Spin Attack
|
||||||
|
|
||||||
|
- Spin Dash
|
||||||
|
|
||||||
|
- Chaos Boost (boost temporairement ses stats)
|
||||||
|
|
||||||
|
- Chaos Control (comme le Time Stop en plus puissant)
|
||||||
|
|
||||||
|
- Chaos Blast (dégats à tout les ennemis)
|
||||||
|
|
||||||
|
- Chaos Burst (attaque autour)
|
||||||
|
|
||||||
|
- Chaos Rift (fusion avec Chaos Magic de Sonic Battle)
|
||||||
|
|
||||||
|
- Chaos Nightmare/Sphere (affect les deux côtés, ralenti les ennemis)
|
||||||
|
|
||||||
|
- Chaos Spear (attaque droit devent, perce les armures)
|
||||||
|
|
||||||
|
- Maria's Wish (regénération)
|
||||||
|
|
||||||
|
- Chaos Lance
|
||||||
|
|
||||||
|
- Light Speed Attack
|
5
game-design/characters/sidecharacters/chaos-breakers.md
Normal file
5
game-design/characters/sidecharacters/chaos-breakers.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Sonic Radiance - Les "Chaos Breaker"
|
||||||
|
|
||||||
|
- **Ys Gradlon** est l'ancien commander des armées du GUN, disparu il y a de cela 15 ans lors de l'incident de Gosth Island.
|
||||||
|
|
||||||
|
- **??? ???** (maire de l'île) :: Le maire de l'île est un homme en apparence joviale mais stressée, constamment sur le qui-vive à cause des attaques d'Eggman. C'est lui qui a appellé nos héros pour qu'ils s'occupent d'Eggman.
|
19
game-design/characters/sidecharacters/sidh-characters.md
Normal file
19
game-design/characters/sidecharacters/sidh-characters.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Sonic Radiance - Membres du peuple Sidh
|
||||||
|
|
||||||
|
## Génération Actuel
|
||||||
|
|
||||||
|
- **Taranis** est le nouveau guerrier de l’Eggman Empire ! S’attaquant à Sonic lorsque celui-ci tente de sauver Sunlit Metropolis, il semble défendre à sa manière les intérêt de son peuple, et en vouloir aux humains pour l’expropriation qu’ils ont subit. Il a 14 ans. Il a une dent toute particulière contre Ankou.
|
||||||
|
|
||||||
|
- **Anrad** est une barde, sœur de Taranis, qui a refusée d’aider Eggman dans sa quête. Elle est gardienne des anciens chants de la civilisation Sidh. Elle suivra les héros plusieurs fois, intéressée par leurs objectif, et voulant sauver son frère des griffes d'Eggman.
|
||||||
|
|
||||||
|
- **Ankou** : Jeune garçon de 15 ans du village secondaire, Ankou est le fils de la défunte Dahut, et est l'actuel posesseur du médaillon de Dahut. Non-combattant, il a été toute sa vie la cible d'attaque de ses camarades, et est de ce fait plutôt réservé et peu loquace. Il préfère la nuit et le calme.
|
||||||
|
|
||||||
|
## Génération précédante
|
||||||
|
|
||||||
|
- **Morrigan** est l'ancienne prétresse du Stygian Rift. Ancienne ami de Athair. Elle est la grand-mère de Taranis.
|
||||||
|
|
||||||
|
- **Benelos** est un ancien chef du peuple Sidh, à l'époque où ils avaient un conseil des chef. Guerrier fier et féroce
|
||||||
|
|
||||||
|
- **Lug** est le chef actuel du peuple Sidh. Il est le fils de Morrigan, et le père de Taran.
|
||||||
|
|
||||||
|
- **Dahut** est une ancienne scientifique, membre du peuple Sidh. Elle a fait partie du projet Millenium, et est considéré comme étant la coupable de tout ce qui s'est passé durant ce projet. Ankou subit les contrecoup de cette réputation.
|
67
game-design/characters/sonic-the-hedgehog.md
Normal file
67
game-design/characters/sonic-the-hedgehog.md
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Sonic the Hedgehog
|
||||||
|
|
||||||
|
Le hérisson le plus rapide du monde est de retour ! Orgueilleux, fier et inarrêtable, Sonic est toujours l'amoureux de la liberté et de la course qu'il a toujours été. Étonnamment plus malin qu'il le laisse paraître, le hérisson a été appellé par le maire de l'île avec Tails et Amy afin d'aider Sunlit Island dans une invasion de Badniks.
|
||||||
|
|
||||||
|
Toujours heureux de pouvoir aider les gens, il n'a pas hésité et le voilà présent sur l'île.
|
||||||
|
|
||||||
|
## Caractéristiques principales
|
||||||
|
|
||||||
|
- Type Speed
|
||||||
|
|
||||||
|
- Agit 3× par tour
|
||||||
|
|
||||||
|
- Est pas extrèmement fort mais rapide et avec une bonne précision
|
||||||
|
|
||||||
|
- Peut passer en super state.
|
||||||
|
|
||||||
|
### Sur le terrain
|
||||||
|
|
||||||
|
- Homming Dash
|
||||||
|
|
||||||
|
- Course (permet de courir sur l'eau, les tapis roulant, etc)
|
||||||
|
|
||||||
|
- Light Dash
|
||||||
|
|
||||||
|
## Système de combat
|
||||||
|
|
||||||
|
- **HP** :: Rank B
|
||||||
|
|
||||||
|
- **PP** :: Rank C
|
||||||
|
|
||||||
|
- **Attack** :: Rank B
|
||||||
|
|
||||||
|
- **Defense** :: Rank B
|
||||||
|
|
||||||
|
- **Technique** :: Rank C
|
||||||
|
|
||||||
|
- **Power** :: Rank C
|
||||||
|
|
||||||
|
- **Mind** :: Rank D
|
||||||
|
|
||||||
|
- **Speed** :: Rank S
|
||||||
|
|
||||||
|
### Techniques
|
||||||
|
|
||||||
|
- Spin Attack :: une course tourbillonante attaquant un ennemi au sol sur trois case devant le personnage.
|
||||||
|
|
||||||
|
- Spin Dash :: une course tourbillonante chargée attaquant un ennemi au sol sur cinq case devant le personnage. Le personnage se défend automatiquement pendant le tour de charge et inflige des dégats sur les techniques direct.
|
||||||
|
|
||||||
|
- Homming Attack :: Le personnage peut viser un ennemi autour de lui sur un rayon de deux case, le frappant automatiquement. Touche les ennemis aérien. Avec les niveaux, ils peut viser plus d'ennemi.
|
||||||
|
|
||||||
|
- Light Speed Attack :: Technique ultime Speed - homming attack sur tous les ennemis du terrain.
|
||||||
|
|
||||||
|
- Boost :: Le personnage attaque un ennemi sur cinq case devant lui, traverse les ennemis.
|
||||||
|
|
||||||
|
- Blue Tornado :: Le personnage balance une tornade attaquant sur un carée de 3×3 case devant lui. Les ennemis au centre se prennent plus de dégats. Supprime les bouclier.
|
||||||
|
|
||||||
|
- Sonic Wind :: Brise les bouclier de tous les ennemis devant le personnage.
|
||||||
|
|
||||||
|
- Sonic Cracker (piège) : Le personnage pose un piège qui explose au contact d'un ennemi.
|
||||||
|
|
||||||
|
- Sonic Flare : Le personnage attaque tous les ennemis autour de lui, attaque lourde.
|
||||||
|
|
||||||
|
- Sonic Wave : Le personnage envoie une vague d'énergie devant lui. Peut étourdir les ennemis.
|
||||||
|
|
||||||
|
- Speed Up : Le personnage produit l'effet "Speed Up" à un personnage au choix, lui apportant une attaque supplémentaire.
|
||||||
|
|
||||||
|
- Time Stop : Le personnage produit l'effet "figé" à un ennemi pendant 1 tours.
|
84
game-design/locations/readme.md
Normal file
84
game-design/locations/readme.md
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# Sonic Radiance - Locations
|
||||||
|
|
||||||
|
Le jeu entier se passe sur deux îles : Sunlit Island et Gosth Island.
|
||||||
|
|
||||||
|
Le jeu étant inspiré en grande partie des jeux type Shadow Shoot et autre jeux de l'époque 1998-2006 du hérisson, le jeu fera un hommage au peu connu Shadow Shoot en reprenant une partie de ces niveaux comme emplacement sur Sunlit Island, et reprenant le thème global ("Pierre Précieuse + Nom de Lieu") pour nommer une partie des zones.
|
||||||
|
|
||||||
|
Certains noms seront un peu modifié afin de ressembler plus aux thèmes du jeu.
|
||||||
|
|
||||||
|
## Les cinq grands espaces de l'île
|
||||||
|
|
||||||
|
### La ville :: Diamound Metropolis (au centre)
|
||||||
|
|
||||||
|
- **Diamound Metropolis** :: La plus grande ville de l'île, Diamound Metropolis est une ville touristique puissante et impressionnante. Ce niveau est inspiré du niveau "Diamound Highway" de Shadow Shoot.
|
||||||
|
|
||||||
|
- **Diamound Highway** :: La "Diamound Highway" est la voie rapide amenant de la ville au centre industrielle de Rimlight. Il s'agit en quelque sorte d'une "extension" de cette première
|
||||||
|
|
||||||
|
- **Rimlight HQ** :: Le centre de commande de l'entreprise Rimlight sur l'île, qui contrôle tout les générateurs Chaotique de l'île.
|
||||||
|
|
||||||
|
- **Agate Park** :: Un park/casino immense, située dnas la Diamound Metropolis. Un endroit utile pour gagner… ou bien pour tout perdre ?
|
||||||
|
|
||||||
|
- **Obsidian Station** :: Au dessus de l'île, en orbite géostationnaire, passe régulièrement cette station spatiale, station principale du projet ERINYE.
|
||||||
|
|
||||||
|
|
||||||
|
### La zone aride/sèche (ouest)
|
||||||
|
|
||||||
|
- **Moonstone Valley** :: Une ancienne vallée, située à l'ouest de la grande ville. Cette vallée plutôt aride est ce qui offre l'accès à plusieurs endroits connus.
|
||||||
|
|
||||||
|
- **Amber Canyon** :: Un canyon ambré, uniquement masqué par le *Sidh Village*. C'est ici que se trouve l'un des dongeons du peuple Sidh, *Ancient Scripture*.
|
||||||
|
|
||||||
|
- **Sidh Village** :: Le village historique des Sidh, juste avant l'Amber Canyon.
|
||||||
|
|
||||||
|
- **Zircon Desert** :: Tout une zone désertique, située au Nord-Ouest de l'île, ou se trouve quelques secrets…
|
||||||
|
|
||||||
|
- **Justice Altar** :: Les anciennes épreuves de la justice du peuple Sidh.
|
||||||
|
|
||||||
|
- **Moonstone Graveyard** :: Un ancien cimetierre, où se trouve de nombreuses choses étranges et quelques fantomes…
|
||||||
|
|
||||||
|
### La zone montagneuse (Nord)
|
||||||
|
|
||||||
|
- **Pearl Mountain** :: Une montagne neigeuse, et au sommet de laquelle se trouve d'ancienne installation du *Projet Millenium*.
|
||||||
|
|
||||||
|
- **Garnet Crater** :: L'intérieur de la *Pearl Mountain* est un cratère volcanique étrangement plein de lave… serait-ce les expériementation d'Eggman ou celles d'il y a 10 ans qui ont fait revenir son activité ?
|
||||||
|
|
||||||
|
- **Thermal Station** :: Une base thermique à l'intérieur des volcans.
|
||||||
|
|
||||||
|
- **Force Cave** :: Les anciennes épreuves de la force du peuple Sidh.
|
||||||
|
|
||||||
|
- **Crystal Utopia** :: Un immense complexe technologique situé au sommet de la Pearl Mountain. Aucune information n'est disponible dessus.
|
||||||
|
|
||||||
|
### La zone forestière (est + nord-est)
|
||||||
|
|
||||||
|
- **Calcite Hills** :: Le niveau type "GHZ", les Calcite Hill sont des collines verdoyantes à l'est de la grande ville. Il s'agit d'un espace souvent visité par les habitant, pour accéder soit à la forêt soit aux ruines.
|
||||||
|
|
||||||
|
- **Jadeite Forest** :: Jadeite Forest est une forêt s'accédant depuis les Calcites Hills, située au Nord-Ouest de la ville (niveau provenant de Shadow Shoot). Cette forêt tempérée est un espace en retrait souvent apprécié des gens voulant de la tranquilité.
|
||||||
|
|
||||||
|
- **Temperence Grounds** :: Les anciennes épreuves de la tempérence du peuple Sidh.
|
||||||
|
|
||||||
|
- **Graphit Base** :: Une des bases d'Eggman sur l'île, abandonnée en première vue...
|
||||||
|
|
||||||
|
- **Ametist Temples** :: D'ancien temples du peuples Sidh, abandonnés depuis longtemps. Ils contiennent des informations sur une mystérieuse force nommée le Styx (Niveau situé au sud des Calcites Hills, inspiré de l'Amethist Castle de Shadow Shoot)
|
||||||
|
|
||||||
|
### La plage et l'océan (sud)
|
||||||
|
|
||||||
|
- **Olivine Coast** : Une côte mélangeant palmier et sable fin. Le premier niveau de tout Sonic qui se respecte.
|
||||||
|
|
||||||
|
- **Olivine Groove** : Une petite zone boisée située tout à l'est de l'île. C'est souvent un lieu de cachète et de jeu pour les enfants du *Merchant Village*.
|
||||||
|
|
||||||
|
- **Wisdom Trials** :: Les anciennes épreuves de la sagesse du peuple Sidh. Sous la mer.
|
||||||
|
|
||||||
|
- **Carnelian Bridge** :: Un immense pont militarisé reliant les deux île de Sunlit Island et Gosth Island. Au milieu se trouve une base de GUN éponyme, représentant la limite de où les gens peuvent aller sans autorisation spécial du gouvernement de l'île. (niveau repris de Shadow Shoot)
|
||||||
|
|
||||||
|
- **Peridot Archive** :: Une archive sous-marine (inspirée du Peridot Tunnel de Shadow Shoot) du G.U.N. se trouvant en dessous de la base militaire de Carnelian Bridge.
|
||||||
|
|
||||||
|
|
||||||
|
## 2. Gosth Island
|
||||||
|
Le but des niveaux complémentaires va être de contenir les tropes qui ne sont pas présents dans les niveaux de bases du jeu. Il y aura 9 niveaux, pour atteindre un total de 16 niveaux (4×4 pour le mode "tournois).
|
||||||
|
|
||||||
|
- **Gosth Central/Gadget** :: Le donjon principal de l'île. Une immense centrale chaotique, pleine de phénomènes étranges lié à l'énergie crée. Ce à quoi Eggman s'intéresse beaucoup sur cette île.
|
||||||
|
|
||||||
|
- **Chaos Core** :: Le Centre de la centrale. Danger. Énergie sauvage qui traverse partout.
|
||||||
|
|
||||||
|
- **Silicon Matrix** :: Le système numérique principale de l'île, où se trouvent toute les véritables informations sur le déroulement du *projet Millenium*.
|
||||||
|
|
||||||
|
- **Stygian Realm** :: L'univers-bulle d'où provient la Stygian Energy. Espace instable et peu compréhensible.
|
19
game-design/locations/sonic-boost.md
Normal file
19
game-design/locations/sonic-boost.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
## Levels in Sonic Boost game mode
|
||||||
|
|
||||||
|
## Correspondance de niveaux (mode classique)
|
||||||
|
|
||||||
|
- Niveau 1 : **Jadeite Forest** / **Calcite Hill**
|
||||||
|
- Niveau 2 : **Diamound Metropolis** / **Agate Park**
|
||||||
|
- Niveau 3 : **Peridot Archive** / **Zircon Desert**
|
||||||
|
- Niveau 4 : **Pearl Mountain** / **Carnelian Bridge**
|
||||||
|
- Niveau 5 : **Graphit Base** / **Moonstone Ruins**
|
||||||
|
- Niveau 6 : **Garnet Crater** / **Silicon Matrix**
|
||||||
|
- Niveau 7 : **Obsidian Station** / **Ametist Temples**
|
||||||
|
- Niveau Bonus : **Crystal Utopia**
|
||||||
|
|
||||||
|
## Coupes (mode tournoi)
|
||||||
|
|
||||||
|
- Chao Cup : Calcite Hill - ????? - ????? - ?????
|
||||||
|
- Ring Cup : ????? - ????? - ????? - ?????
|
||||||
|
- Emerald Cup : ????? - ????? - ????? - ?????
|
||||||
|
- Master Cup : ????? - ????? - ????? - **Crystal Cosmos**
|
21
game-design/lore-and-story/1-main-act1.md
Normal file
21
game-design/lore-and-story/1-main-act1.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Sonic Radiance - Main Story - Act 1 : Des robots partouts
|
||||||
|
|
||||||
|
## Synopsis
|
||||||
|
|
||||||
|
Sonic, Tails et Amy ont été appellée sur l'ile de Sunlit Island par le maire de l'ile. L'ile fait en effet face à des attaques ponctuelles de Badnics, mais sans qu'Eggman ait fait la moindre annonce ou menace. Les trois héros arrivent donc sur l'île, et vont d'abord devoir explorer la ville principale (Sunlit Metropolis) pour trouver le maire, et avoir leur mission.
|
||||||
|
|
||||||
|
Il apprendront que les badnics attaquent en ce moment un petit village touristique du sud de l'île, le Merchant Village. Les trois héros devront se diriger dans ce village, afin d'aller sauver les habitants.
|
||||||
|
|
||||||
|
Pour cela, ils vont devoir tout d'abord traverser les plages, qui sont infestées de badnics. Ce sera l'occasion d'avoir les premiers combats expliquant le système de combat. Traversant la plage jusqu'au village, ils vont trouver un village attaqué par les robots d'Eggman, qui cherchent *un mystérieux médaillon*. Le garçon qui le possède, ainsi que certains autres jeunes du village, vont fuir jusqu'à la crique à l'autre bout de la plage. Les héros devront aller les chercher.
|
||||||
|
|
||||||
|
Le groupe de cinq ados se trouve dans le premier "mini-donjon". Les personnages y utiliseront leur première capacité, le saut, montrant le mélange zelda+plateformer de l'overworld et des donjons. A la fin de ce mini-donjon, les personnages découvriront le premier boss, un Egg-Robo. Cependant, après l'avoir vaincu, ils ne réussiront à sauver que quatre des cinq enfant. Ankou, *l'enfant au médaillon* se fera capturer par un autre EggRobo, qui l'aménera à un endroit inconnu.
|
||||||
|
|
||||||
|
Les héros devront alors retrouver le jeune Ankou, mais découvriront que cela ne sera pas forcément facile...
|
||||||
|
|
||||||
|
## Éléments importants
|
||||||
|
|
||||||
|
- 2 ville : Sunlit Metropolis (peut-être changer le nom pour faire référence aux niveaux tirés de Shadow Boost ?)
|
||||||
|
|
||||||
|
- 1 zone de l'overworld (la plage)
|
||||||
|
|
||||||
|
- 1 donjon (le bosquet)
|
11
game-design/lore-and-story/lore/readme.md
Normal file
11
game-design/lore-and-story/lore/readme.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Sonic Radiance - Lore
|
||||||
|
|
||||||
|
Le Lore de Sonic Radiance contiendra principalement deux éléments : des éléments qui sont spécifiques au jeu, à savoir des éléments sur l'histoire du jeu et la backstory de l'île ou se produit du jeu, et des éléments qui seront spécifique à la vision du monde de Sonic qui est développé dans le jeu.
|
||||||
|
|
||||||
|
Le but de ce fichier est de présenter quelques uns des éléments de base qui constituent ce lore :
|
||||||
|
|
||||||
|
- **Un jeu centré autour d'un lieu en particulier** : Comme SA (et SA2 dans une moindre mesure ?), le jeu se situe autour d'une histoire bien particulière qui aura des répercutions sur le "général". L'histoire sera situé autour de l'île de Sunlit Island, et le lore sera donc adapté autour de cet histoire.
|
||||||
|
|
||||||
|
- **Ambiance typé 2000** : Ce jeu est également un hommage à toute une époque. L'ambiance sera donc à la fois typé Adventure, mais surtout un hommage à tous les "petit Sonic" sorti à cette époque (Advance, Battle, et les jeux mobiles). En découlera une ambiance qui sera unique, et des références à des plot-lines oubliées depuis un moment. Cependant, cela ne veut pas dire que *rien* des jeux récent ne sera adapté ! Certains éléments considérés comme pertinant pour l'histoire ou le gameplay seront présent. Idem pour les éléments du "monde classic", certains seront adapté.
|
||||||
|
|
||||||
|
- **Two-World** : Ce jeu utilise le two-world, parce qu'une des sous-intrigue est fondée dessus.
|
69
game-design/lore-and-story/lore/the-millenium-project.md
Normal file
69
game-design/lore-and-story/lore/the-millenium-project.md
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# Backstory : Le projet Gosth Island
|
||||||
|
|
||||||
|
## Le projet Millenium, le rêve d’une Utopie
|
||||||
|
|
||||||
|
Le projet Gosth Island était l’un des projets fondé par le « projet Millenium », un projet qui visait à construire partout sur la planète des « villes du futur ». Parmi les autres villes fondées par ce projet, il y a Monopole, Metal City et Grand Metropolis. Ce projet était financé par Rimlight et les Fédérations Unis, et tout un tas d’autres acteurs privés. Les Fédérations Unis avaient pour but d’offrir un monde meilleurs à tout les habitants de la planète, et empêcher à nouveau les guerres d’intervenir. Et le but de ces villes étaient d’illustrer la meilleure vie qu’ils auraient tous.
|
||||||
|
|
||||||
|
Ils se sont intéressé à Gosth Island, parce qu’ils estimaient que cette nouvelle énergie présente sur l’île serait un moyen de créer une ville comme jamais vu auparavant, qui dépasserait toute les autres, surtout s’ils réussissaient à faire venir avec eux le peuple qui justement « contrôlait » pour GUN la faille dont provenait l’énergie.
|
||||||
|
|
||||||
|
## L’achat de Gosth Island
|
||||||
|
|
||||||
|
Rimlight s’est donc rendu chez les Sidh et leur a proposé un deal : la coalition pour le projet pourrait travailler sur le Stygian Rift, à condition d’offrir une compensation financière à tout les membres du peuple qui accepteraient. Ils pourraient alors soit aller vers un autre de leur village, le Sidh Village, soit participer à la construction de la cité du futur et des installations scientifiques.
|
||||||
|
|
||||||
|
Le conseil réfléchi plusieurs jours, dans des débats extrêmement tendu. Sur les chefs, une partie étaient pour accepter l’offre, une partie contre. Contre toute attente, c’est la fille de l’un des membres du conseil et une apprentie-prêtresse qui fit basculer la balance en rejoignant elle-même le projet, voulant participer à cette création d’une utopie. Son argumentation fit que quatre des membres du conseil furent pour la vente de l’île. Trois encore refusaient :
|
||||||
|
|
||||||
|
- Benelos, un grand guerrier, qui estimait Ys Gradlon indigne de confiance, et qui refusait de vendre un territoire sacré à des humains. Il estimait que même pour une vie meilleurs, c’était une insulte envers leur membre, et qu’il fallait chercher ailleurs les ressources dont ils manquaient cruellement.
|
||||||
|
|
||||||
|
- Morrigan, la prêtresse de la Stygian Rift estimait qu’utiliser comme une source d’énergie quelque chose d’aussi instable les faisait courir à la catastrophe. L’énergie Stygienne était par nature difficile à contrôler, et son aspect corrupteur aurait sans doute des effects catastrophique sur l’utopie.
|
||||||
|
Cependant, Dahut réussi à convaincre les autres que les risques étaient minimes par rapport à leur situation actuelle : Elle mis en valeur le faible tôt d’alphabétisation de leur population, la mortalité infantile très élevée. La jeune femme estimait qu’ils réussiraient avec leur force à préserver leur civilisation dans la modernité, comme la plupars des autres royaumes mobiens. Elle voulait utiliser la modernité comme un moyen pour les Sidh de continuer à exister, parce qu’elle estimait que simplement l’ignorer serait juste faire une politique de l’autruche jusqu’à ce qu’il serait trop tard pour eux et se ferait engloutir parce qu’ils n’auraient plus le choix.
|
||||||
|
|
||||||
|
- Lug, le plus jeune des membres du conseil, qui estimait que les contreparties étaient très faible, en vue du fait qu'avant l'arrivée des humains sur l'île, ils étaient maître de toutes l'ile.
|
||||||
|
|
||||||
|
Pendant plusieurs jours, les débats devinrent de plus en plus violent, la situation étant bloqué.
|
||||||
|
|
||||||
|
Les débats montèrent en intensité jusqu’à ce que Morrigan et Benelos furent radié du conseil pour les empêcher de continuer à poser leur véto, et exclu avec ceux qui les suivaient de la compensation financière. Ce fut le père de Dahut qui signa le traité avec les Fédérations Unis, ouvrant le début du projet Gosth Island. Il émigra dans le Feral Village pour assurer l’ordre dans son peuple, tandis que les autres chefs décidèrent de rester avec plusieurs Sidh pour participer aux travaux. Ceux qui étaient contre les travaux durent partir aussi, mais sans avoir le droit à une meilleure vie.
|
||||||
|
|
||||||
|
## Des débuts prometteurs mais sous les tensions
|
||||||
|
|
||||||
|
Les travaux commencèrent avec pas mal de succès dans les premiers temps. Ils réussirent à extraire et convertir l’énergie de la faille, et la centrale fut rapidement opérationnelle. Dahut gagna rapidement un rôle important, puisqu’elle était celle qui connaissait le mieux le fonctionnement de la faille. L’énergie était plus forte que tout ce que Rimlight avait prévu, et ils comprirent qu’ils pourraient allimenter toute la région avec. L’avancement de la ville se passait bien, et la construction d’un dôme capable de recréer des climats impressionnait les masses.
|
||||||
|
|
||||||
|
Dans la population de la ville, ce choix fut très décrier, et une partie du conseil du s'en aller de la ville principal, ou ne restèrent que Lug, Benelos et Morrigan pour diriger la ville. Un nouveau village fut entièrement financé par Rimlight pour celles et ceux qui voudraient collaborer avec eux.
|
||||||
|
|
||||||
|
Ils commencèrent à construire une route vers le Feral Village, pour les alimenter aussi, après des négociations du père de Dahut qui estimait que puisqu’ils allaient réussir plus, le peuple Sidh méritait aussi plus. Ce fut accepter, et le Dahut commença à jouir d’une réputation d’excellent négociateur. Mais une négociation était bien plus difficile, celle avec les membres de l'ancien village. Ceux-ci avaient cristalisé l’opposition, pour qu’elle continue à exister. Ils continuèrent d’adresser des critiques au projet, et commencèrent a tenter d’alerter la population générale des deux villages sur la situation de leur peuple.
|
||||||
|
|
||||||
|
Ys Gradlon, commandant du GUN, rejoint alors le projet pour pacifier la situation. Sa stratégie consistait à essayer de diviser le peuple Sidh entre les pour et les contre, pour pouvoir isoler les contre et éviter leur sentiment de s’étendre.
|
||||||
|
Cette situation dura plusieurs mois, dans un statut quo où d’un côté le projet avançait bien, mais où les critiques étaient toujours plus présente.
|
||||||
|
|
||||||
|
Cependant, un incident eut lieu. Un jeune guerrier, Albius, s’était infiltré dans la centrale pour faire un coup d’éclat. Il fut blessé par balle et plongé dans la faille, directement dans le Stygian Realm. Personne ne sut si le soldat avait fait exprès de jeter le jeune adulte blessé dans la faille ou si c'était un accident. Mais c’était un acte d’une violence incroyable pour le peuple Sidh, ce qui provoqua des troubles, et surtout la colère de Morrigan et de Benelos.
|
||||||
|
|
||||||
|
La guerre avait été déclarée.
|
||||||
|
|
||||||
|
## La bataille de Gosth Island
|
||||||
|
|
||||||
|
Une nuit, Benelos attaqua l’ile avec une armée de soldat. Leur but était simple : reprendre leur territoire. La bataille fit rage pendant des heures, et Benelos alla s’attaquer directement à Ys Gradlon, qui avait décidé de garder la faille. Le combat entre les deux fut violent, Ys Gradlon compensant sa faiblesse physique face au mobien bien plus fort par la supériorité de ses armes. En dehors, les soldats du G.U.N. et les guerriers Sidh se battait avec une férocité jamais vue.
|
||||||
|
La bataille fut remportée par GUN, et les guerriers Sidh furent fait prisonnier. Bénélos cependant n’eut pas cette chance, sombrant lors du combat dans la faille. Ys Gradlon tenta de sauver son adversaire, voulant lui offrir un procès équitable, mais en vain. Quant à Morrigan, elle tomba au combat, laissant la faille sans prêtre qualifié pour la première fois depuis des siècles.
|
||||||
|
|
||||||
|
Le peuple Sidh ne retrouva jamais des forces de batailles pour affronter GUN. Cependant, cet événement eut des conséquences très néfastes sur la stabilité de Gradlon, qui sombra petit à petit dans la paranoïa.
|
||||||
|
|
||||||
|
## La démesure de Gradlon
|
||||||
|
|
||||||
|
Ys Gradlon déclara que le projet devait passer entièrement sous contrôle de GUN. Si dans les faits toutes les sociétés qui travaillaient sur le projet pûrent continuer, l’ile se militarisa de plus en plus. Les habitants du peuple Sidh sur l’ile furent déplacé dans des dortoirs, afin de s’assurer de l’absence de traître. Il commença à s’intéresser également aux application militaire du projet. Il considérait l’attaque des Sidh comme une trahison envers la Fédération même, et commença a craindre plus d’ennemi intérieurs. Faisant des cauchemars chaque nuit, devenant de plus en plus méfiant, il garda le même but qu’avant – protéger les Fédérations Unis et ses habitants – mais devint prêt à le faire à tout prix.
|
||||||
|
|
||||||
|
Il fit étudier le Stygian Realm, et la manière dont la faille pouvait happer êtres vivants, songeant aux possibilités de l’utiliser pour faire disparaître des « ennemis des United Federation » (c’est-à-dire les forces militaires opposées à l’unification des états dans la Fédération, les grands bandits, et les groupes terroristes). Il voyait cette dimension comme « la prison ultime », puisque ce serait une prison ou il serait impossible de s’évader, à cause de la corruption provoqué par le Styx. Il commença à théoriser qu'en fin de compte, les mobiens n'étaient rien de plus que des aliens proche à envahir la Terre.
|
||||||
|
|
||||||
|
Ce fut à ce moment là que Dahut commença a se rebeller. Elle refusa de coopérer, estimant qu’une telle prison était contraire à ce que la Fédération Unie et leur projet tenaient. Ys Gradlon s’énerva, et fit emprisonner Dahut. Le Syphon d’Energie du Gosth Core fut maintenu par une plus petite équipe, dont une partie des membres était dévoué à garantir la stability du Stygian Rift.
|
||||||
|
Ys Gradlon fit construit une armée de satellite, les Erinyie, capable de capter l’énergie de la centrale et de se la transmettre, afin de créer une armada de satellite capable de toucher n’importe quel point de la terre. Les forces automatisé de construction furent lancé en orbite, et les Fédérations Unis seraient doté de la capacité de frapper partout sur la Terre, à n’importe quel moment.
|
||||||
|
|
||||||
|
Les Fédérations Unis seraient désormais protégée de toute menace.
|
||||||
|
|
||||||
|
## La fin du projet.
|
||||||
|
|
||||||
|
Cependant, Dahut fini par réussir à s’évader. Elle vit ce qu’était devenu le projet : en place de ville du futur, d’utopie pour l’avenir, il n’y aurait qu’une arme ultime. Elle décida alors de détruire toute l’œuvre de sa vie – ce pour quoi elle avait été même prête à trahir sa famille – et possiblement se tuer avec : Saboter la Gosth Central, pour mettre fin définitivement au projet.
|
||||||
|
|
||||||
|
Les humains la retrouvèrent en train de saboter plusieurs parties de la centrale, brisant tuyaux, détruisant machines de contrôles. Avait-elle pour but de faire exploser la centrale ? Elle fut attrapé après son œuvre, et exécutée après son refus de dire comment réparer ce qu’elle avait fait. Cependant, Gradlon refusa d’évacuer l’île, et ordonna de couper la centrale le temps de réparer les dégats.
|
||||||
|
|
||||||
|
Ce fut là que l’incident se produisit. Dahut avait réussi à placer au fond du Stygian Altar, dans une petite crypte qu’aucun humain ne connaissait, un appareil de sa création. Un petit appareil qui capitait l’énergie et la renvoyait, de sorte à faire saturer toute l’énergie Stygienne. Comme un immense court-circuit d’énergie, à l’intérieur même de la faille. Cela provoquerait le même phénomène de « happe » qui avait touché Bénélos et qu’utilisait Gradlon, mais à l’échelle de l’ile entière. Les sabotages n’avaient été qu’une couverture à sa véritable action, et la machine n’avait pas été retrouvée.
|
||||||
|
|
||||||
|
La saturation d’énergie se produisit donc, et, en une fraction de seconde, il ne resta plus un seul humain ou sidh sur l’île de Gosth Island. De nombreux dégats furent produits sur les mini-générateurs Chaotique de Sunlit Island également. Face à la disparition de leur commandant, et à la perte de toutes les personnes savant faire fonctionner l’île, elle fut désaffectée, et l’île passa sous surveillance de GUN pendant 15 ans.
|
||||||
|
|
||||||
|
Ce fut la fin du projet Millenium.
|
61
game-design/lore-and-story/lore/the-sidh.md
Normal file
61
game-design/lore-and-story/lore/the-sidh.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Sonic Radiance - Peuple Sidth
|
||||||
|
|
||||||
|
Le peuple Sidh est un peuple de Mobien hétéroclite qui vit sur l'île de Sunlit Island. Ayant autrefois occupé toute l'île, ainsi que la petite île au large de Gosth Island, ils ne vivent aujourd'hui que dans le XXXXXX Village, suité dans les plaines de XXXXX.
|
||||||
|
|
||||||
|
Plus qu'un peuple basé sur une race (genre les échidnés), ils sont plus réunis par une culture. Basée sur une culture celtique (notamment les mythologies Bretonne, Irlandaise, Galloise et Ecossaise), les personnages de ce peuple ne sont pas nommés suivant la base normale de Sonic (des mots anglais), mais selon des termes de la culture visée (ici des dieux ou mots celtes), un peu comme Tikal et Pachacamac ont des noms basé sur des temples améridiens.
|
||||||
|
|
||||||
|
Leur rôle dans l'intrigue sera un peu ambigüe, notamment à travers les personnages de *Taranis*, *Ankou* ou *Anrad*. Leur nom vient du nom du *Sidh*, royaume des morts chez les Celtes.
|
||||||
|
|
||||||
|
## Culture et mode de vie
|
||||||
|
|
||||||
|
Vivant dans leur village, dans les plaines et juste avant le XXXX (qu'ils protègent des intrus). Ils vivent principalement en autarcie, et ont une grande méfiance pour les humains. Ils accepteront même de s'allier avec Eggman en échange de vivre et de soutiens financier (estimant que la guerre contre Eggman est une affaire d'humain).
|
||||||
|
|
||||||
|
Aujourd'hui, ils protègent surtout le Super Warp Ring qui se trouve dans les cavernes au fond de leur vallée.
|
||||||
|
|
||||||
|
Panthéïstes, ils croient en la *Grande Mère*, qui est une déesse de la nature qui est présente en chaque endroit en chaque instant. De ce fait, ils vénèrent tout ce qui est surnaturel, et proche du Chaos, la principale des forces naturelles. En cela, ils ont longtemps vénéré le Stygian Rift situé sur l'île de Gosth Island, qu'ils alimentaient en sacrifice jusqu'à ce qu'ils soient exproprié de l'île de Gosth Island, lors de la construction de la Gosth Central
|
||||||
|
|
||||||
|
La "faille noire" comme ils l'appelle reste aujourd'hui encore ce qu'ils vénèrent le plus, et cette expropriation est source de leur colère contre les Fédérations Unis.
|
||||||
|
|
||||||
|
Leur chef actuel est Lug, père de Taranis et Anrad, qui est prêt à tout ce qu'il faudra pour aider ses enfants et surtout son peuple.
|
||||||
|
|
||||||
|
### Le second village
|
||||||
|
|
||||||
|
Il existe un second village, se trouvant au niveau des plages du sud. Ce village est constitué des Sidh ayant désiré participé dans le projet Gosth Island et n'ayant pas péri lors de l'incident.
|
||||||
|
|
||||||
|
Les villageois du second village (surnommé le village de *Bhàin*) vivent principalement du revenu du tourisme, et sont assez pauvre mais restent attaché à leur indépendance et à leur mode de vie.
|
||||||
|
|
||||||
|
Cependant, ils sont beaucoup moins attaché au fait de rester séparer des humains, et sont considéré par leur pairs du village principal comme des traitres.
|
||||||
|
|
||||||
|
## Histoire du peuple Sidh
|
||||||
|
|
||||||
|
### Les premiers Sidh
|
||||||
|
|
||||||
|
Le peuple Sidh sont à l’origine un groupe de mobien hétéroclite qui s’est installé sur l’île, qui était déserte à l’origine, il y a très longtemps par le Super Warp Ring. Suivant un petit groupe de penseurs, les Sages, ils fondèrent une société, visant à vivre suivant leur vertu et leurs idéaux. Fondée sur la pensée et la recherche du bien absolu, ils ont fondé une civilisation surtout centrée sur la discussion et la présence de ces Sages qui devaient conduire les débats et faire évoluer la science.
|
||||||
|
|
||||||
|
Tous les sages et les chefs se devaient de passer quatre épreuve, liées aux quatre vertues cardinales (le courage, la tempérence, la justice et la sagesse). Leur but était de créer une civilisation d'être qui auraient atteint l'état de perfection morale. Cependant, dans leur vision, ils avaient également un aspect "pédant". S'ils voulaient aider les autres, ils avaient tendance à voir de haut les peuples "moins intelligents"
|
||||||
|
|
||||||
|
Petit à petit, ils sont devenus une civilisation bénévole et qui a eut de grande avancées techniques, à la manière des Chozos de Metroid. Ils découvrirent sur la petite île de Gosth Island au large de leur île principale la *Chaos Vein*, qu'ils commencèrent à étudier. C'est eux qui sont à l'origine de tous les temples et technologies anciennes qui se trouvent sur l'île. Leur but était d'utiliser la technologie de la faille pour améliorer la santé de leur peuple, ainsi que de facilité la vie des différents peuples.
|
||||||
|
|
||||||
|
### La chute
|
||||||
|
|
||||||
|
Cependant, ce qui a été la source de leur brillance fut celle aussi de leur fin, à cause de la corruption de la Chaos Vein. En effet, vers la fin de l'île, un groupe de sage commença un nouveau projet. Leur but était de créer un pouvoir qui vaincrait la malchance et l'infortune, la maladie et la peur. Leur but n'était pas de "vaincre la mort" ou d'autres choses du genre, mais de créer une énergie qui apporterait à tous santé et bonne fortune.
|
||||||
|
|
||||||
|
Mais une chose leur manquait. La puissance. La Chaos Vein leur apportait énormément de puissance, mais ce n'était pas suffisant pour leur plan. Cela provoqua chez les sages qui travaillait sur le plan une dispute. Que faire ? Des tas de possibilités existaient. Faire une arme provoquant méfortune, et aller chercher d'autres failles ? Abandonner le projet et se contenter des améliorations déjà présente ? Leur choix fut tout autre.
|
||||||
|
|
||||||
|
Les Sidh tentèrent de faire évoluer la Chaos Vein afin de pouvoir avoir plus d'énergie. Utilisant un dispositif pour centrer l'énerige de la faille, cette machine s'emballa, provoquant une catastrophe. Cela provoqua l'évolution du Chaos en une autre force, le Styx. Cette énergie corrompu commença a tout envahir, et la faille commença a happer des êtres vivants.
|
||||||
|
|
||||||
|
### La période sombre et la reconstruction
|
||||||
|
|
||||||
|
Le peuple Sidh, ayant perdu une grande partie de son peuple et de sa civilisation, ne survécu pas en tant que civilisation. Il s'effondra progressivement, et bientôt ne resta de l'ancienne époque qu'un souvenir lointant, celui de l'époque ou le peuple Sidh était "proche des dieux".
|
||||||
|
|
||||||
|
Ils commencèrent à craindre et à vénérer la Chaos Vein, désormais devenu le Stygian Rift. Entendant ses murmures, ils le voyaient comme une émanation maléfique de la puissance naturelle. Ils y ont longtemps conduit des sacrifices. Cependant, cela ne fit qu'augmenter la voracité et l'instabilité de la faille, jusqu'à ce qu'ils approchent de l'extermination à cause de cette voracité.
|
||||||
|
|
||||||
|
Les quelques sages qui restaient continuèrent à faire les épreuves et à conduire la civilisation, mais ne connaissaient que de moins en moins les origines de leur culture. Ils s'isolèrent de plus en plus du monde, conscient d'avoir été à l'origine de la naissance d'un danger, et effrayé par ce danger. Ce n'était pas qu'ils ne comprenaient pas ce danger ou qu'ils étaient stupide : ils étaient effrayé, et n'avaient plus le choix.
|
||||||
|
|
||||||
|
Ce fut lorsque la civilisation approcha encore de la chute qu'une des sages décida de tenter autre chose *Dana*, toute sa vie, elle étudia et appris de la faille, avec ses collègues, jusqu'à aller affronter la faille pour la calmer. Elle réussi à apaiser la faille, devenant la première des pretresses de la faille.
|
||||||
|
|
||||||
|
Petit à petit, les sages et les rituels disparurent, remplacé petit à petit par un conseil, qui prenait les décisions avec le reste du peuple. Le peuple continua à exister, et évolua perdant son accès à ses technologies. L'île de Gosth Island devint une île sacrée. Les prêtres de la faille continuèrent à exister, et cet état de fait évolua, jusqu'à ce qu'arrive le *projet Millienium* il y a quinze ans.
|
||||||
|
|
||||||
|
En effet, l'incident du projet Millienium provoqua le décès d'une partie du conseil, et la perte de Gosth Island qui devint une île interdite, protégée militairement par les *GUN*. Il ne resta du conseil plus qu'un des chefs (Lug, le chef actuel), une chef existant à l'état spectral (Morrigan) et une fille de chef supposée vivante mais disparue depuis l'incident de Gosth Island. (Dahut)
|
||||||
|
|
||||||
|
Le peuple vit aujourd’hui divisé, dans une certaine précarité, et une partie s’est tourné récemment vers Eggman, lui offrant des connaissance sur le Stygian Rift en échange de nourriture, de médicament et de vengeance pour certains. Les survivants de l'incident ayant été membres du projet furent bannis, et fondèrent un petit village sur la côte, vivant désormais des revenus touristiques.
|
41
game-design/lore-and-story/lore/world/earth.md
Normal file
41
game-design/lore-and-story/lore/world/earth.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# La Terre
|
||||||
|
|
||||||
|
La "Terre" de Sonic Radiance est une terre alternative à la notre, ou les pays sont principalement remplacé par leurs Erzats de Sonic Radiance, et où la forme des continent est légèrement différent.
|
||||||
|
|
||||||
|
Cette Terre possède comme principale puissance politique les *Fédérations Unis*, et comme plus grande force militaire les *Guardian Units of Nations* (ou GUN).
|
||||||
|
|
||||||
|
## Les Fédérations Unis
|
||||||
|
|
||||||
|
Les Fédérations Unis sont la république parlementaire avec un régime présidentiel qui couvre une grande partie de la planète Terre. Plus puissante nation de la planète, elle a subit plusieurs crise depuis le retour du Docteur *Ivo Robotnik* sur Terre lors de l'incident de Christmas Island.
|
||||||
|
|
||||||
|
Elles peuvent être vu comme quelque chose entres les Nations-Unis et les États-Unis. Le but de cet état est de garantir un développement tout en respectant les libertés des pays qui en font partie (et qui conserve une certaine indépendance). L'île de Sunlit Island est sous contrôle des Fédérations Unis, avec comme représentant le maire de la ville de *Sunlit Metropolis*.
|
||||||
|
|
||||||
|
Leur technologie est majoritairement contemporaire, mais a réussi de grande avancée depuis 50 ans à l'aide des technologies chaotique, en grande partie découvertes par le professeur Gérald
|
||||||
|
|
||||||
|
Dans le jeu, les Fédérations Unis, n'ont pas une présence très forte dans le jeu, en tout cas pas directement.
|
||||||
|
|
||||||
|
## Les Gardian Units of Nations
|
||||||
|
|
||||||
|
Les Gardian Units of Nations (ou G.U.N. parfois orthographié en GUN) sont la principale force militaire des Fédérations Unis, et ont comme rôle de protéger à la fois les Fédérations Unis, et à la fois les pays alliés. Cette armée est dirigé par un Commandant.
|
||||||
|
|
||||||
|
L'armée est actuellement en grande partie dédiée à affronter le Dr. Ivo Robotnik, qui vise à conquérir le monde, mais n’arrive pas à le localiser et à lancer des attaques précises. La capacité à disparaître de ce savant étant déconcertante, et ses moyens encore inconnus. L'inefficacité de GUN à vaincre les menances originaire du monde des animaux est la source d'une méfiance chez certaines personnes.
|
||||||
|
|
||||||
|
## Rimlight, les Chaos Central et les Chaos Vein
|
||||||
|
|
||||||
|
La société Rimlight est l’un des éléments scénaristique important du jeu. Holding co-détenu par des actionnaires privés et par le GUN, la société recherche dans le domaine des énergies propres, et plus précisément dans le domaine des énergies chaotiques.
|
||||||
|
|
||||||
|
Ils utilisent notamment pour cela les thèses de Gerald Robotnik sur ce type d’énergie, quand il était chercheur à l’université de Central City, thèses qui avaient conduit Gerald à faire des recherches sur les civilisations anciennes échidnés, pour mieux comprendre leur rapport à l’énergie chaotique (d’où les découvertes sur Chaos, la prophétie échidné, Gizoïd…) et le fait qu’il fut choisit comme chef de l’équipe du projet Shadow. Ils sont les producteurs des Chaos Drive, et des Chaos Central.
|
||||||
|
|
||||||
|
### Les Chaos Central
|
||||||
|
|
||||||
|
Les Chaos Central sont une source d’énergie de plus en plus présente sur toute la planète. Construite sur des zones nommées les Veines Chaotiques (Chaos Vein) et sont utilisées depuis environs vingt ans (pour le marché publique) à vingt-cinq ans (pour les Guardian Units of Nations). L’énergie produite par les Chaos Central revet deux forme : l’éléctricité, ou les Chaos Drives, des batteries chaotiques extrêmement durable utilisée notamment par les mécha des Guardian Units of Nations.
|
||||||
|
|
||||||
|
L’intérieur d’une centrale chaotique est cependant un endroit dangereux à cause de l’influence de l’énergie chaotique et de sa transformation en électricité ou en *Chaos Shard* pour les Chaos Drive, et est réservé à des connaisseurs de l’énergie chaotique. Le cœur d’une centrale chaotique, nommé le *Chaos Core*, est un endroit ou l’énergie circule suivant des patterns très précis.
|
||||||
|
|
||||||
|
C’est la deuxième source d’énergie la plus utilisée, après l’énergie thermique liquide produite par la société HexaECO, utilisée depuis à peu près la même période.
|
||||||
|
|
||||||
|
### Les Chaos Vein
|
||||||
|
|
||||||
|
Les Chaos Vein (Veines Chaotiques) sont la source de l’énergie utilisée dans les Chaos Central du conglomérat Rimlight. Il s’agit de zones d’émission spontanée d’Énergie Chaotique qui existent à quelques endroits de la planète, relativement rares mais trouvable sur tout les continents. Même si ces failles sont rien face à des Chaos Emerald, il est possible de produire de une énergie propre et durable à l’aide de ces failles. Quand elles ne sont pas sur-exploité (comme ce qui est arrivé dans un incident avec l’une des premières centrales chaotiques), ces failles sont contrairement au Chaos Emerald particulièrement docile à utiliser.
|
||||||
|
|
||||||
|
Les Chaos Vein sont des failles vers des univers-bulles qui leur sont propre (des sortes de Special Zone à petite échelle, mais qui ne contiennent pas de Chaos Emerald) et qui est la source de cette énergie. C’est ce qui fait l’aspect durable et stable à la fois de cette énergie : seul très peu d’énergie s’échappe de la veine, mais la reserve qu’il y a derrière est largement au dessus de toute la consommation humaine planétaire depuis les débuts de la révolution industrielle, au bordure de l’immesurable.
|
29
game-design/lore-and-story/lore/world/two-world.md
Normal file
29
game-design/lore-and-story/lore/world/two-world.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# Sonic Radiance - Les deux mondes
|
||||||
|
|
||||||
|
Sonic Radiance se base au niveau de son univers sur le concept des deux mondes (à la Sonic X) : Deux univers coexistent, et les personnages peuvent passer de l'un à l'autre. Ces deux mondes sont ceux de la Terre, une planète organisée et peuplée surtout par les humains, et un monde sans nom nommé le "monde des animaux", peuplé surtout par des animaux anthropomorphiques intelligents.
|
||||||
|
|
||||||
|
Le passage d'un monde à l'autre est conduit par l'existence de *Super Warp Ring*, des immenses anneaux qui ont le pouvoir d'être des passage entre les deux mondes. Ils étaient inactif depuis des milliers d'année quand les *expéditions interdimensionnelles* d'il y a 50 ans, puis *l'incident de Christmas Island* ont provoqué leur réactivation, et la connexion des deux mondes. Depuis, les deux mondes se connaissent, et doivent coéxister, non sans quelques… réserves, parfois.
|
||||||
|
|
||||||
|
## Les quatres grandes civilisations
|
||||||
|
|
||||||
|
Les quatres grandes civilisations sont les quatres peuples qui ont les premiers été capable de voyager entre les mondes et de construire quelque chose hors de leur monde d'origine. Ce sont tous des peuples originaires du monde des animaux.
|
||||||
|
|
||||||
|
### La première grande civilisation
|
||||||
|
|
||||||
|
Peu est connu de la première grande civilisation. Ils sont surnommés les *Premiers* et autres surnoms vagues du genre, mais n'ont laissé que peu de trace, à part quelques éléments.
|
||||||
|
|
||||||
|
Ils sont théorisés comme étant les premiers utilisateurs de la puissance des *Chaos Emerald*, des *Power Rings*, et comme étant ceux qui ont créé de nombreux artefacts et entités tels que les *Super Warp Rings*. Nous ne savons cependant rien de leur culture, ni de leur évolution. Ils semblent avoir été présent sur les deux planètes, et avoir prospéré il y a plus de 10 000 ans.
|
||||||
|
|
||||||
|
### La seconde grande civilisation
|
||||||
|
|
||||||
|
***TODO***: écrire un truc dessus.
|
||||||
|
|
||||||
|
### La troisième grande civilisation
|
||||||
|
|
||||||
|
(note : voir *peuple-sidh*)
|
||||||
|
|
||||||
|
La troisième grande civilisation est le peuple Sidh ancien, qui a proposéré sur Sunlit Island.
|
||||||
|
|
||||||
|
### La quatrième grande civilisation
|
||||||
|
|
||||||
|
***TODO***: Choisir si je reprend les Nocturnus ou en fait une civilisation qui aurait exclavagiser les échidnés ?
|
15
game-design/lore-and-story/subquest/ideas.md
Normal file
15
game-design/lore-and-story/subquest/ideas.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Subquest - ideas
|
||||||
|
|
||||||
|
- **The last of his kind** -- une histoire (peut-être en plusieur partie ?) où Shadow et Knuckles doivent travailler ensemble pour traquer le dernier Shadow Android. Knuckles ressent un peu de compassion envers le robot (qui a une conscience, et qui doit faire avec le fait de ne pas être Shadow et de savoir que le vrai Shadow à détruit une grande partie de son "espèce"), sachant ce que c'est d'être le dernier de son espace
|
||||||
|
|
||||||
|
- **Frozen Earth Theory** -- un event spécial noel, où Eggman à décidé de refroidir la planète comme chantage mondial, en précisant bien les riques pour les recoltes, l'alimentation, etc… Particularité graphique de ce scénario : Tout les niveaux utilisent les tiles de neige de Pearl Mountain.
|
||||||
|
|
||||||
|
- **Phantom Doll** -- Event special halloween : Tails Doll à récupéré un prototype de Phantom Ruby. Ouch.
|
||||||
|
|
||||||
|
- **The Chaos Breakers** -- La Team Dark est appellé sur Terre pour affronter de nouveaux ennemis, qui menace le portail dimensionnel : Les Chaos Breakers. Cette faction rebelle de GUN estime que les mobiens étant des aliens, on ne peut pas leur faire confiance, et qu'ils ont déjà apporté le chaos sur Terre.
|
||||||
|
- Un scénario Rouge + Amy
|
||||||
|
- Un scénario Cream + Omega
|
||||||
|
- Un scénario special saint-velentin Omega + Blaze :DDD
|
||||||
|
- Une série de scénario où GUN travaille pour connecter le monde de Blaze aux deux autres… malheureusement, des soucis se passeront.
|
||||||
|
|
||||||
|
- Scénario post-Forces avec l'explication de pourquoi GUN n'a pas aidé Mobius/le monde des animaux pendant l'invasion d'Eggman.
|
79
game-design/main-gameplay/battle-system.md
Normal file
79
game-design/main-gameplay/battle-system.md
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Sonic Radiance - Battle System
|
||||||
|
|
||||||
|
Le système de combat de Sonic Radiance est un système de combat basée à la fois sur du tactical RPG et sur du JRPG classique. Les personnages et les ennemis sont placé sur une grille de 13×7 case, sur laquelle ils peuvent se déplacer de deux cases par tour.
|
||||||
|
|
||||||
|
Les personnages et ennemis agissent plusieurs fois par tour, entre 1 et 3 fois (hors augmentations comme le status *Speed Up*), et agissent à la suite du plus au moins rapide (avec un malus dans les "actions suivantes par tour", ce qui fait que les trois actions de Sonic ne seront pas forcément à la suite, mais cela pourra arriver tout de même). Les actions sont choisies juste avant le tours (à la Octopath Traveller)
|
||||||
|
|
||||||
|
## Compétences utilisables
|
||||||
|
|
||||||
|
- Attaquer : le personnage fait une série de trois attaques consécutives à 33% de l'attaque. Chaque attaques consécutives peut faire séparément un critique. Certaines capacités passives pourront augmenter le nombre d'attaques pour leur puissance.
|
||||||
|
|
||||||
|
- Compétences : Le personnage utilise l'une des compétences qui lui est propre. Voir dans les fiches de personnages les compétences utilisables.
|
||||||
|
|
||||||
|
- Objet : utilisation d'un objet (voir objets)
|
||||||
|
|
||||||
|
- Défendre : Le personnage se défend, lui permettant d'éviter ce tour-ci les effets d'un piège s'il se retrouve dessus, et d'encaisser une partie des dégats jusqu'à son tour suivant.
|
||||||
|
|
||||||
|
- Fuir : le personnage tente de fuir.
|
||||||
|
|
||||||
|
## Stats
|
||||||
|
|
||||||
|
Le système de statistique de Sonic Radiance est inspiré de Sonic Chronicles, mais amélioré pour offrir une vrai stat d'attaque et de défense aux personnages.
|
||||||
|
|
||||||
|
- **HP** :: les points utilisé pour la vie
|
||||||
|
|
||||||
|
- **PP** :: les points utilisé pour les POW Moves
|
||||||
|
|
||||||
|
- **Attack** :: Affecte les dégats commis avec des attaques directes
|
||||||
|
|
||||||
|
- **Defense** :: Affecte les dégats qu'on se prend par attaque physique
|
||||||
|
|
||||||
|
- **Technique** :: Affecte les coups critiques et les effets secondaires des attaques (face à Mind)
|
||||||
|
|
||||||
|
- **Power** :: Affecte les coups à distance (genre attaque de chaos)
|
||||||
|
|
||||||
|
- **Mind** :: Affecte la resistance aux attaques à distance (face à Power) et aux effets secondaires (face à Technique)
|
||||||
|
|
||||||
|
- **Speed** :: Affecte l'évasion et quand le personnage va attaquer
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
### Positif
|
||||||
|
|
||||||
|
- Camouflé : Augmente l'esquive
|
||||||
|
|
||||||
|
- Focus : Augmente chances de réussir un coup + coup critique
|
||||||
|
|
||||||
|
- Fortifié : Baisse les dégats subits
|
||||||
|
|
||||||
|
- Elem Fortif' : Comme Fortifié, mais rajoute des dégats de l'élément du bouclier
|
||||||
|
|
||||||
|
- Elem Power : Les attaques sont fortifié de 110% et rajoutent des dégats élémentaires
|
||||||
|
|
||||||
|
- Hyper : Augmente la puissance des coups (inspiré plus du mode Hyper de Sonic Fighter)
|
||||||
|
|
||||||
|
- Chanceux : Baisse les coups critique de l'ennemi + les risques de se prendre un effet secondaire
|
||||||
|
|
||||||
|
- Invincible : Annule tout les dégats subit
|
||||||
|
|
||||||
|
- Acceléré : Augmente vitesse + fait agir 1 fois supplémentaire
|
||||||
|
|
||||||
|
- Attrap'Anneaux : Augmente les anneaux gagné à la fin du combat
|
||||||
|
|
||||||
|
### Négatif
|
||||||
|
|
||||||
|
- Empoisonné : Perd 15% de PV/tour
|
||||||
|
|
||||||
|
- Affaibli : Baisse les dégats provoqués
|
||||||
|
|
||||||
|
- Vulnérable : Augmente les dégats subits
|
||||||
|
|
||||||
|
- Paralysé : Ne peut plus agir pendant x tours
|
||||||
|
|
||||||
|
- Endormi : Ne peut plus agir ni se déplacer. % de chance de se réveiller à chaque tours
|
||||||
|
|
||||||
|
- Maudit : L'ennemi subit plus facilement tout les effets secondaire et les coups critiques
|
||||||
|
|
||||||
|
- Distrait : L'ennemi rate plus souvent ses coups
|
||||||
|
|
||||||
|
- KO : ne peut plus agir, n'a plus de PV, etc.
|
57
game-design/subgames/sonic-boost/README.md
Normal file
57
game-design/subgames/sonic-boost/README.md
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Sonic Boost
|
||||||
|
|
||||||
|
## Principe de base
|
||||||
|
|
||||||
|
Sonic Boost est un sub-game avec un aspect un peu "mobile" inspiré de plusieurs sources, qui fera partie du projet Sonic Radiance. Ce subgame sera utilise dans le scénario pour des phases "rapides", dans des quêtes annexes comme mini-jeu/mini-niveau, et apparaitra en tant que bonus complet (un peu à la manière du jeu de kart de Sonic Adventure 2)
|
||||||
|
|
||||||
|
Ses inspirations sont les suivantes :
|
||||||
|
|
||||||
|
- Le premier fangame jamais créé ([Sonic Boom](https://www.youtube.com/watch?v=jYT28un2pEY)), ainsi que son remake ([Neo Sonic Boom](https://www.youtube.com/watch?v=MSofjiB_4J8&t=577s)). Ce jeu se veut sur certain point une combinaison de ce premier fangame avec d'autres sources d'éléments.
|
||||||
|
|
||||||
|
- Shadow Shoot, un jeu mobile SEGA du Sonic Café, qui va être la principale source des graphismes et du gameplay de base. Le jeu va reprenddre ses graphismes, et le fait de déplacer sur 5 rails (mais l'aspect "shoot" sera réservé à certains personnages).
|
||||||
|
|
||||||
|
- La série de vieux fangame de course "Sonic R GM", "Sonic R DX", "Sonic R MV" de NICKtendo DS
|
||||||
|
|
||||||
|
- Les jeux mobile Sonic récent (c'est à dire post-Sonic Café) comme Sonic Dash ou Sonic Runners
|
||||||
|
|
||||||
|
- Smash Bros, sur certains points (aspect "collection" et tout)
|
||||||
|
|
||||||
|
## Modes de jeux (si c'est possible à mettre dans les temps)
|
||||||
|
|
||||||
|
Sonic Boost sera un subgame avec quelques utilisations dans le mode histoire, et quelques modes bonus accessible pour plus de rejouabilité. Les passages en mode "Sonic Boost" seront rejouable dans le menu en plus d'être jouable dans le jeu principal.
|
||||||
|
|
||||||
|
### Utilisation dans le mode de jeu principal
|
||||||
|
|
||||||
|
Le jeu principal et ses quêtes annexes contiendront plusieurs moments où vous devrez foncer à travers des niveaux en utilisant le gameplay type Boost. Si dans l'aventure principal ils ne seront pas forcément très souvent utilisé (3~4 fois au total), ils seront plus régulièrement présent en tant que niveau bonus permettant d'obtenir des emblèmes supplémentaires, ou en faisant partie de quêtes secondaires.
|
||||||
|
|
||||||
|
TODO: Definir exactement quels types de manière dont on peut jouer à un niveau.
|
||||||
|
|
||||||
|
Chaque niveau déjà joué sera rejouable dans le menu principal. Ce mode de jeu jeu s'inspirera alors à la fois du mode "Brawl/Smash" de Smash Bros, et de la manière dont se joue les niveaux de Sonic R GM. En effet, vous pourrez choisir votre personnage, puis choisir vos niveaux et la manière dont vous voulez y jouer (mission, mode "rival", etc).
|
||||||
|
|
||||||
|
### Mode "Endless"
|
||||||
|
|
||||||
|
Ce mode de jeu s'inspire des jeux comme Sonic Runners et Sonic Dash. Vous courrez dans l'un des niveaux disponible, avec des chunks qui se rajouteront au fur et à mesure. Vous avez un temps limité, mais qui se rempli à chaque checkpoint. Ces niveaux devront d'abord être débloqué dans chaque environnement de l'île pour être rejouable dans le menu principal.
|
||||||
|
|
||||||
|
Vous n'avez cependant qu'une vie, et le niveau se termine dès que vous perdez.
|
||||||
|
|
||||||
|
### Mode Classic
|
||||||
|
|
||||||
|
Inspiré du mode classic de Smash Bros, et du premier fangame (Sonic Boom / Neo Sonic Boom).
|
||||||
|
|
||||||
|
Vous jouez une série de 7 niveaux, chacun se terminant par un boss. Les niveaux sont choisi de manière semi aléatoire (pour chaque place, il y aura 2 niveaux possible). Dans chaque niveau, vous pouvez trouver une clef, qui vous permettra d'aller dans le special stage ensuite (sauf si vous perdez une vie). Comme dans Smash Ultimate, chaque personnage jouable aura son propre mini "scénario".
|
||||||
|
|
||||||
|
Si vous réussissez tout les special stage, vous pouvez aller au dernier niveau spécial "Milky Way".
|
||||||
|
|
||||||
|
Certains niveaux auront des gimmicks particulier, basé sur les différentes manières de jouer du mode normal (qui servira aussi à choisir des missions)
|
||||||
|
|
||||||
|
### Mode Tournoi
|
||||||
|
|
||||||
|
Il sera possible de débloquer quatre "coupe" dans le jeu principal en allant parler à Jet. Chaque coupe terminée pourra être rejouée dans le menu principal.
|
||||||
|
|
||||||
|
Cela consistera en un nombre de 4 coupes de 16 niveaux qui seront forcément en mode rival. Il sera possible d'utiliser des Wisps pour déconcentrer les adversaires, avec des effets similaires à ceux du mode combat.
|
||||||
|
|
||||||
|
### Mode "Shadow Shoot"
|
||||||
|
|
||||||
|
Ce mode de jeu sera aussi utilisé pour créer un clone de Shadow Shoot sur Mobile. Il sera possible de le débloqué après une quête annexe mineure centrée sur Shadow.
|
||||||
|
|
||||||
|
NOTE : Cela pourrait simplement être le mode Classic de Shadow ?
|
BIN
sonic-radiance.love/assets/artworks/logo.png
Normal file
BIN
sonic-radiance.love/assets/artworks/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
sonic-radiance.love/assets/artworks/titlescreen_sonic.png
Normal file
BIN
sonic-radiance.love/assets/artworks/titlescreen_sonic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
BIN
sonic-radiance.love/assets/backgrounds/titlescreen.png
Normal file
BIN
sonic-radiance.love/assets/backgrounds/titlescreen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 118 KiB |
BIN
sonic-radiance.love/assets/gui/borders.png
Normal file
BIN
sonic-radiance.love/assets/gui/borders.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 425 B |
41
sonic-radiance.love/conf.lua
Normal file
41
sonic-radiance.love/conf.lua
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
function love.conf(t)
|
||||||
|
t.identity = "space.kazhnuz.SonicRadiance" -- The name of the save directory (string)
|
||||||
|
t.version = "11.1" -- The LÖVE version this game was made for (string)
|
||||||
|
t.console = false -- Attach a console (boolean, Windows only)
|
||||||
|
t.accelerometerjoystick = false -- Enable the accelerometer on iOS and Android by exposing it as a Joystick (boolean)
|
||||||
|
t.gammacorrect = false -- Enable gamma-correct rendering, when supported by the system (boolean)
|
||||||
|
|
||||||
|
t.window.title = "Sonic Radiance" -- The window title (string)
|
||||||
|
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
|
||||||
|
t.window.width = 424 -- The window width (number)
|
||||||
|
t.window.height = 240 -- The window height (number)
|
||||||
|
t.window.borderless = false -- Remove all border visuals from the window (boolean)
|
||||||
|
t.window.resizable = false -- Let the window be user-resizable (boolean)
|
||||||
|
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
|
||||||
|
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
|
||||||
|
t.window.fullscreen = false -- Enable fullscreen (boolean)
|
||||||
|
t.window.fullscreentype = "exclusive" -- Choose between "desktop" fullscreen or "exclusive" fullscreen mode (string)
|
||||||
|
t.window.vsync = true -- Enable vertical sync (boolean)
|
||||||
|
t.window.msaa = 0 -- The number of samples to use with multi-sampled antialiasing (number)
|
||||||
|
t.window.display = 1 -- Index of the monitor to show the window in (number)
|
||||||
|
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
|
||||||
|
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
|
||||||
|
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
|
||||||
|
|
||||||
|
t.modules.audio = true -- Enable the audio module (boolean)
|
||||||
|
t.modules.event = true -- Enable the event module (boolean)
|
||||||
|
t.modules.graphics = true -- Enable the graphics module (boolean)
|
||||||
|
t.modules.image = true -- Enable the image module (boolean)
|
||||||
|
t.modules.joystick = true -- Enable the joystick module (boolean)
|
||||||
|
t.modules.keyboard = true -- Enable the keyboard module (boolean)
|
||||||
|
t.modules.math = true -- Enable the math module (boolean)
|
||||||
|
t.modules.mouse = true -- Enable the mouse module (boolean)
|
||||||
|
t.modules.physics = true -- Enable the physics module (boolean)
|
||||||
|
t.modules.sound = true -- Enable the sound module (boolean)
|
||||||
|
t.modules.system = true -- Enable the system module (boolean)
|
||||||
|
t.modules.timer = true -- Enable the timer module (boolean), Disabling it will result 0 delta time in love.update
|
||||||
|
t.modules.touch = true -- Enable the touch module (boolean)
|
||||||
|
t.modules.video = true -- Enable the video module (boolean)
|
||||||
|
t.modules.window = true -- Enable the window module (boolean)
|
||||||
|
t.modules.thread = true -- Enable the thread module (boolean)
|
||||||
|
end
|
38
sonic-radiance.love/core/debug.lua
Normal file
38
sonic-radiance.love/core/debug.lua
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
-- core/debug.lua :: Debug functions for the core system.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local DebugSystem = Object:extend()
|
||||||
|
|
||||||
|
local lovebird = require("libs.lovebird")
|
||||||
|
|
||||||
|
function DebugSystem:new(controller, active)
|
||||||
|
self.controller = controller
|
||||||
|
lovebird.update()
|
||||||
|
self.active = active or false
|
||||||
|
end
|
||||||
|
|
||||||
|
function DebugSystem:update(dt)
|
||||||
|
lovebird.update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
return DebugSystem
|
71
sonic-radiance.love/core/init.lua
Normal file
71
sonic-radiance.love/core/init.lua
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
-- core/init.lua :: The main file of the core system, an object full of subsystem
|
||||||
|
-- loaded by the game to handle the main functions (like screen, translation,
|
||||||
|
-- inputs…)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local CoreSystem = Object:extend()
|
||||||
|
|
||||||
|
local DebugSystem = require "core.debug"
|
||||||
|
|
||||||
|
local Options = require "core.options"
|
||||||
|
local Input = require "core.input"
|
||||||
|
local Screen = require "core.screen"
|
||||||
|
local Lang = require "core.lang"
|
||||||
|
local SceneManager= require "core.scenemanager"
|
||||||
|
|
||||||
|
function CoreSystem:new()
|
||||||
|
self.debug = DebugSystem(self)
|
||||||
|
self.options = Options(self)
|
||||||
|
self.input = Input(self)
|
||||||
|
self.screen = Screen(self)
|
||||||
|
self.scenemanager = SceneManager(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CoreSystem:mousemoved(x, y, dx, dy)
|
||||||
|
local x, y = self.screen:project(x, y)
|
||||||
|
local dx, dy = self.screen:project(dx, dy)
|
||||||
|
self.scenemanager:mousemoved(x, y, dx, dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CoreSystem:mousepressed( x, y, button, istouch )
|
||||||
|
local x, y = self.screen:project(x, y)
|
||||||
|
self.scenemanager:mousepressed( x, y, button, istouch )
|
||||||
|
end
|
||||||
|
|
||||||
|
function CoreSystem:update(dt)
|
||||||
|
self.debug:update(dt)
|
||||||
|
self.input:update(dt)
|
||||||
|
|
||||||
|
self.scenemanager:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CoreSystem:draw()
|
||||||
|
self.scenemanager:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function CoreSystem:exit()
|
||||||
|
self.options:save()
|
||||||
|
love.event.quit()
|
||||||
|
end
|
||||||
|
|
||||||
|
return CoreSystem
|
108
sonic-radiance.love/core/input.lua
Normal file
108
sonic-radiance.love/core/input.lua
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
-- core/input.lua :: The input system. This object take care of transforming the
|
||||||
|
-- differents inputs source in a virtual controller for the game.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local InputManager = Object:extend()
|
||||||
|
|
||||||
|
function InputManager:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.data = self.controller.options.data.input[1]
|
||||||
|
|
||||||
|
self.keys = {}
|
||||||
|
for k,v in pairs(self.data.keys) do
|
||||||
|
self.keys[k] = {}
|
||||||
|
self.keys[k].isDown = false
|
||||||
|
self.keys[k].isPressed = false
|
||||||
|
self.keys[k].isReleased = false
|
||||||
|
self.keys[k].test = "ok"
|
||||||
|
end
|
||||||
|
|
||||||
|
self.fakekeys = {}
|
||||||
|
for k,v in pairs(self.data.keys) do
|
||||||
|
self.fakekeys[k] = {}
|
||||||
|
self.fakekeys[k].isDown = false
|
||||||
|
self.fakekeys[k].isPressed = false
|
||||||
|
self.fakekeys[k].isReleased = false
|
||||||
|
self.fakekeys[k].test = "ok"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputManager:isDown(padkey)
|
||||||
|
local isdown = false
|
||||||
|
if self.data.type == "keyboard" then
|
||||||
|
local key = self.data.keys[padkey]
|
||||||
|
isdown = love.keyboard.isDown(key)
|
||||||
|
if isdown then
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print("Warning: unsupported input device")
|
||||||
|
end
|
||||||
|
|
||||||
|
return isdown
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputManager:translateAction(key)
|
||||||
|
--TODO:depreciated function
|
||||||
|
local padkey = ""
|
||||||
|
for k,v in pairs(self.data.keys) do
|
||||||
|
if v == key then padkey = k end
|
||||||
|
end
|
||||||
|
return padkey
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputManager:getKey(padkey)
|
||||||
|
local padkey = padkey
|
||||||
|
for k,v in pairs(self.data.keys) do
|
||||||
|
if (k == padkey) then key = v end
|
||||||
|
end
|
||||||
|
return key
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputManager:update(dt)
|
||||||
|
for k,v in pairs(self.keys) do
|
||||||
|
local isDown = self:isDown(k)
|
||||||
|
if (isDown) then
|
||||||
|
if not (self.keys[k].isDown) then
|
||||||
|
self.keys[k].isDown = true
|
||||||
|
self.keys[k].isPressed = true
|
||||||
|
self.keys[k].isReleased = false
|
||||||
|
else
|
||||||
|
if (self.keys[k].isPressed) then
|
||||||
|
self.keys[k].isPressed = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (self.keys[k].isDown) then
|
||||||
|
self.keys[k].isDown = false
|
||||||
|
self.keys[k].isPressed = false
|
||||||
|
self.keys[k].isReleased = true
|
||||||
|
else
|
||||||
|
if (self.keys[k].isReleased) then
|
||||||
|
self.keys[k].isReleased = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return InputManager
|
52
sonic-radiance.love/core/lang.lua
Normal file
52
sonic-radiance.love/core/lang.lua
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
-- core/langs.lua :: The translation system. Transform a string to another
|
||||||
|
-- according to the translations files in the datas/ folder.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local LanguageManager = Object:extend()
|
||||||
|
local langs = require "datas.languages"
|
||||||
|
|
||||||
|
function LanguageManager:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self:setLang(self.controller.options.data.language)
|
||||||
|
end
|
||||||
|
|
||||||
|
function LanguageManager:getStringList(library, file)
|
||||||
|
return require(self.lang .. "." .. library .. "." .. file)
|
||||||
|
end
|
||||||
|
|
||||||
|
function LanguageManager:getLangName(lang)
|
||||||
|
local langnames = langs.available_langs
|
||||||
|
return langnames[lang]
|
||||||
|
end
|
||||||
|
|
||||||
|
function LanguageManager:getCurrentLangName()
|
||||||
|
local langnames = langs.available_langs
|
||||||
|
return langnames[self.lang]
|
||||||
|
end
|
||||||
|
|
||||||
|
function LanguageManager:setLang(lang)
|
||||||
|
self.controller.options.data.language = lang
|
||||||
|
self.lang = self.controller.options.data.language
|
||||||
|
end
|
||||||
|
|
||||||
|
return LanguageManager
|
87
sonic-radiance.love/core/modules/assets/animator.lua
Normal file
87
sonic-radiance.love/core/modules/assets/animator.lua
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
-- assets/animator :: the animator object. The animator object handle what
|
||||||
|
-- frame a sprite should draw.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Animator = Object:extend()
|
||||||
|
|
||||||
|
function Animator:new(sprite)
|
||||||
|
self.sprite = sprite
|
||||||
|
self.frame = 1
|
||||||
|
self.frameTimer = 0
|
||||||
|
self.currentAnimation = ""
|
||||||
|
self.animationData = {}
|
||||||
|
|
||||||
|
self.customSpeed = 0
|
||||||
|
|
||||||
|
self:changeToDefaultAnimation()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:setCustomSpeed(customSpeed)
|
||||||
|
self.customSpeed = customSpeed or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:update(dt)
|
||||||
|
if (self.currentAnimation == "") then
|
||||||
|
print("warning: no current animation data")
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local speed = self.animationData.speed
|
||||||
|
if (self.animationData.speed) == -1 then
|
||||||
|
speed = self.customSpeed --math.abs(self.xsp / 16)
|
||||||
|
end
|
||||||
|
self.frameTimer = self.frameTimer + (speed * dt)
|
||||||
|
if self.frameTimer > 1 then
|
||||||
|
self.frameTimer = 0
|
||||||
|
if self.frame == self.animationData.endAt then
|
||||||
|
self.frame = self.animationData.loop
|
||||||
|
else
|
||||||
|
self.frame = self.frame + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:getFrame()
|
||||||
|
return self.frame
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:draw(x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self.sprite:drawFrame(self.frame, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:changeAnimation(name, restart)
|
||||||
|
self.currentAnimation = name
|
||||||
|
self.animationData = self.sprite.data.animations[self.currentAnimation]
|
||||||
|
local restart = restart or true
|
||||||
|
|
||||||
|
if (restart) then
|
||||||
|
self.frame = self.animationData.startAt
|
||||||
|
self.frameTimer = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animator:changeToDefaultAnimation(restart)
|
||||||
|
self:changeAnimation(self.sprite.data.metadata.defaultAnim, restart)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Animator
|
110
sonic-radiance.love/core/modules/assets/autotile.lua
Normal file
110
sonic-radiance.love/core/modules/assets/autotile.lua
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
-- assets/autotile :: The autotile object : this is an object that draw tiles
|
||||||
|
-- automatically with borders in rectangles.
|
||||||
|
-- It works with a 3×3 tileset showing all borders.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Tileset = require "core.modules.assets.tileset"
|
||||||
|
local Autotile = Object:extend()
|
||||||
|
|
||||||
|
function Autotile:new(filepath)
|
||||||
|
self.tileset = Tileset(filepath)
|
||||||
|
|
||||||
|
self.data = require(filepath .. ".lua")
|
||||||
|
self.metadata = self.data.metadata
|
||||||
|
|
||||||
|
self.tilesize = self.metadata.width
|
||||||
|
end
|
||||||
|
|
||||||
|
function Autotile:drawtile(i, j, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
local i = i or 1
|
||||||
|
local j = j or 1
|
||||||
|
local tilesize = self.tilesize / 2
|
||||||
|
i = (i - 1) * 2 + 1
|
||||||
|
j = (j - 1) * 2 + 1
|
||||||
|
self.tileset:drawTile_Grid(i , j , x , y , r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self.tileset:drawTile_Grid(i + 1, j , x + tilesize, y , r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self.tileset:drawTile_Grid(i , j + 1, x , y + tilesize, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self.tileset:drawTile_Grid(i + 1, j + 1, x + tilesize, y + tilesize, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Autotile:draw(x, y, w, h)
|
||||||
|
local w = w or self.tilesize
|
||||||
|
local h = h or self.tilesize
|
||||||
|
w = math.max(math.floor(w / self.tilesize), 1)
|
||||||
|
h = math.max(math.floor(h / self.tilesize), 1)
|
||||||
|
local halfsize = self.tilesize / 2
|
||||||
|
local tilesize = self.tilesize
|
||||||
|
if (w == 1) then
|
||||||
|
self.tileset:drawtile_Grid(1, 1, x , y)
|
||||||
|
self.tileset:drawtile_Grid(1, 6, x , y + (h*2 - 1) * halfsize)
|
||||||
|
self.tileset:drawtile_Grid(6, 1, x + (w*2 - 1) * halfsize, y)
|
||||||
|
self.tileset:drawtile_Grid(6, 6, x + (w*2 - 1) * halfsize, y + (h*2 - 1) * halfsize)
|
||||||
|
if (h > 1) then
|
||||||
|
h = h - 1
|
||||||
|
for i = 1, h do
|
||||||
|
self.tileset:drawtile_Grid(1, 3, x, y + (i * tilesize) - halfsize)
|
||||||
|
self.tileset:drawtile_Grid(6, 3, x + halfsize, y + (i * tilesize) - halfsize)
|
||||||
|
|
||||||
|
self.tileset:drawtile_Grid(1, 4, x , y + (i * tilesize))
|
||||||
|
self.tileset:drawtile_Grid(6, 4, x + halfsize, y + (i * tilesize))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- draw just one stuff
|
||||||
|
else
|
||||||
|
if (h == 1) then
|
||||||
|
self.tileset:drawtile_Grid(1, 1, x , y)
|
||||||
|
self.tileset:drawtile_Grid(1, 6, x , y + (h*2 - 1) * halfsize)
|
||||||
|
self.tileset:drawtile_Grid(6, 1, x + (w*2 - 1) * halfsize, y)
|
||||||
|
self.tileset:drawtile_Grid(6, 6, x + (w*2 - 1) * halfsize, y + (h*2 - 1) * halfsize)
|
||||||
|
w = w - 1
|
||||||
|
for i = 1, w do
|
||||||
|
self.tileset:drawtile_Grid(3, 1, x + (i * tilesize) - halfsize, y)
|
||||||
|
self.tileset:drawtile_Grid(3, 6, x + (i * tilesize) - halfsize, y + halfsize)
|
||||||
|
|
||||||
|
self.tileset:drawtile_Grid(4, 1, x + (i * tilesize) , y )
|
||||||
|
self.tileset:drawtile_Grid(4, 6, x + (i * tilesize), y +halfsize)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:drawtile(1, 1, x , y)
|
||||||
|
self:drawtile(1, 3, x , y + (h - 1) * tilesize)
|
||||||
|
self:drawtile(3, 1, x + (w - 1) * tilesize, y)
|
||||||
|
self:drawtile(3, 3, x + (w - 1) * tilesize, y + (h - 1) * tilesize)
|
||||||
|
w = w - 2
|
||||||
|
h = h - 2
|
||||||
|
for i=1, w do
|
||||||
|
self:drawtile(2, 1, i * tilesize, y)
|
||||||
|
self:drawtile(2, 3, i * tilesize, y + (h + 1) * tilesize)
|
||||||
|
for j=1, h do
|
||||||
|
self:drawtile(2, 2, i * tilesize, j * tilesize)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1, h do
|
||||||
|
self:drawtile(1, 2, x , i * tilesize)
|
||||||
|
self:drawtile(3, 2, x + (w + 1) * tilesize, i * tilesize)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Autotile
|
50
sonic-radiance.love/core/modules/assets/background.lua
Normal file
50
sonic-radiance.love/core/modules/assets/background.lua
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
-- assets/sprite :: the background object, which is an image that draw itself
|
||||||
|
-- automatically to fill a texture.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Background = Object:extend()
|
||||||
|
|
||||||
|
function Background:new(filepath)
|
||||||
|
self.image = love.graphics.newImage(filepath)
|
||||||
|
self.batch = love.graphics.newSpriteBatch(self.image , 1000 )
|
||||||
|
|
||||||
|
self.width, self.height = self.image:getDimensions()
|
||||||
|
|
||||||
|
local w = math.floor(424 / self.width) * self.width + 1
|
||||||
|
local h = math.floor(240 / self.height) * self.height + 1
|
||||||
|
|
||||||
|
for i=-1, w do
|
||||||
|
for j=-1, h do
|
||||||
|
self.batch:add(i * self.width, j * self.height)
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Background:draw(ox, oy)
|
||||||
|
love.graphics.setColor(1, 1, 1)
|
||||||
|
love.graphics.draw(self.batch, ox, oy)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Background
|
178
sonic-radiance.love/core/modules/assets/fonts.lua
Normal file
178
sonic-radiance.love/core/modules/assets/fonts.lua
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
-- assets/fonts :: the fonts object, which is a simple way to draw text with font.
|
||||||
|
-- Some of these functions are quite slow, so it's better to use them to generate
|
||||||
|
-- texture instead of text.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Font = Object:extend()
|
||||||
|
|
||||||
|
-- Initilizing and configuring option
|
||||||
|
|
||||||
|
function Font:new(filename, size)
|
||||||
|
local filename = filename
|
||||||
|
self.font = love.graphics.newFont(filename, size)
|
||||||
|
self.filter = ""
|
||||||
|
self:setColor(1, 1, 1, 1)
|
||||||
|
self:setSpacing(false, 0)
|
||||||
|
self.align = "left"
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:set()
|
||||||
|
love.graphics.setFont(self.font)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:setColor(r, g, b, a)
|
||||||
|
self.color = {}
|
||||||
|
self.color.r = r
|
||||||
|
self.color.g = g
|
||||||
|
self.color.b = b
|
||||||
|
self.color.a = a
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:setColorFromTable(color)
|
||||||
|
self.color = color
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:setSpacing(use_custom, size)
|
||||||
|
self.spacing = {}
|
||||||
|
self.spacing.active = use_custom
|
||||||
|
self.spacing.size = size
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:setAlign(align)
|
||||||
|
self.align = align
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:setFilter(filter)
|
||||||
|
self.filter = filter
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:setLineHeight(height)
|
||||||
|
self.font:setLineHeight(height)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get information functions
|
||||||
|
|
||||||
|
function Font:getHeight()
|
||||||
|
local font = self.font
|
||||||
|
return font:getHeight()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:getWidth(string)
|
||||||
|
local spacing = 0
|
||||||
|
if (self.spacing.active == true) then
|
||||||
|
local charNumber = string.len(string)
|
||||||
|
spacing = self.spacing.size * charNumber
|
||||||
|
end
|
||||||
|
local width = self.font:getWidth(string) + spacing
|
||||||
|
return width
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:getColor()
|
||||||
|
return self.color
|
||||||
|
end
|
||||||
|
|
||||||
|
-- print functions
|
||||||
|
|
||||||
|
function Font:draw(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
-- draw text with color and effect applied
|
||||||
|
local limit = limit or 0
|
||||||
|
local align = align or self.align
|
||||||
|
|
||||||
|
self:applyFilter(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
love.graphics.setColor(self.color.r, self.color.g, self.color.b, self.color.a)
|
||||||
|
self:printf(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:print(text, x, y, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
|
||||||
|
self:set()
|
||||||
|
if (self.spacing.active) then
|
||||||
|
utils.draw.printWithSpacing(text, self.spacing.size, align, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
else
|
||||||
|
utils.draw.print(text, align, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:printf(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:set()
|
||||||
|
if (limit > 0) then
|
||||||
|
love.graphics.printf(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
else
|
||||||
|
self:print(text, x, y, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- FILTER SYSTEM
|
||||||
|
|
||||||
|
function Font:applyFilter(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
if self.filter == "shadow" then
|
||||||
|
self:applyFilterShadow(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
elseif self.filter == "border" then
|
||||||
|
self:applyFilterBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
elseif self.filter == "doubleborder" then
|
||||||
|
self:applyFilterDoubleBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:applyFilterShadow(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
love.graphics.setColor(0, 0, 0, 1)
|
||||||
|
self:printf(text, x+1, y+1, limit, align, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
utils.draw.resetColor()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:applyFilterBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
love.graphics.setColor(0, 0, 0, 1)
|
||||||
|
|
||||||
|
self:printf(text, x-1, y-1, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x , y-1, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x+1, y-1, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
|
||||||
|
self:printf(text, x+1, y , limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x-1, y , limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
|
||||||
|
self:printf(text, x-1, y+1, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x , y+1, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x+1, y+1, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
|
||||||
|
utils.draw.resetColor()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Font:applyFilterDoubleBorder(text, x, y, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
love.graphics.setColor(0, 0, 0, 1)
|
||||||
|
|
||||||
|
self:printf(text, x-2, y-2, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x , y-2, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x+2, y-2, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
|
||||||
|
self:printf(text, x+2, y , limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x-2, y , limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
|
||||||
|
self:printf(text, x-2, y+2, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x , y+2, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self:printf(text, x+2, y+2, limit, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
|
||||||
|
utils.draw.resetColor()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Font
|
187
sonic-radiance.love/core/modules/assets/init.lua
Normal file
187
sonic-radiance.love/core/modules/assets/init.lua
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
-- modules/assets :: a simple assets manager, aim to put every assets in a simple
|
||||||
|
-- serie of table in order to find them easily.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Assets = Object:extend()
|
||||||
|
|
||||||
|
local Sprite = require "core.modules.assets.sprites"
|
||||||
|
local Font = require "core.modules.assets.fonts"
|
||||||
|
local Tileset = require "core.modules.assets.tileset"
|
||||||
|
local Autotile = require "core.modules.assets.autotile"
|
||||||
|
local Background = require "core.modules.assets.background"
|
||||||
|
|
||||||
|
|
||||||
|
function Assets:new()
|
||||||
|
self.sprites = {}
|
||||||
|
self.sfx = {}
|
||||||
|
self.fonts = {}
|
||||||
|
self.music = nil
|
||||||
|
self:clearBackgrounds()
|
||||||
|
self:clearFonts()
|
||||||
|
self:clearAutotile()
|
||||||
|
self:clearTileset()
|
||||||
|
|
||||||
|
self.images = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:init()
|
||||||
|
self.sprites = {}
|
||||||
|
self.sfx = {}
|
||||||
|
self.fonts = {}
|
||||||
|
self.music = nil
|
||||||
|
self.backgrounds= {}
|
||||||
|
self:clearFonts()
|
||||||
|
|
||||||
|
self.images = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:clear()
|
||||||
|
-- TODO: destroy individually each texture/image when assets are cleared
|
||||||
|
self.sprites = {}
|
||||||
|
self.sfx = {}
|
||||||
|
self.fonts = {}
|
||||||
|
self.music = nil
|
||||||
|
self.backgrounds= {}
|
||||||
|
self:clearFonts()
|
||||||
|
|
||||||
|
self.images = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:update(dt)
|
||||||
|
self:animationsUpdate(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- SFX et Musique
|
||||||
|
|
||||||
|
function Assets:addSFX(name, filepath)
|
||||||
|
self:newSFX(name, filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:newSFX(name, filepath)
|
||||||
|
self.sfx[name] = love.audio.newSource( filepath, "static" )
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:clearSFX()
|
||||||
|
love.audio.stop( )
|
||||||
|
self.sfx = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:setMusic(filename)
|
||||||
|
if filename ~= nil then
|
||||||
|
love.audio.stop( )
|
||||||
|
self.music = love.audio.newSource(filename, "stream" )
|
||||||
|
self.music:setVolume(game.options.data.audio.music / 100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:playSFX(filename)
|
||||||
|
if not (self.sfx[filename] == nil) then
|
||||||
|
self.sfx[filename]:stop()
|
||||||
|
self.sfx[filename]:setVolume(game.options.data.audio.sfx / 100)
|
||||||
|
love.audio.play( self.sfx[filename] )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:playMusic()
|
||||||
|
if not (self.music == nil) then
|
||||||
|
love.audio.play(self.music)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:silence()
|
||||||
|
love.audio.stop()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Background --
|
||||||
|
|
||||||
|
function Assets:addImage(name, filename)
|
||||||
|
self.images[name] = love.graphics.newImage(filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:drawImage(name, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
love.graphics.draw(self.images[name], x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Images --
|
||||||
|
|
||||||
|
function Assets:clearBackgrounds()
|
||||||
|
self.backgrounds = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:addBackground(name, filepath)
|
||||||
|
self.backgrounds[name] = Background(filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- SPRITES --
|
||||||
|
|
||||||
|
|
||||||
|
function Assets:addSprite(name, filepath)
|
||||||
|
self.sprites[name] = Sprite(filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:clearSprites()
|
||||||
|
self.sprites = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:animationsUpdate(dt)
|
||||||
|
for i,v in pairs(self.sprites) do
|
||||||
|
v:update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- FONTS --
|
||||||
|
|
||||||
|
function Assets:clearFonts()
|
||||||
|
self.fonts = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:addFont(key, filename, size)
|
||||||
|
local font = Font(filename, size)
|
||||||
|
self.fonts[key] = font
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:getFont(filename)
|
||||||
|
return self.fonts[filename]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Tileset
|
||||||
|
|
||||||
|
function Assets:addTileset(name, filepath)
|
||||||
|
self.tileset[name] = Tileset(filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:clearTileset()
|
||||||
|
self.tileset = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Autotile
|
||||||
|
|
||||||
|
function Assets:addAutotile(name, tilesize)
|
||||||
|
self.autotile[name] = Autotile(name, tilesize)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Assets:clearAutotile()
|
||||||
|
self.autotile = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return Assets
|
78
sonic-radiance.love/core/modules/assets/sprites.lua
Normal file
78
sonic-radiance.love/core/modules/assets/sprites.lua
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
-- assets/sprite :: the assets object, which is basically a tileset animated by
|
||||||
|
-- an animator object. An animator object is always tied to a sprite, but a sprite
|
||||||
|
-- can use different animator in order to make several animator share the same sprite
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Sprite = Object:extend()
|
||||||
|
local Animator = require("core.modules.assets.animator")
|
||||||
|
local Tileset = require("core.modules.assets.tileset")
|
||||||
|
|
||||||
|
function Sprite:new(filepath)
|
||||||
|
self.tileset = Tileset(filepath)
|
||||||
|
self.data = require(filepath)
|
||||||
|
|
||||||
|
self.animator = Animator(self)
|
||||||
|
|
||||||
|
self.customSpeed = 0
|
||||||
|
|
||||||
|
self:changeToDefaultAnimation(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sprite:update(dt)
|
||||||
|
self.animator:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sprite:setCustomSpeed(customSpeed)
|
||||||
|
self.animator:setCustomSpeed(customSpeed)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sprite:changeToDefaultAnimation(restart)
|
||||||
|
self.animator:changeToDefaultAnimation(restart)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sprite:changeAnimation(name, restart)
|
||||||
|
self.animator:changeAnimation(name, restart)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sprite:drawAnimation(x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self.animator:draw(x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sprite:drawFrame(frame, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
self.tileset:drawTile(frame, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Sprite:drawPart(x, y, w, h, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
local w = math.floor(w)
|
||||||
|
local h = math.floor(h)
|
||||||
|
|
||||||
|
if w >= 0 and h <= 0 then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.setScissor(x - ox, y - oy, w, h)
|
||||||
|
self:drawAnimation(x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
love.graphics.setScissor( )
|
||||||
|
end
|
||||||
|
|
||||||
|
return Sprite
|
89
sonic-radiance.love/core/modules/assets/tileset.lua
Normal file
89
sonic-radiance.love/core/modules/assets/tileset.lua
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
-- assets/tileset :: tileset are automatic breakage of texture into quads. they
|
||||||
|
-- have the adventage of being automatized, reducing the ammount of code needed
|
||||||
|
-- to create quads.
|
||||||
|
|
||||||
|
-- They have two manners to be draw: with their quad id (in 1D) or by using their
|
||||||
|
-- place in the grid, in 2D.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Tileset = Object:extend()
|
||||||
|
|
||||||
|
function Tileset:new(filepath)
|
||||||
|
self.texture = love.graphics.newImage(filepath .. ".png")
|
||||||
|
|
||||||
|
local data = require(filepath)
|
||||||
|
self.metadata = data.metadata
|
||||||
|
|
||||||
|
self:createQuads()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tileset:createGrid()
|
||||||
|
self.textureWidth, self.textureHeight = self.texture:getDimensions()
|
||||||
|
self.width, self.height = self.metadata.width, self.metadata.height
|
||||||
|
self.gridWidth, self.gridHeight = math.floor(self.textureWidth / self.width),
|
||||||
|
math.floor(self.textureHeight / self.height)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tileset:createQuads()
|
||||||
|
self.quads = {}
|
||||||
|
|
||||||
|
|
||||||
|
self:createGrid()
|
||||||
|
|
||||||
|
local quad, n
|
||||||
|
|
||||||
|
n = 1
|
||||||
|
for i=0, (self.gridHeight-1) do
|
||||||
|
for j=0, (self.gridWidth-1) do
|
||||||
|
quad = love.graphics.newQuad(j * self.width, i * self.height, self.width, self.height, self.textureWidth, self.textureHeight)
|
||||||
|
self.quads[n] = quad
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tileset:getTileID_Grid(x, y)
|
||||||
|
local n = (y - 1) * self.gridWidth + x
|
||||||
|
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tileset:getTile_Grid(x, y)
|
||||||
|
return self:getTile(self:getTileID_Grid(x, y))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tileset:getTile(n)
|
||||||
|
return self.quads[n]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tileset:drawTile_Grid(i, j, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
local tileID = self:getTileID_Grid(i, j)
|
||||||
|
love.graphics.draw(self.texture, self.quads[tileID], x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tileset:drawTile(id, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
love.graphics.draw(self.texture, self.quads[id], x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Tileset
|
59
sonic-radiance.love/core/modules/scenes.lua
Normal file
59
sonic-radiance.love/core/modules/scenes.lua
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
-- scenes.lua :: the scene object, that aim to give a better control to the engine
|
||||||
|
-- to the different scene, without having to call too much boilerplate
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Scene = Object:extend()
|
||||||
|
local Assets = require "core.modules.assets"
|
||||||
|
|
||||||
|
function Scene:new()
|
||||||
|
self.mouse = {}
|
||||||
|
self.mouse.x, self.mouse.y = core.screen:getMousePosition()
|
||||||
|
|
||||||
|
self.assets = Assets()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scene:register()
|
||||||
|
core.scenemanager.currentScene = self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scene:update(dt)
|
||||||
|
-- Empty function, is just here to avoid crash
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scene:mousemoved(x, y, dx, dy)
|
||||||
|
-- Empty function, is just here to avoid crash
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scene:mousepressed( x, y, button, istouch )
|
||||||
|
-- Empty function, is just here to avoid crash
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scene:draw()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scene:clear()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return Scene
|
96
sonic-radiance.love/core/options.lua
Normal file
96
sonic-radiance.love/core/options.lua
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
-- core/options.lua :: The options loading/saving system. Is used by the other
|
||||||
|
-- modules to save their settings.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local OptionsManager = Object:extend()
|
||||||
|
|
||||||
|
local binser = require "libs.binser"
|
||||||
|
|
||||||
|
function OptionsManager:new()
|
||||||
|
-- We begin by creating an empty data table before reading the data.
|
||||||
|
self.data = {}
|
||||||
|
self:read()
|
||||||
|
end
|
||||||
|
|
||||||
|
function OptionsManager:reset()
|
||||||
|
-- Reset the option to the game defaults.
|
||||||
|
self.data.video = {}
|
||||||
|
self.data.video.crtfilter = false
|
||||||
|
self.data.video.resolution = 1
|
||||||
|
self.data.video.border = true
|
||||||
|
self.data.video.vsync = false
|
||||||
|
self.data.video.fullscreen = false
|
||||||
|
|
||||||
|
-- We load the default files
|
||||||
|
self.data.input = require "datas.inputs"
|
||||||
|
|
||||||
|
-- TODO: have a way to auto-load a language according to the OS ?
|
||||||
|
self.data.language = "en"
|
||||||
|
|
||||||
|
self.data.audio = {}
|
||||||
|
self.data.audio.music = 100
|
||||||
|
self.data.audio.sfx = 100
|
||||||
|
end
|
||||||
|
|
||||||
|
function OptionsManager:getFile(absolute)
|
||||||
|
local dir = ""
|
||||||
|
if absolute then
|
||||||
|
dir = love.filesystem.getSaveDirectory() .. "/"
|
||||||
|
if not utils.filesystem.exists(dir) then
|
||||||
|
love.filesystem.createDirectory( "" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local filepath = dir .. "options.data"
|
||||||
|
|
||||||
|
return filepath
|
||||||
|
end
|
||||||
|
|
||||||
|
function OptionsManager:write()
|
||||||
|
local data = self:getData()
|
||||||
|
|
||||||
|
filepath = self:getFile(true)
|
||||||
|
binser.writeFile(filepath, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
function OptionsManager:read()
|
||||||
|
filepath = self:getFile(true)
|
||||||
|
if utils.filesystem.exists("options.data") then
|
||||||
|
local loadedDatas = binser.readFile(filepath)
|
||||||
|
print("data file found, loading it")
|
||||||
|
self:setData(loadedDatas[1])
|
||||||
|
else
|
||||||
|
self:reset()
|
||||||
|
print("no data file found, reseting data")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function OptionsManager:getData(data)
|
||||||
|
return self.data
|
||||||
|
end
|
||||||
|
|
||||||
|
function OptionsManager:setData(data)
|
||||||
|
self.data = data
|
||||||
|
end
|
||||||
|
|
||||||
|
return OptionsManager
|
64
sonic-radiance.love/core/scenemanager.lua
Normal file
64
sonic-radiance.love/core/scenemanager.lua
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
-- scene.lua :: a basic scene management system, that work by sending the different
|
||||||
|
-- core functions to the scene, normally without the scene itself having to manage
|
||||||
|
-- them.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local SceneManager = Object:extend()
|
||||||
|
|
||||||
|
function SceneManager:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.currentScene = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function SceneManager:update(dt)
|
||||||
|
if (self.currentScene ~= nil) then
|
||||||
|
local keys = self.controller.input.keys
|
||||||
|
self.currentScene.keys = keys
|
||||||
|
self.currentScene.assets:update(dt)
|
||||||
|
self.currentScene:update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function SceneManager:mousemoved(x, y, dx, dy)
|
||||||
|
self.currentScene.mouse.x,
|
||||||
|
self.currentScene.mouse.y = x, y
|
||||||
|
self.currentScene:mousemoved(x, y, dx, dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SceneManager:mousepressed( x, y, button, istouch )
|
||||||
|
self.currentScene:mousepressed( x, y, button, istouch )
|
||||||
|
end
|
||||||
|
|
||||||
|
function SceneManager:clearScene()
|
||||||
|
self.currentScene = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function SceneManager:draw()
|
||||||
|
self.controller.screen:apply()
|
||||||
|
if (self.currentScene ~= nil) then
|
||||||
|
self.currentScene:draw(dt)
|
||||||
|
end
|
||||||
|
self.controller.screen:cease()
|
||||||
|
end
|
||||||
|
|
||||||
|
return SceneManager
|
72
sonic-radiance.love/core/screen.lua
Normal file
72
sonic-radiance.love/core/screen.lua
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
-- core/screen.lua :: Basic screen manager. Use CScreen as a backend, and works
|
||||||
|
-- as an abstraction layer around CScreen.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local ScreenManager = Object:extend()
|
||||||
|
|
||||||
|
local CScreen = require "libs.cscreen"
|
||||||
|
|
||||||
|
local SCREEN_HEIGHT = 240
|
||||||
|
local SCREEN_WIDTH = 424
|
||||||
|
|
||||||
|
function ScreenManager:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.data = self.controller.options.data.video
|
||||||
|
self:applySettings()
|
||||||
|
CScreen.init(SCREEN_WIDTH, SCREEN_HEIGHT, true)
|
||||||
|
CScreen.setColor(0, 0, 0, 1)
|
||||||
|
|
||||||
|
love.graphics.setDefaultFilter( "nearest", "nearest", 1 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function ScreenManager:applySettings()
|
||||||
|
self.data = self.controller.options.data.video
|
||||||
|
|
||||||
|
local flags = {}
|
||||||
|
flags.vsync = self.data.vsync
|
||||||
|
flags.borderless = (self.data.border == false)
|
||||||
|
|
||||||
|
love.window.setMode(SCREEN_WIDTH * self.data.resolution, SCREEN_HEIGHT * self.data.resolution, flags)
|
||||||
|
love.window.setFullscreen( self.data.fullscreen )
|
||||||
|
|
||||||
|
local width, height = love.window.getMode()
|
||||||
|
CScreen.update(width, height)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ScreenManager:project(x, y)
|
||||||
|
return CScreen.project(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ScreenManager:getMousePosition()
|
||||||
|
return CScreen.project(love.mouse.getX(), love.mouse.getY())
|
||||||
|
end
|
||||||
|
|
||||||
|
function ScreenManager:apply()
|
||||||
|
CScreen.apply()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ScreenManager:cease()
|
||||||
|
CScreen.cease()
|
||||||
|
end
|
||||||
|
|
||||||
|
return ScreenManager
|
|
@ -0,0 +1 @@
|
||||||
|
return {"sonic"}
|
57
sonic-radiance.love/datas/gamedata/characters/default.lua
Normal file
57
sonic-radiance.love/datas/gamedata/characters/default.lua
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
return {
|
||||||
|
name = "Default",
|
||||||
|
name_full = "Default the Character",
|
||||||
|
class = "Speedster",
|
||||||
|
startlevel = 1,
|
||||||
|
isUnlockedAtStart = true,
|
||||||
|
|
||||||
|
base_stats = {
|
||||||
|
hpmax = 200, --
|
||||||
|
ppmax = 50, --
|
||||||
|
|
||||||
|
attack = 50, --
|
||||||
|
power = 50, --
|
||||||
|
defense = 50, --
|
||||||
|
technic = 50, --
|
||||||
|
mind = 50, --
|
||||||
|
speed = 50, --
|
||||||
|
|
||||||
|
turns = 3, -- number of attacks by turn (unused)
|
||||||
|
move = 2, -- how far the character can get in one turn
|
||||||
|
},
|
||||||
|
|
||||||
|
color = {1, 1, 1},
|
||||||
|
|
||||||
|
skill_list = {
|
||||||
|
--{attack_name, level},
|
||||||
|
},
|
||||||
|
|
||||||
|
flags = {
|
||||||
|
canGoSuper = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
assets = {
|
||||||
|
charset = {"", 1},
|
||||||
|
lifeicon = 1,
|
||||||
|
spriteset = "sonic",
|
||||||
|
},
|
||||||
|
|
||||||
|
inventory = {
|
||||||
|
haveShoes = true,
|
||||||
|
haveMechs = false,
|
||||||
|
haveGlove = true,
|
||||||
|
haveHammer= false,
|
||||||
|
accessories_number = 3,
|
||||||
|
chao_number = 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
boost_stats = {
|
||||||
|
spd = 5,
|
||||||
|
jmp = 3,
|
||||||
|
jumpaction = "doublejump",
|
||||||
|
jumpaction_power = 2,
|
||||||
|
action = "spinattack",
|
||||||
|
action_power = 1,
|
||||||
|
canBreakCraft = false,
|
||||||
|
}
|
||||||
|
}
|
9
sonic-radiance.love/datas/gamedata/characters/init.lua
Normal file
9
sonic-radiance.love/datas/gamedata/characters/init.lua
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
return {
|
||||||
|
"sonic",
|
||||||
|
"tails",
|
||||||
|
"knuckles",
|
||||||
|
"amy",
|
||||||
|
"cream",
|
||||||
|
"shadow",
|
||||||
|
"rouge",
|
||||||
|
}
|
70
sonic-radiance.love/datas/gamedata/characters/sonic.lua
Normal file
70
sonic-radiance.love/datas/gamedata/characters/sonic.lua
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
return {
|
||||||
|
name = "Default",
|
||||||
|
name_full = "Default the Character",
|
||||||
|
class = "Speedster",
|
||||||
|
startlevel = 100,
|
||||||
|
isUnlockedAtStart = true,
|
||||||
|
|
||||||
|
base_stats = {
|
||||||
|
hpmax = 200, --
|
||||||
|
ppmax = 50, --
|
||||||
|
|
||||||
|
attack = 50, --
|
||||||
|
power = 50, --
|
||||||
|
defense = 50, --
|
||||||
|
technic = 50, --
|
||||||
|
mind = 50, --
|
||||||
|
speed = 50, --
|
||||||
|
|
||||||
|
turns = 3, -- number of attacks by turn (unused)
|
||||||
|
move = 3, -- how far the character can get in one turn
|
||||||
|
},
|
||||||
|
|
||||||
|
color = {1, 1, 1},
|
||||||
|
|
||||||
|
skill_list = {
|
||||||
|
--{attack_name, level},
|
||||||
|
{"spinattack", 2},
|
||||||
|
{"spinjump", 3},
|
||||||
|
{"spindash", 8},
|
||||||
|
{"hommingattack", 11},
|
||||||
|
{"spinattack", 15},
|
||||||
|
{"sonicflare", 18},
|
||||||
|
{"bluetornado", 22},
|
||||||
|
{"spindash", 26},
|
||||||
|
{"soniccracker", 30},
|
||||||
|
{"hommingattack", 35},
|
||||||
|
{"bluetornado", 40},
|
||||||
|
{"boost", 62},
|
||||||
|
{"lightspeedattack", 70},
|
||||||
|
},
|
||||||
|
|
||||||
|
flags = {
|
||||||
|
canGoSuper = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
assets = {
|
||||||
|
charset = {"", 1},
|
||||||
|
lifeicon = 1,
|
||||||
|
spriteset = "sonic",
|
||||||
|
},
|
||||||
|
|
||||||
|
inventory = {
|
||||||
|
haveShoes = true,
|
||||||
|
haveMechs = false,
|
||||||
|
haveGlove = true,
|
||||||
|
haveHammer= false,
|
||||||
|
accessories_number = 3,
|
||||||
|
chao_number = 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
boost_stats = {
|
||||||
|
spd = 5,
|
||||||
|
jmp = 3,
|
||||||
|
jumpaction = "doublejump",
|
||||||
|
jumpaction_power = 2,
|
||||||
|
action = "spinattack",
|
||||||
|
action_power = 1,
|
||||||
|
canBreakCraft = false,
|
||||||
|
}
|
||||||
|
}
|
16
sonic-radiance.love/datas/inputs.lua
Normal file
16
sonic-radiance.love/datas/inputs.lua
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
return {
|
||||||
|
[1] = {
|
||||||
|
type = "keyboard",
|
||||||
|
keys = {
|
||||||
|
["left"] = "left",
|
||||||
|
["right"] = "right",
|
||||||
|
["up"] = "up",
|
||||||
|
["down"] = "down",
|
||||||
|
["A"] = "a",
|
||||||
|
["B"] = "z",
|
||||||
|
["C"] = "e",
|
||||||
|
["start"] = "return",
|
||||||
|
["select"] = "space"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
return {
|
||||||
|
['false'] = "False",
|
||||||
|
['true'] = "True"
|
||||||
|
}
|
18
sonic-radiance.love/datas/languages/en/options/menu.lua
Normal file
18
sonic-radiance.love/datas/languages/en/options/menu.lua
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
return {
|
||||||
|
["options"] = "OPTIONS",
|
||||||
|
["video"] = "VIDEO",
|
||||||
|
["audio"] = "AUDIO",
|
||||||
|
["lang"] = "LANGUES",
|
||||||
|
["input"] = "CONTROLES",
|
||||||
|
["reset"] = "RESET",
|
||||||
|
["exit"] = "EXIT",
|
||||||
|
["back"] = "BACK",
|
||||||
|
["sfx"] = "SFX",
|
||||||
|
["music"] = "MUSIC",
|
||||||
|
["keyboard"] = "Clavier",
|
||||||
|
["inputtype"] = "SOURCE",
|
||||||
|
["vsync"] = "VSYNC",
|
||||||
|
["borders"] = "BORDURES",
|
||||||
|
["fullscreen"] = "PLEIN ECRAN",
|
||||||
|
["resolution"] = "RESOLUTION"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
return {
|
||||||
|
['false'] = "Faux",
|
||||||
|
['true'] = "Vrai"
|
||||||
|
}
|
18
sonic-radiance.love/datas/languages/fr/options/menu.lua
Normal file
18
sonic-radiance.love/datas/languages/fr/options/menu.lua
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
return {
|
||||||
|
["options"] = "SETTINGS",
|
||||||
|
["video"] = "VIDEO",
|
||||||
|
["audio"] = "AUDIO",
|
||||||
|
["lang"] = "LANGUAGES",
|
||||||
|
["input"] = "CONTROLS",
|
||||||
|
["reset"] = "RESET",
|
||||||
|
["exit"] = "EXIT",
|
||||||
|
["back"] = "BACK",
|
||||||
|
["sfx"] = "SFX",
|
||||||
|
["music"] = "MUSIC",
|
||||||
|
["keyboard"] = "Keyboard",
|
||||||
|
["inputtype"] = "SOURCE",
|
||||||
|
["vsync"] = "VSYNC",
|
||||||
|
["borders"] = "BORDERS",
|
||||||
|
["fullscreen"] = "FULLSCREEN",
|
||||||
|
["resolution"] = "RESOLUTION"
|
||||||
|
}
|
6
sonic-radiance.love/datas/languages/init.lua
Normal file
6
sonic-radiance.love/datas/languages/init.lua
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
return {
|
||||||
|
available_langs = {
|
||||||
|
["en"] = "English",
|
||||||
|
["fr"] = "Français"
|
||||||
|
}
|
||||||
|
}
|
199
sonic-radiance.love/game/characters.lua
Normal file
199
sonic-radiance.love/game/characters.lua
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
-- game/characters :: The character handler. This object handle all the character
|
||||||
|
-- and is able to get and set datas about them.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local CharacterManager = Object:extend()
|
||||||
|
|
||||||
|
function CharacterManager:new(controller)
|
||||||
|
self.controller = controller
|
||||||
|
self.namelist = require "datas.characters"
|
||||||
|
self.list = {}
|
||||||
|
self.team = require "datas.characters.baseteam"
|
||||||
|
self.active = 1
|
||||||
|
self:init()
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:init()
|
||||||
|
for k, v in pairs(self.namelist) do
|
||||||
|
local dir = "datas/characters/" .. v .. ".lua"
|
||||||
|
local fileinfo = love.filesystem.getInfo(dir)
|
||||||
|
if fileinfo ~= nil then
|
||||||
|
self:initCharacter(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getCharacterData(charname)
|
||||||
|
-- va eprmettre de récupérer les données d'un personnage
|
||||||
|
return require("datas.characters." .. charname)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:initCharacter(id)
|
||||||
|
local stats = {}
|
||||||
|
local character = self:getCharacterData(id)
|
||||||
|
|
||||||
|
stats.level = character.startlevel
|
||||||
|
stats.exp = self:getExpValue(stats.level)
|
||||||
|
stats.exp_next = self:getExpValue(stats.level + 1)
|
||||||
|
stats.hpmax = character.base_stats.hpmax
|
||||||
|
stats.ppmax = character.base_stats.ppmax
|
||||||
|
stats.attack = character.base_stats.attack
|
||||||
|
stats.power = character.base_stats.power
|
||||||
|
stats.defense = character.base_stats.defense
|
||||||
|
stats.technic = character.base_stats.technic
|
||||||
|
stats.mind = character.base_stats.mind
|
||||||
|
stats.speed = character.base_stats.speed
|
||||||
|
stats.turns = character.base_stats.turns
|
||||||
|
|
||||||
|
character.stats = stats
|
||||||
|
self.list[id] = character
|
||||||
|
|
||||||
|
self:recalculateStats(id)
|
||||||
|
|
||||||
|
stats.hp = stats.hpmax
|
||||||
|
stats.pp = stats.ppmax
|
||||||
|
stats.status = 0
|
||||||
|
|
||||||
|
character.stats = stats
|
||||||
|
self.list[id] = character
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getExpValue(level)
|
||||||
|
return math.floor( ( 4 * ( level ^ 3 ) ) / 5 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:setLevel(id, newlevel)
|
||||||
|
self.list[id].stats.level = newlevel
|
||||||
|
local stats = self.list[id].stats
|
||||||
|
local exp, exp_next, exp_current
|
||||||
|
exp = self:getExpValue(stats.level)
|
||||||
|
exp_next = self:getExpValue(stats.level + 1)
|
||||||
|
exp_current = self.list[id].stats.exp
|
||||||
|
|
||||||
|
self.list[id].stats.exp = math.max(math.min(exp_current, exp_next - 1), exp)
|
||||||
|
self.list[id].stats.exp_next = exp_next
|
||||||
|
|
||||||
|
self:recalculateStats(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:levelUp(id)
|
||||||
|
self:setLevel(id, self.list[id].stats.level + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getStatValue(level, base)
|
||||||
|
return math.floor( (((base * 2) * level)/100) ) + 5
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getHPValue(level, base)
|
||||||
|
return math.floor( (((base * 2.7) * level)/100) ) + 15 + level
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getPPValue(level, base)
|
||||||
|
return math.floor( (((base * 1.5) * level)/100) ) + 8
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:recalculateStats(id)
|
||||||
|
local character = self.list[id]
|
||||||
|
local stats = character.stats
|
||||||
|
local base_stats = character.base_stats
|
||||||
|
|
||||||
|
stats.hpmax = self:getHPValue(stats.level, base_stats.hpmax)
|
||||||
|
stats.ppmax = self:getPPValue(stats.level, base_stats.ppmax)
|
||||||
|
stats.attack = self:getStatValue(stats.level, base_stats.attack)
|
||||||
|
stats.power = self:getStatValue(stats.level, base_stats.power)
|
||||||
|
stats.defense = self:getStatValue(stats.level, base_stats.defense)
|
||||||
|
stats.mind = self:getStatValue(stats.level, base_stats.mind)
|
||||||
|
stats.technic = self:getStatValue(stats.level, base_stats.technic)
|
||||||
|
stats.speed = self:getStatValue(stats.level, base_stats.speed)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getSkillList(id)
|
||||||
|
local character = self.list[id]
|
||||||
|
local learnedlist = {}
|
||||||
|
|
||||||
|
for i, v in ipairs(character.skill_list) do
|
||||||
|
local tech_name, tech_level, isLearned = v[1], v[2], false
|
||||||
|
if tech_level <= character.stats.level then
|
||||||
|
|
||||||
|
if learnedlist[tech_name] == nil then
|
||||||
|
learnedlist[tech_name] = 1
|
||||||
|
else
|
||||||
|
learnedlist[tech_name] = learnedlist[tech_name] + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
-- On continue ensuite d'itérer dans la liste
|
||||||
|
end
|
||||||
|
|
||||||
|
return learnedlist
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getData()
|
||||||
|
local data = {}
|
||||||
|
data.list = self.list
|
||||||
|
data.team = self.team
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:setData(data)
|
||||||
|
local data = data
|
||||||
|
self.list = data.list
|
||||||
|
self.team = data.team
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:heal(id)
|
||||||
|
self.list[id].stats.hp = self.list[id].stats.hpmax
|
||||||
|
self.list[id].stats.hp = self.list[id].stats.ppmax
|
||||||
|
self.list[id].stats.status = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:addToTeam(id)
|
||||||
|
self:heal(id)
|
||||||
|
table.insert(self.team, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:removeToTeam(teamid)
|
||||||
|
self.team[teamid] = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:getActiveCharacter()
|
||||||
|
return self.team[self.active]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- DEBUG FUNCTIONS
|
||||||
|
|
||||||
|
function CharacterManager:printCharacter(id)
|
||||||
|
local character = self.list[id]
|
||||||
|
local stats = character.stats
|
||||||
|
print(id .. ". " .. character.fullname)
|
||||||
|
print("Lvl " .. character.stats.level .. " (" .. stats.exp .. "/" .. stats.exp_next .. " exp)")
|
||||||
|
end
|
||||||
|
|
||||||
|
function CharacterManager:printTeam()
|
||||||
|
for i,v in ipairs(self.team) do
|
||||||
|
self:printCharacter(v)
|
||||||
|
print("-----")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return CharacterManager
|
140
sonic-radiance.love/game/init.lua
Normal file
140
sonic-radiance.love/game/init.lua
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
-- game :: The main game subsystem. Basically a big object that handle all the
|
||||||
|
-- game-related data like characters, monsters, etc. While the core aim to be
|
||||||
|
-- reusable at will, the game is specifically made for the current game.
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Game = Object:extend()
|
||||||
|
|
||||||
|
|
||||||
|
local binser = require "libs.binser"
|
||||||
|
|
||||||
|
function Game:new()
|
||||||
|
self.slot = -1
|
||||||
|
self.gametime = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:setData(data)
|
||||||
|
local data = data
|
||||||
|
self.gametime = data.gametime
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:getData()
|
||||||
|
local data = {}
|
||||||
|
data.gametime = self.gametime
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:read(save_id)
|
||||||
|
self.slot = save_id
|
||||||
|
if (self.slot > 0) then
|
||||||
|
filepath = self:getSaveFile(self.slot, true)
|
||||||
|
if love.filesystem.exists("save" .. self.slot .. ".save") then
|
||||||
|
local loadedDatas = binser.readFile(filepath)
|
||||||
|
|
||||||
|
self:setData(loadedDatas[1])
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:write(save_id)
|
||||||
|
if (self.slot > 0) then
|
||||||
|
local data = self:getData()
|
||||||
|
|
||||||
|
filepath = self:getSaveFile(self.slot, true)
|
||||||
|
binser.writeFile(filepath, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:getSaveFile(saveslot, absolute)
|
||||||
|
local dir = ""
|
||||||
|
if absolute then
|
||||||
|
dir = love.filesystem.getSaveDirectory() .. "/"
|
||||||
|
if not love.filesystem.exists(dir) then
|
||||||
|
love.filesystem.createDirectory( "" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local filepath = dir .. "save" .. saveslot .. ".save"
|
||||||
|
|
||||||
|
return filepath
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:resetSaves()
|
||||||
|
for i=1, 3 do
|
||||||
|
filepath = self:getSaveFile(i, true)
|
||||||
|
if love.filesystem.exists("save" .. i .. ".save") then
|
||||||
|
love.filesystem.remove( "save" .. i .. ".save" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:update(dt)
|
||||||
|
self.gametime = self.gametime + dt
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:getTime()
|
||||||
|
local time = self.gametime
|
||||||
|
local hours, minute, seconds
|
||||||
|
seconds = math.floor(self.gametime)
|
||||||
|
minutes = math.floor(seconds / 60)
|
||||||
|
hours = math.floor(minutes / 60)
|
||||||
|
seconds = seconds % 60
|
||||||
|
minutes = minutes % 60
|
||||||
|
hours = hours
|
||||||
|
|
||||||
|
return seconds, minutes, hours
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:getTimeString()
|
||||||
|
local string
|
||||||
|
local seconds, minutes, hours = self:getTime()
|
||||||
|
local stringSeconds, stringMinutes, stringHours
|
||||||
|
if (seconds <= 9) then
|
||||||
|
stringSeconds = 0 .. seconds
|
||||||
|
else
|
||||||
|
stringSeconds = seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
if (minutes <= 9) then
|
||||||
|
stringMinutes = 0 .. minutes
|
||||||
|
else
|
||||||
|
stringMinutes = minutes
|
||||||
|
end
|
||||||
|
|
||||||
|
if (hours <= 9) then
|
||||||
|
stringHours = 0 .. hours
|
||||||
|
else
|
||||||
|
stringHours = hours
|
||||||
|
end
|
||||||
|
|
||||||
|
string = stringHours .. ":" .. stringMinutes .. ":" .. stringSeconds
|
||||||
|
return string
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:printTime()
|
||||||
|
print(self:getTimeString())
|
||||||
|
end
|
||||||
|
|
||||||
|
return Game
|
41
sonic-radiance.love/game/modules/gui/init.lua
Normal file
41
sonic-radiance.love/game/modules/gui/init.lua
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
local gui = {}
|
||||||
|
|
||||||
|
function gui.newBorder(width, height, middlePosition)
|
||||||
|
local tileset = love.graphics.newImage("assets/gui/borders.png")
|
||||||
|
local tilequad = {}
|
||||||
|
local w, h = tileset:getDimensions()
|
||||||
|
tilequad[1] = love.graphics.newQuad(0, 0, 20, 20, w, h)
|
||||||
|
tilequad[2] = love.graphics.newQuad(20, 0, 20, 20, w, h)
|
||||||
|
tilequad[3] = love.graphics.newQuad(40, 0, 20, 20, w, h)
|
||||||
|
tilequad[4] = love.graphics.newQuad(60, 0, 20, 20, w, h)
|
||||||
|
|
||||||
|
local Texture = love.graphics.newCanvas(width, height)
|
||||||
|
|
||||||
|
love.graphics.setCanvas(Texture)
|
||||||
|
utils.graphics.resetColor()
|
||||||
|
|
||||||
|
local height = math.ceil(height / 20)
|
||||||
|
local width = math.ceil(width / 20)
|
||||||
|
|
||||||
|
for i=1, width do
|
||||||
|
if i < middlePosition then
|
||||||
|
love.graphics.draw(tileset, tilequad[1], (i-1) * 20, 0)
|
||||||
|
elseif (i == middlePosition) then
|
||||||
|
love.graphics.draw(tileset, tilequad[2], (i-1) * 20, 0)
|
||||||
|
else
|
||||||
|
love.graphics.draw(tileset, tilequad[3], (i-1) * 20, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if height > 1 then
|
||||||
|
for j = 2, height do
|
||||||
|
love.graphics.draw(tileset, tilequad[4], (i-1) * 20, (j-1) * 20)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.setCanvas( )
|
||||||
|
|
||||||
|
return Texture
|
||||||
|
end
|
||||||
|
|
||||||
|
return gui
|
687
sonic-radiance.love/libs/binser.lua
Normal file
687
sonic-radiance.love/libs/binser.lua
Normal file
|
@ -0,0 +1,687 @@
|
||||||
|
-- binser.lua
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright (c) 2016 Calvin Rose
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local assert = assert
|
||||||
|
local error = error
|
||||||
|
local select = select
|
||||||
|
local pairs = pairs
|
||||||
|
local getmetatable = getmetatable
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local tonumber = tonumber
|
||||||
|
local type = type
|
||||||
|
local loadstring = loadstring or load
|
||||||
|
local concat = table.concat
|
||||||
|
local char = string.char
|
||||||
|
local byte = string.byte
|
||||||
|
local format = string.format
|
||||||
|
local sub = string.sub
|
||||||
|
local dump = string.dump
|
||||||
|
local floor = math.floor
|
||||||
|
local frexp = math.frexp
|
||||||
|
local unpack = unpack or table.unpack
|
||||||
|
|
||||||
|
-- Lua 5.3 frexp polyfill
|
||||||
|
-- From https://github.com/excessive/cpml/blob/master/modules/utils.lua
|
||||||
|
if not frexp then
|
||||||
|
local log, abs, floor = math.log, math.abs, math.floor
|
||||||
|
local log2 = log(2)
|
||||||
|
frexp = function(x)
|
||||||
|
if x == 0 then return 0, 0 end
|
||||||
|
local e = floor(log(abs(x)) / log2 + 1)
|
||||||
|
return x / 2 ^ e, e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- NIL = 202
|
||||||
|
-- FLOAT = 203
|
||||||
|
-- TRUE = 204
|
||||||
|
-- FALSE = 205
|
||||||
|
-- STRING = 206
|
||||||
|
-- TABLE = 207
|
||||||
|
-- REFERENCE = 208
|
||||||
|
-- CONSTRUCTOR = 209
|
||||||
|
-- FUNCTION = 210
|
||||||
|
-- RESOURCE = 211
|
||||||
|
-- INT64 = 212
|
||||||
|
|
||||||
|
local mts = {}
|
||||||
|
local ids = {}
|
||||||
|
local serializers = {}
|
||||||
|
local deserializers = {}
|
||||||
|
local resources = {}
|
||||||
|
local resources_by_name = {}
|
||||||
|
|
||||||
|
local function pack(...)
|
||||||
|
return {...}, select("#", ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function not_array_index(x, len)
|
||||||
|
return type(x) ~= "number" or x < 1 or x > len or x ~= floor(x)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function type_check(x, tp, name)
|
||||||
|
assert(type(x) == tp,
|
||||||
|
format("Expected parameter %q to be of type %q.", name, tp))
|
||||||
|
end
|
||||||
|
|
||||||
|
local bigIntSupport = false
|
||||||
|
local isInteger
|
||||||
|
if math.type then -- Detect Lua 5.3
|
||||||
|
local mtype = math.type
|
||||||
|
bigIntSupport = loadstring[[
|
||||||
|
local char = string.char
|
||||||
|
return function(n)
|
||||||
|
local nn = n < 0 and -(n + 1) or n
|
||||||
|
local b1 = nn // 0x100000000000000
|
||||||
|
local b2 = nn // 0x1000000000000 % 0x100
|
||||||
|
local b3 = nn // 0x10000000000 % 0x100
|
||||||
|
local b4 = nn // 0x100000000 % 0x100
|
||||||
|
local b5 = nn // 0x1000000 % 0x100
|
||||||
|
local b6 = nn // 0x10000 % 0x100
|
||||||
|
local b7 = nn // 0x100 % 0x100
|
||||||
|
local b8 = nn % 0x100
|
||||||
|
if n < 0 then
|
||||||
|
b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4
|
||||||
|
b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8
|
||||||
|
end
|
||||||
|
return char(212, b1, b2, b3, b4, b5, b6, b7, b8)
|
||||||
|
end]]()
|
||||||
|
isInteger = function(x)
|
||||||
|
return mtype(x) == 'integer'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
isInteger = function(x)
|
||||||
|
return floor(x) == x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Copyright (C) 2012-2015 Francois Perrad.
|
||||||
|
-- number serialization code modified from https://github.com/fperrad/lua-MessagePack
|
||||||
|
-- Encode a number as a big-endian ieee-754 double, big-endian signed 64 bit integer, or a small integer
|
||||||
|
local function number_to_str(n)
|
||||||
|
if isInteger(n) then -- int
|
||||||
|
if n <= 100 and n >= -27 then -- 1 byte, 7 bits of data
|
||||||
|
return char(n + 27)
|
||||||
|
elseif n <= 8191 and n >= -8192 then -- 2 bytes, 14 bits of data
|
||||||
|
n = n + 8192
|
||||||
|
return char(128 + (floor(n / 0x100) % 0x100), n % 0x100)
|
||||||
|
elseif bigIntSupport then
|
||||||
|
return bigIntSupport(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local sign = 0
|
||||||
|
if n < 0.0 then
|
||||||
|
sign = 0x80
|
||||||
|
n = -n
|
||||||
|
end
|
||||||
|
local m, e = frexp(n) -- mantissa, exponent
|
||||||
|
if m ~= m then
|
||||||
|
return char(203, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
elseif m == 1/0 then
|
||||||
|
if sign == 0 then
|
||||||
|
return char(203, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
else
|
||||||
|
return char(203, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
e = e + 0x3FE
|
||||||
|
if e < 1 then -- denormalized numbers
|
||||||
|
m = m * 2 ^ (52 + e)
|
||||||
|
e = 0
|
||||||
|
else
|
||||||
|
m = (m * 2 - 1) * 2 ^ 52
|
||||||
|
end
|
||||||
|
return char(203,
|
||||||
|
sign + floor(e / 0x10),
|
||||||
|
(e % 0x10) * 0x10 + floor(m / 0x1000000000000),
|
||||||
|
floor(m / 0x10000000000) % 0x100,
|
||||||
|
floor(m / 0x100000000) % 0x100,
|
||||||
|
floor(m / 0x1000000) % 0x100,
|
||||||
|
floor(m / 0x10000) % 0x100,
|
||||||
|
floor(m / 0x100) % 0x100,
|
||||||
|
m % 0x100)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Copyright (C) 2012-2015 Francois Perrad.
|
||||||
|
-- number deserialization code also modified from https://github.com/fperrad/lua-MessagePack
|
||||||
|
local function number_from_str(str, index)
|
||||||
|
local b = byte(str, index)
|
||||||
|
if b < 128 then
|
||||||
|
return b - 27, index + 1
|
||||||
|
elseif b < 192 then
|
||||||
|
return byte(str, index + 1) + 0x100 * (b - 128) - 8192, index + 2
|
||||||
|
end
|
||||||
|
local b1, b2, b3, b4, b5, b6, b7, b8 = byte(str, index + 1, index + 8)
|
||||||
|
if b == 212 then
|
||||||
|
local flip = b1 >= 128
|
||||||
|
if flip then -- negative
|
||||||
|
b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4
|
||||||
|
b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8
|
||||||
|
end
|
||||||
|
local n = ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||||
|
if flip then
|
||||||
|
return (-n) - 1, index + 9
|
||||||
|
else
|
||||||
|
return n, index + 9
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local sign = b1 > 0x7F and -1 or 1
|
||||||
|
local e = (b1 % 0x80) * 0x10 + floor(b2 / 0x10)
|
||||||
|
local m = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||||
|
local n
|
||||||
|
if e == 0 then
|
||||||
|
if m == 0 then
|
||||||
|
n = sign * 0.0
|
||||||
|
else
|
||||||
|
n = sign * (m / 2 ^ 52) * 2 ^ -1022
|
||||||
|
end
|
||||||
|
elseif e == 0x7FF then
|
||||||
|
if m == 0 then
|
||||||
|
n = sign * (1/0)
|
||||||
|
else
|
||||||
|
n = 0.0/0.0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
n = sign * (1.0 + m / 2 ^ 52) * 2 ^ (e - 0x3FF)
|
||||||
|
end
|
||||||
|
return n, index + 9
|
||||||
|
end
|
||||||
|
|
||||||
|
local types = {}
|
||||||
|
|
||||||
|
types["nil"] = function(x, visited, accum)
|
||||||
|
accum[#accum + 1] = "\202"
|
||||||
|
end
|
||||||
|
|
||||||
|
function types.number(x, visited, accum)
|
||||||
|
accum[#accum + 1] = number_to_str(x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function types.boolean(x, visited, accum)
|
||||||
|
accum[#accum + 1] = x and "\204" or "\205"
|
||||||
|
end
|
||||||
|
|
||||||
|
function types.string(x, visited, accum)
|
||||||
|
local alen = #accum
|
||||||
|
if visited[x] then
|
||||||
|
accum[alen + 1] = "\208"
|
||||||
|
accum[alen + 2] = number_to_str(visited[x])
|
||||||
|
else
|
||||||
|
visited[x] = visited.next
|
||||||
|
visited.next = visited.next + 1
|
||||||
|
accum[alen + 1] = "\206"
|
||||||
|
accum[alen + 2] = number_to_str(#x)
|
||||||
|
accum[alen + 3] = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function check_custom_type(x, visited, accum)
|
||||||
|
local res = resources[x]
|
||||||
|
if res then
|
||||||
|
accum[#accum + 1] = "\211"
|
||||||
|
types[type(res)](res, visited, accum)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
local mt = getmetatable(x)
|
||||||
|
local id = mt and ids[mt]
|
||||||
|
if id then
|
||||||
|
if x == visited.temp then
|
||||||
|
error("Infinite loop in constructor.")
|
||||||
|
end
|
||||||
|
visited.temp = x
|
||||||
|
accum[#accum + 1] = "\209"
|
||||||
|
types[type(id)](id, visited, accum)
|
||||||
|
local args, len = pack(serializers[id](x))
|
||||||
|
accum[#accum + 1] = number_to_str(len)
|
||||||
|
for i = 1, len do
|
||||||
|
local arg = args[i]
|
||||||
|
types[type(arg)](arg, visited, accum)
|
||||||
|
end
|
||||||
|
visited[x] = visited.next
|
||||||
|
visited.next = visited.next + 1
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function types.userdata(x, visited, accum)
|
||||||
|
if visited[x] then
|
||||||
|
accum[#accum + 1] = "\208"
|
||||||
|
accum[#accum + 1] = number_to_str(visited[x])
|
||||||
|
else
|
||||||
|
if check_custom_type(x, visited, accum) then return end
|
||||||
|
error("Cannot serialize this userdata.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function types.table(x, visited, accum)
|
||||||
|
if visited[x] then
|
||||||
|
accum[#accum + 1] = "\208"
|
||||||
|
accum[#accum + 1] = number_to_str(visited[x])
|
||||||
|
else
|
||||||
|
if check_custom_type(x, visited, accum) then return end
|
||||||
|
visited[x] = visited.next
|
||||||
|
visited.next = visited.next + 1
|
||||||
|
local xlen = #x
|
||||||
|
accum[#accum + 1] = "\207"
|
||||||
|
accum[#accum + 1] = number_to_str(xlen)
|
||||||
|
for i = 1, xlen do
|
||||||
|
local v = x[i]
|
||||||
|
types[type(v)](v, visited, accum)
|
||||||
|
end
|
||||||
|
local key_count = 0
|
||||||
|
for k in pairs(x) do
|
||||||
|
if not_array_index(k, xlen) then
|
||||||
|
key_count = key_count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
accum[#accum + 1] = number_to_str(key_count)
|
||||||
|
for k, v in pairs(x) do
|
||||||
|
if not_array_index(k, xlen) then
|
||||||
|
types[type(k)](k, visited, accum)
|
||||||
|
types[type(v)](v, visited, accum)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
types["function"] = function(x, visited, accum)
|
||||||
|
if visited[x] then
|
||||||
|
accum[#accum + 1] = "\208"
|
||||||
|
accum[#accum + 1] = number_to_str(visited[x])
|
||||||
|
else
|
||||||
|
if check_custom_type(x, visited, accum) then return end
|
||||||
|
visited[x] = visited.next
|
||||||
|
visited.next = visited.next + 1
|
||||||
|
local str = dump(x)
|
||||||
|
accum[#accum + 1] = "\210"
|
||||||
|
accum[#accum + 1] = number_to_str(#str)
|
||||||
|
accum[#accum + 1] = str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
types.cdata = function(x, visited, accum)
|
||||||
|
if visited[x] then
|
||||||
|
accum[#accum + 1] = "\208"
|
||||||
|
accum[#accum + 1] = number_to_str(visited[x])
|
||||||
|
else
|
||||||
|
if check_custom_type(x, visited, #accum) then return end
|
||||||
|
error("Cannot serialize this cdata.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
types.thread = function() error("Cannot serialize threads.") end
|
||||||
|
|
||||||
|
local function deserialize_value(str, index, visited)
|
||||||
|
local t = byte(str, index)
|
||||||
|
if not t then return end
|
||||||
|
if t < 128 then
|
||||||
|
return t - 27, index + 1
|
||||||
|
elseif t < 192 then
|
||||||
|
return byte(str, index + 1) + 0x100 * (t - 128) - 8192, index + 2
|
||||||
|
elseif t == 202 then
|
||||||
|
return nil, index + 1
|
||||||
|
elseif t == 203 then
|
||||||
|
return number_from_str(str, index)
|
||||||
|
elseif t == 204 then
|
||||||
|
return true, index + 1
|
||||||
|
elseif t == 205 then
|
||||||
|
return false, index + 1
|
||||||
|
elseif t == 206 then
|
||||||
|
local length, dataindex = deserialize_value(str, index + 1, visited)
|
||||||
|
local nextindex = dataindex + length
|
||||||
|
local substr = sub(str, dataindex, nextindex - 1)
|
||||||
|
visited[#visited + 1] = substr
|
||||||
|
return substr, nextindex
|
||||||
|
elseif t == 207 then
|
||||||
|
local count, nextindex = number_from_str(str, index + 1)
|
||||||
|
local ret = {}
|
||||||
|
visited[#visited + 1] = ret
|
||||||
|
for i = 1, count do
|
||||||
|
ret[i], nextindex = deserialize_value(str, nextindex, visited)
|
||||||
|
end
|
||||||
|
count, nextindex = number_from_str(str, nextindex)
|
||||||
|
for i = 1, count do
|
||||||
|
local k, v
|
||||||
|
k, nextindex = deserialize_value(str, nextindex, visited)
|
||||||
|
v, nextindex = deserialize_value(str, nextindex, visited)
|
||||||
|
ret[k] = v
|
||||||
|
end
|
||||||
|
return ret, nextindex
|
||||||
|
elseif t == 208 then
|
||||||
|
local ref, nextindex = number_from_str(str, index + 1)
|
||||||
|
return visited[ref], nextindex
|
||||||
|
elseif t == 209 then
|
||||||
|
local count
|
||||||
|
local name, nextindex = deserialize_value(str, index + 1, visited)
|
||||||
|
count, nextindex = number_from_str(str, nextindex)
|
||||||
|
local args = {}
|
||||||
|
for i = 1, count do
|
||||||
|
args[i], nextindex = deserialize_value(str, nextindex, visited)
|
||||||
|
end
|
||||||
|
local ret = deserializers[name](unpack(args))
|
||||||
|
visited[#visited + 1] = ret
|
||||||
|
return ret, nextindex
|
||||||
|
elseif t == 210 then
|
||||||
|
local length, dataindex = deserialize_value(str, index + 1, visited)
|
||||||
|
local nextindex = dataindex + length
|
||||||
|
local ret = loadstring(sub(str, dataindex, nextindex - 1))
|
||||||
|
visited[#visited + 1] = ret
|
||||||
|
return ret, nextindex
|
||||||
|
elseif t == 211 then
|
||||||
|
local res, nextindex = deserialize_value(str, index + 1, visited)
|
||||||
|
return resources_by_name[res], nextindex
|
||||||
|
elseif t == 212 then
|
||||||
|
return number_from_str(str, index)
|
||||||
|
else
|
||||||
|
error("Could not deserialize type byte " .. t .. ".")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serialize(...)
|
||||||
|
local visited = {next = 1}
|
||||||
|
local accum = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
local x = select(i, ...)
|
||||||
|
types[type(x)](x, visited, accum)
|
||||||
|
end
|
||||||
|
return concat(accum)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_file_writer(file)
|
||||||
|
return setmetatable({}, {
|
||||||
|
__newindex = function(_, _, v)
|
||||||
|
file:write(v)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serialize_to_file(path, mode, ...)
|
||||||
|
local file, err = io.open(path, mode)
|
||||||
|
assert(file, err)
|
||||||
|
local visited = {next = 1}
|
||||||
|
local accum = make_file_writer(file)
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
local x = select(i, ...)
|
||||||
|
types[type(x)](x, visited, accum)
|
||||||
|
end
|
||||||
|
-- flush the writer
|
||||||
|
file:flush()
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function writeFile(path, ...)
|
||||||
|
return serialize_to_file(path, "wb", ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function appendFile(path, ...)
|
||||||
|
return serialize_to_file(path, "ab", ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function deserialize(str, index)
|
||||||
|
assert(type(str) == "string", "Expected string to deserialize.")
|
||||||
|
local vals = {}
|
||||||
|
index = index or 1
|
||||||
|
local visited = {}
|
||||||
|
local len = 0
|
||||||
|
local val
|
||||||
|
while index do
|
||||||
|
val, index = deserialize_value(str, index, visited)
|
||||||
|
if index then
|
||||||
|
len = len + 1
|
||||||
|
vals[len] = val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return vals, len
|
||||||
|
end
|
||||||
|
|
||||||
|
local function deserializeN(str, n, index)
|
||||||
|
assert(type(str) == "string", "Expected string to deserialize.")
|
||||||
|
n = n or 1
|
||||||
|
assert(type(n) == "number", "Expected a number for parameter n.")
|
||||||
|
assert(n > 0 and floor(n) == n, "N must be a poitive integer.")
|
||||||
|
local vals = {}
|
||||||
|
index = index or 1
|
||||||
|
local visited = {}
|
||||||
|
local len = 0
|
||||||
|
local val
|
||||||
|
while index and len < n do
|
||||||
|
val, index = deserialize_value(str, index, visited)
|
||||||
|
if index then
|
||||||
|
len = len + 1
|
||||||
|
vals[len] = val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
vals[len + 1] = index
|
||||||
|
return unpack(vals, 1, n + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readFile(path)
|
||||||
|
local file, err = io.open(path, "rb")
|
||||||
|
assert(file, err)
|
||||||
|
local str = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
return deserialize(str)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function default_deserialize(metatable)
|
||||||
|
return function(...)
|
||||||
|
local ret = {}
|
||||||
|
for i = 1, select("#", ...), 2 do
|
||||||
|
ret[select(i, ...)] = select(i + 1, ...)
|
||||||
|
end
|
||||||
|
return setmetatable(ret, metatable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function default_serialize(x)
|
||||||
|
assert(type(x) == "table",
|
||||||
|
"Default serialization for custom types only works for tables.")
|
||||||
|
local args = {}
|
||||||
|
local len = 0
|
||||||
|
for k, v in pairs(x) do
|
||||||
|
args[len + 1], args[len + 2] = k, v
|
||||||
|
len = len + 2
|
||||||
|
end
|
||||||
|
return unpack(args, 1, len)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Templating
|
||||||
|
|
||||||
|
local function normalize_template(template)
|
||||||
|
local ret = {}
|
||||||
|
for i = 1, #template do
|
||||||
|
ret[i] = template[i]
|
||||||
|
end
|
||||||
|
local non_array_part = {}
|
||||||
|
-- The non-array part of the template (nested templates) have to be deterministic, so they are sorted.
|
||||||
|
-- This means that inherently non deterministicly sortable keys (tables, functions) should NOT be used
|
||||||
|
-- in templates. Looking for way around this.
|
||||||
|
for k in pairs(template) do
|
||||||
|
if not_array_index(k, #template) then
|
||||||
|
non_array_part[#non_array_part + 1] = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(non_array_part)
|
||||||
|
for i = 1, #non_array_part do
|
||||||
|
local name = non_array_part[i]
|
||||||
|
ret[#ret + 1] = {name, normalize_template(template[name])}
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
local function templatepart_serialize(part, argaccum, x, len)
|
||||||
|
local extras = {}
|
||||||
|
local extracount = 0
|
||||||
|
for k, v in pairs(x) do
|
||||||
|
extras[k] = v
|
||||||
|
extracount = extracount + 1
|
||||||
|
end
|
||||||
|
for i = 1, #part do
|
||||||
|
extracount = extracount - 1
|
||||||
|
if type(part[i]) == "table" then
|
||||||
|
extras[part[i][1]] = nil
|
||||||
|
len = templatepart_serialize(part[i][2], argaccum, x[part[i][1]], len)
|
||||||
|
else
|
||||||
|
extras[part[i]] = nil
|
||||||
|
len = len + 1
|
||||||
|
argaccum[len] = x[part[i]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if extracount > 0 then
|
||||||
|
argaccum[len + 1] = extras
|
||||||
|
else
|
||||||
|
argaccum[len + 1] = nil
|
||||||
|
end
|
||||||
|
return len + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function templatepart_deserialize(ret, part, values, vindex)
|
||||||
|
for i = 1, #part do
|
||||||
|
local name = part[i]
|
||||||
|
if type(name) == "table" then
|
||||||
|
local newret = {}
|
||||||
|
ret[name[1]] = newret
|
||||||
|
vindex = templatepart_deserialize(newret, name[2], values, vindex)
|
||||||
|
else
|
||||||
|
ret[name] = values[vindex]
|
||||||
|
vindex = vindex + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local extras = values[vindex]
|
||||||
|
if extras then
|
||||||
|
for k, v in pairs(extras) do
|
||||||
|
ret[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return vindex + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function template_serializer_and_deserializer(metatable, template)
|
||||||
|
return function(x)
|
||||||
|
argaccum = {}
|
||||||
|
local len = templatepart_serialize(template, argaccum, x, 0)
|
||||||
|
return unpack(argaccum, 1, len)
|
||||||
|
end, function(...)
|
||||||
|
local ret = {}
|
||||||
|
local len = select("#", ...)
|
||||||
|
local args = {...}
|
||||||
|
templatepart_deserialize(ret, template, args, 1)
|
||||||
|
return setmetatable(ret, metatable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function register(metatable, name, serialize, deserialize)
|
||||||
|
name = name or metatable.name
|
||||||
|
serialize = serialize or metatable._serialize
|
||||||
|
deserialize = deserialize or metatable._deserialize
|
||||||
|
if not serialize then
|
||||||
|
if metatable._template then
|
||||||
|
local t = normalize_template(metatable._template)
|
||||||
|
serialize, deserialize = template_serializer_and_deserializer(metatable, t)
|
||||||
|
elseif not deserialize then
|
||||||
|
serialize = default_serialize
|
||||||
|
deserialize = default_deserialize(metatable)
|
||||||
|
else
|
||||||
|
serialize = metatable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
type_check(metatable, "table", "metatable")
|
||||||
|
type_check(name, "string", "name")
|
||||||
|
type_check(serialize, "function", "serialize")
|
||||||
|
type_check(deserialize, "function", "deserialize")
|
||||||
|
assert(not ids[metatable], "Metatable already registered.")
|
||||||
|
assert(not mts[name], ("Name %q already registered."):format(name))
|
||||||
|
mts[name] = metatable
|
||||||
|
ids[metatable] = name
|
||||||
|
serializers[name] = serialize
|
||||||
|
deserializers[name] = deserialize
|
||||||
|
return metatable
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unregister(item)
|
||||||
|
local name, metatable
|
||||||
|
if type(item) == "string" then -- assume name
|
||||||
|
name, metatable = item, mts[item]
|
||||||
|
else -- assume metatable
|
||||||
|
name, metatable = ids[item], item
|
||||||
|
end
|
||||||
|
type_check(name, "string", "name")
|
||||||
|
type_check(metatable, "table", "metatable")
|
||||||
|
mts[name] = nil
|
||||||
|
ids[metatable] = nil
|
||||||
|
serializers[name] = nil
|
||||||
|
deserializers[name] = nil
|
||||||
|
return metatable
|
||||||
|
end
|
||||||
|
|
||||||
|
local function registerClass(class, name)
|
||||||
|
name = name or class.name
|
||||||
|
if class.__instanceDict then -- middleclass
|
||||||
|
register(class.__instanceDict, name)
|
||||||
|
else -- assume 30log or similar library
|
||||||
|
register(class, name)
|
||||||
|
end
|
||||||
|
return class
|
||||||
|
end
|
||||||
|
|
||||||
|
local function registerResource(resource, name)
|
||||||
|
type_check(name, "string", "name")
|
||||||
|
assert(not resources[resource],
|
||||||
|
"Resource already registered.")
|
||||||
|
assert(not resources_by_name[name],
|
||||||
|
format("Resource %q already exists.", name))
|
||||||
|
resources_by_name[name] = resource
|
||||||
|
resources[resource] = name
|
||||||
|
return resource
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unregisterResource(name)
|
||||||
|
type_check(name, "string", "name")
|
||||||
|
assert(resources_by_name[name], format("Resource %q does not exist.", name))
|
||||||
|
local resource = resources_by_name[name]
|
||||||
|
resources_by_name[name] = nil
|
||||||
|
resources[resource] = nil
|
||||||
|
return resource
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
-- aliases
|
||||||
|
s = serialize,
|
||||||
|
d = deserialize,
|
||||||
|
dn = deserializeN,
|
||||||
|
r = readFile,
|
||||||
|
w = writeFile,
|
||||||
|
a = appendFile,
|
||||||
|
|
||||||
|
serialize = serialize,
|
||||||
|
deserialize = deserialize,
|
||||||
|
deserializeN = deserializeN,
|
||||||
|
readFile = readFile,
|
||||||
|
writeFile = writeFile,
|
||||||
|
appendFile = appendFile,
|
||||||
|
register = register,
|
||||||
|
unregister = unregister,
|
||||||
|
registerResource = registerResource,
|
||||||
|
unregisterResource = unregisterResource,
|
||||||
|
registerClass = registerClass
|
||||||
|
}
|
68
sonic-radiance.love/libs/classic.lua
Normal file
68
sonic-radiance.love/libs/classic.lua
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
--
|
||||||
|
-- classic
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2014, rxi
|
||||||
|
--
|
||||||
|
-- This module is free software; you can redistribute it and/or modify it under
|
||||||
|
-- the terms of the MIT license. See LICENSE for details.
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
local Object = {}
|
||||||
|
Object.__index = Object
|
||||||
|
|
||||||
|
|
||||||
|
function Object:new()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Object:extend()
|
||||||
|
local cls = {}
|
||||||
|
for k, v in pairs(self) do
|
||||||
|
if k:find("__") == 1 then
|
||||||
|
cls[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cls.__index = cls
|
||||||
|
cls.super = self
|
||||||
|
setmetatable(cls, self)
|
||||||
|
return cls
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Object:implement(...)
|
||||||
|
for _, cls in pairs({...}) do
|
||||||
|
for k, v in pairs(cls) do
|
||||||
|
if self[k] == nil and type(v) == "function" then
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Object:is(T)
|
||||||
|
local mt = getmetatable(self)
|
||||||
|
while mt do
|
||||||
|
if mt == T then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
mt = getmetatable(mt)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Object:__tostring()
|
||||||
|
return "Object"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Object:__call(...)
|
||||||
|
local obj = setmetatable({}, self)
|
||||||
|
obj:new(...)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return Object
|
99
sonic-radiance.love/libs/cscreen.lua
Normal file
99
sonic-radiance.love/libs/cscreen.lua
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
--[[
|
||||||
|
CScreen v1.3 by CodeNMore
|
||||||
|
A simple way to make resolution-independent Love2D games
|
||||||
|
Tested for LOVE 0.10.1
|
||||||
|
See: https://github.com/CodeNMore/CScreen
|
||||||
|
Zlib License:
|
||||||
|
Copyright (c) 2016 CodeNMore
|
||||||
|
This software is provided 'as-is', without any express or implied warranty.
|
||||||
|
In no event will the authors be held liable for any damages arising from
|
||||||
|
the use of this software.
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software in
|
||||||
|
a product, an acknowledgment in the product documentation would be appreciated
|
||||||
|
but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local CScreen = {}
|
||||||
|
local rx, ry, ctr = 800, 600, true
|
||||||
|
local rxv, ryv, fsv, fsvr = 800, 600, 1.0, 1.0
|
||||||
|
local tx, ty, rwf, rhf = 0, 0, 800, 600
|
||||||
|
local cr, cg, cb, ca = 0, 0, 0, 255
|
||||||
|
|
||||||
|
-- Initializes CScreen with the initial size values
|
||||||
|
function CScreen.init(tw, th, cntr)
|
||||||
|
rx = tw or 800
|
||||||
|
ry = th or 600
|
||||||
|
ctr = cntr or false
|
||||||
|
CScreen.update(love.graphics.getWidth(), love.graphics.getHeight())
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draws letterbox borders
|
||||||
|
function CScreen.cease()
|
||||||
|
if ctr then
|
||||||
|
local pr, pg, pb, pa = love.graphics.getColor()
|
||||||
|
love.graphics.setColor(cr, cg, cb, ca)
|
||||||
|
love.graphics.scale(fsvr, fsvr)
|
||||||
|
|
||||||
|
if tx ~= 0 then
|
||||||
|
love.graphics.rectangle("fill", -tx, 0, tx, rhf)
|
||||||
|
love.graphics.rectangle("fill", rxv, 0, tx, rhf)
|
||||||
|
elseif ty ~= 0 then
|
||||||
|
love.graphics.rectangle("fill", 0, -ty, rwf, ty)
|
||||||
|
love.graphics.rectangle("fill", 0, ryv, rwf, ty)
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.setColor(pr, pg, pb, pa)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Scales and centers all graphics properly
|
||||||
|
function CScreen.apply()
|
||||||
|
if ctr then
|
||||||
|
love.graphics.translate(tx, ty)
|
||||||
|
end
|
||||||
|
love.graphics.scale(fsv, fsv)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Updates CScreen when the window size changes
|
||||||
|
function CScreen.update(w, h)
|
||||||
|
local sx = w / rx
|
||||||
|
local sy = h / ry
|
||||||
|
fsv = math.min(sx, sy)
|
||||||
|
fsvr = 1 / fsv
|
||||||
|
-- Centering
|
||||||
|
if ctr and fsv == sx then -- Vertically
|
||||||
|
tx = 0
|
||||||
|
ty = (h / 2) - (ry * fsv / 2)
|
||||||
|
elseif ctr and fsv == sy then -- Horizontally
|
||||||
|
ty = 0
|
||||||
|
tx = (w / 2) - (rx * fsv / 2)
|
||||||
|
end
|
||||||
|
-- Variable sets
|
||||||
|
rwf = w
|
||||||
|
rhf = h
|
||||||
|
rxv = rx * fsv
|
||||||
|
ryv = ry * fsv
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Convert from window coordinates to target coordinates
|
||||||
|
function CScreen.project(x, y)
|
||||||
|
return math.floor((x - tx) / fsv), math.floor((y - ty) / fsv)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Change letterbox color
|
||||||
|
function CScreen.setColor(r, g, b, a)
|
||||||
|
cr = r
|
||||||
|
cg = g
|
||||||
|
cb = b
|
||||||
|
ca = a
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return the table for use
|
||||||
|
return CScreen
|
737
sonic-radiance.love/libs/lovebird.lua
Normal file
737
sonic-radiance.love/libs/lovebird.lua
Normal file
|
@ -0,0 +1,737 @@
|
||||||
|
--
|
||||||
|
-- lovebird
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2017 rxi
|
||||||
|
--
|
||||||
|
-- This library is free software; you can redistribute it and/or modify it
|
||||||
|
-- under the terms of the MIT license. See LICENSE for details.
|
||||||
|
--
|
||||||
|
|
||||||
|
local socket = require "socket"
|
||||||
|
|
||||||
|
local lovebird = { _version = "0.4.3" }
|
||||||
|
|
||||||
|
lovebird.loadstring = loadstring or load
|
||||||
|
lovebird.inited = false
|
||||||
|
lovebird.host = "*"
|
||||||
|
lovebird.buffer = ""
|
||||||
|
lovebird.lines = {}
|
||||||
|
lovebird.connections = {}
|
||||||
|
lovebird.pages = {}
|
||||||
|
|
||||||
|
lovebird.wrapprint = true
|
||||||
|
lovebird.timestamp = true
|
||||||
|
lovebird.allowhtml = false
|
||||||
|
lovebird.echoinput = true
|
||||||
|
lovebird.port = 8000
|
||||||
|
lovebird.whitelist = { "127.0.0.1" }
|
||||||
|
lovebird.maxlines = 200
|
||||||
|
lovebird.updateinterval = .5
|
||||||
|
|
||||||
|
|
||||||
|
lovebird.pages["index"] = [[
|
||||||
|
<?lua
|
||||||
|
-- Handle console input
|
||||||
|
if req.parsedbody.input then
|
||||||
|
local str = req.parsedbody.input
|
||||||
|
if lovebird.echoinput then
|
||||||
|
lovebird.pushline({ type = 'input', str = str })
|
||||||
|
end
|
||||||
|
if str:find("^=") then
|
||||||
|
str = "print(" .. str:sub(2) .. ")"
|
||||||
|
end
|
||||||
|
xpcall(function() assert(lovebird.loadstring(str, "input"))() end,
|
||||||
|
lovebird.onerror)
|
||||||
|
end
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="x-ua-compatible" content="IE=Edge"/>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>lovebird</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: helvetica, verdana, sans;
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
.timestamp {
|
||||||
|
color: #909090;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.repeatcount {
|
||||||
|
color: #F0F0F0;
|
||||||
|
background: #505050;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
border-radius: 7px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.errormarker {
|
||||||
|
color: #F0F0F0;
|
||||||
|
background: #8E0000;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 17px;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.greybordered {
|
||||||
|
margin: 12px;
|
||||||
|
background: #F0F0F0;
|
||||||
|
border: 1px solid #E0E0E0;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.inputline {
|
||||||
|
font-family: mono, courier;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606060;
|
||||||
|
}
|
||||||
|
.inputline:before {
|
||||||
|
content: '\00B7\00B7\00B7';
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
.errorline {
|
||||||
|
color: #8E0000;
|
||||||
|
}
|
||||||
|
#header {
|
||||||
|
background: #101010;
|
||||||
|
height: 25px;
|
||||||
|
color: #F0F0F0;
|
||||||
|
padding: 9px
|
||||||
|
}
|
||||||
|
#title {
|
||||||
|
float: left;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
#title a {
|
||||||
|
color: #F0F0F0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
#title a:hover {
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
#version {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
#status {
|
||||||
|
float: right;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
#main a {
|
||||||
|
color: #000000;
|
||||||
|
text-decoration: none;
|
||||||
|
background: #E0E0E0;
|
||||||
|
border: 1px solid #D0D0D0;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#main a:hover {
|
||||||
|
background: #D0D0D0;
|
||||||
|
border: 1px solid #C0C0C0;
|
||||||
|
}
|
||||||
|
#console {
|
||||||
|
position: absolute;
|
||||||
|
top: 40px; bottom: 0px; left: 0px; right: 312px;
|
||||||
|
}
|
||||||
|
#input {
|
||||||
|
position: absolute;
|
||||||
|
margin: 10px;
|
||||||
|
bottom: 0px; left: 0px; right: 0px;
|
||||||
|
}
|
||||||
|
#inputbox {
|
||||||
|
width: 100%;
|
||||||
|
font-family: mono, courier;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
#output {
|
||||||
|
overflow-y: scroll;
|
||||||
|
position: absolute;
|
||||||
|
margin: 10px;
|
||||||
|
line-height: 17px;
|
||||||
|
top: 0px; bottom: 36px; left: 0px; right: 0px;
|
||||||
|
}
|
||||||
|
#env {
|
||||||
|
position: absolute;
|
||||||
|
top: 40px; bottom: 0px; right: 0px;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
#envheader {
|
||||||
|
padding: 5px;
|
||||||
|
background: #E0E0E0;
|
||||||
|
}
|
||||||
|
#envvars {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px; right: 0px; top: 25px; bottom: 0px;
|
||||||
|
margin: 10px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="header">
|
||||||
|
<div id="title">
|
||||||
|
<a href="https://github.com/rxi/lovebird">lovebird</a>
|
||||||
|
<span id="version"><?lua echo(lovebird._version) ?></span>
|
||||||
|
</div>
|
||||||
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
<div id="main">
|
||||||
|
<div id="console" class="greybordered">
|
||||||
|
<div id="output"> <?lua echo(lovebird.buffer) ?> </div>
|
||||||
|
<div id="input">
|
||||||
|
<form method="post"
|
||||||
|
onkeydown="return onInputKeyDown(event);"
|
||||||
|
onsubmit="onInputSubmit(); return false;">
|
||||||
|
<input id="inputbox" name="input" type="text"
|
||||||
|
autocomplete="off"></input>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="env" class="greybordered">
|
||||||
|
<div id="envheader"></div>
|
||||||
|
<div id="envvars"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById("inputbox").focus();
|
||||||
|
|
||||||
|
var changeFavicon = function(href) {
|
||||||
|
var old = document.getElementById("favicon");
|
||||||
|
if (old) document.head.removeChild(old);
|
||||||
|
var link = document.createElement("link");
|
||||||
|
link.id = "favicon";
|
||||||
|
link.rel = "shortcut icon";
|
||||||
|
link.href = href;
|
||||||
|
document.head.appendChild(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
var truncate = function(str, len) {
|
||||||
|
if (str.length <= len) return str;
|
||||||
|
return str.substring(0, len - 3) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
var geturl = function(url, onComplete, onFail) {
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.onreadystatechange = function() {
|
||||||
|
if (req.readyState != 4) return;
|
||||||
|
if (req.status == 200) {
|
||||||
|
if (onComplete) onComplete(req.responseText);
|
||||||
|
} else {
|
||||||
|
if (onFail) onFail(req.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url += (url.indexOf("?") > -1 ? "&_=" : "?_=") + Math.random();
|
||||||
|
req.open("GET", url, true);
|
||||||
|
req.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
var divContentCache = {}
|
||||||
|
var updateDivContent = function(id, content) {
|
||||||
|
if (divContentCache[id] != content) {
|
||||||
|
document.getElementById(id).innerHTML = content;
|
||||||
|
divContentCache[id] = content
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onInputSubmit = function() {
|
||||||
|
var b = document.getElementById("inputbox");
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.open("POST", "/", true);
|
||||||
|
req.send("input=" + encodeURIComponent(b.value));
|
||||||
|
/* Do input history */
|
||||||
|
if (b.value && inputHistory[0] != b.value) {
|
||||||
|
inputHistory.unshift(b.value);
|
||||||
|
}
|
||||||
|
inputHistory.index = -1;
|
||||||
|
/* Reset */
|
||||||
|
b.value = "";
|
||||||
|
refreshOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input box history */
|
||||||
|
var inputHistory = [];
|
||||||
|
inputHistory.index = 0;
|
||||||
|
var onInputKeyDown = function(e) {
|
||||||
|
var key = e.which || e.keyCode;
|
||||||
|
if (key != 38 && key != 40) return true;
|
||||||
|
var b = document.getElementById("inputbox");
|
||||||
|
if (key == 38 && inputHistory.index < inputHistory.length - 1) {
|
||||||
|
/* Up key */
|
||||||
|
inputHistory.index++;
|
||||||
|
}
|
||||||
|
if (key == 40 && inputHistory.index >= 0) {
|
||||||
|
/* Down key */
|
||||||
|
inputHistory.index--;
|
||||||
|
}
|
||||||
|
b.value = inputHistory[inputHistory.index] || "";
|
||||||
|
b.selectionStart = b.value.length;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output buffer and status */
|
||||||
|
var refreshOutput = function() {
|
||||||
|
geturl("/buffer", function(text) {
|
||||||
|
updateDivContent("status", "connected ●");
|
||||||
|
if (updateDivContent("output", text)) {
|
||||||
|
var div = document.getElementById("output");
|
||||||
|
div.scrollTop = div.scrollHeight;
|
||||||
|
}
|
||||||
|
/* Update favicon */
|
||||||
|
changeFavicon("data:image/png;base64," +
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAP1BMVEUAAAAAAAAAAAD////19fUO"+
|
||||||
|
"Dg7v7+/h4eGzs7MlJSUeHh7n5+fY2NjJycnGxsa3t7eioqKfn5+QkJCHh4d+fn7zU+b5AAAAAnRS"+
|
||||||
|
"TlPlAFWaypEAAABRSURBVBjTfc9HDoAwDERRQ+w0ern/WQkZaUBC4e/mrWzppH9VJjbjZg1Ii2rM"+
|
||||||
|
"DyR1JZ8J0dVWggIGggcEwgbYCRbuPRqgyjHNpzUP+39GPu9fgloC5L9DO0sAAAAASUVORK5CYII="
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function(text) {
|
||||||
|
updateDivContent("status", "disconnected ○");
|
||||||
|
/* Update favicon */
|
||||||
|
changeFavicon("data:image/png;base64," +
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAYFBMVEUAAAAAAAAAAADZ2dm4uLgM"+
|
||||||
|
"DAz29vbz8/Pv7+/h4eHIyMiwsLBtbW0lJSUeHh4QEBDn5+fS0tLDw8O0tLSioqKfn5+QkJCHh4d+"+
|
||||||
|
"fn5ycnJmZmZgYGBXV1dLS0tFRUUGBgZ0He44AAAAAnRSTlPlAFWaypEAAABeSURBVBjTfY9HDoAw"+
|
||||||
|
"DAQD6Z3ey/9/iXMxkVDYw0g7F3tJReosUKHnwY4pCM+EtOEVXrb7wVRA0dMbaAcUwiVeDQq1Jp4a"+
|
||||||
|
"xUg5kE0ooqZu68Di2Tgbs/DiY/9jyGf+AyFKBAK7KD2TAAAAAElFTkSuQmCC"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setInterval(refreshOutput,
|
||||||
|
<?lua echo(lovebird.updateinterval) ?> * 1000);
|
||||||
|
|
||||||
|
/* Environment variable view */
|
||||||
|
var envPath = "";
|
||||||
|
var refreshEnv = function() {
|
||||||
|
geturl("/env.json?p=" + envPath, function(text) {
|
||||||
|
var json = eval("(" + text + ")");
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
var html = "<a href='#' onclick=\"setEnvPath('')\">env</a>";
|
||||||
|
var acc = "";
|
||||||
|
var p = json.path != "" ? json.path.split(".") : [];
|
||||||
|
for (var i = 0; i < p.length; i++) {
|
||||||
|
acc += "." + p[i];
|
||||||
|
html += " <a href='#' onclick=\"setEnvPath('" + acc + "')\">" +
|
||||||
|
truncate(p[i], 10) + "</a>";
|
||||||
|
}
|
||||||
|
updateDivContent("envheader", html);
|
||||||
|
|
||||||
|
/* Handle invalid table path */
|
||||||
|
if (!json.valid) {
|
||||||
|
updateDivContent("envvars", "Bad path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
var html = "<table>";
|
||||||
|
for (var i = 0; json.vars[i]; i++) {
|
||||||
|
var x = json.vars[i];
|
||||||
|
var fullpath = (json.path + "." + x.key).replace(/^\./, "");
|
||||||
|
var k = truncate(x.key, 15);
|
||||||
|
if (x.type == "table") {
|
||||||
|
k = "<a href='#' onclick=\"setEnvPath('" + fullpath + "')\">" +
|
||||||
|
k + "</a>";
|
||||||
|
}
|
||||||
|
var v = "<a href='#' onclick=\"insertVar('" +
|
||||||
|
fullpath.replace(/\.(-?[0-9]+)/g, "[$1]") +
|
||||||
|
"');\">" + x.value + "</a>"
|
||||||
|
html += "<tr><td>" + k + "</td><td>" + v + "</td></tr>";
|
||||||
|
}
|
||||||
|
html += "</table>";
|
||||||
|
updateDivContent("envvars", html);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var setEnvPath = function(p) {
|
||||||
|
envPath = p;
|
||||||
|
refreshEnv();
|
||||||
|
}
|
||||||
|
var insertVar = function(p) {
|
||||||
|
var b = document.getElementById("inputbox");
|
||||||
|
b.value += p;
|
||||||
|
b.focus();
|
||||||
|
}
|
||||||
|
setInterval(refreshEnv, <?lua echo(lovebird.updateinterval) ?> * 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
|
lovebird.pages["buffer"] = [[ <?lua echo(lovebird.buffer) ?> ]]
|
||||||
|
|
||||||
|
|
||||||
|
lovebird.pages["env.json"] = [[
|
||||||
|
<?lua
|
||||||
|
local t = _G
|
||||||
|
local p = req.parsedurl.query.p or ""
|
||||||
|
p = p:gsub("%.+", "."):match("^[%.]*(.*)[%.]*$")
|
||||||
|
if p ~= "" then
|
||||||
|
for x in p:gmatch("[^%.]+") do
|
||||||
|
t = t[x] or t[tonumber(x)]
|
||||||
|
-- Return early if path does not exist
|
||||||
|
if type(t) ~= "table" then
|
||||||
|
echo('{ "valid": false, "path": ' .. string.format("%q", p) .. ' }')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
?>
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"path": "<?lua echo(p) ?>",
|
||||||
|
"vars": [
|
||||||
|
<?lua
|
||||||
|
local keys = {}
|
||||||
|
for k in pairs(t) do
|
||||||
|
if type(k) == "number" or type(k) == "string" then
|
||||||
|
table.insert(keys, k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(keys, lovebird.compare)
|
||||||
|
for _, k in pairs(keys) do
|
||||||
|
local v = t[k]
|
||||||
|
?>
|
||||||
|
{
|
||||||
|
"key": "<?lua echo(k) ?>",
|
||||||
|
"value": <?lua echo(
|
||||||
|
string.format("%q",
|
||||||
|
lovebird.truncate(
|
||||||
|
lovebird.htmlescape(
|
||||||
|
tostring(v)), 26))) ?>,
|
||||||
|
"type": "<?lua echo(type(v)) ?>",
|
||||||
|
},
|
||||||
|
<?lua end ?>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.init()
|
||||||
|
-- Init server
|
||||||
|
lovebird.server = assert(socket.bind(lovebird.host, lovebird.port))
|
||||||
|
lovebird.addr, lovebird.port = lovebird.server:getsockname()
|
||||||
|
lovebird.server:settimeout(0)
|
||||||
|
-- Wrap print
|
||||||
|
lovebird.origprint = print
|
||||||
|
if lovebird.wrapprint then
|
||||||
|
local oldprint = print
|
||||||
|
print = function(...)
|
||||||
|
oldprint(...)
|
||||||
|
lovebird.print(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Compile page templates
|
||||||
|
for k, page in pairs(lovebird.pages) do
|
||||||
|
lovebird.pages[k] = lovebird.template(page, "lovebird, req",
|
||||||
|
"pages." .. k)
|
||||||
|
end
|
||||||
|
lovebird.inited = true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.template(str, params, chunkname)
|
||||||
|
params = params and ("," .. params) or ""
|
||||||
|
local f = function(x) return string.format(" echo(%q)", x) end
|
||||||
|
str = ("?>"..str.."<?lua"):gsub("%?>(.-)<%?lua", f)
|
||||||
|
str = "local echo " .. params .. " = ..." .. str
|
||||||
|
local fn = assert(lovebird.loadstring(str, chunkname))
|
||||||
|
return function(...)
|
||||||
|
local output = {}
|
||||||
|
local echo = function(str) table.insert(output, str) end
|
||||||
|
fn(echo, ...)
|
||||||
|
return table.concat(lovebird.map(output, tostring))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.map(t, fn)
|
||||||
|
local res = {}
|
||||||
|
for k, v in pairs(t) do res[k] = fn(v) end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.trace(...)
|
||||||
|
local str = "[lovebird] " .. table.concat(lovebird.map({...}, tostring), " ")
|
||||||
|
print(str)
|
||||||
|
if not lovebird.wrapprint then lovebird.print(str) end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.unescape(str)
|
||||||
|
local f = function(x) return string.char(tonumber("0x"..x)) end
|
||||||
|
return (str:gsub("%+", " "):gsub("%%(..)", f))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.parseurl(url)
|
||||||
|
local res = {}
|
||||||
|
res.path, res.search = url:match("/([^%?]*)%??(.*)")
|
||||||
|
res.query = {}
|
||||||
|
for k, v in res.search:gmatch("([^&^?]-)=([^&^#]*)") do
|
||||||
|
res.query[k] = lovebird.unescape(v)
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local htmlescapemap = {
|
||||||
|
["<"] = "<",
|
||||||
|
["&"] = "&",
|
||||||
|
['"'] = """,
|
||||||
|
["'"] = "'",
|
||||||
|
}
|
||||||
|
|
||||||
|
function lovebird.htmlescape(str)
|
||||||
|
return ( str:gsub("[<&\"']", htmlescapemap) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.truncate(str, len)
|
||||||
|
if #str <= len then
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
return str:sub(1, len - 3) .. "..."
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.compare(a, b)
|
||||||
|
local na, nb = tonumber(a), tonumber(b)
|
||||||
|
if na then
|
||||||
|
if nb then return na < nb end
|
||||||
|
return false
|
||||||
|
elseif nb then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return tostring(a) < tostring(b)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.checkwhitelist(addr)
|
||||||
|
if lovebird.whitelist == nil then return true end
|
||||||
|
for _, a in pairs(lovebird.whitelist) do
|
||||||
|
local ptn = "^" .. a:gsub("%.", "%%."):gsub("%*", "%%d*") .. "$"
|
||||||
|
if addr:match(ptn) then return true end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.clear()
|
||||||
|
lovebird.lines = {}
|
||||||
|
lovebird.buffer = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.pushline(line)
|
||||||
|
line.time = os.time()
|
||||||
|
line.count = 1
|
||||||
|
table.insert(lovebird.lines, line)
|
||||||
|
if #lovebird.lines > lovebird.maxlines then
|
||||||
|
table.remove(lovebird.lines, 1)
|
||||||
|
end
|
||||||
|
lovebird.recalcbuffer()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.recalcbuffer()
|
||||||
|
local function doline(line)
|
||||||
|
local str = line.str
|
||||||
|
if not lovebird.allowhtml then
|
||||||
|
str = lovebird.htmlescape(line.str):gsub("\n", "<br>")
|
||||||
|
end
|
||||||
|
if line.type == "input" then
|
||||||
|
str = '<span class="inputline">' .. str .. '</span>'
|
||||||
|
else
|
||||||
|
if line.type == "error" then
|
||||||
|
str = '<span class="errormarker">!</span> ' .. str
|
||||||
|
str = '<span class="errorline">' .. str .. '</span>'
|
||||||
|
end
|
||||||
|
if line.count > 1 then
|
||||||
|
str = '<span class="repeatcount">' .. line.count .. '</span> ' .. str
|
||||||
|
end
|
||||||
|
if lovebird.timestamp then
|
||||||
|
str = os.date('<span class="timestamp">%H:%M:%S</span> ', line.time) ..
|
||||||
|
str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
lovebird.buffer = table.concat(lovebird.map(lovebird.lines, doline), "<br>")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.print(...)
|
||||||
|
local t = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
table.insert(t, tostring(select(i, ...)))
|
||||||
|
end
|
||||||
|
local str = table.concat(t, " ")
|
||||||
|
local last = lovebird.lines[#lovebird.lines]
|
||||||
|
if last and str == last.str then
|
||||||
|
-- Update last line if this line is a duplicate of it
|
||||||
|
last.time = os.time()
|
||||||
|
last.count = last.count + 1
|
||||||
|
lovebird.recalcbuffer()
|
||||||
|
else
|
||||||
|
-- Create new line
|
||||||
|
lovebird.pushline({ type = "output", str = str })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.onerror(err)
|
||||||
|
lovebird.pushline({ type = "error", str = err })
|
||||||
|
if lovebird.wrapprint then
|
||||||
|
lovebird.origprint("[lovebird] ERROR: " .. err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.onrequest(req, client)
|
||||||
|
local page = req.parsedurl.path
|
||||||
|
page = page ~= "" and page or "index"
|
||||||
|
-- Handle "page not found"
|
||||||
|
if not lovebird.pages[page] then
|
||||||
|
return "HTTP/1.1 404\r\nContent-Length: 8\r\n\r\nBad page"
|
||||||
|
end
|
||||||
|
-- Handle page
|
||||||
|
local str
|
||||||
|
xpcall(function()
|
||||||
|
local data = lovebird.pages[page](lovebird, req)
|
||||||
|
local contenttype = "text/html"
|
||||||
|
if string.match(page, "%.json$") then
|
||||||
|
contenttype = "application/json"
|
||||||
|
end
|
||||||
|
str = "HTTP/1.1 200 OK\r\n" ..
|
||||||
|
"Content-Type: " .. contenttype .. "\r\n" ..
|
||||||
|
"Content-Length: " .. #data .. "\r\n" ..
|
||||||
|
"\r\n" .. data
|
||||||
|
end, lovebird.onerror)
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.receive(client, pattern)
|
||||||
|
while 1 do
|
||||||
|
local data, msg = client:receive(pattern)
|
||||||
|
if not data then
|
||||||
|
if msg == "timeout" then
|
||||||
|
-- Wait for more data
|
||||||
|
coroutine.yield(true)
|
||||||
|
else
|
||||||
|
-- Disconnected -- yielding nil means we're done
|
||||||
|
coroutine.yield(nil)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.send(client, data)
|
||||||
|
local idx = 1
|
||||||
|
while idx < #data do
|
||||||
|
local res, msg = client:send(data, idx)
|
||||||
|
if not res and msg == "closed" then
|
||||||
|
-- Handle disconnect
|
||||||
|
coroutine.yield(nil)
|
||||||
|
else
|
||||||
|
idx = idx + res
|
||||||
|
coroutine.yield(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.onconnect(client)
|
||||||
|
-- Create request table
|
||||||
|
local requestptn = "(%S*)%s*(%S*)%s*(%S*)"
|
||||||
|
local req = {}
|
||||||
|
req.socket = client
|
||||||
|
req.addr, req.port = client:getsockname()
|
||||||
|
req.request = lovebird.receive(client, "*l")
|
||||||
|
req.method, req.url, req.proto = req.request:match(requestptn)
|
||||||
|
req.headers = {}
|
||||||
|
while 1 do
|
||||||
|
local line, msg = lovebird.receive(client, "*l")
|
||||||
|
if not line or #line == 0 then break end
|
||||||
|
local k, v = line:match("(.-):%s*(.*)$")
|
||||||
|
req.headers[k] = v
|
||||||
|
end
|
||||||
|
if req.headers["Content-Length"] then
|
||||||
|
req.body = lovebird.receive(client, req.headers["Content-Length"])
|
||||||
|
end
|
||||||
|
-- Parse body
|
||||||
|
req.parsedbody = {}
|
||||||
|
if req.body then
|
||||||
|
for k, v in req.body:gmatch("([^&]-)=([^&^#]*)") do
|
||||||
|
req.parsedbody[k] = lovebird.unescape(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Parse request line's url
|
||||||
|
req.parsedurl = lovebird.parseurl(req.url)
|
||||||
|
-- Handle request; get data to send and send
|
||||||
|
local data = lovebird.onrequest(req)
|
||||||
|
lovebird.send(client, data)
|
||||||
|
-- Clear up
|
||||||
|
client:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.update()
|
||||||
|
if not lovebird.inited then lovebird.init() end
|
||||||
|
-- Handle new connections
|
||||||
|
while 1 do
|
||||||
|
-- Accept new connections
|
||||||
|
local client = lovebird.server:accept()
|
||||||
|
if not client then break end
|
||||||
|
client:settimeout(0)
|
||||||
|
local addr = client:getsockname()
|
||||||
|
if lovebird.checkwhitelist(addr) then
|
||||||
|
-- Connection okay -- create and add coroutine to set
|
||||||
|
local conn = coroutine.wrap(function()
|
||||||
|
xpcall(function() lovebird.onconnect(client) end, function() end)
|
||||||
|
end)
|
||||||
|
lovebird.connections[conn] = true
|
||||||
|
else
|
||||||
|
-- Reject connection not on whitelist
|
||||||
|
lovebird.trace("got non-whitelisted connection attempt: ", addr)
|
||||||
|
client:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Handle existing connections
|
||||||
|
for conn in pairs(lovebird.connections) do
|
||||||
|
-- Resume coroutine, remove if it has finished
|
||||||
|
local status = conn()
|
||||||
|
if status == nil then
|
||||||
|
lovebird.connections[conn] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return lovebird
|
16
sonic-radiance.love/libs/loveutils/filesystem.lua
Normal file
16
sonic-radiance.love/libs/loveutils/filesystem.lua
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
local Filesystem = {}
|
||||||
|
|
||||||
|
function Filesystem.exists(filepath)
|
||||||
|
local info = love.filesystem.getInfo( filepath )
|
||||||
|
local exists = false
|
||||||
|
|
||||||
|
if (info == nil) then
|
||||||
|
exists = false
|
||||||
|
else
|
||||||
|
exists = true
|
||||||
|
end
|
||||||
|
|
||||||
|
return exists
|
||||||
|
end
|
||||||
|
|
||||||
|
return Filesystem
|
64
sonic-radiance.love/libs/loveutils/graphics.lua
Normal file
64
sonic-radiance.love/libs/loveutils/graphics.lua
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
local Graphics = {}
|
||||||
|
|
||||||
|
function Graphics.resetColor()
|
||||||
|
love.graphics.setColor(1,1,1,1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Graphics.box(x, y, w, h)
|
||||||
|
local x = math.floor(x)
|
||||||
|
local y = math.floor(y)
|
||||||
|
local w = math.floor(w)
|
||||||
|
local h = math.floor(h)
|
||||||
|
local a = a or 1
|
||||||
|
|
||||||
|
local r, g, b, a = love.graphics.getColor( )
|
||||||
|
|
||||||
|
love.graphics.setColor(r, g, b, 0.3 * a)
|
||||||
|
love.graphics.rectangle("fill", x, y, w, h)
|
||||||
|
|
||||||
|
love.graphics.setColor(r, g, b, a)
|
||||||
|
love.graphics.rectangle("line", x, y, w, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Graphics.print(text, x, y, align, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
local width
|
||||||
|
local font = love.graphics.getFont()
|
||||||
|
width = font:getWidth(text)
|
||||||
|
|
||||||
|
if align == "center" then
|
||||||
|
width = (width/2)
|
||||||
|
elseif align == "right" then
|
||||||
|
width = width
|
||||||
|
else
|
||||||
|
width = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.print(text, x - (width), y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Graphics.printWithSpacing(text, spacing, align, x, y, r, sx, sy, ox, oy, kx, ky)
|
||||||
|
-- DO NOT USE THIS FUNCTION IN A "UPDATE" FUNCTION !
|
||||||
|
-- it's pretty heavy to use as it use a loop to get every character in a text
|
||||||
|
local font = love.graphics.getFont()
|
||||||
|
local xx = 0
|
||||||
|
local lenght = string.len(text)
|
||||||
|
local basewidth = font:getWidth(text)
|
||||||
|
local width = basewidth + (spacing * lenght)
|
||||||
|
|
||||||
|
if align == "center" then
|
||||||
|
width = (width/2)
|
||||||
|
elseif align == "right" then
|
||||||
|
width = width
|
||||||
|
else
|
||||||
|
width = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1, lenght do
|
||||||
|
local char = string.sub(text, i, i)
|
||||||
|
pos = math.floor(x + xx - width)
|
||||||
|
love.graphics.print(char, pos, y)
|
||||||
|
xx = xx + font:getWidth(char) + spacing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Graphics
|
7
sonic-radiance.love/libs/loveutils/init.lua
Normal file
7
sonic-radiance.love/libs/loveutils/init.lua
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
local cwd = (...):gsub('%.init$', '') .. "."
|
||||||
|
|
||||||
|
return {
|
||||||
|
math = require(cwd .. "math"),
|
||||||
|
graphics = require(cwd .. "graphics"),
|
||||||
|
filesystem = require(cwd .. "filesystem")
|
||||||
|
}
|
88
sonic-radiance.love/libs/loveutils/math.lua
Normal file
88
sonic-radiance.love/libs/loveutils/math.lua
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
local Math = {}
|
||||||
|
|
||||||
|
function Math.sign(x)
|
||||||
|
if (x < 0) then
|
||||||
|
return -1
|
||||||
|
elseif (x > 0) then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.round(num)
|
||||||
|
return math.floor(num + 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.vector(x1, y1, x2, y2)
|
||||||
|
local vecx, vecy
|
||||||
|
|
||||||
|
vecx = x2 - x1
|
||||||
|
vexy = y2 - y1
|
||||||
|
|
||||||
|
return vecx, vecy
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.getMiddlePoint(x1, y1, x2, y2)
|
||||||
|
local newx, newy, vecx, vecy
|
||||||
|
|
||||||
|
vecx = math.max(x1, x2) - math.min(x1, x2)
|
||||||
|
vecy = math.max(y1, y2) - math.min(y1, y2)
|
||||||
|
|
||||||
|
newx = math.min(x1, x2) + (vecx / 2)
|
||||||
|
newy = math.min(y1, y2) + (vecy / 2)
|
||||||
|
|
||||||
|
return newx, newy
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.pointDistance(x1, y1, x2, y2)
|
||||||
|
local vecx, vecy
|
||||||
|
|
||||||
|
vecx = math.max(x1, x2) - math.min(x1, x2)
|
||||||
|
vexy = math.max(y1, y2) - math.min(y1, y2)
|
||||||
|
|
||||||
|
return math.sqrt(vecx^2 + vecy^2)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.pointDirection(x1,y1,x2,y2)
|
||||||
|
local vecx, vecy, angle
|
||||||
|
vecy = y2 - y1
|
||||||
|
vecx = x2 - x1
|
||||||
|
angle = math.atan2(vecy, vecx)
|
||||||
|
|
||||||
|
return angle
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.numberToString(x, length)
|
||||||
|
local length = length or 1
|
||||||
|
local string = ""
|
||||||
|
local x = x
|
||||||
|
if (x >= math.pow(10, length)) then
|
||||||
|
x = unitsNumber*10 - 1
|
||||||
|
string = string .. x
|
||||||
|
else
|
||||||
|
for i=1, (length-1) do
|
||||||
|
if (x < math.pow(10, length-i)) then
|
||||||
|
string = string .. "0"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
string = string .. x
|
||||||
|
end
|
||||||
|
return string
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.floorCoord(x, y)
|
||||||
|
return math.floor(x), math.floor(y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math.pixeliseCoord(x, y, factor)
|
||||||
|
x, y = Math.floorCoord(x / factor, y / factor)
|
||||||
|
|
||||||
|
x = x * factor
|
||||||
|
y = y * factor
|
||||||
|
|
||||||
|
return x, y
|
||||||
|
end
|
||||||
|
|
||||||
|
return Math
|
53
sonic-radiance.love/main.lua
Normal file
53
sonic-radiance.love/main.lua
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
-- main.lua :: the core file of the game, will load main libs and core system
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
utils = require "libs.loveutils"
|
||||||
|
Object = require "libs.classic"
|
||||||
|
Core = require "core"
|
||||||
|
Game = require "game"
|
||||||
|
|
||||||
|
scenes = require "scenes"
|
||||||
|
|
||||||
|
function love.load()
|
||||||
|
core = Core()
|
||||||
|
game = Game()
|
||||||
|
|
||||||
|
scenes.title()
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.update(dt)
|
||||||
|
core:update(dt)
|
||||||
|
game:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.draw()
|
||||||
|
core:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.mousemoved(x, y, dx, dy)
|
||||||
|
core:mousemoved(x, y, dx, dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.mousepressed( x, y, button, istouch )
|
||||||
|
core:mousemoved(x, y, button, istouch)
|
||||||
|
end
|
4
sonic-radiance.love/scenes/init.lua
Normal file
4
sonic-radiance.love/scenes/init.lua
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
return {
|
||||||
|
test = require "scenes.test_scene",
|
||||||
|
title = require "scenes.titlescreen",
|
||||||
|
}
|
47
sonic-radiance.love/scenes/test_scene/init.lua
Normal file
47
sonic-radiance.love/scenes/test_scene/init.lua
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
-- scenes/test :: a basic test scene
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Scene = require "core.modules.scenes"
|
||||||
|
local TestScene = Scene:extend()
|
||||||
|
|
||||||
|
function TestScene:new()
|
||||||
|
TestScene.super.new(self)
|
||||||
|
|
||||||
|
self.i = 0
|
||||||
|
|
||||||
|
self:register()
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScene:update(dt)
|
||||||
|
self.i = self.i + dt
|
||||||
|
end
|
||||||
|
|
||||||
|
function TestScene:draw()
|
||||||
|
love.graphics.setColor(0, 0, 1, 1)
|
||||||
|
love.graphics.rectangle("fill", 0, 0, 424, 240)
|
||||||
|
|
||||||
|
love.graphics.setColor(0, 0, 0, 1)
|
||||||
|
love.graphics.print(self.i .. " ; " .. self.mouse.x .. ":" .. self.mouse.y, 16, 16)
|
||||||
|
end
|
||||||
|
|
||||||
|
return TestScene
|
55
sonic-radiance.love/scenes/titlescreen/init.lua
Normal file
55
sonic-radiance.love/scenes/titlescreen/init.lua
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
-- scenes/test :: a basic test scene
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright © 2019 Kazhnuz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Scene = require "core.modules.scenes"
|
||||||
|
local TitleScreen = Scene:extend()
|
||||||
|
local gui = require "game.modules.gui"
|
||||||
|
|
||||||
|
function TitleScreen:new()
|
||||||
|
TitleScreen.super.new(self)
|
||||||
|
|
||||||
|
self.borders = gui.newBorder(424, 30, 8)
|
||||||
|
self.assets:addImage("background", "assets/backgrounds/titlescreen.png")
|
||||||
|
self.assets:addImage("sonic", "assets/artworks/titlescreen_sonic.png")
|
||||||
|
self.assets:addImage("logo", "assets/artworks/logo.png")
|
||||||
|
|
||||||
|
self:register()
|
||||||
|
end
|
||||||
|
|
||||||
|
function TitleScreen:update(dt)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function TitleScreen:draw()
|
||||||
|
utils.graphics.resetColor()
|
||||||
|
self.assets:drawImage("background", 0, 0)
|
||||||
|
|
||||||
|
love.graphics.draw(self.borders, 0, 25, 0, 1, -1)
|
||||||
|
love.graphics.draw(self.borders, 424, 215, 0, -1, 1)
|
||||||
|
|
||||||
|
self.assets:drawImage("sonic", 90, 128, 0, 1, 1, 92, 106)
|
||||||
|
self.assets:drawImage("logo", 290, 60, 0, 1, 1, 150, 71)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return TitleScreen
|
Loading…
Reference in a new issue