├── .gitignore ├── doc ├── artwork │ ├── name.png │ ├── concept1.png │ └── coverart.png ├── common.ent ├── story │ ├── story.xml │ └── outline.xml ├── design │ ├── design.xml │ └── focus.xml ├── Makefile ├── generate.sh ├── appendices │ ├── history │ │ ├── history.xml │ │ ├── inspirations.xml │ │ ├── prototypes.xml │ │ └── prepublish.xml │ ├── appendices.xml │ ├── license.xml │ └── contributions.xml ├── LovelyRPG.xml ├── style.xsl ├── legal.xml ├── authors.xml └── style.sty ├── proto └── proto1 │ ├── lib │ ├── essential.lua │ ├── oop │ │ ├── middleclass.lua │ │ └── BSD-LICENSE.txt │ ├── states │ │ ├── Stateful.lua │ │ └── BSD-LICENSE.txt │ ├── utils.lua │ ├── math │ │ ├── rect.lua │ │ └── vector2.lua │ └── projection.lua │ ├── resources │ ├── mapeditor │ │ └── gridsquare.png │ └── images │ │ ├── characters │ │ └── hatter.png │ │ └── tiles │ │ ├── grass_dark.png │ │ ├── grass_fall.png │ │ ├── grass_light.png │ │ └── tile_concrete.png │ ├── conf.lua │ ├── README.md │ ├── main.lua │ ├── character.lua │ └── map.lua └── README /.gitignore: -------------------------------------------------------------------------------- 1 | temp 2 | *.pdf 3 | *.data 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /doc/artwork/name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/doc/artwork/name.png -------------------------------------------------------------------------------- /doc/artwork/concept1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/doc/artwork/concept1.png -------------------------------------------------------------------------------- /doc/artwork/coverart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/doc/artwork/coverart.png -------------------------------------------------------------------------------- /proto/proto1/lib/essential.lua: -------------------------------------------------------------------------------- 1 | require("lib/utils") 2 | require("lib/oop/middleclass") 3 | require("lib/states/Stateful") 4 | -------------------------------------------------------------------------------- /proto/proto1/lib/oop/middleclass.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/lib/oop/middleclass.lua -------------------------------------------------------------------------------- /proto/proto1/lib/states/Stateful.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/lib/states/Stateful.lua -------------------------------------------------------------------------------- /doc/common.ent: -------------------------------------------------------------------------------- 1 | 2 | 3 | A Whiff of Steam'> 4 | -------------------------------------------------------------------------------- /proto/proto1/resources/mapeditor/gridsquare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/resources/mapeditor/gridsquare.png -------------------------------------------------------------------------------- /proto/proto1/resources/images/characters/hatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/resources/images/characters/hatter.png -------------------------------------------------------------------------------- /proto/proto1/resources/images/tiles/grass_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/resources/images/tiles/grass_dark.png -------------------------------------------------------------------------------- /proto/proto1/resources/images/tiles/grass_fall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/resources/images/tiles/grass_fall.png -------------------------------------------------------------------------------- /proto/proto1/resources/images/tiles/grass_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/resources/images/tiles/grass_light.png -------------------------------------------------------------------------------- /proto/proto1/resources/images/tiles/tile_concrete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovelyrpgcommunity/A-Whiff-of-Steam/HEAD/proto/proto1/resources/images/tiles/tile_concrete.png -------------------------------------------------------------------------------- /proto/proto1/lib/utils.lua: -------------------------------------------------------------------------------- 1 | function table.last (t) 2 | return t[table.maxn(t)] 3 | end 4 | 5 | function math.clamp (value, min, max) 6 | return math.min(math.max(value,min),max) 7 | end 8 | 9 | -------------------------------------------------------------------------------- /proto/proto1/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.screen.width = 800 3 | t.screen.height = 600 4 | t.title = "A Whiff of Steam" 5 | t.author = "The LovelyRPG Community" 6 | t.version = 062 7 | t.console = true --we'll be nice for windows users during testing 8 | end 9 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A Whiff of Steam: LövelyRPG Community project 2 | ============================================= 3 | 4 | You are browsing through the LövelyRPG Community project, 5 | published and developed on GitHub: 6 | 7 | http://github.com/lovelyrpgcommunity/a-whiff-of-steam 8 | 9 | See the documentation and wiki published there for further information. 10 | 11 | -------------------------------------------------------------------------------- /proto/proto1/README.md: -------------------------------------------------------------------------------- 1 | # Iteration 2 2 | 3 | This is the second prototype for _A Whiff of Steam_. This prototype aims to add a few new features onto prototype 1, still heading in the same direction of a single somewhat functionally-complete map. 4 | 5 | The ticket for this prototype can be found here: http://codaset.com/lovelyrpgcommunity/a-whiff-of-steam/tickets/2 6 | -------------------------------------------------------------------------------- /doc/story/story.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Story 8 | 9 | 10 | -------------------------------------------------------------------------------- /doc/design/design.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Design 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | NAME=LovelyRPG 2 | 3 | all: data 4 | dblatex -p ./style.xsl -s ./style.sty ${NAME}.xml 5 | 6 | debug: data 7 | dblatex -p ./style.xsl -s ./style.sty -d --tmpdir=./temp ${NAME}.xml 8 | 9 | validate: data 10 | xmllint --noout --xinclude --postvalid --noent ${NAME}.xml 11 | 12 | data: 13 | ./generate.sh 14 | 15 | dataclean: 16 | rm -rf revision.data 17 | rm -rf history.data 18 | 19 | clean: dataclean 20 | rm -rf temp 21 | 22 | distclean: clean 23 | rm -rf ${NAME}.pdf 24 | 25 | -------------------------------------------------------------------------------- /proto/proto1/main.lua: -------------------------------------------------------------------------------- 1 | require("map") 2 | require("character") 3 | 4 | local map 5 | 6 | function love.load (args) 7 | title = love.graphics.getCaption() 8 | map = Map:new() 9 | end 10 | 11 | function love.update (dt) 12 | map:update(dt) 13 | end 14 | 15 | function love.draw () 16 | love.graphics.setCaption(title .. " (fps " .. love.timer.getFPS() .. ")") 17 | map:draw() 18 | end 19 | 20 | function love.mousepressed (x, y, button) 21 | map:mousepressed(x, y, button) 22 | end 23 | 24 | function love.mousereleased (x, y, button) 25 | map:mousereleased(x, y, button) 26 | end 27 | 28 | function love.keypressed (key, unicode) 29 | map:keypressed(key, unicode) 30 | end 31 | 32 | function love.keyreleased (key) 33 | map:keyreleased(key) 34 | end 35 | 36 | -------------------------------------------------------------------------------- /doc/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # generate revision number 4 | echo "" > revision.data 5 | git log -1 --pretty=format:"Rev: %h" >> revision.data 6 | 7 | # generate history data 8 | echo "" > history.data 9 | echo "" >> history.data 10 | git log --reverse --pretty=format:"%h%ai%cN%s" | \ 11 | sed "s/bartbes<\/authorinitials>/Bart van Strien<\/authorinitials>/g" | \ 12 | sed "s/root<\/authorinitials>/William Bowers<\/authorinitials>/g" | \ 13 | sed "s/&/&/g" >> history.data 14 | echo "" >> history.data 15 | 16 | -------------------------------------------------------------------------------- /doc/appendices/history/history.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Project history 8 | Here is place for whole project history to date. We hope it's valuable to write down here every important decision for future generations. 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /doc/appendices/appendices.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Appendices 8 | 9 | 10 | 11 | 12 | Detailed revision table 13 | (Auto generated table, you shouldn't see this unless you edit files in some editor! Don't change id of this appendix or you break transformation rules for revision table.) 14 | 15 | 16 | -------------------------------------------------------------------------------- /doc/LovelyRPG.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | 8 | A Whiff of Steam: Game Design Document 9 | &lovelyrpg; project 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /doc/style.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | \renewcommand{\DBKindexation}{\begin{DBKindtable} 6 | 7 | \end{DBKindtable}} 8 | 9 | 10 | 11 | \DBKinditem{ 12 | 13 | }{ 14 | 15 | }{ 16 | 17 | 18 | 19 | } 20 | 21 | 22 | 23 | 24 | \begin{appendices} 25 | 26 | \chapter{\DBKrevhistorychapter}\begin{sffamily}\DBKrevhistory\end{sffamily} 27 | 28 | 29 | \end{appendices} 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /doc/legal.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Legal information 8 | This game design documentation is part of &lovelyrpg; project, community project that aims to create first made and owned by whole &lovelyrpg; community game of that size. The &lovelyrpg; community is built around of community of &love; engine. For full definition of The &lovelyrpg; Community which owns this project, see , . 9 | &lovelyrpg; project (produced game, parts of produced game made especially for this project and this documentation itself) is licensed for The &lovelyrpg; Community under Lovely Public Community License version 1.3f. See for full license text. 10 | In rest of this document, game design documentation will be referred to as "documentation", &lovelyrpg; project will be referred to as "project" and The &lovelyrpg; Community will be referred to as "community", unless explicitly stated otherwise. 11 | 12 | -------------------------------------------------------------------------------- /proto/proto1/lib/math/rect.lua: -------------------------------------------------------------------------------- 1 | require('lib/essential') 2 | require('lib/math/vector2') 3 | 4 | Rect = class('Rect') 5 | 6 | Rect.DEFAULT_POSITION = Vector2:new(0, 0) 7 | Rect.DEFAULT_WIDTH = 1 8 | Rect.DEFAULT_HEIGHT = 1 9 | 10 | function Rect:initialize (x, y, width, height) 11 | self.position = (x and y) and Vector2:new(x, y) or Rect.DEFAULT_POSITION 12 | self.width = width or Rect.DEFAULT_WIDTH 13 | self.height = height or Rect.DEFAULT_HEIGHT 14 | end 15 | 16 | function Rect:__tostring () 17 | local p = self.position 18 | return string.format("", p.x, p.y, 19 | self.width, self.height) 20 | end 21 | 22 | function Rect:intersectsWithPoint (x, y) 23 | if type("x") ~= "number" then 24 | y = x.y 25 | x = x.x 26 | end 27 | local p = self.position 28 | return x >= p.x and 29 | y >= p.y and 30 | x <= p.x + self.width and 31 | y <= p.y + self.height 32 | end 33 | 34 | function Rect:intersectsWithRect (rect) 35 | error("Not implemented") 36 | end 37 | 38 | function Rect:intersectsWithCircle (circle) 39 | local p = self.position 40 | local hw = self.width / 2 41 | local hh = self.height / 2 42 | local dx = math.abs(circle.position.x - p.x - hw) 43 | local dy = math.abs(circle.position.y - p.y - hh) 44 | 45 | if dx > (hw + circle.radius) then return false end 46 | if dy > (hh + circle.radius) then return false end 47 | 48 | if dx <= hw then return true end 49 | if dy <= hh then return true end 50 | 51 | local cd = ((dx - hw) ^ 2) + ((dy - hh) ^ 2) 52 | return cd <= circle.radius ^ 2 53 | end 54 | -------------------------------------------------------------------------------- /doc/appendices/history/inspirations.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 |
7 | Influences 8 | The history was chosen as one of the crucial elements of the game, thus we had to put a lot of effort in it and tried to create the real masterpiece. There aren't many games settled in steampunk world, but we can easily point them. However we didn't want to look for the inspirations only in video games. Some books and movies were significant to us and had an impact on the final product. Here's the complete list of our inspirations. 9 | 10 | 11 | Games: 12 | 13 | Final Fantasy VI 14 | 15 | 16 | Arcanum 17 | 18 | 19 | 20 | 21 | Books: 22 | 23 | 1984 by George Orwell 24 | 25 | 26 | For Whom the Bell Tolls by Ermest Hemingway 27 | 28 | 29 | 30 | 31 |
32 | -------------------------------------------------------------------------------- /proto/proto1/lib/oop/BSD-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Enrique García Cota 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 3. Neither the name of MiddleClass nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 20 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 24 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 25 | OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | -------------------------------------------------------------------------------- /proto/proto1/lib/states/BSD-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Enrique García Cota 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 3. Neither the name of middleclass-extras nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 20 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 24 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 25 | OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /doc/authors.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | 8 | Concept Artist 9 | Krzysztof 10 | giniust/** 11 | Giniewicz 12 | 13 | 14 | Engine Coder 15 | Bart 16 | bartbes 17 | van Strien 18 | 19 | 20 | Lead Coder/Gameplay Coder 21 | William 22 | willurd 23 | Bowers 24 | 25 | 26 | Chuck/Game Designer 27 | Andrzej 28 | giniu 29 | Giniewicz 30 | 31 | 32 | Chuck/Game Designer 33 | Małgorzata 34 | xnellex/nelle 35 | Napiontek 36 | 37 | 38 | Webmaster 39 | Robin 40 | Robin/gvx 41 | Wellner 42 | 43 | 44 | Writer 45 | Bartłomiej 46 | jester/* 47 | Knapik 48 | 49 | 50 | Content Artist 51 | ? 52 | kaminkatze/* 53 | ? 54 | 55 | 56 | Gameplay Coder 57 | ? 58 | kalle2990/* 59 | ? 60 | 61 | 62 | -------------------------------------------------------------------------------- /doc/story/outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Story outline 8 | The hero, Cornelius King, is a double agent. Community of the Faithful, or to be more precise one of them, mage called Oren Worthington, placed him into the pro technical underground after few of the prestigious mages died in suspicious circumstances. King's work proved that there are terrorists cells inside protech underground. He was placed in one of them. And then the troubles for him begins. He got emotionally involved with the leader of his cell Lutricia McNeal. Little did he knew that Worthington had foreseen that something like this happened. All the time King was working for him - he was carefully watched by another agent, whose only mission was to control King's actions. Magician carefully choose a person to do that. It was Kings cousin: Eleanor "Ellen" Montgomery. It was Ellen who put King into the crushed train. And she was the person he saw "trying to rob him". In fact Ellen had to report about King's relation with McNeal to the Community of the Faithful. But she would not want her cousin’s death. So she started her own game. 9 | When Ellen discovered that in the train there will be traveling Worthington's daughter Devon, a very talented young magician, she made sure that the underground will get to know about it. And that McNeal's cell will be involved into the ambush. Her plan was McNeal to die into the crash. And King's memories about her to be wiped out with powerful spell. The second part of the plan went perfect. Cornelius was hit by the spell. The entire terrorist cell die in the trap (killed by people hired by Ellen - who was assigned to this mission by Wortington) except McNeal. She was not there by mysterious reasons. Ellen was furious cause she was for her the most important person to eliminate. She wanted to erase his cousin memory and kill her underground lover - to make sure Cornelius to stay alive. When McNeal didn't show at the scene of a terrorist attack Ellen decided to change her plan. And leave his cousin outside as a bait in outside world for McNeal to find. 10 | Mages made sure that King's wounds will look as he was in the train during the crash. They left him with a card with the name: Lutricia McNeal and the phone number in one of terrorist's flat. Hoping that he will find McNeal and they would be able to catch and question her. 11 | 12 | -------------------------------------------------------------------------------- /proto/proto1/lib/math/vector2.lua: -------------------------------------------------------------------------------- 1 | require('lib/essential') 2 | 3 | Vector2 = class('Vector2') 4 | 5 | Vector2.DEFAULT_X = 0 6 | Vector2.DEFAULT_Y = 0 7 | 8 | function Vector2:initialize (paramsOrX, y) 9 | if type(paramsOrX) == "number" then 10 | self.x = paramsOrX or Vector2.DEFAULT_X 11 | self.y = y or Vector2.DEFAULT_Y 12 | else 13 | local p = paramsOrX or {} 14 | self.x = p.x or Vector2.DEFAULT_X 15 | self.y = p.y or Vector2.DEFAULT_Y 16 | end 17 | end 18 | 19 | function Vector2:copy () 20 | return Vector2:new({x=self.x, y=self.y}) 21 | end 22 | 23 | function Vector2:__tostring () 24 | return string.format("", self.x, self.y) 25 | end 26 | 27 | function Vector2:__eq (other) 28 | return self.x == other.x and self.y == other.y 29 | end 30 | 31 | function Vector2:__add (other) 32 | return Vector2:new({x=(self.x + other.x), y=(self.y + other.y)}) 33 | end 34 | 35 | function Vector2:__sub (other) 36 | return Vector2:new({x=(self.x - other.x), y=(self.y - other.y)}) 37 | end 38 | 39 | function Vector2:__mul (value) 40 | return Vector2:new({x=(self.x * value), y=(self.y * value)}) 41 | end 42 | 43 | function Vector2:__div (value) 44 | return Vector2:new({x=(self.x / value), y=(self.y / value)}) 45 | end 46 | 47 | function Vector2:zero () 48 | self.x = 0 49 | self.y = 0 50 | end 51 | 52 | function Vector2:isZero () 53 | return self.x == 0 and self.y == 0 54 | end 55 | 56 | function Vector2:length () 57 | return math.sqrt((self.x ^ 2) + (self.y ^ 2)) 58 | end 59 | 60 | function Vector2:lengthSq () 61 | return (self.x ^ 2) + (self.y ^ 2) 62 | end 63 | 64 | function Vector2:normalize () 65 | local length = self:length() 66 | if length > 0 then 67 | self.x = self.x / length 68 | self.y = self.y / length 69 | end 70 | end 71 | 72 | function Vector2:dot (other) 73 | return (self.x * other.x) + (self.y * other.y) 74 | end 75 | 76 | function Vector2:perpdot (other) 77 | return self:perp():dot(other) 78 | end 79 | 80 | function Vector2:perp () 81 | return Vector2:new(-self.y, self.x) 82 | end 83 | 84 | function Vector2:angle (other) 85 | local rad = math.acos(self:dot(other) / (self:length() * other:length())) 86 | return rad * 180 / math.pi 87 | end 88 | 89 | function Vector2:truncate (max) 90 | if self:length() > max then 91 | self:normalize() 92 | self.x = self.x * max 93 | self.y = self.y * max 94 | end 95 | end 96 | 97 | function Vector2:min (min) 98 | if self:length() < min then 99 | self:normalize() 100 | self.x = self.x * min 101 | self.y = self.y * min 102 | end 103 | end 104 | 105 | function Vector2:clamp (rect) 106 | self.x = math.clamp(self.x, rect.position.x, rect.position.x+rect.width) 107 | self.y = math.clamp(self.y, rect.position.y, rect.position.y+rect.height) 108 | end 109 | 110 | -------------------------------------------------------------------------------- /proto/proto1/lib/projection.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | projection setup: 4 | 5 | I) 3D space 6 | 7 | We have axis Y pointing upwards, axis X pointing right and axis Z pointing 8 | outside of screen. To create projection we do: 9 | 10 | 1) apply scale equal in each X,Y,Z directions 11 | 2) apply additional scale of up to +/- 5% to Y axis if necessary 12 | 3) rotate by beta around Y 13 | 4) rotate by alpha around X 14 | 5) project to screen plane (Z=0) 15 | 16 | II) 2D Space 17 | 18 | We have axis X pointing right, and axis Y pointing down, with (0,0) in 19 | top-left corner of screen. Because axis Y is mirrored (pointing downward), we 20 | have to apply one more correction: 21 | 22 | 6) mirror axis Y 23 | 24 | III) World to screen transformation 25 | 26 | Point (x,y,z) in world coordinates, scales to point (x',y') in screen 27 | coordinates by: 28 | 29 | x' = scale*(cos(beta)*x + sin(beta)*z) 30 | y' = -scale*(sin(alpha)*sin(beta)*x + yrescale*cos(alpha)*y - sin(alpha)*cos(beta)*z) 31 | 32 | IV) Spanning vectors 33 | 34 | In world coordinates unit cube is spanned by vectors 35 | vx=(1,0,0), vy=(0,1,0) and vz=(0,0,1). 36 | 37 | Projected vectors have coordinates: 38 | 39 | vx' = (scale*cos(beta), -scale*sin(alpha)*sin(beta)) 40 | vy' = (0 , -scale*yrescale*cos(alpha)) 41 | vz' = (scale*sin(beta), scale*sin(alpha)*cos(beta)) 42 | 43 | When we take: 44 | 45 | scale = 30*math.sqrt(2) 46 | yrescale = 1 -- exact projection 47 | alpha = math.asin(1/3) 48 | beta = math.pi/4 -- dimetric 49 | 50 | we have 51 | 52 | sin(beta) = 1/2*math.sqrt(2) 53 | cos(beta) = 1/2*math.sqrt(2) 54 | sin(alpha) = 1/3 55 | cos(alpha) = 2/3*math.sqrt(2) 56 | 57 | which leads to 58 | 59 | vx'.x = scale*cos(beta) = 30*math.sqrt(2)*1/2*math.sqrt(2) = 30 60 | vx'.y = -scale*sin(alpha)*sin(beta) = -30*math.sqrt(2)*1/3*1/2*math.sqrt(2) = -10 61 | vy'.x = 0 62 | vy'.y = -scale*yrescale*cos(alpha) = -30*math.sqrt(2)*1*2/3*math.sqrt(2) = -40 63 | vz'.x = scale*sin(beta) = 30*math.sqrt(2)*1/2*math.sqrt(2) = 30 64 | vz'.y = scale*sin(alpha)*cos(beta) = 30*math.sqrt(2)*1/3*1/2*math.sqrt(2) = 10 65 | 66 | So 67 | 68 | vx' = (30, -10) 69 | vz' = ( 0, -40) 70 | vz' = (30, 10) 71 | 72 | and the projection can be written in matrix form using only integer values: 73 | 74 | | x'| | 30 0 30 0| |x| 75 | | y'| = |-10 -40 10 0|*|y| 76 | | 0 | | 0 0 0 0| |z| 77 | | 1 | | 0 0 0 1| |1| 78 | 79 | ]] 80 | 81 | require("lib/math/vector2") 82 | 83 | projection = {} 84 | 85 | local vx = Vector2:new(30,-10) 86 | local vy = Vector2:new( 0,-40) 87 | local vz = Vector2:new(30, 10) 88 | local perpdot = vx:perpdot(vz) 89 | 90 | -- spanning vectors 91 | projection.vx = vx 92 | projection.vy = vy 93 | projection.vz = vz 94 | 95 | -- World coordinates to screen coordinates conversion 96 | function projection.worldToScreen(w) 97 | local x = vx.x*w.x + vz.x*w.z 98 | local y = vx.y*w.x + vy.y*w.y + vz.y*w.z 99 | return Vector2:new(x,y) 100 | end 101 | 102 | -- Screen coordinates to world coordinates conversion assuming y 103 | function projection.screenToWorld(s, y) 104 | local y = y or 0 105 | local x = (vz.y*s.x - vz.x*(s.y-vy.y*y))/perpdot 106 | local z = (s.x - vx.x*x)/vz.x 107 | return {x=x,y=y,z=z} 108 | end 109 | 110 | -- World coordinates to screen coordinates conversion, using 2D vector 111 | function projection.worldToScreen2(p, level) 112 | return projection.worldToScreen({x=p.x, y=level or 0, z=p.y}) 113 | end 114 | 115 | -- World coordinates to screen coordinates conversion, using separate values 116 | function projection.worldToScreen3(x, z, level) 117 | return projection.worldToScreen({x=x, y=level or 0, z=z}) 118 | end 119 | 120 | -- Screen coordinates to world coordinates conversion assuming y, using separate values 121 | function projection.screenToWorld2(x, y, level) 122 | return projection.screenToWorld({x=x,y=y}, level) 123 | end 124 | 125 | -- World coordinates to view coordinates conversion 126 | function projection.worldToView(w, view) 127 | return view.position+projection.worldToScreen(w)*view.scale 128 | end 129 | 130 | -- World coordinates to view coordinates conversion, using 2D vector 131 | function projection.worldToView2(p, view, level) 132 | return view.position+projection.worldToScreen2(p,level)*view.scale 133 | end 134 | 135 | -- World coordinates to view coordinates conversion, using separate values 136 | function projection.worldToView3(x, y, view, level) 137 | return view.position+projection.worldToScreen3(x,y,level)*view.scale 138 | end 139 | 140 | -- View coordinates to world coordinates conversion 141 | function projection.viewToWorld(v, view, y) 142 | return projection.screenToWorld((v-view.position)/view.scale,y) 143 | end 144 | 145 | -- View coordinates to world coordinates conversion, using separate values 146 | function projection.viewToWorld2(x, y, view, level) 147 | return projection.screenToWorld((Vector2:new(x,y)-view.position)/view.scale,level) 148 | end 149 | 150 | -------------------------------------------------------------------------------- /doc/style.sty: -------------------------------------------------------------------------------- 1 | \NeedsTeXFormat{LaTeX2e} 2 | \ProvidesPackage{style}[2010/03/01 Docbook style modification for LovelyRPG] 3 | \RequirePackageWithOptions{docbook} 4 | 5 | % strings 6 | \def\DBKrole{ROLE} 7 | \def\DBKlineage{L\"OVE CLUB NICK} 8 | \def\DBKothername{GITHUB NICK / CONTRIBUTIONS} 9 | \def\DBKrealname{REAL NAME} 10 | \def\DBKrevhistorychapter{Detailed Revision History} 11 | 12 | % images 13 | \def\DBKartName{artwork/name.png} 14 | \def\DBKartCover{artwork/coverart.png} 15 | 16 | % we set numbering of enumerations to include parent enumeration number, like 1., 17 | % 1.1., 1.1.1, etc. 18 | \setenumerate{label*=\arabic*.} 19 | 20 | % set penalty se we don't leave single lines if possible 21 | \widowpenalty=300 22 | \clubpenalty=300 23 | 24 | % remove revision history table from begin of document, and prepare to add 25 | % pictures on title page, also more space for title in header 26 | \chead[]{% 27 | \begin{tabular}{ 28 | >{\raggedright}p{5.6cm} >{\centering}p{5.6cm} >{\raggedleft}p{5.6cm}} % 29 | \multirow{3}{7cm}{\DBKtitle} 30 | & & \tabularnewline% 31 | & \releasebox 32 | & \textsf{\thepage} \tabularnewline % 33 | & & \tabularnewline% 34 | \end{tabular}% 35 | } % 36 | \def\DBKcheadfront{% 37 | \begin{tabular}{ 38 | >{\raggedright}p{5.6cm} >{\centering}p{5.6cm} >{\raggedleft}p{5.6cm}} % 39 | \multirow{3}{7cm}{\DBKtitle} 40 | & & \textsf{\DBKreference{} \edhead} \tabularnewline% 41 | & \releasebox & \tabularnewline % 42 | & & \textsf{\thepage} 43 | \tabularnewline% 44 | \end{tabular}% 45 | } 46 | \def\DBKcheadbody{% 47 | \begin{tabular}{ 48 | >{\raggedright}p{5.6cm} >{\centering}p{5.6cm} >{\raggedleft}p{5.6cm}} % 49 | \multirow{3}{7cm}{\DBKtitle} 50 | & & \textsf{\DBKreference{} \edhead} \tabularnewline% 51 | & \releasebox & \tabularnewline % 52 | & & \textsf{\thepage{} / \getpagerefnumber{LastPage}} 53 | \tabularnewline% 54 | \end{tabular}% 55 | } 56 | \def\maketitle{ 57 | \ifthenelse{\equal{\DBKedition}{}}{\def\edhead{}}{\def\edhead{Ed. \DBKedition}} 58 | \setlength{\oldbaselineskip}{\baselineskip} 59 | \setlength{\baselineskip}{2\oldbaselineskip} 60 | \textsf{ 61 | \vfill 62 | \begin{center} 63 | \includegraphics[height=7cm]{\DBKartName}\\ 64 | \vspace{1cm} 65 | \huge{\textbf{\DBKtitle}}\\ % 66 | \ifx\DBKsubtitle\relax\else% 67 | \underline{\ \ \ \ \ \ \ \ \ \ \ }\\ % 68 | \ \\ % 69 | \huge{\textbf{\DBKsubtitle}}\\ % 70 | \fi 71 | \vspace{1cm} 72 | \includegraphics[height=7cm]{\DBKartCover} 73 | \end{center} 74 | \vfill 75 | \setlength{\baselineskip}{\oldbaselineskip} 76 | \hspace{1cm} 77 | \vspace{1cm} 78 | \begin{center} 79 | \begin{tabular}{p{7cm} p{7cm}} 80 | \Large{\DBKreference{} \edhead} & \\ 81 | \end{tabular} 82 | \end{center} 83 | } 84 | \newpage 85 | \setlength{\baselineskip}{\oldbaselineskip} 86 | \chead[]{\DBKcheadfront} 87 | \lfoot[]{} 88 | \ifx\DBKcopyright\relax\else 89 | \DBKcopyright \\ 90 | \fi 91 | \ifx\DBKlegalblock\relax\else 92 | \DBKlegalblock 93 | \fi 94 | \newpage 95 | \begin{sffamily} 96 | \DBKindexation 97 | \end{sffamily} 98 | } 99 | 100 | % make it possible to use role, nicks and real name instead of action, name, 101 | % date and signature... 102 | \def\DBKinditem#1#2#3{% more complicated macro so needs some comments 103 | \def\DBK@split##1/##2\till@this{% we will split both nicknames from single othername 104 | \def\DBK@unslash####1/{####1}% to remove slash from end 105 | \gdef\DBK@left{##1}% first nick 106 | \ifx\DBK@right\@empty\gdef\DBK@left{-}\fi% if empty replace with "-" 107 | \gdef\DBK@right{##2}% second nick 108 | \ifx\DBK@right\@empty\gdef\DBK@right{\DBK@left}% if empty replace with first nick 109 | \else% but if wasn't empty, we have redutant shash at end to remove 110 | \expandafter\gdef\expandafter\DBK@right\expandafter{\DBK@unslash##2}\fi% so we do it in place 111 | }% and call splitting rutine 112 | \DBK@split#2/\till@this% to get both nicks in DBK@left and DBK@right! 113 | & & & & \tabularnewline 114 | #1 & \DBK@left & \DBK@right & \multicolumn{2}{c|}{#3} \tabularnewline 115 | & & & & \tabularnewline 116 | \hline 117 | } 118 | 119 | % and label the table right (change in two lines, see below) 120 | \renewenvironment{DBKindtable}{ 121 | \begin{center} 122 | \begin{tabular}{ |>{\centering}p{3cm} |>{\centering}p{3cm} 123 | |>{\centering}p{3.5cm} 124 | |>{\centering}p{3cm} >{\centering}p{3cm} | } 125 | \hline 126 | \multicolumn{5}{|c|}{\textbf{\DBKindtitle}} \tabularnewline 127 | \hline 128 | \multicolumn{5}{c}{\ } \tabularnewline 129 | \hline 130 | & \multicolumn{2}{l|}{} & \multicolumn{2}{l|}{} \tabularnewline 131 | \multicolumn{1}{|p{3cm}|}{} & \multicolumn{2}{p{6.5cm}|}{\DBKtitle} 132 | & \multicolumn{2}{p{3cm}|}{\DBKreference}\tabularnewline 133 | & \multicolumn{2}{l|}{} & \multicolumn{2}{l|}{} \tabularnewline 134 | \hline 135 | \hline 136 | & & & & \tabularnewline 137 | \emph{\DBKrole} & \emph{\DBKlineage} & \emph{\DBKothername} & % change here 138 | \multicolumn{2}{c|}{\emph{\DBKrealname}} % and there 139 | \tabularnewline 140 | & & & & \tabularnewline 141 | \hline 142 | }{ 143 | \end{tabular} 144 | \end{center} 145 | } 146 | 147 | -------------------------------------------------------------------------------- /doc/design/focus.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Focus 8 | &whiff; is a 2D role-playing game settled in steam-punk world. The magic is fully fused with the technology. Technology is explained in terms of magic and considered as one of the fields of magical studies. 9 | The view in the game will be divided in two groups - separate detailed tactical view for fights, and consistent view using trimetric projection for travel and location exploration, as seen in Fallout 1 and 2 or Sim City 4. Main locations, like cities, dungeons, quest related places will be created by artists and the rest of the world, i.e., wilderness, is going to be automatically generated, because of size of the world. The general stylistic of game will be eastern flavoured realistic comics style, slightly taking from Chinese Manhua comics tradition. Dreams will be visualised as comic stripes. 10 | Hero has access to every part of a game world and can travel freely. But it doesn't mean that he has to walk everywhere. He will be able to choose between using public means of transport, like trains, traveling on foot between artists' created locations or using fast travel option. Fast travel option is available only by using road signs, showing directions between locations connected by roads. Fast traveling is risky, hero and/or his team can randomly encounter hostile creatures. Also not all locations are accessible in fast travel mode, the country is not urbanised enough to get everywhere by train. The protagonist must travel on foot to reach some essential parts or hidden, secluded places. This, in conjunction with size of the world, will encourage player to explore. 11 | Another important feature of &whiff; are interactions with NPCs. Hero will have a chance to build a complex interpersonal relationships, while it won't be easy. He will have to prove being reliable first. The way the hero dresses will have an impact on people's reactions. They will be more eager to share their thoughts and troubles with wealthy mysterious stranger than the poor one. Generosity also affects their attitude. Protagonist's lies, while discovered, change the trust people have in him. Hero may team up with someone to complete the task, but it won't mean that certain person will follow him, everywhere he goes. In the game world people have their own lives as well and they won't sacrifice everything so easily. Unless he proofs them being somehow useful, they won't support him. Creating such huge number of various personalities wouldn't be possible, unless minor characters will have generated personalities. For generating personalities we will use personality archetypes, zodiac stereotypes and enneagram, for short time attitude and feelings, we will use biorhythms. 12 | Game's story is built around mysterious events. Protagonist awakes near the crashed train, he's severe wounded and can't move but notices that someone is trying to rob him. Soon he looses consciousness again and wakes up in hospital with amnesia. He is tormented with bad dreams. The only hint about his past he has is a note with name and phone number in his pocket. Nobody recognises him, it seems that he has never been to this part of the country. Soon he starts looking for his own past, uncovering his criminal deeds conflicting with his good subconscious side. His dreams become longer and more clear - seems that he was the one who caused the train crash. Police starts to investigate the case and conceives that he was involved in it, while he still cannot remember anything except for causing the crash. The truth won't be easy to discover. 13 | The country, where action takes place, lies on a large island and is isolated from rest of the world. It is governed by Community of the Faithful, church led by orthodox mages. The government is afraid that the world's order is about to change, and carefully watches every revolutionary move. For many years, technology commonly considered as equal to magical science, has been inferior to magic, in fact. But some people fascinated by the power of technology tried to separate those two fields of science. Governors are afraid that technology can eventually became more popular than magic, and people will no longer accept the control and authority of Community of the Faithful. Special services invigilate citizens and report suspicious activities. Single citizen is not aware of the scale of invigilation until he or she is being watched and loses control over his or her life. 14 | The hero's story, although important to him, doesn't play a big role in the world order, it is just a part of larger picture. It's going to have an influence on the behaviour of the people struggling with their own concerns but not on the large-scale world events themselves. 15 | &whiff; will strongly affect on players' emotions. Story totally supports emotions that we want to show - rejection, surprise, yet still some hope. The protagonist will be cast out of the society and scared to discover his own past, the past of cruel and heartless man. 16 | Fights will not be key feature of the game, but will be present. They keep the game world more realistic and well-balanced. They also provide optional things to do while traveling. There will be used timed turn based system and action points, that can be spend on movement or other actions. Fights as non key element of gameplay will be optional. Player will be able to choose in the menu if he wants to enable "automatic conflict resolution", that will make whole fight led by AI. 17 | Most skills will evolve by training, the more player use certain skills the better they will get. But there will be more skills than those related to fights, there will be also plenty of practical skills, and what’s new, social skills. All built to same level of complexity. 18 |
19 | Stylistics concept 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | -------------------------------------------------------------------------------- /proto/proto1/character.lua: -------------------------------------------------------------------------------- 1 | require("lib/essential") 2 | require("lib/math/rect") 3 | require("lib/projection") 4 | 5 | Character = class("Character") 6 | Character:include(Stateful) 7 | 8 | Character.IMAGES = { 9 | rectprism = love.graphics.newImage("resources/images/characters/hatter.png"), 10 | } 11 | 12 | local IMAGE_HEIGHT = 128 13 | local IMAGE_WIDTH = 64 14 | local FRAMES = 13 15 | local FRAME_DURATION = 0.06 16 | 17 | local function createQuad(i,f) 18 | return love.graphics.newQuad(IMAGE_WIDTH*i,IMAGE_HEIGHT*f,IMAGE_WIDTH,IMAGE_HEIGHT,IMAGE_WIDTH*FRAMES,IMAGE_HEIGHT*8) 19 | end 20 | 21 | Character.QUADS = { 22 | rectprism = { 23 | se = {}, 24 | ne = {}, 25 | nw = {}, 26 | sw = {}, 27 | s = {}, 28 | e = {}, 29 | n = {}, 30 | w = {}, 31 | } 32 | } 33 | 34 | for i = 1,13 do 35 | Character.QUADS.rectprism.se[i] = createQuad(i, 7) 36 | Character.QUADS.rectprism.ne[i] = createQuad(i, 5) 37 | Character.QUADS.rectprism.nw[i] = createQuad(i, 3) 38 | Character.QUADS.rectprism.sw[i] = createQuad(i, 1) 39 | Character.QUADS.rectprism.s[i] = createQuad(i, 0) 40 | Character.QUADS.rectprism.e[i] = createQuad(i, 6) 41 | Character.QUADS.rectprism.n[i] = createQuad(i, 4) 42 | Character.QUADS.rectprism.w[i] = createQuad(i, 2) 43 | end 44 | 45 | function Character:initialize () 46 | local x0 = 0 47 | local y0 = 0 48 | local x1 = #Map.TILES[1] 49 | local y1 = #Map.TILES 50 | self.image = "rectprism" 51 | self.position = Vector2:new(x1/2, y1/2) 52 | self.velocity = Vector2:new(0, 0) 53 | self.bounds = Rect:new(x0+0.5, y0+0.5, x1-0.5, y1-0.5) 54 | self.direction = "sw" 55 | self.speed = Map.WALK_SPEED 56 | self.frame = 1 57 | self.next_frame = FRAME_DURATION 58 | end 59 | 60 | local CHARACTER_SHIFT = Vector2:new(0,IMAGE_HEIGHT-projection.vz.y)+Map.TILE_CENTRE 61 | 62 | function Character:draw (view) 63 | local image = Character.IMAGES[self.image] 64 | local quads = Character.QUADS[self.image] 65 | love.graphics.push() 66 | love.graphics.scale(view.scale) 67 | local p = projection.worldToView2(self.position,view)/view.scale-CHARACTER_SHIFT 68 | if quads then 69 | local quad = quads[self.direction][self.frame] 70 | love.graphics.drawq(image, quad, math.floor(p.x), math.floor(p.y)) 71 | else 72 | love.graphics.draw(image, math.floor(p.x), math.floor(p.y)) 73 | end 74 | love.graphics.pop() 75 | end 76 | 77 | function Character:mousepressed (x, y, button, view) 78 | self.speed = Map.WALK_SPEED 79 | if love.keyboard.isDown("lctrl") then 80 | self.speed = Map.SNEAK_SPEED 81 | elseif love.keyboard.isDown("lshift") then 82 | self.speed = Map.RUN_SPEED 83 | end 84 | local temp = projection.viewToWorld2(x, y, view) 85 | self.goal = Vector2:new(temp.x, temp.z) 86 | self.goal:clamp(self.bounds) 87 | self:gotoState('MoveToPosition') 88 | end 89 | 90 | -------------------------------------------------------------------------------- 91 | -- state: Base 92 | 93 | local Base = Character:addState('Base') 94 | 95 | local dict = {"ne", "n", "nw", "w", "sw", "s", "se", "e"} 96 | 97 | local function getOrientation(vec) 98 | dir = math.floor(4*math.atan2(-vec.y,vec.x)/math.pi+0.5)%8+1 99 | return dict[dir] 100 | end 101 | 102 | function Base:update (dt) 103 | -- First determine desired direction... 104 | if math.abs(self.velocity.x)+math.abs(self.velocity.y)>0 then 105 | self.direction = getOrientation(self.velocity) 106 | end 107 | 108 | -- do not move outside of map... 109 | local temp = self.position + self.velocity 110 | local b = self.bounds 111 | if temp.x < b.position.x or temp.x > b.width then 112 | self.velocity.x = 0 113 | if self.goal then self.goal.x = self.position.x end 114 | end 115 | if temp.y < b.position.y or temp.y > b.height then 116 | self.velocity.y = 0 117 | if self.goal then self.goal.y = self.position.y end 118 | end 119 | 120 | -- finally, update direction and postion if we move. 121 | if math.abs(self.velocity.x)+math.abs(self.velocity.y)>0 then 122 | self.direction = getOrientation(self.velocity) 123 | self.position = self.position + self.velocity 124 | 125 | self.next_frame = self.next_frame - dt 126 | if self.next_frame < 0 then 127 | self.frame = self.frame + 1 128 | if self.frame == FRAMES then 129 | self.frame = 1 130 | end 131 | self.next_frame = FRAME_DURATION 132 | end 133 | else 134 | self.frame = 1 135 | self.next_frame = FRAME_DURATION 136 | end 137 | end 138 | 139 | -------------------------------------------------------------------------------- 140 | -- state: ArrowKeysMovement 141 | 142 | local ArrowKeysMovement = Character:addState('ArrowKeysMovement', Base) 143 | 144 | function ArrowKeysMovement:update (dt) 145 | -- determine direction in world coordinates 146 | local d = Vector2:new(0,0) 147 | if love.keyboard.isDown("w") or love.keyboard.isDown("up") then 148 | d.y = -1 149 | end 150 | if love.keyboard.isDown("s") or love.keyboard.isDown("down") then 151 | d.y = 1 152 | end 153 | if love.keyboard.isDown("a") or love.keyboard.isDown("left") then 154 | d.x = -1 155 | end 156 | if love.keyboard.isDown("d") or love.keyboard.isDown("right") then 157 | d.x = 1 158 | end 159 | 160 | -- scale diagonals to make movement speed natual 161 | d:normalize() 162 | 163 | -- adjust speed movement 164 | d=d*Map.BASE_SPEED*dt 165 | 166 | -- rotate to alight movement to screen 167 | self.velocity.x = (d.x-d.y)/math.sqrt(2) 168 | self.velocity.y = (d.x+d.y)/math.sqrt(2) 169 | 170 | -- then adjust speed 171 | local speed = Map.WALK_SPEED 172 | if love.keyboard.isDown("lctrl") then 173 | speed = Map.SNEAK_SPEED 174 | elseif love.keyboard.isDown("lshift") then 175 | speed = Map.RUN_SPEED 176 | end 177 | self.velocity = self.velocity * speed 178 | 179 | Base.update(self, dt) 180 | end 181 | 182 | -------------------------------------------------------------------------------- 183 | -- state: MoveToPosition 184 | 185 | local MoveToPosition = Character:addState('MoveToPosition', Base) 186 | 187 | function MoveToPosition:update (dt) 188 | if not self.goal then return end 189 | 190 | local p = self.position 191 | local testWidth = 0.1*self.speed 192 | local test = Rect:new(p.x-testWidth/2, p.y-testWidth/2, testWidth, testWidth) 193 | 194 | if test:intersectsWithPoint(self.goal) then 195 | self.goal = nil 196 | self:gotoState('ArrowKeysMovement') 197 | else 198 | -- determine desired move 199 | local d = self.goal - self.position 200 | 201 | -- round angle to align with character images and normalize 202 | local angle = math.floor(8+math.atan2(d.x,d.y)/math.pi*4)%8*math.pi/4 203 | d = Vector2:new(math.sin(angle),math.cos(angle)) 204 | 205 | -- adjust speed 206 | self.velocity=d*Map.BASE_SPEED*dt*self.speed 207 | end 208 | Base.update(self, dt) 209 | end 210 | 211 | -------------------------------------------------------------------------------- /doc/appendices/history/prototypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 |
7 | Prototyping phase 8 | Following prototypes were made in project progress 9 |
10 | Prototype 1, world map 11 | Because exploration was picked as one of more important features, and adding features to game works better when game world is already here, it was decided that we start with prototyping game world display. 12 |
13 | Iteration 1, small hand made area 14 | This iteration is very small, warm-up activity. 15 | Our aim here is to adjust tile size and scrolling speed, also to see how designed view will behave in practice. 16 | We should create part of world view, using only temporary tiles for now. Map should have different tile types and user should be able to scroll it at least one screen up, down, left or right in fluid, pixel based manner. Projection used should be trimetric, tile size could be around 1/8 of screen height and scrolling whole screen should take about 2 seconds. 17 | For now we can assume 800x600 in windowed mode. 18 |
19 | Results 20 | Most works on the prototype was done by willurd with support from bartbes. They spent lots of time on this one, and created even more than described here. Description was extended with zoom functionality that allowed to test/simulate look with different resolutions and editor features for making map creation easier. First iteration can be marked as success. Work on this iteration started at 18th March and ended at 21st March. 21 |
22 |
23 |
24 | Iteration 2, here comes our hero 25 | This iteration will add scale to world, foliage and road sign, but most important - it will add the hero. This time we will try to make the character move around and catch proper proportions. 26 | 27 | Scale - lets for now assume for need of this prototype, that tile as we have it now, have side of 2 meters, making it screen height about 18 meters - lets create character with height of about 60 pixels. We should have 8 sprites for now of character facing 8 different directions. Character could be made of cone or cylinder, with somehow marked direction it's pointing. 28 | 29 | 30 | 31 | Character movement - character movement should be fluent, for now no collisions are needed, let's assume that walking from bottom to top of screen should take about 4 seconds running, 8 walking and 16 sneaking. For the prototype it should be enough to implement one speed, say running. We should use two methods of walking, one based on a keyboard, second by using a mouse. 32 | 33 | 34 | 35 | Foliage and sign - let's create simple higher brush grass asset to place around - reaching character's knees and one road sign a bit higher than character. Purpose of this is to test out drawing order and stuff assets placement, no collisions required yet. Assets should be placed on a pixel based manner, i.e., it should be possible to place sign on left or right part of certain tile. 36 | 37 | 38 |
39 | Important events during iteration 2 40 | During this phase project had hard days. After some work, most people had troubles finding time to work. It resulted in long break. After this, project had to be refreshed. To bring new life to it again, some decisions were made. 41 | 42 | 43 | We moved to GitHub, so more people can contribute. 44 | 45 | 46 | We welcomed a new Developer and issued one promotion to Lead Developer role. 47 | 48 | 49 | We implemented new ticketing system using GitHub labels. 50 | 51 | 52 | First developer meeting was held (on 1st August 2010, 15:00-16:00 GMT), with some important decisions made: 53 | 54 | 55 | We restrict the way community can force in destructive changes to current project, it is no longer possible to force developers to change nearly finished game and rewrite all of it, even if 100% of community wants it and provides patches, unless developers agree to this. It was necessary to protect developers work, who gave a lot of themselves to project. 56 | 57 | 58 | It was decided to prepare description of next iterations of prototype 1, and some sketch of what they could be was made. 59 | 60 | 61 | Based on prepared iterations for prototype one, we will prepare open task-tickets for community to take on, giving some directions to potential contributors. 62 | 63 | 64 | Finally, it was decided to repeat meeting, probably during next week. 65 | 66 | 67 | 68 | 69 |
70 |
71 |
72 | Iteration 3, start bumping into things 73 | After previous iteration, the world will have already proper scale, hero and some foliage and road signs. This iteration will concentrate on collisions. 74 | We should add some larger stones, a tree with big trunk and (currently closed, probably very similar to a box) house. Different things that will stop our hero from movement. 75 |
76 |
77 | Iteration 4, Willma - I'm home 78 | With all this done, we will open the previously added house. Add ability to enter it and move around. The building should still have only one floor available. We should decide how to show the interior. For this prototype, the building can be empty box with holes for windows or doors. Proper doors will be added in later prototypes, when working on interactions. 79 |
80 |
81 | Iteration 5, Stairway to Heaven 82 | When character will have freedom to walk around on the map and into buildings, we will add stairs to second floor, and probably basement too. We need to make sure, that the transition of view between floors works, especially while still on stairs, and that collisions are working too, so character does not get stuck. Both keyboard and mouse movement should work. 83 |
84 |
85 |
86 | -------------------------------------------------------------------------------- /doc/appendices/license.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Full license information 8 | Lovely Public Community License (LPCL) Version 1.3f, November 1, 2009 9 | Copyright © 2009 Bart Bes and Osuf Oboys 10 | This license, but not the licensed product, is licensed to "the LCPL Development Team" under LPCL. 11 | The intention of this license is to provide a means to release a product in a community without any particular user governing the project. In particular, the license should encourage the liberties taken in wiki systems, provide a means to settle disputes, and allow new users to continue a project where every developer has gone inactive. This license imposes NO restrictions on work that is released under other names than the name of this product. If such restrictions are desired, the work may be licensed under more than one license. 12 |
13 | Definitions. 14 | "This product" refers to all work licensed under this license. A "derivative work" is any copyrightable material based on or using this product. "The community of this product" is a body of entities, usually people, which may be defined in an accompanying policy document or section as stated below. 15 | A separate document or section defining this product's community and policy may accompany this license and is treated as constituent of the referenced product as far as this license is concerned. Such a document or section may only define "the community" and how "the community" may exercise Clause 3.2 of this license. In particular, changing such a document does not demand a new release of this license but it does demand a new release of the product. If no such document or clause accompanies the product, then "the community" is the empty set. 16 |
17 |
18 | Terms and Conditions. 19 | All rights including, but not restricted to, copyright and trademark rights are reserved to this product's copyright holders. 20 | This product comes without any warranty, expressed or otherwise. Under no circumstances will the creators of this product be held responsible for any negative effects arising from the use of this product or derivative works. 21 | Permission is granted to use this product in any fashion and for any purpose, including commercial, with or without this license, provided the following conditions are met. 22 | 23 | 24 | Do whatever you want as long as you do not advertise anything as a version of this product. 25 | 26 | 27 | Any derivative work or copy of this product released under the same name and the same version as this product must constitute a complete unaltered copy of this product without any additions and be accompanied by an unaltered copy of this license. A product may contain an unaltered copy of this product without labelling the copy a derivative work under a different name. 28 | 29 | 30 | If a derivative work is advertised under the same name as this product but under a different version, then the following additional conditions apply. 31 | 32 | 33 | The derivative work must clearly state that it is a derivative of this product and the license of the derivative work must include a clause equivalent to this clause had this license been used. 34 | 35 | 36 | The community of this product has the right to demand that any derivative work, or any derivative work of a derivative in any number of steps, changes the name of the derivative work to a name other than the name of this product or else be subject to copyright and/or trademark infringement. The owners of the derivative work must in such an event be notified about the decision. The community has no right in choosing the new name. 37 | 38 | 39 | Clause 3.2 may be invoked any number of times permitted by the accompanying policy. 40 | 41 | 42 | 43 | 44 |
45 |
46 | The LPCL Development Team. 47 | This section does not apply to work licensed under LPCL other than the license itself. The LPCL Development Team can NOT invoke Clause 3.2 for any work other than the license and the license only. 48 | The LPCL Development Team consists of every real person with an unbanned account on any forum hosted by love2d.org with at least ten posts made a week or more ago. 49 | To invoke Clause 3.2, a post must be made by a team member in a new thread hosted by the domain love2d.org. This post must clearly state the opening of a vote, the expected format of votes, and the effect of the respective answers. 50 | A valid vote is any post in this thread made by a member of the LPCL development team which 51 | 52 | 53 | contains "yes." or "no.", but not both, as a subword of any capitalization, 54 | 55 | 56 | does not have any other valid vote cast by the same member of the same or a newer time, 57 | 58 | 59 | is stamped at most 604800 seconds (one week) older than the post that opened the vote. 60 | 61 | 62 | After one week, the votes are tallied with the winning being either 'yes.' or 'no.', whichever received the most answers. In the case of a draw, clause 3.2 is not invoked. 63 | At most three votes per can be made about the same product and at least one week apart. A vote may concern more than one product. No legal person may open more than four votes per month. 64 |
65 |
66 | The &lovelyrpg; Community. 67 | This section defines the community which this product is owned by. 68 | The &lovelyrpg; Community consists of every real person with an unbanned account on any forum hosted by love2d.org with at least ten posts made a week or more ago and the &lovelyrpg; developers, as described in separate clause in . 69 | To invoke clause 3.2, a post must be made by a team member in a new thread hosted by the domain love2d.org. This post must clearly state the opening of a vote, the expected format of votes, and the effect of the respective answers. 70 | A valid vote is any post in this thread made by a member of the &lovelyrpg; Community which 71 | 72 | 73 | contains "yes." or "no.", but not both, as a subword of any capitalization, 74 | 75 | 76 | does not have any other valid vote cast by the same member of the same or a newer time, 77 | 78 | 79 | is stamped at most 604800 seconds (one week) older than the post that opened the vote. 80 | 81 | 82 | After one week, the votes are tallied with the winning being either 'yes.' or 'no.', whichever received the most answers. In the case of a draw, clause 3.2 is not invoked. 83 | At most three votes per can be made about the same product and at least one week apart. A vote may concern more than one product. No legal person may open more than four votes per month. 84 |
85 |
86 | -------------------------------------------------------------------------------- /proto/proto1/map.lua: -------------------------------------------------------------------------------- 1 | require("lib/essential") 2 | require("lib/math/vector2") 3 | require("lib/projection") 4 | 5 | Map = class("Map") 6 | 7 | Map.TILES = { 8 | { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 9 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 10 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 11 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 12 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 13 | { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 14 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 15 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 16 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 17 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 18 | { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 19 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 20 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 21 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 22 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 23 | { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 24 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 25 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 26 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 27 | { 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, }, 28 | { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 29 | } 30 | 31 | Map.TILE_WIDTH = projection.vz.x+projection.vx.x 32 | Map.TILE_HEIGHT = projection.vz.y-projection.vx.y 33 | Map.TILE_CENTRE = Vector2:new( 34 | (projection.vx.x+projection.vz.x)/2, 35 | (projection.vx.y+projection.vz.y)/2 36 | ) 37 | Map.MAX_SCALE = 2.0 38 | Map.MIN_SCALE = 0.1 39 | Map.WALK_SPEED = 1 40 | Map.RUN_SPEED = 2 41 | Map.SNEAK_SPEED = 0.5 42 | Map.BASE_SPEED = 3 -- m/s in world coords 43 | Map.BORDER = 100 44 | 45 | Map.IMAGES = { 46 | gridsquare = love.graphics.newImage("resources/mapeditor/gridsquare.png"), 47 | tiles = { 48 | love.graphics.newImage("resources/images/tiles/tile_concrete.png"), 49 | love.graphics.newImage("resources/images/tiles/grass_dark.png"), 50 | love.graphics.newImage("resources/images/tiles/grass_light.png"), 51 | love.graphics.newImage("resources/images/tiles/grass_fall.png"), 52 | } 53 | } 54 | 55 | Map.IMAGES.gridsquare:setFilter("nearest","nearest") 56 | Map.IMAGES.tiles[1]:setFilter("nearest","nearest") 57 | Map.IMAGES.tiles[2]:setFilter("nearest","nearest") 58 | Map.IMAGES.tiles[3]:setFilter("nearest","nearest") 59 | Map.IMAGES.tiles[4]:setFilter("nearest","nearest") 60 | 61 | function Map:initialize () 62 | self.view = {} 63 | self.view.position = Vector2:new(0,0) 64 | self.view.scale = 1 65 | self.size = {width=#Map.TILES[1], length=#Map.TILES} 66 | self:lookAt3(self.size.width/2,self.size.length/2) 67 | self.velocity = Vector2:new(0, 0) 68 | self.displayControls = true 69 | self.editorEnabled = false 70 | self.editorSelecting = false 71 | self.canDrag = false 72 | self.mdp = nil -- mouse down position 73 | self.selectedTiles = {} 74 | self.selectedTilesCount = 0 75 | self.character = Character:new() 76 | self.character:gotoState('ArrowKeysMovement') 77 | end 78 | 79 | function Map:lookAt(w) 80 | local v = projection.worldToView(w, self.view) 81 | local centre = Vector2:new( 82 | love.graphics.getWidth()/2, 83 | love.graphics.getHeight()/2 84 | ) 85 | self.view.position=self.view.position+centre-v 86 | end 87 | 88 | function Map:lookAt2(d, level) 89 | self:lookAt({x=d.x,y=level or 0, z=d.y}) 90 | end 91 | 92 | function Map:lookAt3(x, y, level) 93 | self:lookAt({x=x, y=level or 0, z=y}) 94 | end 95 | 96 | function Map:lookingAt(level) 97 | local centre = Vector2:new( 98 | love.graphics.getWidth()/2, 99 | love.graphics.getHeight()/2 100 | ) 101 | return projection.viewToWorld(centre,self.view,level) 102 | end 103 | 104 | function Map:update (dt) 105 | -- Update the map position 106 | if self.canDrag and self.mdp then 107 | local mx, my = love.mouse.getPosition() 108 | local mp = Vector2:new(mx, my) 109 | self.view.position = self.view.position + mp - self.mdp 110 | self.mdp = mp 111 | end 112 | 113 | if self.editorEnabled then 114 | if self.editorSelecting then 115 | local x, y = love.mouse.getPosition() 116 | local t = self:coordsToTile(x, y) 117 | if t.x>=1 and t.y>=1 and t.x<=self.size.width and t.y<=self.size.length then 118 | if self.selectedTiles[t.x] and self.selectedTiles[t.x][t.y] then 119 | self.selectedTilesCount = self.selectedTilesCount-1 120 | else 121 | self.selectedTiles[t.x]=self.selectedTiles[t.x] or {} 122 | self.selectedTiles[t.x][t.y]=true 123 | self.selectedTilesCount = self.selectedTilesCount+1 124 | end 125 | end 126 | end 127 | else 128 | local temp = projection.worldToView2(self.character.position, self.view) 129 | if temp.xright then 134 | self.view.position.x = self.view.position.x + right - temp.x 135 | end 136 | end 137 | if temp.ybottom then 142 | self.view.position.y = self.view.position.y + bottom - temp.y 143 | end 144 | end 145 | 146 | self.character:update(dt) 147 | end 148 | end 149 | 150 | function Map:draw () 151 | love.graphics.push() 152 | love.graphics.scale(self.view.scale) 153 | local mx, my = love.mouse.getPosition() 154 | local mouseover = self:coordsToTile(mx,my) 155 | for i = 1,self.size.width do 156 | for j = 1,self.size.length do 157 | if self:tileIsInView(i, j) then 158 | if self.editorEnabled and not self.canDrag and 159 | mouseover.x==i and mouseover.y==j then 160 | if self:isSelectedTile(i, j) then 161 | love.graphics.setColor(230,210,255) 162 | else 163 | love.graphics.setColor(255,255,255) 164 | end 165 | else 166 | if self:isSelectedTile(i, j) then 167 | love.graphics.setColor(255,255,255) 168 | else 169 | love.graphics.setColor(180,180,180) 170 | end 171 | end 172 | 173 | local x, y = self:tileToCoords(i, j) 174 | if Map.TILES[i] and Map.TILES[i][j] then 175 | love.graphics.draw(Map.IMAGES.tiles[Map.TILES[i][j]], x, y) 176 | else 177 | love.graphics.draw(Map.IMAGES.gridsquare, x, y) 178 | love.graphics.setColor(100,100,100) 179 | love.graphics.print(string.format("%s,%s",i,j), x+10, y+5) 180 | end 181 | end 182 | end 183 | end 184 | love.graphics.setColor(255,255,255) 185 | love.graphics.pop() 186 | 187 | if self.displayControls then 188 | if self.editorEnabled then 189 | love.graphics.setColor(255,255,255,80) 190 | love.graphics.rectangle("fill",10,10,250,267) 191 | love.graphics.setColor(255,255,255,255) 192 | love.graphics.print("Toggle help: h",15,25) 193 | love.graphics.print("Toggle editor: e",15,45) 194 | love.graphics.print("Move: w/a/s/d, arrows or LMB click",15,65) 195 | love.graphics.print("Run: lshift + move",15,85) 196 | love.graphics.print("Sneak: lctrl + move",15,105) 197 | love.graphics.print("Move map: space + LMB drag",15,125) 198 | love.graphics.print("Scale: -/+",15,145) 199 | love.graphics.print("Edit: click a tile and press",15,165) 200 | love.graphics.print(" backspace, delete or ` - remove tile",15,185) 201 | love.graphics.print(" 1 - Concrete",15,200) 202 | love.graphics.print(" 2 - Grass (dark)",15,215) 203 | love.graphics.print(" 3 - Grass (light)",15,230) 204 | love.graphics.print(" 4 - Grass (fall)",15,245) 205 | love.graphics.print(" c - Clear selection",15,260) 206 | else 207 | love.graphics.setColor(255,255,255,80) 208 | love.graphics.rectangle("fill",10,10,250,157) 209 | love.graphics.setColor(255,255,255,255) 210 | love.graphics.print("Toggle help: h",15,25) 211 | love.graphics.print("Toggle editor: e",15,45) 212 | love.graphics.print("Move: w/a/s/d, arrows or LMB click",15,65) 213 | love.graphics.print("Run: lshift + move",15,85) 214 | love.graphics.print("Sneak: lctrl + move",15,105) 215 | love.graphics.print("Move map: space + LMB drag",15,125) 216 | love.graphics.print("Scale: -/+",15,145) 217 | end 218 | 219 | love.graphics.printf(string.format("Scale: %s%%", math.floor(100*self.view.scale)), 10, 25, 220 | love.graphics.getWidth()-20, "right") 221 | end 222 | if not self.editorEnabled then 223 | self.character:draw(self.view) 224 | end 225 | end 226 | 227 | function Map:tileIsInView (tx, ty) 228 | local temp = projection.worldToView3(tx-1,ty-1,self.view) 229 | return temp.x+Map.TILE_WIDTH*self.view.scale>0 and 230 | temp.x0 233 | end 234 | 235 | function Map:isSelectedTile (x, y, checkEditorEnabled) 236 | checkEditorEnabled = checkEditorEnabled or true 237 | if checkEditorEnabled and not self.editorEnabled then 238 | return false 239 | end 240 | return (checkEditorEnabled and self.editorEnabled or true) and 241 | self.selectedTiles[x] and self.selectedTiles[x][y] 242 | end 243 | 244 | function Map:tileToCoords (tx, ty) 245 | local temp = projection.worldToView3(tx-1,ty-1,self.view)/self.view.scale 246 | return math.floor(temp.x), math.floor(temp.y+projection.vx.y) 247 | end 248 | 249 | function Map:coordsToTile (cx, cy) 250 | local temp = projection.viewToWorld2(cx,cy,self.view) 251 | return {x=1+math.floor(temp.x),y=1+math.floor(temp.z)} 252 | end 253 | 254 | function Map:mousepressed (x, y, button) 255 | if button == "l" then 256 | if self.canDrag then 257 | self.mdp = Vector2:new(x, y) 258 | elseif self.editorEnabled then 259 | local t = self:coordsToTile(x, y) 260 | if t.x>=1 and t.y>=1 and t.x<=self.size.width and t.y<=self.size.length then 261 | if self.selectedTiles[t.x] and self.selectedTiles[t.x][t.y] then 262 | self.selectedTiles[t.x][t.y]=nil 263 | self.selectedTilesCount = self.selectedTilesCount-1 264 | else 265 | self.editorSelecting = true 266 | self.selectedTiles[t.x]=self.selectedTiles[t.x] or {} 267 | self.selectedTiles[t.x][t.y]=true 268 | self.selectedTilesCount = self.selectedTilesCount+1 269 | end 270 | end 271 | end 272 | end 273 | if not self.editorEnabled and not self.canDrag then 274 | self.character:mousepressed(x, y, button, self.view) 275 | end 276 | end 277 | 278 | function Map:mousereleased (x, y, button) 279 | if self.canDrag and button == "l" then 280 | self.mdp = nil 281 | end 282 | self.editorSelecting = false 283 | end 284 | 285 | local lastView = {} 286 | 287 | function Map:keypressed (key, unicode) 288 | if key == "h" then 289 | self.displayControls = not self.displayControls 290 | elseif key == "e" then 291 | if self.editorEnabled then 292 | self.view.position.x = lastView.x 293 | self.view.position.y = lastView.y 294 | self.view.scale = lastView.scale 295 | else 296 | lastView.x = self.view.position.x 297 | lastView.y = self.view.position.y 298 | lastView.scale = self.view.scale 299 | end 300 | self.editorEnabled = not self.editorEnabled 301 | elseif key == "=" then 302 | if self.view.scale <= (Map.MAX_SCALE - 0.09) then 303 | local before = projection.worldToView2(self.character.position,self.view) 304 | self.view.scale = self.view.scale + 0.1 305 | local after = projection.worldToView2(self.character.position,self.view) 306 | self.view.position = self.view.position+before-after 307 | end 308 | elseif key == "-" then 309 | if self.view.scale >= (Map.MIN_SCALE + 0.09) then 310 | local before = projection.worldToView2(self.character.position,self.view) 311 | self.view.scale = self.view.scale - 0.1 312 | local after = projection.worldToView2(self.character.position,self.view) 313 | self.view.position = self.view.position+before-after 314 | end 315 | elseif key == "c" then 316 | self.selectedTiles = {} 317 | self.selectedTilesCount=0 318 | elseif key == " " then 319 | self.canDrag = true 320 | end 321 | 322 | if self.editorEnabled then 323 | local t = self.selectedTiles 324 | if self.selectedTilesCount==1 then 325 | local moved = false 326 | for x,ylist in pairs(t) do if not moved then 327 | for y,selected in pairs(ylist) do if not moved then 328 | if selected then moved=true 329 | if key == "up" or key=="w" then 330 | if y-1 >= 1 then 331 | self.selectedTiles[x][y]=nil 332 | self.selectedTiles[x][y-1]=true 333 | self.view.position = self.view.position + projection.vz 334 | end 335 | end 336 | if key == "down" or key=="s" then 337 | if y+1 <= self.size.length then 338 | self.selectedTiles[x][y]=nil 339 | self.selectedTiles[x][y+1]=true 340 | self.view.position = self.view.position - projection.vz 341 | end 342 | end 343 | if key == "left" or key=="a" then 344 | if x-1 >= 1 then 345 | self.selectedTiles[x][y]=nil 346 | self.selectedTiles[x-1]={} 347 | self.selectedTiles[x-1][y]=true 348 | self.view.position = self.view.position + projection.vx 349 | end 350 | end 351 | if key == "right" or key=="d" then 352 | if x+1 <= self.size.width then 353 | self.selectedTiles[x][y]=nil 354 | self.selectedTiles[x+1]={} 355 | self.selectedTiles[x+1][y]=true 356 | self.view.position = self.view.position - projection.vx 357 | end 358 | end 359 | end 360 | end end 361 | end end 362 | end 363 | if t and Map.TILES then 364 | if key == "`" or key == "delete" then 365 | love.event.push("kp", "backspace") 366 | elseif key == "backspace" then 367 | for x,ylist in pairs(t) do 368 | for y,selected in pairs(ylist) do 369 | if selected and Map.TILES[x] then 370 | Map.TILES[x][y]=nil 371 | end 372 | end 373 | end 374 | else 375 | local byte = string.byte(key) 376 | if byte >= 48 and byte <= 57 then 377 | local tile = byte - 48 -- to get numbers 0-9 378 | if Map.IMAGES.tiles[tile] and Map.TILES then 379 | for x,ylist in pairs(t) do 380 | for y,selected in pairs(ylist) do 381 | if selected then 382 | if not Map.TILES[x] then Map.TILES[x] = {} end 383 | Map.TILES[x][y] = tile 384 | end 385 | end 386 | end 387 | end 388 | end 389 | end 390 | end 391 | end 392 | end 393 | 394 | function Map:keyreleased (key) 395 | if key == " " then 396 | self.canDrag = false 397 | self.mdp = nil 398 | end 399 | end 400 | 401 | -------------------------------------------------------------------------------- /doc/appendices/history/prepublish.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 |
7 | Changes before project publishing 8 | It took two months from project start, to moment when we are ready to publish project page on the web. Up to this point, it was mostly lots of work related to deciding goals, concept and focus of game. 9 |
10 | Early History 11 | Project began on 17th-18th January 2010, during these two days it grown from zero to stage of choosing design goals. It was napco's idea to create a Role-playing Game, as stated by him Final Fantasy VI-like. Same day giniu stated to like the idea to some extent, but we should concentrate on doing something new and fresh, not another clone - but we can stick with idea of making RPG (Role Playing Game). Next day napco agreed and giniu proposed to use 10 questions to focus methodology, where key decisions enclosed in 10 important questions answered by the community. 12 | Two out of ten questions were turned into assumptions, it was assumed that designing would start by choosing genre, and genre would be RPG. It was a small assumption to make it keep in original idea. 13 | Still on 18th January, first of eight remaining questions was asked publicly. 14 |
15 |
16 | Road to design goals 17 | First question asked on 18th January had to decide setting we would work in. After few days, on 22nd, by 9 out of 16 votes it was decided that we will use steampunk setting. Next question, asked on 22nd January and available to vote till 2nd February was about key gameplay elements. Three key gameplay elements were chosen, first and most important, exploration picked by 13 out of 20 people. Story was runner-up with 11 out of 20 votes and finally last gameplay element that made it in was non player characters interactions with 10 out of 20 votes. This question also was last to be asked using forum features, following questions were asked using separate third-party polling tool. 18 | From 2nd to 8th February, question about emotions that we want out game to trigger in hero or player was on, results included surprise with 7 out of 11 votes, rejection with 5 out of 11 votes and hope with 4 out of 11 votes. Following two questions were asked in parallel, from 8th to 11th February. During this stage by 5 of 11 votes it was chosen, that magic and technology are in full fusion, with one described in terms of another, and with 5 out of 10 votes, that we would use separate detailed tactical view for fights and consistent view for travel and location exploration, with parts of large world randomly generated. 19 | Next two questions again asked in parallel were on from 11th to 14th February. During this stage it was decided on visual stylistic, i.e., that we would use eastern flavoured realistic comics style. This time 4 out of 8 people voted for it. At same time, there was question about view projection that would be used for travel and location exploration, again 4 out of 8 people voted for one option, the trimetric projection. 20 | The last and one of the most important questions was asked on 14th February and was on for a whole week, till 21st February. Question was about proposed beginning of storyline, and with 4 out of 10 votes it was decided, to go with the story based on amnesia, i.e., "Hero awakes for short while on train crash site, he manages to notice he is highly wounded and someone is trying to rob him. Soon he looses consciousness again to wake up in hospital with amnesia. He is tormented with bad dreams, soon he starts his seek for his own past, uncovering his criminal deeds conflicting with his good subconscious side. His dreams become longer and more clear - seems that he was one that caused train crash. Police starts to take interest in him, but he still cannot remember anything other than causing the crash. Was it really him who did it and is there something behind it?" 21 | To sum up, design goals were decided on 21st February, and current form is defined. 22 |
23 | Design goals 24 | We are making 2D RPG game in steampunk setting in a world, where magic is fully fused with technology (magic described in terms of technology or other way around). In hero or player, during and after playing the game we want to trigger emotions like surprise, rejection and hope. Most important role in gameplay building will lead exploration, NPC interactions and story. We will use separate detailed tactical view for fights, but travel and location exploration will use consistent view using trimetric projection, because of size of the world part of it will be automatically generated. The general stylistic of game will be eastern flavoured realistic comics style. Hero awakes for short while on train crash site, he manages to notice he is highly wounded and someone is trying to rob him. Soon he looses consciousness again to wake up in hospital with amnesia. He is tormented with bad dreams, soon he starts his seek for his own past, uncovering his criminal deeds conflicting with his good subconscious side. His dreams become longer and more clear - seems that he was one that caused train crash. Police starts to take interest in him, but he still cannot remember anything other than causing the crash. Was it really him who did it and is there something behind it? 25 |
26 |
27 |
28 | From goals to focus 29 | After taking short break, followed by a massive amount of work and design goals extensions, on 4th March focus was posted for voting. 30 |
31 | Focus proposition 1 32 | THEGAME is a 2D role-playing game settled in steam-punk world. The magic is fully fused with the technology. Technology is explained in terms of magic and considered as one of the fields of magical studies. 33 | The view in the game will be divided in two groups - separate detailed tactical view for fights, and consistent view using trimetric projection for travel and location exploration, as seen in Fallout 1 and 2 or Sim City 4. Main locations, like cities, dungeons, quest related places will be created by artists and the rest of the world, i.e., wilderness, is going to be automatically generated, because of size of the world. The general stylistic of game will be eastern flavoured realistic comics style, slightly taking from Chinese Manhua comics tradition. 34 | Hero has access to every part of a game world and can travel freely. But it doesn't mean that he has to walk everywhere. He will be able to choose between using public means of transport, like trains, traveling on foot between artists' created locations or using fast travel option. Fast travel option is available only by using road signs, showing directions between locations connected by roads. Fast traveling is risky, hero and/or his team can randomly encounter hostile creatures. Also not all locations are accessible in fast travel mode, the country is not urbanised enough to get everywhere by train. The protagonist must travel on foot to reach some essential parts or hidden, secluded places. This, in conjunction with size of the world, will encourage player to explore. 35 | Another important feature of THEGAME are interactions with NPCs. Hero will have a chance to build a complex interpersonal relationships, while it won't be easy. He will have to prove being reliable first. The way the hero dresses will have an impact on people's reactions. They will be more eager to share their thoughts and troubles with wealthy mysterious stranger than the poor one. Generosity also affects their attitude. Protagonist's lies, while discovered, change the trust people have in him. Hero may team up with someone to complete the task, but it won't mean that certain person will follow him, everywhere he goes. In the game world people have their own lives as well and they won't sacrifice everything so easily. Unless he proofs them being somehow useful, they won't support him. Creating such huge number of various personalities wouldn't be possible, unless minor characters will have generated personalities. For generating personalities we will use personality archetypes, zodiac stereotypes and enneagram, for short time attitude and feelings, we will use biorhythms. 36 | Game's story is built around mysterious events. Protagonist awakes near the crashed train, he's severe wounded and can't move but notices that someone is trying to rob him. Soon he looses consciousness again and wakes up in hospital with amnesia. The only hint about his past he has is a note with name and phone number in his pocket. He is tormented with bad dreams. Soon he starts looking for his own past, uncovering his criminal deeds conflicting with his good subconscious side. His dreams become longer and more clear - seems that he was the one who caused the train crash. Police starts to investigate the case and conceives that he was involved in it, while he still cannot remember anything except for causing the crash. The truth won't be easy to discover. 37 | The country, where action takes place, is isolated from rest of the world. It is governed by Community of the Faithful, church led by orthodox mages. The government is afraid that the world's order is about to change, and carefully watches every revolutionary move. For many years, technology commonly considered as equal to magical science, has been inferior to magic, in fact. But some people fascinated by the power of technology tried to separate those two fields of science. Governors are afraid that technology can eventually became more popular than magic, and people will no longer accept the control and authority of Community of the Faithful. Special services invigilate citizens and report suspicious activities. Single citizen is not aware of the scale of invigilation until he or she is being watched and loses control over his or her life. 38 | The hero's story, although important to him, doesn't play a big role in the world order, it is just a part of larger picture. It's going to have an influence on the behaviour of the people struggling with their own concerns but not on the large-scale world events themselves. 39 | THEGAME will strongly affect on players' emotions. Story totally supports emotions that we want to show - rejection, surprise, yet still some hope. The protagonist will be cast out of the society and scared to discover his own past, the past of cruel and heartless man. 40 | Fights will not be key feature of the game, but will be present. There will be used timed turn based system and action points, that can be spend on movement or other actions. Most skills will evolve by training, the more player use certain skills the better they will get. But there will be more skills than those related to fights, there will be also plenty of practical skills, and what’s new, social skills. All built to same level of complexity. 41 |
42 | Proposed stylistics concept 43 | 44 | 45 | 46 | 47 | 48 |
49 |
50 |
51 | Community reaction 52 | This time community was able to vote in Yes/No poll, but every "No" vote must had been given reasons, to make changes possible. For convenience community couldn't vote against something that had already been decided in process of preparing design goals. Also, people who didn't have anything against it were asked to vote for it, because if they don't care what they will do, they will be happy to do what we proposed. 53 | At this stage, due to uncertainty, if focus would change or not, game hadn't given name yet, setting name was postponed to after focus is accepted. There was 7 votes for proposed focus and 0 votes against it. Following comments were made by community: 54 | 55 | 56 | willurd, 4th March, voted: yes. 57 | I like where this is going. I just have a few thoughts. 58 | I like that NPCs respond to things like lies the hero tells and the clothes he is wearing. The usefulness of that can be illustrated in an example: 59 | After the hero wakes up in the hospital after the train crash he'll be setting out to find answers. He will soon find that not too many people want to talk to him because his clothes are dirty and torn. He'll have to find new clothes before he'll be able to get any answers from anybody that might know something. He was robbed just after the train crash, so he won't have any money. This quest will require him to get money and then purchase clothes, or do something for someone in return for clothes. This job he'll have to do could be any number of things: the tried-and-true "rid our village of some monsters" job, the "deliver this package to some person" job, the "deliver this message to another town" job, etc. The point is that a smart AI opens up storyline possibilities. 60 | As far as the train crash goes, maybe this is implied by the fact that the hero was on a train, but I think it's crucial to the story that the city he's in when he wakes up is a place he's never been to. That way no one (probably) will know him. The story will have to advance somehow, so maybe somebody in that city will know something about the crash or send him on a quest that will "luckily" lead him to information about the crash and about who he his. If that's what happens, our hero might soon discover that nothing is coincidence, that somebody is out there pulling the strings. 61 | Of course, that's just a thought. I'm trying to take in everything that has been discussed up until this point. The focus doc mentioned something about the hero's story playing a non-critical role in the overarching story of the game world, which might negate the previous idea. 62 | I also like the fact that the hero has nightmares. If we have the resources, we could use that to play, for instance, moving comic-style animated sequences that would serve to further the story, give more "character" to the characters, and give us a chance to show the world in a more vibrant manor. 63 | I do have a couple of questions as well. You said the hero has access to every part of the game world, but what makes up that game world? You mentioned that the country where the action takes place is secluded from the rest of the world. Does that mean that no other countries beside that one will play a part in this game? Or has that not been decided yet? 64 | Also, the doc says that battles will not be an important part of the game. I'm wondering, then, what is their purpose for existing in the game at all? I'm not suggesting that you can't have battles in games without them playing a crucial role. I'm just wondering what role they play in "this" game. 65 | My biggest question though is, what really is the point of this game/story? Is it to discover who you once were? Is it to stop some sort of conspiracy (the Community of the Faithful comes to mind...)? Is it to right the wrongs of your past? Is it let go of your past and find a new "you", maybe by helping the people around you? In other words, what is going to keep the player playing? What is their motivation? 66 | Anyway, I voted yes. 67 | 68 | 69 | On 8th March, focus was accepted in current form, yet based on community comments and late ideas, we decided to include following, non-breaking changes and additions: 70 | 71 | nobody knows hero, at least in starting regions, 72 | 73 | 74 | dreams will be visualised as comic strips, 75 | 76 | 77 | action will take place on large island or small continent, 78 | 79 | 80 | obviously fights aren't key gameplay feature, but they play a role in keeping whole game more realistic and balanced, also provide more optional stuff to do while traveling, 81 | 82 | 83 | added automatic conflict resolution as option for the player. 84 | 85 | 86 |
87 |
88 |
89 | Naming game 90 | On 8th March project naming was initiated. This time, no poll with ready-made propositions was made to encourage creativity in people gathered around project. Until 11th March anyone can propose any number of proposed names of project, from which designers pick those that will go into the poll and be voted for. Unfortunately no community propositions were made, so on 11th March, poll with designers ideas for the name was posted. 91 | There were following propositions: 92 | 93 | Grimoire of Steam 94 | 95 | 96 | Concealed 97 | 98 | 99 | Crusade of the Faithful 100 | 101 | 102 | Steampunked 103 | 104 | 105 | After publishing designer proposed names, 6 more were proposed by community: 106 | 107 | A Whiff of Steam (Robin) 108 | 109 | 110 | Unraveled (willurd) 111 | 112 | 113 | Fray (willurd) 114 | 115 | 116 | In the Fray (willurd) 117 | 118 | 119 | The Flying Scotsman (giniust) 120 | 121 | 122 | Flash'n'Steam (giniust) 123 | 124 | 125 | Poll stayed open until 13th March and &whiff; was selected with 5 out of 9 votes in total. 126 |
127 |
128 | -------------------------------------------------------------------------------- /doc/appendices/contributions.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %entities; 5 | ]> 6 | 7 | Contribution guide and project rules 8 | If you want to contribute to &lovelyrpg; project, this is good place to start. 9 |
10 | Project page 11 | Project page is hosted at GitHub (github.com). Project was created using virtual user lovelyrpgcommunity, so full link to it is github.com/lovelyrpgcommunity/a-whiff-of-steam. Community also have virtual Google Gmail account registered with same name (lovelyrpgcommunity@gmail.com) and Google Wave account connected with above e-mail. Currently all developers and contributors are added to wave named &lovelyrpg; --- A Whiff of Steam: Communication Wave. 12 | Project page includes webpage (lovelyrpgcommunity.github.com), tickets, downloads, wiki and Git (git-scm.com) source hosting. All developers have access to most parts of project and can freely modify its content. All contributors and community will have read-only access to project page. 13 | There is also official topic on &love; forum, love2d.org/forum/viewtopic.php?f=5&t=1422, named Official "A Whiff of Steam", the &lovelyrpg; project topic. 14 |
15 |
16 | Project roles and permissions 17 | Non-members in project have very limited permission set. To do anything other than reading you need to be logged in. After logging in, you are allowed to create code comments, create and comment in tickets, and create-edit wiki pages. All developers have full rights, including uploading of downloads, etc. Our previous hosting service (Codaset) offered more control-level, but currently permissions are only enforced by developers, not restricted by system. 18 | Each member can have one or more roles assigned. Here is a list of available special roles: 19 | 20 | Chuck - Supervisor and moderator. Uses special roundhouse kick to motivate lazy members of the team or gets rid of unwanted stuff. Can be selected from people with Lead Developer roles on developers meetings. For security reasons, all Chucks have access to virtual lovelyrpgcommunity Gmail and Wave accounts, and are added to GitHub account as owners. 21 | 22 | 23 | Lead Developer roles: 24 | 25 | Lead Designer - Person responsible for design work and controlling work of designers. Can be selected from people with Designer roles on developers meetings. 26 | 27 | 28 | Lead Artist - Person responsible for artistic work and controlling work of artists. Can be selected from people with Artist roles on developers meetings. 29 | 30 | 31 | Lead Coder - Person responsible for coding work and controlling work of coders. Can be selected from people with Coder roles on developers meetings. 32 | 33 | 34 | Lead Webmaster - Person responsible for webmastering and controlling work of webmasters. Can be selected from people with Webmaster role on developers meetings. 35 | 36 | 37 | 38 | 39 | Above special, honorable roles cannot exist alone. If someone has one of those, he needs to have also at least one of standard developer roles below, thus honorable title is connected with usually more work than plain developers tends to normally do. 40 | Developer roles include: 41 | 42 | Designer roles: 43 | 44 | 45 | Game Designer - Person taking care of design of the game. 46 | 47 | 48 | Writer - Person responsible for creating history and scenario of the game, quests and dialogs. 49 | 50 | 51 | Level Designer - Person who creates levels for the game. 52 | 53 | 54 | Content Editor - Person assembling work of others inside game editor into working game, still have to do lots of decisions when doing this. 55 | 56 | 57 | 58 | 59 | Artist roles: 60 | 61 | 62 | Concept Artist - Person who creates concept arts for documentation. By his or her influence on resulting project, this role is similar to designers role. 63 | 64 | 65 | Content Artist - Artist responsible for animations, terrain and items in released game. 66 | 67 | 68 | Sound Engineer - Person who creates sound effects. 69 | 70 | 71 | Musician - Person who creates music of the game, both background and theme. 72 | 73 | 74 | 75 | 76 | Coder roles: 77 | 78 | 79 | Gameplay Coder - Person who codes gameplay and/or prototypes. By his or her influence on resulting project, this role is again similar to designers role. 80 | 81 | 82 | Engine Coder - Person who program efficient and complex algorithms for the engine, mainly stuff related to graphics and sound. 83 | 84 | 85 | AI Coder - Person dedicated to code NPC behaviour in game. 86 | 87 | 88 | Editor Coder - Person responsible for program editor for final version of the game. 89 | 90 | 91 | 92 | 93 | Webmaster - Person responsible for managing webpage of project. 94 | 95 | 96 | In role section of developer data, highest and lowest role should be specified in form "role/role". For example, "Chuck/Musician", means that someone have all rights of Chuck, Lead Artist and Musician. If both roles are same, it can be specified alone, i.e., "Musician" is a musician without special permissions of Lead Artist. Lead developer or Chuck role shouldn't be specified without lower role, i.e., someone cannot be Lead Artist without being Concept Artist, Content Artist, Sound Engineer or Musician. It's also possible to provide few lower roles if they fit same higher role, like "Lead Artist/Concept Artist, Musician". If someone belongs to two or more Lead Developer roles, he is automatically promoted into Chuck. 97 |
98 |
99 | Repository structure 100 | Repository hosted on GitHub is split into three elements, each in separate directory. 101 |
102 | Documentation (./doc) 103 | Documentation that you are reading, resides in a directory named ./doc. Documentation is made in DocBook V4.5 (docbook.org), with a main file named LovelyRPG.xml. PDF file is made out of sources periodically, using provided Makefile and generate.sh scripts. Generation scripts, use Git logging features and DBLaTeX (dblatex.sf.net) to produce resulting file with automatically inserted latest revision and whole history of commits into detailed revision table. Scripts for now work only on Linux, but PDF file resulting from compilation will be regularly posted to the download section on project page. 104 | To edit DocBook file, we recommend open source WYSIWYG XML editor Serna (syntext.com/products/serna-free). All documentation artwork, that goes into a directory named ./doc/artwork, is best made with applications like Gimp (gimp.org), or MyPaint (mypaint.intilinux.com), both of which support ORA/Open Raster (.ora) file format - but we don't force specific graphical toolchain, it's best left to artist decision. It is recommended to submit your work in ORA and PNG format, where first will be used to modify picture if needed, and second will be for inclusion in documentation itself. 105 |
106 |
107 | Prototypes (./proto) 108 | Before we start working on a final version, series of prototypes will be made. Each prototype will show small parts of functionality, later small prototypes will be merged and tested, to finally build a large prototype of whole game. Prototypes will be built in smaller iterations, each forming larger part of prototype functionality. All prototypes will be stored inside separate subdirectories, that in turn will contain subdirectories related to iterations. 109 | By using prototypes, we can concentrate on one part of functionality at time, and balance it early, allowing for quick adjustments in design before it's fully done. Each prototype should have its own milestone with its own set of tickets, and after it is done, whole history of how it was changing and forming should be written in of this document. 110 | Prototypes are one of first project aims, they should evolve at same time as design documentation. Later, when all gameplay elements will be described and tested by prototypes, it will be known what can be made and what is available to content authors. 111 |
112 |
113 | Final Game (./src) 114 | All files related to final game, that of course will be made using &love; engine (love2d.org), are stored in ./src directory. This directory can hold only what is known to go into final release of game. All prototypes related work, should go into separate directory. 115 | Work on final game, that include final artistic content and optimized, specialized engine with an editor, should start after design part of documentation and prototypes are finished. That way, we can reach for a lot more playable and consistent scenario and content, that when it wouldn't be known what features will game have. It will also spare us need to rework what's already done. Scenario, artwork, engine and editor should be finished at same time, to allow content editor to assemble whole project into final game, that will be then rigorously tested. 116 |
117 |
118 |
119 | Contributions 120 | All contributions are welcome. If you have no idea what you can do, you can check milestones on project page (see ), but you are free to make up something else. To do your first contribution, you need to send your work to one of developers, best to one responsible for a certain role, for example if you are sending art, send it to artist. If you don't know who send to your work, send it to developers with role set as "Designer". 121 | You might also create a ticket with your contribution, you must be logged in to GitHub to do so, but it would allow you to keep track on state of your contribution. Other way of getting in touch with developers about your contribution, is through our official topic on the &love; forum (see ). Note, that when creating ticket you need to pick right labels for it. There are five labels that contributors can pick: contribution art, contribution code, contribution design, contribution webpage and idea bucket. The last one is special place for not specified ideas, while the contribution labels are for patches, pictures, specific ready to use content. Usual life of contribution would be creation of idea, followed by discussion and implementing with help of some developer, then building contribution our of it and merging it. 122 | If your contribution will be accepted, it will be fused into main repository and credited to you. For crediting your work, you will be asked for your &love; forum nick, role you want to be listed with, and your real name. You, as long as you are contributor, don't have to provide GitHub nick - even if you have one - that's the way we distinct developers from contributors in author list. Instead, in place of GitHub nick, number of your contributions will be listed (up to 3 it will be marked as stars, then as star with number of contributions). You might also omit your real name, but then you lock yourself a way of promotion to developer. 123 | Contributions are best sent as patches generated from Git or as pull request from your version of repository. We accept all ways to contribute to project, that are described and recommended in Git documentation (see book.git-scm.com, progit.org/book, or other documentation linked from git-scm.org/documentation). 124 | Data of contributors and developers are stored in authors.xml file as othercredit tags. Most important fields are contrib tag containing role and firstname and surname tags describing real personal data. All nicks and contributions are listed in othercredit nick. In case only one nick is given, it's used as both forum and GitHub nick. If different data are required for both, slash must be used. For example, "nick/**" describes a contributor with two contributions and "nick1/nick2" describes a developer with different nicks on the forum and GitHub. 125 |
126 |
127 | Developers 128 | To become a developer, you need to have valid accounts on both, &love; forum and GitHub project hosting (see ), and get three accepted contributions (see ) - then opt in to become official developer. Alternatively, if you have 10 or more posts on the forum older than week, it is enough to have one accepted contribution instead of three. You will be asked for your GitHub nick and given write access to main repository. 129 | Each week most developers are asked to attend to on-line meeting, where most hot stuff is discussed. For meeting Google Wave or IRC will be used, with first preferred because of its history features. Date and hour of next meeting is set on previous, to fit plans of most developers. Those meetings are also important for people wanting promotions, this is moment, when developer can become Lead developer, or Lead developer can become Chuck. Voting should be done using Yes/No poll to which each developer can answer, to receive promotion, more than 50% of votes is required. 130 | Meetings should always be announced and reminded about in official topic on &love; forum. 131 |
132 |
133 | Ticketing system 134 | All work should be controlled and described in ticketing system. 135 | When creating ticket, title and exact description should be entered. All other features are implemented using labels. There are labels which can be applied by anyone, labels that can be applied by contributors and labels to be applied by developers. Anyone can apply idea bucket label. This label allows us to group all ideas that are not shaped enough to be posted yet, or their author does not know how to implement them. Contributors can apply labels contribution art, contribution code, contribution design and contribution webpage. Developers shouldn't apply those labels, unless ticket was created by someone else and reporting person forgot to apply the label. This label informs developers that inside this ticket, there is contribution ready for inclusion and person with certain role will take care of this ticket, that's why it is important to apply the proper label. Finally, developers can apply labels that describe a milestone, owner, priority and type. All those labels are made using two or more words, first describing label types. For example, there is type bug, type enhancement and type task to describe what is being considered in the ticket. The priority high, priority low and priority medium is also important field. If priority is omitted, it is assumed to be medium. If type of ticket is omitted, it is assumed to be a task. Finally, there are milestone tickets formed in the way: milestone [number] name and owner ticket formed in the way: owner [nick on github]. If milestone is missing it means that ticket is prepared for future, but it isn't decided yet when it have to be finished. If owner is missing, it means the ticket is unassigned to nobody and can be taken by any developer who wants to spend time on it. 136 |
137 |
138 | Decision procedures 139 | Sometimes there are some decisions to be made, when developers cannot decide on what option to pick, or just want to hear community opinion, poll should be created. 140 | At such polls, anyone with account on &love; forum can vote. Poll should stay on at least 3 days, and closing day should be posted along with poll opening. Fact of opening poll should be noted in . To all such polls, developers should vote last to not influence results, and they should note down their votes in project history documentation. 141 | In case of ties, any Developer vote for option that tied, counts as two. In case it doesn't resolve ties, Lead Developers decisions for votes that tied, counts as four votes. In case this still doesn't resolve conflict, option is picked by Chucks from tied options, again if this doesn't bring resolution, final decision is randomly taken from all that tied. Whole procedure of selecting result, should be appropriately noted in project history documentation. 142 |
143 |
144 | Coding standards 145 | TODO: Decide on first developer meeting. 146 | Till then, for documentation apply to what is a default for WYSIWYG editor of choice, Serna, i.e., current docs style. What is sure, we should use only UTF-8 character encoding and UNIX-style line endings. 147 |
148 |
149 | --------------------------------------------------------------------------------