├── .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 |
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 |
--------------------------------------------------------------------------------